jaxp/src/com/sun/org/apache/xml/internal/serialize/XMLSerializer.java
changeset 12457 c348e06f0e82
parent 6 7f561c08de6b
child 23777 ce87cedb71cf
equal deleted inserted replaced
12324:1d7e6da6adc8 12457:c348e06f0e82
       
     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 }