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

Save This Page
Home » openjdk-7 » com.sun.org.apache.xml.internal » serialize » [javadoc | source]