Save This Page
Home » Xerces-J-src.2.9.1 » org.apache.xml » serialize » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   // Sep 14, 2000:
   19   //  Fixed problem with namespace handling. Contributed by
   20   //  David Blondeau <blondeau@intalio.com>
   21   // Sep 14, 2000:
   22   //  Fixed serializer to report IO exception directly, instead at
   23   //  the end of document processing.
   24   //  Reported by Patrick Higgins <phiggins@transzap.com>
   25   // Aug 21, 2000:
   26   //  Fixed bug in startDocument not calling prepare.
   27   //  Reported by Mikael Staldal <d96-mst-ingen-reklam@d.kth.se>
   28   // Aug 21, 2000:
   29   //  Added ability to omit DOCTYPE declaration.
   30   
   31   package org.apache.xml.serialize;
   32   
   33   import java.io.IOException;
   34   import java.io.OutputStream;
   35   import java.io.Writer;
   36   import java.util.Enumeration;
   37   
   38   import org.apache.xerces.dom.DOMMessageFormatter;
   39   import org.apache.xerces.util.NamespaceSupport;
   40   import org.apache.xerces.util.SymbolTable;
   41   import org.apache.xerces.util.XMLChar;
   42   import org.apache.xerces.util.XMLSymbols;
   43   import org.apache.xerces.xni.NamespaceContext;
   44   import org.w3c.dom.Attr;
   45   import org.w3c.dom.DOMError;
   46   import org.w3c.dom.Element;
   47   import org.w3c.dom.NamedNodeMap;
   48   import org.w3c.dom.Node;
   49   import org.w3c.dom.traversal.NodeFilter;
   50   import org.xml.sax.AttributeList;
   51   import org.xml.sax.Attributes;
   52   import org.xml.sax.SAXException;
   53   import org.xml.sax.helpers.AttributesImpl;
   54   
   55   /**
   56    * Implements an XML serializer supporting both DOM and SAX pretty
   57    * serializing. For usage instructions see {@link Serializer}.
   58    * <p>
   59    * If an output stream is used, the encoding is taken from the
   60    * output format (defaults to <tt>UTF-8</tt>). If a writer is
   61    * used, make sure the writer uses the same encoding (if applies)
   62    * as specified in the output format.
   63    * <p>
   64    * The serializer supports both DOM and SAX. SAX serializing is done by firing
   65    * SAX events and using the serializer as a document handler. DOM serializing is done
   66    * by calling {@link #serialize(Document)} or by using DOM Level 3  
   67    * {@link org.w3c.dom.ls.LSSerializer} and
   68    * serializing with {@link org.w3c.dom.ls.LSSerializer#write},
   69    * {@link org.w3c.dom.ls.LSSerializer#writeToString}.
   70    * <p>
   71    * If an I/O exception occurs while serializing, the serializer
   72    * will not throw an exception directly, but only throw it
   73    * at the end of serializing (either DOM or SAX's {@link
   74    * org.xml.sax.DocumentHandler#endDocument}.
   75    * <p>
   76    * For elements that are not specified as whitespace preserving,
   77    * the serializer will potentially break long text lines at space
   78    * boundaries, indent lines, and serialize elements on separate
   79    * lines. Line terminators will be regarded as spaces, and
   80    * spaces at beginning of line will be stripped.
   81    * 
   82    * @deprecated This class was deprecated in Xerces 2.9.0. It is recommended 
   83    * that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation 
   84    * API for XML (TrAX) for serializing XML. See the Xerces documentation for more 
   85    * information.
   86    * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
   87    * @author <a href="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
   88    * @author Elena Litani IBM
   89    * @version $Revision: 476047 $ $Date: 2006-11-16 23:27:45 -0500 (Thu, 16 Nov 2006) $
   90    * @see Serializer
   91    */
   92   public class XMLSerializer
   93   extends BaseMarkupSerializer {
   94   
   95       //
   96       // constants
   97       //
   98   
   99       protected static final boolean DEBUG = false;
  100   
  101       // 
  102       // data
  103       //
  104   
  105       // 
  106       // DOM Level 3 implementation: variables intialized in DOMSerializerImpl
  107       // 
  108   
  109       /** stores namespaces in scope */
  110       protected NamespaceSupport fNSBinder;
  111   
  112       /** stores all namespace bindings on the current element */
  113       protected NamespaceSupport fLocalNSBinder;
  114   
  115       /** symbol table for serialization */
  116       protected SymbolTable fSymbolTable;    
  117   
  118       protected final static String PREFIX = "NS";
  119   
  120       /**
  121        * Controls whether namespace fixup should be performed during
  122        * the serialization. 
  123        * NOTE: if this field is set to true the following 
  124        * fields need to be initialized: fNSBinder, fLocalNSBinder, fSymbolTable, 
  125        * XMLSymbols.EMPTY_STRING, fXmlSymbol, fXmlnsSymbol
  126        */
  127       protected boolean fNamespaces = false;
  128   
  129       /**
  130        * Controls whether namespace prefixes will be printed out during serialization
  131        */
  132       protected boolean fNamespacePrefixes = true;
  133   	
  134   
  135       private boolean fPreserveSpace;
  136   
  137   
  138       /**
  139        * Constructs a new serializer. The serializer cannot be used without
  140        * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
  141        * first.
  142        */
  143       public XMLSerializer() {
  144           super( new OutputFormat( Method.XML, null, false ) );
  145       }
  146   
  147   
  148       /**
  149        * Constructs a new serializer. The serializer cannot be used without
  150        * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
  151        * first.
  152        */
  153       public XMLSerializer( OutputFormat format ) {
  154           super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  155           _format.setMethod( Method.XML );
  156       }
  157   
  158   
  159       /**
  160        * Constructs a new serializer that writes to the specified writer
  161        * using the specified output format. If <tt>format</tt> is null,
  162        * will use a default output format.
  163        *
  164        * @param writer The writer to use
  165        * @param format The output format to use, null for the default
  166        */
  167       public XMLSerializer( Writer writer, OutputFormat format ) {
  168           super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  169           _format.setMethod( Method.XML );
  170           setOutputCharStream( writer );
  171       }
  172   
  173   
  174       /**
  175        * Constructs a new serializer that writes to the specified output
  176        * stream using the specified output format. If <tt>format</tt>
  177        * is null, will use a default output format.
  178        *
  179        * @param output The output stream to use
  180        * @param format The output format to use, null for the default
  181        */
  182       public XMLSerializer( OutputStream output, OutputFormat format ) {
  183           super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  184           _format.setMethod( Method.XML );
  185           setOutputByteStream( output );
  186       }
  187   
  188   
  189       public void setOutputFormat( OutputFormat format ) {
  190           super.setOutputFormat( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  191       }
  192   
  193   
  194       /**
  195        * This methods turns on namespace fixup algorithm during
  196        * DOM serialization.
  197        * @see org.w3c.dom.ls.LSSerializer
  198        * 
  199        * @param namespaces
  200        */
  201       public void setNamespaces (boolean namespaces){
  202           fNamespaces = namespaces;
  203           if (fNSBinder == null) {
  204               fNSBinder = new NamespaceSupport();
  205               fLocalNSBinder = new NamespaceSupport();
  206               fSymbolTable = new SymbolTable();
  207           }
  208       }
  209   
  210       //-----------------------------------------//
  211       // SAX content handler serializing methods //
  212       //-----------------------------------------//
  213   
  214   
  215       public void startElement( String namespaceURI, String localName,
  216                                 String rawName, Attributes attrs )
  217       throws SAXException
  218       {
  219           int          i;
  220           boolean      preserveSpace;
  221           ElementState state;
  222           String       name;
  223           String       value;
  224   
  225           if (DEBUG) {
  226               System.out.println("==>startElement("+namespaceURI+","+localName+
  227                                  ","+rawName+")");
  228           }
  229   
  230           try {
  231               if (_printer == null) {
  232                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
  233                   throw new IllegalStateException(msg);
  234               }
  235   
  236               state = getElementState();
  237               if (isDocumentState()) {
  238                   // If this is the root element handle it differently.
  239                   // If the first root element in the document, serialize
  240                   // the document's DOCTYPE. Space preserving defaults
  241                   // to that of the output format.
  242                   if (! _started)
  243                       startDocument( ( localName == null || localName.length() == 0 ) ? rawName : localName );
  244               } else {
  245                   // For any other element, if first in parent, then
  246                   // close parent's opening tag and use the parnet's
  247                   // space preserving.
  248                   if (state.empty)
  249                       _printer.printText( '>' );
  250                   // Must leave CData section first
  251                   if (state.inCData) {
  252                       _printer.printText( "]]>" );
  253                       state.inCData = false;
  254                   }
  255                   // Indent this element on a new line if the first
  256                   // content of the parent element or immediately
  257                   // following an element or a comment
  258                   if (_indenting && ! state.preserveSpace &&
  259                       ( state.empty || state.afterElement || state.afterComment))
  260                       _printer.breakLine();
  261               }
  262               preserveSpace = state.preserveSpace;
  263   
  264               //We remove the namespaces from the attributes list so that they will
  265               //be in _prefixes
  266               attrs = extractNamespaces(attrs);
  267   
  268               // Do not change the current element state yet.
  269               // This only happens in endElement().
  270               if (rawName == null || rawName.length() == 0) {
  271                   if (localName == null) {
  272                       String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoName", null);
  273                       throw new SAXException(msg);
  274                   }
  275                   if (namespaceURI != null && ! namespaceURI.equals( "" )) {
  276                       String prefix;
  277                       prefix = getPrefix( namespaceURI );
  278                       if (prefix != null && prefix.length() > 0) {
  279                           rawName = prefix + ":" + localName;
  280                       } 
  281                       else {
  282                           rawName = localName;
  283                       }
  284                   } 
  285                   else {
  286                       rawName = localName;
  287                   }
  288               }
  289   
  290               _printer.printText( '<' );
  291               _printer.printText( rawName );
  292               _printer.indent();
  293   
  294               // For each attribute print it's name and value as one part,
  295               // separated with a space so the element can be broken on
  296               // multiple lines.
  297               if (attrs != null) {
  298                   for (i = 0 ; i < attrs.getLength() ; ++i) {
  299                       _printer.printSpace();
  300   
  301                       name = attrs.getQName( i );
  302                       if (name != null && name.length() == 0) {
  303                           String prefix;
  304                           String attrURI;
  305   
  306                           name = attrs.getLocalName( i );
  307                           attrURI = attrs.getURI( i );
  308                           if (( attrURI != null && attrURI.length() != 0 ) &&
  309                               ( namespaceURI == null || namespaceURI.length() == 0 ||
  310                                 ! attrURI.equals( namespaceURI ) )) {
  311                               prefix = getPrefix( attrURI );
  312                               if (prefix != null && prefix.length() > 0)
  313                                   name = prefix + ":" + name;
  314                           }
  315                       }
  316   
  317                       value = attrs.getValue( i );
  318                       if (value == null)
  319                           value = "";
  320                       _printer.printText( name );
  321                       _printer.printText( "=\"" );
  322                       printEscaped( value );
  323                       _printer.printText( '"' );
  324   
  325                       // If the attribute xml:space exists, determine whether
  326                       // to preserve spaces in this and child nodes based on
  327                       // its value.
  328                       if (name.equals( "xml:space" )) {
  329                           if (value.equals( "preserve" ))
  330                               preserveSpace = true;
  331                           else
  332                               preserveSpace = _format.getPreserveSpace();
  333                       }
  334                   }
  335               }
  336   
  337               if (_prefixes != null) {
  338                   Enumeration keys;
  339   
  340                   keys = _prefixes.keys();
  341                   while (keys.hasMoreElements()) {
  342                       _printer.printSpace();
  343                       value = (String) keys.nextElement();
  344                       name = (String) _prefixes.get( value );
  345                       if (name.length() == 0) {
  346                           _printer.printText( "xmlns=\"" );
  347                           printEscaped( value );
  348                           _printer.printText( '"' );
  349                       } else {
  350                           _printer.printText( "xmlns:" );
  351                           _printer.printText( name );
  352                           _printer.printText( "=\"" );
  353                           printEscaped( value );
  354                           _printer.printText( '"' );
  355                       }
  356                   }
  357               }
  358   
  359               // Now it's time to enter a new element state
  360               // with the tag name and space preserving.
  361               // We still do not change the curent element state.
  362               state = enterElementState( namespaceURI, localName, rawName, preserveSpace );
  363               name = ( localName == null || localName.length() == 0 ) ? rawName : namespaceURI + "^" + localName;
  364               state.doCData = _format.isCDataElement( name );
  365               state.unescaped = _format.isNonEscapingElement( name );
  366           } catch (IOException except) {
  367               throw new SAXException( except );
  368           }
  369       }
  370   
  371   
  372       public void endElement( String namespaceURI, String localName,
  373                               String rawName )
  374       throws SAXException
  375       {
  376           try {
  377               endElementIO( namespaceURI, localName, rawName );
  378           } catch (IOException except) {
  379               throw new SAXException( except );
  380           }
  381       }
  382   
  383   
  384       public void endElementIO( String namespaceURI, String localName,
  385                                 String rawName )
  386       throws IOException
  387       {
  388           ElementState state;
  389           if (DEBUG) {
  390               System.out.println("==>endElement: " +rawName);
  391           }
  392           // Works much like content() with additions for closing
  393           // an element. Note the different checks for the closed
  394           // element's state and the parent element's state.
  395           _printer.unindent();
  396           state = getElementState();
  397           if (state.empty) {
  398               _printer.printText( "/>" );
  399           } else {
  400               // Must leave CData section first
  401               if (state.inCData)
  402                   _printer.printText( "]]>" );
  403               // This element is not empty and that last content was
  404               // another element, so print a line break before that
  405               // last element and this element's closing tag.
  406               if (_indenting && ! state.preserveSpace && (state.afterElement || state.afterComment))
  407                   _printer.breakLine();
  408               _printer.printText( "</" );
  409               _printer.printText( state.rawName );
  410               _printer.printText( '>' );
  411           }
  412           // Leave the element state and update that of the parent
  413           // (if we're not root) to not empty and after element.
  414           state = leaveElementState();
  415           state.afterElement = true;
  416           state.afterComment = false;
  417           state.empty = false;
  418           if (isDocumentState())
  419               _printer.flush();
  420       }
  421   
  422   
  423       //------------------------------------------//
  424       // SAX document handler serializing methods //
  425       //------------------------------------------//
  426   
  427   
  428       public void startElement( String tagName, AttributeList attrs )
  429       throws SAXException
  430       {
  431           int          i;
  432           boolean      preserveSpace;
  433           ElementState state;
  434           String       name;
  435           String       value;
  436   
  437   
  438           if (DEBUG) {
  439               System.out.println("==>startElement("+tagName+")");
  440           }
  441   
  442           try {
  443               if (_printer == null) {
  444                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
  445                   throw new IllegalStateException(msg);
  446               }
  447   
  448               state = getElementState();
  449               if (isDocumentState()) {
  450                   // If this is the root element handle it differently.
  451                   // If the first root element in the document, serialize
  452                   // the document's DOCTYPE. Space preserving defaults
  453                   // to that of the output format.
  454                   if (! _started)
  455                       startDocument( tagName );
  456               } else {
  457                   // For any other element, if first in parent, then
  458                   // close parent's opening tag and use the parnet's
  459                   // space preserving.
  460                   if (state.empty)
  461                       _printer.printText( '>' );
  462                   // Must leave CData section first
  463                   if (state.inCData) {
  464                       _printer.printText( "]]>" );
  465                       state.inCData = false;
  466                   }
  467                   // Indent this element on a new line if the first
  468                   // content of the parent element or immediately
  469                   // following an element.
  470                   if (_indenting && ! state.preserveSpace &&
  471                       ( state.empty || state.afterElement || state.afterComment))
  472                       _printer.breakLine();
  473               }
  474               preserveSpace = state.preserveSpace;
  475   
  476               // Do not change the current element state yet.
  477               // This only happens in endElement().
  478   
  479               _printer.printText( '<' );
  480               _printer.printText( tagName );
  481               _printer.indent();
  482   
  483               // For each attribute print it's name and value as one part,
  484               // separated with a space so the element can be broken on
  485               // multiple lines.
  486               if (attrs != null) {
  487                   for (i = 0 ; i < attrs.getLength() ; ++i) {
  488                       _printer.printSpace();
  489                       name = attrs.getName( i );
  490                       value = attrs.getValue( i );
  491                       if (value != null) {
  492                           _printer.printText( name );
  493                           _printer.printText( "=\"" );
  494                           printEscaped( value );
  495                           _printer.printText( '"' );
  496                       }
  497   
  498                       // If the attribute xml:space exists, determine whether
  499                       // to preserve spaces in this and child nodes based on
  500                       // its value.
  501                       if (name.equals( "xml:space" )) {
  502                           if (value.equals( "preserve" ))
  503                               preserveSpace = true;
  504                           else
  505                               preserveSpace = _format.getPreserveSpace();
  506                       }
  507                   }
  508               }
  509               // Now it's time to enter a new element state
  510               // with the tag name and space preserving.
  511               // We still do not change the curent element state.
  512               state = enterElementState( null, null, tagName, preserveSpace );
  513               state.doCData = _format.isCDataElement( tagName );
  514               state.unescaped = _format.isNonEscapingElement( tagName );
  515           } catch (IOException except) {
  516               throw new SAXException( except );
  517           }
  518   
  519       }
  520   
  521   
  522       public void endElement( String tagName )
  523       throws SAXException
  524       {
  525           endElement( null, null, tagName );
  526       }
  527   
  528   
  529   
  530       //------------------------------------------//
  531       // Generic node serializing methods methods //
  532       //------------------------------------------//
  533   
  534   
  535       /**
  536        * Called to serialize the document's DOCTYPE by the root element.
  537        * The document type declaration must name the root element,
  538        * but the root element is only known when that element is serialized,
  539        * and not at the start of the document.
  540        * <p>
  541        * This method will check if it has not been called before ({@link #_started}),
  542        * will serialize the document type declaration, and will serialize all
  543        * pre-root comments and PIs that were accumulated in the document
  544        * (see {@link #serializePreRoot}). Pre-root will be serialized even if
  545        * this is not the first root element of the document.
  546        */
  547       protected void startDocument( String rootTagName )
  548       throws IOException
  549       {
  550           int    i;
  551           String dtd;
  552   
  553           dtd = _printer.leaveDTD();
  554           if (! _started) {
  555   
  556               if (! _format.getOmitXMLDeclaration()) {
  557                   StringBuffer    buffer;
  558   
  559                   // Serialize the document declaration appreaing at the head
  560                   // of very XML document (unless asked not to).
  561                   buffer = new StringBuffer( "<?xml version=\"" );
  562                   if (_format.getVersion() != null)
  563                       buffer.append( _format.getVersion() );
  564                   else
  565                       buffer.append( "1.0" );
  566                   buffer.append( '"' );
  567                   String format_encoding =  _format.getEncoding();
  568                   if (format_encoding != null) {
  569                       buffer.append( " encoding=\"" );
  570                       buffer.append( format_encoding );
  571                       buffer.append( '"' );
  572                   }
  573                   if (_format.getStandalone() && _docTypeSystemId == null &&
  574                       _docTypePublicId == null)
  575                       buffer.append( " standalone=\"yes\"" );
  576                   buffer.append( "?>" );
  577                   _printer.printText( buffer );
  578                   _printer.breakLine();
  579               }
  580   
  581               if (! _format.getOmitDocumentType()) {
  582                   if (_docTypeSystemId != null) {
  583                       // System identifier must be specified to print DOCTYPE.
  584                       // If public identifier is specified print 'PUBLIC
  585                       // <public> <system>', if not, print 'SYSTEM <system>'.
  586                       _printer.printText( "<!DOCTYPE " );
  587                       _printer.printText( rootTagName );
  588                       if (_docTypePublicId != null) {
  589                           _printer.printText( " PUBLIC " );
  590                           printDoctypeURL( _docTypePublicId );
  591                           if (_indenting) {
  592                               _printer.breakLine();
  593                               for (i = 0 ; i < 18 + rootTagName.length() ; ++i)
  594                                   _printer.printText( " " );
  595                           } else
  596                               _printer.printText( " " );
  597                           printDoctypeURL( _docTypeSystemId );
  598                       } else {
  599                           _printer.printText( " SYSTEM " );
  600                           printDoctypeURL( _docTypeSystemId );
  601                       }
  602   
  603                       // If we accumulated any DTD contents while printing.
  604                       // this would be the place to print it.
  605                       if (dtd != null && dtd.length() > 0) {
  606                           _printer.printText( " [" );
  607                           printText( dtd, true, true );
  608                           _printer.printText( ']' );
  609                       }
  610   
  611                       _printer.printText( ">" );
  612                       _printer.breakLine();
  613                   } else if (dtd != null && dtd.length() > 0) {
  614                       _printer.printText( "<!DOCTYPE " );
  615                       _printer.printText( rootTagName );
  616                       _printer.printText( " [" );
  617                       printText( dtd, true, true );
  618                       _printer.printText( "]>" );
  619                       _printer.breakLine();
  620                   }
  621               }
  622           }
  623           _started = true;
  624           // Always serialize these, even if not te first root element.
  625           serializePreRoot();
  626       }
  627   
  628   
  629       /**
  630        * Called to serialize a DOM element. Equivalent to calling {@link
  631        * #startElement}, {@link #endElement} and serializing everything
  632        * inbetween, but better optimized.
  633        */
  634       protected void serializeElement( Element elem )
  635       throws IOException
  636       {
  637           Attr         attr;
  638           NamedNodeMap attrMap;
  639           int          i;
  640           Node         child;
  641           ElementState state;
  642           String       name;
  643           String       value;
  644           String       tagName;
  645   
  646           String prefix, localUri;
  647           String uri;
  648           if (fNamespaces) {
  649               // local binder stores namespace declaration
  650               // that has been printed out during namespace fixup of
  651               // the current element
  652               fLocalNSBinder.reset();
  653   
  654               // add new namespace context        
  655               fNSBinder.pushContext();
  656           }
  657   
  658           if (DEBUG) {
  659               System.out.println("==>startElement: " +elem.getNodeName() +" ns="+elem.getNamespaceURI());
  660           }
  661           tagName = elem.getTagName();
  662           state = getElementState();
  663           if (isDocumentState()) {
  664               // If this is the root element handle it differently.
  665               // If the first root element in the document, serialize
  666               // the document's DOCTYPE. Space preserving defaults
  667               // to that of the output format.
  668   
  669               if (! _started) {
  670                   startDocument( tagName);
  671               }
  672           } else {
  673               // For any other element, if first in parent, then
  674               // close parent's opening tag and use the parent's
  675               // space preserving.
  676               if (state.empty)
  677                   _printer.printText( '>' );
  678               // Must leave CData section first
  679               if (state.inCData) {
  680                   _printer.printText( "]]>" );
  681                   state.inCData = false;
  682               }
  683               // Indent this element on a new line if the first
  684               // content of the parent element or immediately
  685               // following an element.
  686               if (_indenting && ! state.preserveSpace &&
  687                   ( state.empty || state.afterElement || state.afterComment))
  688                   _printer.breakLine();
  689           }
  690   
  691           // Do not change the current element state yet.
  692           // This only happens in endElement().
  693           fPreserveSpace = state.preserveSpace;
  694   
  695   
  696           int length = 0;
  697           attrMap = null;
  698           // retrieve attributes 
  699           if (elem.hasAttributes()) {
  700               attrMap = elem.getAttributes();
  701               length = attrMap.getLength();
  702           }
  703   
  704           if (!fNamespaces) { // no namespace fixup should be performed                    
  705   
  706               // serialize element name
  707               _printer.printText( '<' );
  708               _printer.printText( tagName );
  709               _printer.indent();
  710   
  711               // For each attribute print it's name and value as one part,
  712               // separated with a space so the element can be broken on
  713               // multiple lines.
  714               for ( i = 0 ; i < length ; ++i ) {
  715                   attr = (Attr) attrMap.item( i );
  716                   name = attr.getName();
  717                   value = attr.getValue();
  718                   if ( value == null )
  719                       value = "";
  720                   printAttribute (name, value, attr.getSpecified(), attr);
  721               }
  722           } else { // do namespace fixup
  723   
  724               // REVISIT: some optimization could probably be done to avoid traversing 
  725               //          attributes twice.
  726               //
  727   
  728               // ---------------------------------------
  729               // record all valid namespace declarations
  730               // before attempting to fix element's namespace
  731               // ---------------------------------------
  732   
  733               for (i = 0;i < length;i++) {
  734   
  735                   attr = (Attr) attrMap.item( i );
  736                   uri = attr.getNamespaceURI();
  737                   // check if attribute is a namespace decl 
  738                   if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
  739   
  740                       value = attr.getNodeValue();
  741                       if (value == null) {
  742                           value=XMLSymbols.EMPTY_STRING;
  743                       }
  744   
  745                       if (value.equals(NamespaceContext.XMLNS_URI)) {
  746                           if (fDOMErrorHandler != null) {
  747                               String msg = DOMMessageFormatter.formatMessage(
  748                                   DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null );
  749                               modifyDOMError(msg,  DOMError.SEVERITY_ERROR, null, attr);
  750                               boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
  751                               if (!continueProcess) {
  752                                   // stop the namespace fixup and validation
  753               	                throw new RuntimeException( 
  754               	                    DOMMessageFormatter.formatMessage(
  755               	                    DOMMessageFormatter.SERIALIZER_DOMAIN,
  756               	                    "SerializationStopped", null));
  757                               }
  758                           }
  759                       } else {
  760                           prefix = attr.getPrefix();
  761                           prefix = (prefix == null || 
  762                                     prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  763                           String localpart = fSymbolTable.addSymbol( attr.getLocalName());
  764                           if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
  765                               value = fSymbolTable.addSymbol(value);
  766                               // record valid decl
  767                               if (value.length() != 0) {
  768                                   fNSBinder.declarePrefix(localpart, value);
  769                               } else {
  770                                   // REVISIT: issue error on invalid declarations
  771                                   //          xmlns:foo = ""
  772                               }
  773                               continue;
  774                           }
  775                           // xmlns --- empty prefix is always bound ("" or some string)
  776                           value = fSymbolTable.addSymbol(value);
  777                           fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, value);
  778                           continue;
  779                       }  // end-else: valid declaration
  780                   } // end-if: namespace declaration 
  781               }  // end-for
  782   
  783               //-----------------------
  784               // get element uri/prefix
  785               //-----------------------
  786               uri = elem.getNamespaceURI();            
  787               prefix = elem.getPrefix();
  788   
  789               //----------------------
  790               // output element name
  791               //----------------------
  792               // REVISIT: this could be removed if we always convert empty string to null
  793               //          for the namespaces.
  794               if ((uri !=null && prefix !=null ) && uri.length() == 0 && prefix.length()!=0) {
  795                   // uri is an empty string and element has some prefix
  796                   // the namespace alg later will fix up the namespace attributes
  797                   // remove element prefix 
  798                   prefix = null; 
  799                   _printer.printText( '<' );
  800                   _printer.printText( elem.getLocalName() );
  801                   _printer.indent();
  802               } else {
  803                   _printer.printText( '<' );
  804                   _printer.printText( tagName );
  805                   _printer.indent();
  806               }
  807   
  808   
  809               // ---------------------------------------------------------
  810               // Fix up namespaces for element: per DOM L3 
  811               // Need to consider the following cases:
  812               //
  813               // case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/> 
  814               // Assume "foo", "ns1" are declared on the parent. We should not miss 
  815               // redeclaration for both "ns1" and default namespace. To solve this 
  816               // we add a local binder that stores declaration only for current element.
  817               // This way we avoid outputing duplicate declarations for the same element
  818               // as well as we are not omitting redeclarations.
  819               //
  820               // case 2: <elem xmlns="" xmlns="default"/> 
  821               // We need to bind default namespace to empty string, to be able to 
  822               // omit duplicate declarations for the same element
  823               //
  824               // case 3: <xsl:stylesheet xmlns:xsl="http://xsl">
  825               // We create another element body bound to the "http://xsl" namespace
  826               // as well as namespace attribute rebounding xsl to another namespace.
  827               // <xsl:body xmlns:xsl="http://another">
  828               // Need to make sure that the new namespace decl value is changed to 
  829               // "http://xsl"
  830               //
  831               // ---------------------------------------------------------
  832               // check if prefix/namespace is correct for current element
  833               // ---------------------------------------------------------
  834   
  835   
  836               if (uri != null) {  // Element has a namespace
  837                   uri = fSymbolTable.addSymbol(uri);
  838                   prefix = (prefix == null || 
  839                             prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  840                   if (fNSBinder.getURI(prefix) == uri) {
  841                       // The xmlns:prefix=namespace or xmlns="default" was declared at parent.
  842                       // The binder always stores mapping of empty prefix to "".
  843                       // (NOTE: local binder does not store this kind of binding!)
  844                       // Thus the case where element was declared with uri="" (with or without a prefix)
  845                       // will be covered here.
  846   
  847                   } else {
  848                       // the prefix is either undeclared 
  849                       // or
  850                       // conflict: the prefix is bound to another URI
  851                       if (fNamespacePrefixes) {
  852                           printNamespaceAttr(prefix, uri);
  853                       }
  854                       fLocalNSBinder.declarePrefix(prefix, uri);
  855                       fNSBinder.declarePrefix(prefix, uri);
  856                   }
  857               } else { // Element has no namespace
  858                   if (elem.getLocalName() == null) {
  859                       //  DOM Level 1 node!
  860                       if (fDOMErrorHandler != null) {
  861                           String msg = DOMMessageFormatter.formatMessage(
  862                               DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName", 
  863                               new Object[]{elem.getNodeName()});
  864                           modifyDOMError(msg,DOMError.SEVERITY_ERROR, null, elem);
  865                           boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
  866                           // REVISIT: should we terminate upon request?
  867                           if (!continueProcess) {
  868                              throw new RuntimeException( 
  869               	               DOMMessageFormatter.formatMessage(
  870               	               DOMMessageFormatter.SERIALIZER_DOMAIN,
  871               	               "SerializationStopped", null));
  872                           }
  873                       }
  874                   } else { // uri=null and no colon (DOM L2 node)
  875                       uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  876   
  877                       if (uri !=null && uri.length() > 0) {
  878                           // there is a default namespace decl that is bound to
  879                           // non-zero length uri, output xmlns=""
  880                           if (fNamespacePrefixes) {
  881                               printNamespaceAttr(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  882                           }
  883                           fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  884                           fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  885                       }
  886                   }
  887               }
  888   
  889   
  890               // -----------------------------------------
  891               // Fix up namespaces for attributes: per DOM L3 
  892               // check if prefix/namespace is correct the attributes
  893               // -----------------------------------------
  894   
  895               for (i = 0; i < length; i++) {
  896   
  897                   attr = (Attr) attrMap.item( i );
  898                   value = attr.getValue();
  899                   name = attr.getNodeName();  
  900                 
  901                   uri = attr.getNamespaceURI();
  902   
  903                   // Fix attribute that was declared with a prefix and namespace=""
  904                   if (uri !=null && uri.length() == 0) {
  905                       uri=null;
  906                       // we must remove prefix for this attribute
  907                       name=attr.getLocalName();
  908                   }
  909   
  910                   if (DEBUG) {
  911                       System.out.println("==>process attribute: "+attr.getNodeName());
  912                   }
  913                   // make sure that value is never null.
  914                   if (value == null) {
  915                       value=XMLSymbols.EMPTY_STRING;
  916                   }
  917   
  918                   if (uri != null) {  // attribute has namespace !=null
  919                       prefix = attr.getPrefix();
  920                       prefix = prefix == null ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  921                       String localpart = fSymbolTable.addSymbol( attr.getLocalName());
  922   
  923   
  924   
  925                       // ---------------------------------------------------
  926                       // print namespace declarations namespace declarations 
  927                       // ---------------------------------------------------
  928                       if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
  929                           // check if we need to output this declaration
  930                           prefix = attr.getPrefix();
  931                           prefix = (prefix == null || 
  932                                     prefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(prefix);
  933                           localpart = fSymbolTable.addSymbol( attr.getLocalName());
  934                           if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
  935                               localUri = fLocalNSBinder.getURI(localpart);  // local prefix mapping
  936                               value = fSymbolTable.addSymbol(value);
  937                               if (value.length() != 0 ) {
  938                                   if (localUri == null) {
  939                                       // declaration was not printed while fixing element namespace binding
  940                                       
  941                                       // If the DOM Level 3 namespace-prefixes feature is set to false
  942                                       // do not print xmlns attributes
  943                                       if (fNamespacePrefixes) {
  944                                           printNamespaceAttr(localpart, value);
  945                                       }
  946                                       	
  947                                       // case 4: <elem xmlns:xx="foo" xx:attr=""/>
  948                                       // where attribute is bound to "bar". 
  949                                       // If the xmlns:xx is output here first, later we should not
  950                                       // redeclare "xx" prefix. Instead we would pick up different prefix
  951                                       // for the attribute.
  952                                       // final: <elem xmlns:xx="foo" NS1:attr="" xmlns:NS1="bar"/>
  953                                       fLocalNSBinder.declarePrefix(localpart, value);
  954                                   }
  955                               } else {
  956                                   // REVISIT: issue error on invalid declarations
  957                                   //          xmlns:foo = ""
  958                               }
  959                               continue;
  960                           }
  961                           // xmlns --- empty prefix is always bound ("" or some string)
  962                           uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  963                           localUri= fLocalNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  964                           value = fSymbolTable.addSymbol(value);
  965                           if (localUri == null ) {
  966                               // declaration was not printed while fixing element namespace binding
  967                               if (fNamespacePrefixes) {
  968                                   printNamespaceAttr(XMLSymbols.EMPTY_STRING, value);
  969                               }
  970                               // case 4 does not apply here since attributes can't use
  971                               // default namespace
  972                           }
  973                           continue;
  974   
  975                       }
  976                       uri = fSymbolTable.addSymbol(uri);
  977   
  978                       // find if for this prefix a URI was already declared
  979                       String declaredURI =  fNSBinder.getURI(prefix);
  980   
  981                       if (prefix == XMLSymbols.EMPTY_STRING || declaredURI != uri) {
  982                           // attribute has no prefix (default namespace decl does not apply to attributes) 
  983                           // OR
  984                           // attribute prefix is not declared
  985                           // OR
  986                           // conflict: attr URI does not match the prefix in scope
  987   
  988                           name  = attr.getNodeName();
  989                           // Find if any prefix for attributes namespace URI is available
  990                           // in the scope
  991                           String declaredPrefix = fNSBinder.getPrefix(uri);
  992   
  993                           if (declaredPrefix !=null && declaredPrefix !=XMLSymbols.EMPTY_STRING) {
  994                               // use the prefix that was found
  995                               prefix = declaredPrefix;
  996                               name=prefix+":"+localpart;
  997                           } else {
  998                               if (DEBUG) {
  999                                   System.out.println("==> cound not find prefix for the attribute: " +prefix);
 1000                               }
 1001   
 1002                               if (prefix != XMLSymbols.EMPTY_STRING && fLocalNSBinder.getURI(prefix) == null) {
 1003                                   // the current prefix is not null and it has no in scope declaration
 1004   
 1005                                   // use this prefix
 1006                               } else {
 1007                                   // find a prefix following the pattern "NS" +index (starting at 1)
 1008                                   // make sure this prefix is not declared in the current scope.
 1009                                   int counter = 1;
 1010                                   prefix = fSymbolTable.addSymbol(PREFIX + counter++);
 1011                                   while (fLocalNSBinder.getURI(prefix)!=null) {
 1012                                       prefix = fSymbolTable.addSymbol(PREFIX +counter++);
 1013                                   }
 1014                                   name=prefix+":"+localpart;
 1015                               }
 1016                               // add declaration for the new prefix
 1017                               if (fNamespacePrefixes) {
 1018                                   printNamespaceAttr(prefix, uri);
 1019                               }
 1020                               value = fSymbolTable.addSymbol(value);
 1021                               fLocalNSBinder.declarePrefix(prefix, value);
 1022                               fNSBinder.declarePrefix(prefix, uri);
 1023                           }                        
 1024   
 1025                           // change prefix for this attribute
 1026                       }
 1027   
 1028                       printAttribute (name, (value==null)?XMLSymbols.EMPTY_STRING:value, attr.getSpecified(), attr);
 1029                   } else { // attribute uri == null
 1030                       if (attr.getLocalName() == null) {
 1031                           if (fDOMErrorHandler != null) {
 1032                               String msg = DOMMessageFormatter.formatMessage(
 1033                                   DOMMessageFormatter.DOM_DOMAIN, 
 1034                                   "NullLocalAttrName", new Object[]{attr.getNodeName()});                            
 1035                               modifyDOMError(msg, DOMError.SEVERITY_ERROR, null, attr);
 1036                               boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
 1037                               if (!continueProcess) {
 1038                                   // stop the namespace fixup and validation
 1039                                   throw new RuntimeException( 
 1040               	                   DOMMessageFormatter.formatMessage(
 1041               	                   DOMMessageFormatter.SERIALIZER_DOMAIN,
 1042               	                   "SerializationStopped", null));
 1043                               }
 1044                           }
 1045                           printAttribute (name, value, attr.getSpecified(), attr);
 1046                       } else { // uri=null and no colon
 1047   
 1048                           // no fix up is needed: default namespace decl does not 
 1049                           // apply to attributes
 1050                           printAttribute (name, value, attr.getSpecified(), attr);
 1051                       }
 1052                   }
 1053               } // end loop for attributes
 1054   
 1055           }// end namespace fixup algorithm
 1056   
 1057   
 1058           // If element has children, then serialize them, otherwise
 1059           // serialize en empty tag.        
 1060           if (elem.hasChildNodes()) {
 1061               // Enter an element state, and serialize the children
 1062               // one by one. Finally, end the element.
 1063               state = enterElementState( null, null, tagName, fPreserveSpace );
 1064               state.doCData = _format.isCDataElement( tagName );
 1065               state.unescaped = _format.isNonEscapingElement( tagName );
 1066               child = elem.getFirstChild();
 1067               while (child != null) {
 1068                   serializeNode( child );
 1069                   child = child.getNextSibling();
 1070               }
 1071               if (fNamespaces) {
 1072                   fNSBinder.popContext();
 1073               }
 1074               endElementIO( null, null, tagName );
 1075           } else {
 1076               if (DEBUG) {
 1077                   System.out.println("==>endElement: " +elem.getNodeName());
 1078               }
 1079               if (fNamespaces) {
 1080                   fNSBinder.popContext();
 1081               }
 1082               _printer.unindent();
 1083               _printer.printText( "/>" );
 1084               // After element but parent element is no longer empty.
 1085               state.afterElement = true;
 1086               state.afterComment = false;
 1087               state.empty = false;
 1088               if (isDocumentState())
 1089                   _printer.flush();
 1090           }
 1091       }
 1092   
 1093   
 1094   
 1095       /**
 1096        * Serializes a namespace attribute with the given prefix and value for URI.
 1097        * In case prefix is empty will serialize default namespace declaration.
 1098        * 
 1099        * @param prefix
 1100        * @param uri
 1101        * @exception IOException
 1102        */
 1103   
 1104       private void printNamespaceAttr(String prefix, String uri) throws IOException{
 1105           _printer.printSpace();
 1106           if (prefix == XMLSymbols.EMPTY_STRING) {
 1107               if (DEBUG) {
 1108                   System.out.println("=>add xmlns=\""+uri+"\" declaration");
 1109               }
 1110               _printer.printText( XMLSymbols.PREFIX_XMLNS );
 1111           } else {
 1112               if (DEBUG) {
 1113                   System.out.println("=>add xmlns:"+prefix+"=\""+uri+"\" declaration");
 1114               }
 1115               _printer.printText( "xmlns:"+prefix );
 1116           }
 1117           _printer.printText( "=\"" );
 1118           printEscaped( uri );
 1119           _printer.printText( '"' );
 1120       }
 1121   
 1122   
 1123   
 1124       /**
 1125        * Prints attribute. 
 1126        * NOTE: xml:space attribute modifies output format
 1127        * 
 1128        * @param name
 1129        * @param value
 1130        * @param isSpecified
 1131        * @exception IOException
 1132        */
 1133       private void printAttribute (String name, String value, boolean isSpecified, Attr attr) throws IOException{
 1134   
 1135           if (isSpecified || (features & DOMSerializerImpl.DISCARDDEFAULT) == 0) {
 1136               if (fDOMFilter !=null && 
 1137                   (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)!= 0) {
 1138                   short code = fDOMFilter.acceptNode(attr);
 1139                   switch (code) {
 1140                       case NodeFilter.FILTER_REJECT:
 1141                       case NodeFilter.FILTER_SKIP: { 
 1142                           return;
 1143                       }
 1144                       default: {
 1145                           // fall through
 1146                       }
 1147                   }
 1148               }
 1149               _printer.printSpace();
 1150               _printer.printText( name );
 1151               _printer.printText( "=\"" );
 1152               printEscaped( value );
 1153               _printer.printText( '"' );
 1154           }
 1155   
 1156           // If the attribute xml:space exists, determine whether
 1157           // to preserve spaces in this and child nodes based on
 1158           // its value.
 1159           if (name.equals( "xml:space" )) {
 1160               if (value.equals( "preserve" ))
 1161                   fPreserveSpace = true;
 1162               else
 1163                   fPreserveSpace = _format.getPreserveSpace();
 1164           }
 1165       }
 1166   
 1167       protected String getEntityRef( int ch ) {
 1168           // Encode special XML characters into the equivalent character references.
 1169           // These five are defined by default for all XML documents.
 1170           switch (ch) {
 1171           case '<':
 1172               return "lt";
 1173           case '>':
 1174               return "gt";
 1175           case '"':
 1176               return "quot";
 1177           case '\'':
 1178               return "apos";
 1179           case '&':
 1180               return "amp";
 1181           }
 1182           return null;
 1183       }
 1184   
 1185   
 1186       /** Retrieve and remove the namespaces declarations from the list of attributes.
 1187        *
 1188        */
 1189       private Attributes extractNamespaces( Attributes attrs )
 1190       throws SAXException
 1191       {
 1192           AttributesImpl attrsOnly;
 1193           String         rawName;
 1194           int            i;
 1195           int            length;
 1196   
 1197           if (attrs == null) {
 1198               return null;
 1199           }
 1200           length = attrs.getLength();
 1201           attrsOnly = new AttributesImpl( attrs );
 1202   
 1203           for (i = length - 1 ; i >= 0 ; --i) {
 1204               rawName = attrsOnly.getQName( i );
 1205   
 1206               //We have to exclude the namespaces declarations from the attributes
 1207               //Append only when the feature http://xml.org/sax/features/namespace-prefixes"
 1208               //is TRUE
 1209               if (rawName.startsWith( "xmlns" )) {
 1210                   if (rawName.length() == 5) {
 1211                       startPrefixMapping( "", attrs.getValue( i ) );
 1212                       attrsOnly.removeAttribute( i );
 1213                   } else if (rawName.charAt(5) == ':') {
 1214                       startPrefixMapping(rawName.substring(6), attrs.getValue(i));
 1215                       attrsOnly.removeAttribute( i );
 1216                   }
 1217               }
 1218           }
 1219           return attrsOnly;
 1220       }
 1221   
 1222       //
 1223       // Printing attribute value
 1224       //
 1225       protected void printEscaped(String source) throws IOException {
 1226           int length = source.length();
 1227           for (int i = 0; i < length; ++i) {
 1228               int ch = source.charAt(i);
 1229               if (!XMLChar.isValid(ch)) {
 1230                   if (++i < length) {
 1231                       surrogates(ch, source.charAt(i), false);
 1232                   } else {
 1233                       fatalError("The character '" + (char) ch + "' is an invalid XML character");
 1234                   }
 1235                   continue;
 1236               }
 1237               // escape NL, CR, TAB
 1238               if (ch == '\n' || ch == '\r' || ch == '\t') {
 1239                   printHex(ch);
 1240               } else if (ch == '<') {
 1241                   _printer.printText("&lt;");
 1242               } else if (ch == '&') {
 1243                   _printer.printText("&amp;");
 1244               } else if (ch == '"') {
 1245                   _printer.printText("&quot;");
 1246               } else if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch))) {
 1247                   _printer.printText((char) ch);
 1248               } else {
 1249                   printHex(ch);
 1250               }
 1251           }
 1252       }
 1253   
 1254       /** print text data */
 1255       protected void printXMLChar( int ch) throws IOException {
 1256       	if (ch == '\r') {
 1257   			printHex(ch);
 1258       	} else if ( ch == '<') {
 1259               _printer.printText("&lt;");
 1260           } else if (ch == '&') {
 1261               _printer.printText("&amp;");
 1262           } else if (ch == '>'){
 1263           	// character sequence "]]>" can't appear in content, therefore
 1264           	// we should escape '>' 
 1265   			_printer.printText("&gt;");        	
 1266           } else if ( ch == '\n' ||  ch == '\t' ||
 1267                       ( ch >= ' ' && _encodingInfo.isPrintable((char)ch))) {
 1268               _printer.printText((char)ch);
 1269           } else {
 1270   			printHex(ch);
 1271           }
 1272       }
 1273   
 1274       protected void printText( String text, boolean preserveSpace, boolean unescaped )
 1275       throws IOException {
 1276           int index;
 1277           char ch;
 1278           int length = text.length();
 1279           if ( preserveSpace ) {
 1280               // Preserving spaces: the text must print exactly as it is,
 1281               // without breaking when spaces appear in the text and without
 1282               // consolidating spaces. If a line terminator is used, a line
 1283               // break will occur.
 1284               for ( index = 0 ; index < length ; ++index ) {
 1285                   ch = text.charAt( index );
 1286                   if (!XMLChar.isValid(ch)) {
 1287                       // check if it is surrogate
 1288                       if (++index <length) {
 1289                           surrogates(ch, text.charAt(index), true);
 1290                       } else {
 1291                           fatalError("The character '"+ch+"' is an invalid XML character"); 
 1292                       }
 1293                       continue;
 1294                   }
 1295                   if ( unescaped ) {
 1296                       _printer.printText( ch );
 1297                   } else
 1298                       printXMLChar( ch );
 1299               }
 1300           } else {
 1301               // Not preserving spaces: print one part at a time, and
 1302               // use spaces between parts to break them into different
 1303               // lines. Spaces at beginning of line will be stripped
 1304               // by printing mechanism. Line terminator is treated
 1305               // no different than other text part.
 1306               for ( index = 0 ; index < length ; ++index ) {
 1307                   ch = text.charAt( index );
 1308                   if (!XMLChar.isValid(ch)) {
 1309                       // check if it is surrogate
 1310                       if (++index <length) {
 1311                           surrogates(ch, text.charAt(index), true);
 1312                       } else {
 1313                           fatalError("The character '"+ch+"' is an invalid XML character"); 
 1314                       }
 1315                       continue;
 1316                   }
 1317   
 1318   				if ( unescaped )
 1319                       _printer.printText( ch );
 1320                   else
 1321                       printXMLChar( ch);
 1322               }
 1323           }
 1324       }
 1325   
 1326   
 1327   
 1328       protected void printText( char[] chars, int start, int length,
 1329                                 boolean preserveSpace, boolean unescaped ) throws IOException {
 1330   
 1331           if ( preserveSpace ) {
 1332               // Preserving spaces: the text must print exactly as it is,
 1333               // without breaking when spaces appear in the text and without
 1334               // consolidating spaces. If a line terminator is used, a line
 1335               // break will occur.
 1336               while ( length-- > 0 ) {
 1337                   char ch = chars[start++];
 1338                   if (!XMLChar.isValid(ch)) {
 1339                       // check if it is surrogate
 1340                       if ( length-- > 0 ) {
 1341                           surrogates(ch, chars[start++], true);
 1342                       } 
 1343                       else {
 1344                           fatalError("The character '"+ch+"' is an invalid XML character"); 
 1345                       }
 1346                       continue;
 1347                   }
 1348                   if ( unescaped ) {
 1349                       _printer.printText( ch );
 1350                   } 
 1351                   else {
 1352                       printXMLChar( ch );
 1353                   }
 1354               }
 1355           } else {
 1356               // Not preserving spaces: print one part at a time, and
 1357               // use spaces between parts to break them into different
 1358               // lines. Spaces at beginning of line will be stripped
 1359               // by printing mechanism. Line terminator is treated
 1360               // no different than other text part.
 1361               while ( length-- > 0 ) {
 1362                   char ch = chars[start++];
 1363                   if (!XMLChar.isValid(ch)) {
 1364                       // check if it is surrogate
 1365                       if ( length-- > 0 ) {
 1366                           surrogates(ch, chars[start++], true);
 1367                       } 
 1368                       else {
 1369                           fatalError("The character '"+ch+"' is an invalid XML character"); 
 1370                       }
 1371                       continue;
 1372                   }
 1373                   if ( unescaped ) {
 1374                       _printer.printText( ch );
 1375                   } 
 1376                   else {
 1377                       printXMLChar( ch );
 1378                   }
 1379               }
 1380           }
 1381       }
 1382   
 1383   
 1384      /**
 1385       * DOM Level 3:
 1386       * Check a node to determine if it contains unbound namespace prefixes.
 1387       *
 1388       * @param node The node to check for unbound namespace prefices
 1389       */
 1390   	protected void checkUnboundNamespacePrefixedNode (Node node) throws IOException{
 1391   
 1392   		if (fNamespaces) {
 1393   
 1394      			if (DEBUG) {
 1395   			    System.out.println("==>serializeNode("+node.getNodeName()+") [Entity Reference - Namespaces on]");
 1396   				System.out.println("==>Declared Prefix Count: " + fNSBinder.getDeclaredPrefixCount());
 1397   				System.out.println("==>Node Name: " + node.getNodeName());
 1398   				System.out.println("==>First Child Node Name: " + node.getFirstChild().getNodeName());
 1399   				System.out.println("==>First Child Node Prefix: " + node.getFirstChild().getPrefix());
 1400   				System.out.println("==>First Child Node NamespaceURI: " + node.getFirstChild().getNamespaceURI());			
 1401   			}
 1402   
 1403   		
 1404   			Node child, next;
 1405   	        for (child = node.getFirstChild(); child != null; child = next) {
 1406   	            next = child.getNextSibling();
 1407   			    if (DEBUG) {
 1408   			        System.out.println("==>serializeNode("+child.getNodeName()+") [Child Node]");
 1409   			        System.out.println("==>serializeNode("+child.getPrefix()+") [Child Node Prefix]");
 1410   	            }    
 1411   	
 1412   		 	    //If a NamespaceURI is not declared for the current
 1413   		 	    //node's prefix, raise a fatal error.
 1414   		 	    String prefix = child.getPrefix();
 1415                   prefix = (prefix == null || 
 1416                           prefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(prefix);
 1417   		 	    if (fNSBinder.getURI(prefix) == null && prefix != null) {
 1418   					fatalError("The replacement text of the entity node '" 
 1419   								+ node.getNodeName()  
 1420   								+ "' contains an element node '" 
 1421   								+ child.getNodeName() 
 1422   								+ "' with an undeclared prefix '" 
 1423   								+ prefix + "'.");
 1424   		 	    }	
 1425   
 1426   				if (child.getNodeType() == Node.ELEMENT_NODE) {
 1427   					
 1428   					NamedNodeMap attrs = child.getAttributes();
 1429   					
 1430   					for (int i = 0; i< attrs.getLength(); i++ ) {
 1431   						
 1432   				 	    String attrPrefix = attrs.item(i).getPrefix();
 1433                           attrPrefix = (attrPrefix == null || 
 1434                                   attrPrefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(attrPrefix);
 1435   				 	    if (fNSBinder.getURI(attrPrefix) == null && attrPrefix != null) {
 1436   							fatalError("The replacement text of the entity node '" 
 1437   										+ node.getNodeName()  
 1438   										+ "' contains an element node '" 
 1439   										+ child.getNodeName() 
 1440   										+ "' with an attribute '" 
 1441   										+ attrs.item(i).getNodeName() 										
 1442   										+ "' an undeclared prefix '" 
 1443   										+ attrPrefix + "'.");
 1444   				 	    }	
 1445   						
 1446   					}	
 1447   
 1448   				}
 1449   					
 1450   				if (child.hasChildNodes()) {
 1451   					checkUnboundNamespacePrefixedNode(child);
 1452   				}	
 1453   	        }
 1454   		}    
 1455   	}	
 1456   
 1457       public boolean reset() {
 1458           super.reset();
 1459           if (fNSBinder != null){
 1460               fNSBinder.reset();
 1461               // during serialization always have a mapping to empty string
 1462               // so we assume there is a declaration.
 1463               fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
 1464           }
 1465           return true;
 1466       }
 1467   
 1468   }
 1469   
 1470   
 1471   
 1472   

Save This Page
Home » Xerces-J-src.2.9.1 » org.apache.xml » serialize » [javadoc | source]