KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xml > internal > serialize > XMLSerializer


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58
59 // Sep 14, 2000:
60
// Fixed problem with namespace handling. Contributed by
61
// David Blondeau <blondeau@intalio.com>
62
// Sep 14, 2000:
63
// Fixed serializer to report IO exception directly, instead at
64
// the end of document processing.
65
// Reported by Patrick Higgins <phiggins@transzap.com>
66
// Aug 21, 2000:
67
// Fixed bug in startDocument not calling prepare.
68
// Reported by Mikael Staldal <d96-mst-ingen-reklam@d.kth.se>
69
// Aug 21, 2000:
70
// Added ability to omit DOCTYPE declaration.
71

72
73 package com.sun.org.apache.xml.internal.serialize;
74
75
76 import java.io.IOException JavaDoc;
77 import java.io.OutputStream JavaDoc;
78 import java.io.Writer JavaDoc;
79 import java.util.Enumeration JavaDoc;
80
81 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter;
82 import org.w3c.dom.DOMError JavaDoc;
83 import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
84 import com.sun.org.apache.xerces.internal.util.SymbolTable;
85 import com.sun.org.apache.xerces.internal.util.XMLChar;
86 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
87 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
88 import org.w3c.dom.Attr JavaDoc;
89 import org.w3c.dom.Element JavaDoc;
90 import org.w3c.dom.NamedNodeMap JavaDoc;
91 import org.w3c.dom.Node JavaDoc;
92 import org.w3c.dom.traversal.NodeFilter;
93 import org.xml.sax.AttributeList JavaDoc;
94 import org.xml.sax.Attributes JavaDoc;
95 import org.xml.sax.SAXException JavaDoc;
96 import org.xml.sax.helpers.AttributesImpl JavaDoc;
97
98 /**
99  * Implements an XML serializer supporting both DOM and SAX pretty
100  * serializing. For usage instructions see {@link Serializer}.
101  * <p>
102  * If an output stream is used, the encoding is taken from the
103  * output format (defaults to <tt>UTF-8</tt>). If a writer is
104  * used, make sure the writer uses the same encoding (if applies)
105  * as specified in the output format.
106  * <p>
107  * The serializer supports both DOM and SAX. SAX serializing is done by firing
108  * SAX events and using the serializer as a document handler. DOM serializing is done
109  * by calling {@link #serialize(Document)} or by using DOM Level 3
110  * {@link org.w3c.dom.ls.DOMSerializer} and
111  * serializing with {@link org.w3c.dom.ls.DOMSerializer#write},
112  * {@link org.w3c.dom.ls.DOMSerializer#writeToString}.
113  * <p>
114  * If an I/O exception occurs while serializing, the serializer
115  * will not throw an exception directly, but only throw it
116  * at the end of serializing (either DOM or SAX's {@link
117  * org.xml.sax.DocumentHandler#endDocument}.
118  * <p>
119  * For elements that are not specified as whitespace preserving,
120  * the serializer will potentially break long text lines at space
121  * boundaries, indent lines, and serialize elements on separate
122  * lines. Line terminators will be regarded as spaces, and
123  * spaces at beginning of line will be stripped.
124  * @author <a HREF="mailto:arkin@intalio.com">Assaf Arkin</a>
125  * @author <a HREF="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
126  * @author Elena Litani IBM
127  * @version $Revision: 1.60 $ $Date: 2004/04/13 17:30:01 $
128  * @see Serializer
129  */

130 public class XMLSerializer
131 extends BaseMarkupSerializer {
132
133     //
134
// constants
135
//
136

137     protected static final boolean DEBUG = false;
138
139     //
140
// data
141
//
142

143     //
144
// DOM Level 3 implementation: variables intialized in DOMSerializerImpl
145
//
146

147     /** stores namespaces in scope */
148     protected NamespaceSupport fNSBinder;
149
150     /** stores all namespace bindings on the current element */
151     protected NamespaceSupport fLocalNSBinder;
152
153     /** symbol table for serialization */
154     protected SymbolTable fSymbolTable;
155
156     protected final static String JavaDoc PREFIX = "NS";
157
158     /**
159      * Controls whether namespace fixup should be performed during
160      * the serialization.
161      * NOTE: if this field is set to true the following
162      * fields need to be initialized: fNSBinder, fLocalNSBinder, fSymbolTable,
163      * XMLSymbols.EMPTY_STRING, fXmlSymbol, fXmlnsSymbol
164      */

165     protected boolean fNamespaces = false;
166
167
168     private boolean fPreserveSpace;
169
170
171     /**
172      * Constructs a new serializer. The serializer cannot be used without
173      * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
174      * first.
175      */

176     public XMLSerializer() {
177         super( new OutputFormat( Method.XML, null, false ) );
178     }
179
180
181     /**
182      * Constructs a new serializer. The serializer cannot be used without
183      * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
184      * first.
185      */

186     public XMLSerializer( OutputFormat format ) {
187         super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
188         _format.setMethod( Method.XML );
189     }
190
191
192     /**
193      * Constructs a new serializer that writes to the specified writer
194      * using the specified output format. If <tt>format</tt> is null,
195      * will use a default output format.
196      *
197      * @param writer The writer to use
198      * @param format The output format to use, null for the default
199      */

200     public XMLSerializer( Writer JavaDoc writer, OutputFormat format ) {
201         super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
202         _format.setMethod( Method.XML );
203         setOutputCharStream( writer );
204     }
205
206
207     /**
208      * Constructs a new serializer that writes to the specified output
209      * stream using the specified output format. If <tt>format</tt>
210      * is null, will use a default output format.
211      *
212      * @param output The output stream to use
213      * @param format The output format to use, null for the default
214      */

215     public XMLSerializer( OutputStream JavaDoc output, OutputFormat format ) {
216         super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
217         _format.setMethod( Method.XML );
218         setOutputByteStream( output );
219     }
220
221
222     public void setOutputFormat( OutputFormat format ) {
223         super.setOutputFormat( format != null ? format : new OutputFormat( Method.XML, null, false ) );
224     }
225
226
227     /**
228      * This methods turns on namespace fixup algorithm during
229      * DOM serialization.
230      * @see org.w3c.dom.ls.DOMSerializer
231      *
232      * @param namespaces
233      */

234     public void setNamespaces (boolean namespaces){
235         fNamespaces = namespaces;
236         if (fNSBinder == null) {
237             fNSBinder = new NamespaceSupport();
238             fLocalNSBinder = new NamespaceSupport();
239             fSymbolTable = new SymbolTable();
240         }
241     }
242
243     //-----------------------------------------//
244
// SAX content handler serializing methods //
245
//-----------------------------------------//
246

247
248     public void startElement( String JavaDoc namespaceURI, String JavaDoc localName,
249                               String JavaDoc rawName, Attributes JavaDoc attrs )
250     throws SAXException JavaDoc
251     {
252         int i;
253         boolean preserveSpace;
254         ElementState state;
255         String JavaDoc name;
256         String JavaDoc value;
257         boolean addNSAttr = false;
258
259         if (DEBUG) {
260             System.out.println("==>startElement("+namespaceURI+","+localName+
261                                ","+rawName+")");
262         }
263
264         try {
265             if (_printer == null) {
266                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
267                 throw new IllegalStateException JavaDoc(msg);
268             }
269
270             state = getElementState();
271             if (isDocumentState()) {
272                 // If this is the root element handle it differently.
273
// If the first root element in the document, serialize
274
// the document's DOCTYPE. Space preserving defaults
275
// to that of the output format.
276
if (! _started)
277                     startDocument( ( localName == null || localName.length() == 0 ) ? rawName : localName );
278             } else {
279                 // For any other element, if first in parent, then
280
// close parent's opening tag and use the parnet's
281
// space preserving.
282
if (state.empty)
283                     _printer.printText( '>' );
284                 // Must leave CData section first
285
if (state.inCData) {
286                     _printer.printText( "]]>" );
287                     state.inCData = false;
288                 }
289                 // Indent this element on a new line if the first
290
// content of the parent element or immediately
291
// following an element or a comment
292
if (_indenting && ! state.preserveSpace &&
293                     ( state.empty || state.afterElement || state.afterComment))
294                     _printer.breakLine();
295             }
296             preserveSpace = state.preserveSpace;
297
298             //We remove the namespaces from the attributes list so that they will
299
//be in _prefixes
300
attrs = extractNamespaces(attrs);
301
302             // Do not change the current element state yet.
303
// This only happens in endElement().
304
if (rawName == null || rawName.length() == 0) {
305                 if (localName == null) {
306                     String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoName", null);
307                     throw new SAXException JavaDoc(msg);
308                 }
309                 if (namespaceURI != null && ! namespaceURI.equals( "" )) {
310                     String JavaDoc prefix;
311                     prefix = getPrefix( namespaceURI );
312                     if (prefix != null && prefix.length() > 0)
313                         rawName = prefix + ":" + localName;
314                     else
315                         rawName = localName;
316                 } else
317                     rawName = localName;
318                 addNSAttr = true;
319             }
320
321             _printer.printText( '<' );
322             _printer.printText( rawName );
323             _printer.indent();
324
325             // For each attribute print it's name and value as one part,
326
// separated with a space so the element can be broken on
327
// multiple lines.
328
if (attrs != null) {
329                 for (i = 0 ; i < attrs.getLength() ; ++i) {
330                     _printer.printSpace();
331
332                     name = attrs.getQName( i );
333                     if (name != null && name.length() == 0) {
334                         String JavaDoc prefix;
335                         String JavaDoc attrURI;
336
337                         name = attrs.getLocalName( i );
338                         attrURI = attrs.getURI( i );
339                         if (( attrURI != null && attrURI.length() != 0 ) &&
340                             ( namespaceURI == null || namespaceURI.length() == 0 ||
341                               ! attrURI.equals( namespaceURI ) )) {
342                             prefix = getPrefix( attrURI );
343                             if (prefix != null && prefix.length() > 0)
344                                 name = prefix + ":" + name;
345                         }
346                     }
347
348                     value = attrs.getValue( i );
349                     if (value == null)
350                         value = "";
351                     _printer.printText( name );
352                     _printer.printText( "=\"" );
353                     printEscaped( value );
354                     _printer.printText( '"' );
355
356                     // If the attribute xml:space exists, determine whether
357
// to preserve spaces in this and child nodes based on
358
// its value.
359
if (name.equals( "xml:space" )) {
360                         if (value.equals( "preserve" ))
361                             preserveSpace = true;
362                         else
363                             preserveSpace = _format.getPreserveSpace();
364                     }
365                 }
366             }
367
368             if (_prefixes != null) {
369                 Enumeration JavaDoc keys;
370
371                 keys = _prefixes.keys();
372                 while (keys.hasMoreElements()) {
373                     _printer.printSpace();
374                     value = (String JavaDoc) keys.nextElement();
375                     name = (String JavaDoc) _prefixes.get( value );
376                     if (name.length() == 0) {
377                         _printer.printText( "xmlns=\"" );
378                         printEscaped( value );
379                         _printer.printText( '"' );
380                     } else {
381                         _printer.printText( "xmlns:" );
382                         _printer.printText( name );
383                         _printer.printText( "=\"" );
384                         printEscaped( value );
385                         _printer.printText( '"' );
386                     }
387                 }
388             }
389
390             // Now it's time to enter a new element state
391
// with the tag name and space preserving.
392
// We still do not change the curent element state.
393
state = enterElementState( namespaceURI, localName, rawName, preserveSpace );
394             name = ( localName == null || localName.length() == 0 ) ? rawName : namespaceURI + "^" + localName;
395             state.doCData = _format.isCDataElement( name );
396             state.unescaped = _format.isNonEscapingElement( name );
397         } catch (IOException JavaDoc except) {
398             throw new SAXException JavaDoc( except );
399         }
400     }
401
402
403     public void endElement( String JavaDoc namespaceURI, String JavaDoc localName,
404                             String JavaDoc rawName )
405     throws SAXException JavaDoc
406     {
407         try {
408             endElementIO( namespaceURI, localName, rawName );
409         } catch (IOException JavaDoc except) {
410             throw new SAXException JavaDoc( except );
411         }
412     }
413
414
415     public void endElementIO( String JavaDoc namespaceURI, String JavaDoc localName,
416                               String JavaDoc rawName )
417     throws IOException JavaDoc
418     {
419         ElementState state;
420         if (DEBUG) {
421             System.out.println("==>endElement: " +rawName);
422         }
423         // Works much like content() with additions for closing
424
// an element. Note the different checks for the closed
425
// element's state and the parent element's state.
426
_printer.unindent();
427         state = getElementState();
428         if (state.empty) {
429             _printer.printText( "/>" );
430         } else {
431             // Must leave CData section first
432
if (state.inCData)
433                 _printer.printText( "]]>" );
434             // This element is not empty and that last content was
435
// another element, so print a line break before that
436
// last element and this element's closing tag.
437
if (_indenting && ! state.preserveSpace && (state.afterElement || state.afterComment))
438                 _printer.breakLine();
439             _printer.printText( "</" );
440             _printer.printText( state.rawName );
441             _printer.printText( '>' );
442         }
443         // Leave the element state and update that of the parent
444
// (if we're not root) to not empty and after element.
445
state = leaveElementState();
446         state.afterElement = true;
447         state.afterComment = false;
448         state.empty = false;
449         if (isDocumentState())
450             _printer.flush();
451     }
452
453
454     //------------------------------------------//
455
// SAX document handler serializing methods //
456
//------------------------------------------//
457

458
459     public void startElement( String JavaDoc tagName, AttributeList JavaDoc attrs )
460     throws SAXException JavaDoc
461     {
462         int i;
463         boolean preserveSpace;
464         ElementState state;
465         String JavaDoc name;
466         String JavaDoc value;
467
468
469         if (DEBUG) {
470             System.out.println("==>startElement("+tagName+")");
471         }
472
473         try {
474             if (_printer == null) {
475                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
476                 throw new IllegalStateException JavaDoc(msg);
477             }
478
479             state = getElementState();
480             if (isDocumentState()) {
481                 // If this is the root element handle it differently.
482
// If the first root element in the document, serialize
483
// the document's DOCTYPE. Space preserving defaults
484
// to that of the output format.
485
if (! _started)
486                     startDocument( tagName );
487             } else {
488                 // For any other element, if first in parent, then
489
// close parent's opening tag and use the parnet's
490
// space preserving.
491
if (state.empty)
492                     _printer.printText( '>' );
493                 // Must leave CData section first
494
if (state.inCData) {
495                     _printer.printText( "]]>" );
496                     state.inCData = false;
497                 }
498                 // Indent this element on a new line if the first
499
// content of the parent element or immediately
500
// following an element.
501
if (_indenting && ! state.preserveSpace &&
502                     ( state.empty || state.afterElement || state.afterComment))
503                     _printer.breakLine();
504             }
505             preserveSpace = state.preserveSpace;
506
507             // Do not change the current element state yet.
508
// This only happens in endElement().
509

510             _printer.printText( '<' );
511             _printer.printText( tagName );
512             _printer.indent();
513
514             // For each attribute print it's name and value as one part,
515
// separated with a space so the element can be broken on
516
// multiple lines.
517
if (attrs != null) {
518                 for (i = 0 ; i < attrs.getLength() ; ++i) {
519                     _printer.printSpace();
520                     name = attrs.getName( i );
521                     value = attrs.getValue( i );
522                     if (value != null) {
523                         _printer.printText( name );
524                         _printer.printText( "=\"" );
525                         printEscaped( value );
526                         _printer.printText( '"' );
527                     }
528
529                     // If the attribute xml:space exists, determine whether
530
// to preserve spaces in this and child nodes based on
531
// its value.
532
if (name.equals( "xml:space" )) {
533                         if (value.equals( "preserve" ))
534                             preserveSpace = true;
535                         else
536                             preserveSpace = _format.getPreserveSpace();
537                     }
538                 }
539             }
540             // Now it's time to enter a new element state
541
// with the tag name and space preserving.
542
// We still do not change the curent element state.
543
state = enterElementState( null, null, tagName, preserveSpace );
544             state.doCData = _format.isCDataElement( tagName );
545             state.unescaped = _format.isNonEscapingElement( tagName );
546         } catch (IOException JavaDoc except) {
547             throw new SAXException JavaDoc( except );
548         }
549
550     }
551
552
553     public void endElement( String JavaDoc tagName )
554     throws SAXException JavaDoc
555     {
556         endElement( null, null, tagName );
557     }
558
559
560
561     //------------------------------------------//
562
// Generic node serializing methods methods //
563
//------------------------------------------//
564

565
566     /**
567      * Called to serialize the document's DOCTYPE by the root element.
568      * The document type declaration must name the root element,
569      * but the root element is only known when that element is serialized,
570      * and not at the start of the document.
571      * <p>
572      * This method will check if it has not been called before ({@link #_started}),
573      * will serialize the document type declaration, and will serialize all
574      * pre-root comments and PIs that were accumulated in the document
575      * (see {@link #serializePreRoot}). Pre-root will be serialized even if
576      * this is not the first root element of the document.
577      */

578     protected void startDocument( String JavaDoc rootTagName )
579     throws IOException JavaDoc
580     {
581         int i;
582         String JavaDoc dtd;
583
584         dtd = _printer.leaveDTD();
585         if (! _started) {
586
587             if (! _format.getOmitXMLDeclaration()) {
588                 StringBuffer JavaDoc buffer;
589
590                 // Serialize the document declaration appreaing at the head
591
// of very XML document (unless asked not to).
592
buffer = new StringBuffer JavaDoc( "<?xml version=\"" );
593                 if (_format.getVersion() != null)
594                     buffer.append( _format.getVersion() );
595                 else
596                     buffer.append( "1.0" );
597                 buffer.append( '"' );
598                 String JavaDoc format_encoding = _format.getEncoding();
599                 if (format_encoding != null) {
600                     buffer.append( " encoding=\"" );
601                     buffer.append( format_encoding );
602                     buffer.append( '"' );
603                 }
604                 if (_format.getStandalone() && _docTypeSystemId == null &&
605                     _docTypePublicId == null)
606                     buffer.append( " standalone=\"yes\"" );
607                 buffer.append( "?>" );
608                 _printer.printText( buffer );
609                 _printer.breakLine();
610             }
611
612             if (! _format.getOmitDocumentType()) {
613                 if (_docTypeSystemId != null) {
614                     // System identifier must be specified to print DOCTYPE.
615
// If public identifier is specified print 'PUBLIC
616
// <public> <system>', if not, print 'SYSTEM <system>'.
617
_printer.printText( "<!DOCTYPE " );
618                     _printer.printText( rootTagName );
619                     if (_docTypePublicId != null) {
620                         _printer.printText( " PUBLIC " );
621                         printDoctypeURL( _docTypePublicId );
622                         if (_indenting) {
623                             _printer.breakLine();
624                             for (i = 0 ; i < 18 + rootTagName.length() ; ++i)
625                                 _printer.printText( " " );
626                         } else
627                             _printer.printText( " " );
628                         printDoctypeURL( _docTypeSystemId );
629                     } else {
630                         _printer.printText( " SYSTEM " );
631                         printDoctypeURL( _docTypeSystemId );
632                     }
633
634                     // If we accumulated any DTD contents while printing.
635
// this would be the place to print it.
636
if (dtd != null && dtd.length() > 0) {
637                         _printer.printText( " [" );
638                         printText( dtd, true, true );
639                         _printer.printText( ']' );
640                     }
641
642                     _printer.printText( ">" );
643                     _printer.breakLine();
644                 } else if (dtd != null && dtd.length() > 0) {
645                     _printer.printText( "<!DOCTYPE " );
646                     _printer.printText( rootTagName );
647                     _printer.printText( " [" );
648                     printText( dtd, true, true );
649                     _printer.printText( "]>" );
650                     _printer.breakLine();
651                 }
652             }
653         }
654         _started = true;
655         // Always serialize these, even if not te first root element.
656
serializePreRoot();
657     }
658
659
660     /**
661      * Called to serialize a DOM element. Equivalent to calling {@link
662      * #startElement}, {@link #endElement} and serializing everything
663      * inbetween, but better optimized.
664      */

665     protected void serializeElement( Element JavaDoc elem )
666     throws IOException JavaDoc
667     {
668         Attr JavaDoc attr;
669         NamedNodeMap JavaDoc attrMap;
670         int i;
671         Node JavaDoc child;
672         ElementState state;
673         String JavaDoc name;
674         String JavaDoc value;
675         String JavaDoc tagName;
676
677         String JavaDoc prefix, localUri;
678         String JavaDoc uri;
679         if (fNamespaces) {
680             // local binder stores namespace declaration
681
// that has been printed out during namespace fixup of
682
// the current element
683
fLocalNSBinder.reset();
684
685             // add new namespace context
686
fNSBinder.pushContext();
687         }
688
689         if (DEBUG) {
690             System.out.println("==>startElement: " +elem.getNodeName() +" ns="+elem.getNamespaceURI());
691         }
692         tagName = elem.getTagName();
693         state = getElementState();
694         if (isDocumentState()) {
695             // If this is the root element handle it differently.
696
// If the first root element in the document, serialize
697
// the document's DOCTYPE. Space preserving defaults
698
// to that of the output format.
699

700             if (! _started) {
701                 startDocument( tagName);
702             }
703         } else {
704             // For any other element, if first in parent, then
705
// close parent's opening tag and use the parent's
706
// space preserving.
707
if (state.empty)
708                 _printer.printText( '>' );
709             // Must leave CData section first
710
if (state.inCData) {
711                 _printer.printText( "]]>" );
712                 state.inCData = false;
713             }
714             // Indent this element on a new line if the first
715
// content of the parent element or immediately
716
// following an element.
717
if (_indenting && ! state.preserveSpace &&
718                 ( state.empty || state.afterElement || state.afterComment))
719                 _printer.breakLine();
720         }
721
722         // Do not change the current element state yet.
723
// This only happens in endElement().
724
fPreserveSpace = state.preserveSpace;
725
726
727         int length = 0;
728         attrMap = null;
729         // retrieve attributes
730
if (elem.hasAttributes()) {
731             attrMap = elem.getAttributes();
732             length = attrMap.getLength();
733         }
734
735         if (!fNamespaces) { // no namespace fixup should be performed
736

737             // serialize element name
738
_printer.printText( '<' );
739             _printer.printText( tagName );
740             _printer.indent();
741
742             // For each attribute print it's name and value as one part,
743
// separated with a space so the element can be broken on
744
// multiple lines.
745
for ( i = 0 ; i < length ; ++i ) {
746                 attr = (Attr JavaDoc) attrMap.item( i );
747                 name = attr.getName();
748                 value = attr.getValue();
749                 if ( value == null )
750                     value = "";
751                 printAttribute (name, value, attr.getSpecified(), attr);
752             }
753         } else { // do namespace fixup
754

755             // REVISIT: some optimization could probably be done to avoid traversing
756
// attributes twice.
757
//
758

759             // ---------------------------------------
760
// record all valid namespace declarations
761
// before attempting to fix element's namespace
762
// ---------------------------------------
763

764             for (i = 0;i < length;i++) {
765
766                 attr = (Attr JavaDoc) attrMap.item( i );
767                 uri = attr.getNamespaceURI();
768                 // check if attribute is a namespace decl
769
if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
770
771                     value = attr.getNodeValue();
772                     if (value == null) {
773                         value=XMLSymbols.EMPTY_STRING;
774                     }
775
776                     if (value.equals(NamespaceContext.XMLNS_URI)) {
777                         if (fDOMErrorHandler != null) {
778                             String JavaDoc msg = DOMMessageFormatter.formatMessage(
779                                 DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null );
780                             modifyDOMError(msg, DOMError.SEVERITY_ERROR, attr);
781                             boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
782                             if (!continueProcess) {
783                                 // stop the namespace fixup and validation
784
throw new RuntimeException JavaDoc(
785                                     DOMMessageFormatter.formatMessage(
786                                     DOMMessageFormatter.SERIALIZER_DOMAIN,
787                                     "SerializationStopped", null));
788                             }
789                         }
790                     } else {
791                         prefix = attr.getPrefix();
792                         prefix = (prefix == null ||
793                                   prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
794                         String JavaDoc localpart = fSymbolTable.addSymbol( attr.getLocalName());
795                         if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
796
value = fSymbolTable.addSymbol(value);
797                             // record valid decl
798
if (value.length() != 0) {
799                                 fNSBinder.declarePrefix(localpart, value);
800                             } else {
801                                 // REVISIT: issue error on invalid declarations
802
// xmlns:foo = ""
803
}
804                             continue;
805                         } else { // xmlns
806
// empty prefix is always bound ("" or some string)
807

808                             value = fSymbolTable.addSymbol(value);
809                             fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, value);
810                             continue;
811                         }
812                     } // end-else: valid declaration
813
} // end-if: namespace declaration
814
} // end-for
815

816             //-----------------------
817
// get element uri/prefix
818
//-----------------------
819
uri = elem.getNamespaceURI();
820             prefix = elem.getPrefix();
821
822             //----------------------
823
// output element name
824
//----------------------
825
// REVISIT: this could be removed if we always convert empty string to null
826
// for the namespaces.
827
if ((uri !=null && prefix !=null ) && uri.length() == 0 && prefix.length()!=0) {
828                 // uri is an empty string and element has some prefix
829
// the namespace alg later will fix up the namespace attributes
830
// remove element prefix
831
prefix = null;
832                 _printer.printText( '<' );
833                 _printer.printText( elem.getLocalName() );
834                 _printer.indent();
835             } else {
836                 _printer.printText( '<' );
837                 _printer.printText( tagName );
838                 _printer.indent();
839             }
840
841
842             // ---------------------------------------------------------
843
// Fix up namespaces for element: per DOM L3
844
// Need to consider the following cases:
845
//
846
// case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/>
847
// Assume "foo", "ns1" are declared on the parent. We should not miss
848
// redeclaration for both "ns1" and default namespace. To solve this
849
// we add a local binder that stores declaration only for current element.
850
// This way we avoid outputing duplicate declarations for the same element
851
// as well as we are not omitting redeclarations.
852
//
853
// case 2: <elem xmlns="" xmlns="default"/>
854
// We need to bind default namespace to empty string, to be able to
855
// omit duplicate declarations for the same element
856
//
857
// case 3: <xsl:stylesheet xmlns:xsl="http://xsl">
858
// We create another element body bound to the "http://xsl" namespace
859
// as well as namespace attribute rebounding xsl to another namespace.
860
// <xsl:body xmlns:xsl="http://another">
861
// Need to make sure that the new namespace decl value is changed to
862
// "http://xsl"
863
//
864
// ---------------------------------------------------------
865
// check if prefix/namespace is correct for current element
866
// ---------------------------------------------------------
867

868
869             if (uri != null) { // Element has a namespace
870
uri = fSymbolTable.addSymbol(uri);
871                 prefix = (prefix == null ||
872                           prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
873                 if (fNSBinder.getURI(prefix) == uri) {
874                     // The xmlns:prefix=namespace or xmlns="default" was declared at parent.
875
// The binder always stores mapping of empty prefix to "".
876
// (NOTE: local binder does not store this kind of binding!)
877
// Thus the case where element was declared with uri="" (with or without a prefix)
878
// will be covered here.
879

880                 } else {
881                     // the prefix is either undeclared
882
// or
883
// conflict: the prefix is bound to another URI
884
printNamespaceAttr(prefix, uri);
885                     fLocalNSBinder.declarePrefix(prefix, uri);
886                     fNSBinder.declarePrefix(prefix, uri);
887                 }
888             } else { // Element has no namespace
889
if (elem.getLocalName() == null) {
890                     // DOM Level 1 node!
891
if (fDOMErrorHandler != null) {
892                         String JavaDoc msg = DOMMessageFormatter.formatMessage(
893                             DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName",
894                             new Object JavaDoc[]{elem.getNodeName()});
895                         modifyDOMError(msg,DOMError.SEVERITY_ERROR, elem);
896                         boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
897                         // REVISIT: should we terminate upon request?
898
if (!continueProcess) {
899                            throw new RuntimeException JavaDoc(
900                                DOMMessageFormatter.formatMessage(
901                                DOMMessageFormatter.SERIALIZER_DOMAIN,
902                                "SerializationStopped", null));
903                         }
904                     }
905                 } else { // uri=null and no colon (DOM L2 node)
906
uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
907
908                     if (uri !=null && uri.length() > 0) {
909                         // there is a default namespace decl that is bound to
910
// non-zero length uri, output xmlns=""
911
printNamespaceAttr(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
912                         fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
913                         fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
914                     }
915                 }
916             }
917
918
919             // -----------------------------------------
920
// Fix up namespaces for attributes: per DOM L3
921
// check if prefix/namespace is correct the attributes
922
// -----------------------------------------
923

924             for (i = 0; i < length; i++) {
925
926                 attr = (Attr JavaDoc) attrMap.item( i );
927                 value = attr.getValue();
928                 name = attr.getNodeName();
929               
930                 uri = attr.getNamespaceURI();
931
932                 // Fix attribute that was declared with a prefix and namespace=""
933
if (uri !=null && uri.length() == 0) {
934                     uri=null;
935                     // we must remove prefix for this attribute
936
name=attr.getLocalName();
937                 }
938
939                 if (DEBUG) {
940                     System.out.println("==>process attribute: "+attr.getNodeName());
941                 }
942                 // make sure that value is never null.
943
if (value == null) {
944                     value=XMLSymbols.EMPTY_STRING;
945                 }
946
947                 if (uri != null) { // attribute has namespace !=null
948
prefix = attr.getPrefix();
949                     prefix = prefix == null ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
950                     String JavaDoc localpart = fSymbolTable.addSymbol( attr.getLocalName());
951
952
953
954                     // ---------------------------------------------------
955
// print namespace declarations namespace declarations
956
// ---------------------------------------------------
957
if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
958                         // check if we need to output this declaration
959
prefix = attr.getPrefix();
960                         prefix = (prefix == null ||
961                                   prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
962                         localpart = fSymbolTable.addSymbol( attr.getLocalName());
963                         if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
964
localUri = fLocalNSBinder.getURI(localpart); // local prefix mapping
965
value = fSymbolTable.addSymbol(value);
966                             if (value.length() != 0 ) {
967                                 if (localUri == null) {
968                                     // declaration was not printed while fixing element namespace binding
969
printNamespaceAttr(localpart, value);
970                                     // case 4: <elem xmlns:xx="foo" xx:attr=""/>
971
// where attribute is bound to "bar".
972
// If the xmlns:xx is output here first, later we should not
973
// redeclare "xx" prefix. Instead we would pick up different prefix
974
// for the attribute.
975
// final: <elem xmlns:xx="foo" NS1:attr="" xmlns:NS1="bar"/>
976
fLocalNSBinder.declarePrefix(localpart, value);
977                                 }
978                             } else {
979                                 // REVISIT: issue error on invalid declarations
980
// xmlns:foo = ""
981
}
982                             continue;
983                         } else { // xmlns
984
// empty prefix is always bound ("" or some string)
985

986                             uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
987                             localUri=fLocalNSBinder.getURI(XMLSymbols.EMPTY_STRING);
988                             value = fSymbolTable.addSymbol(value);
989                             if (localUri == null ){
990                                 // declaration was not printed while fixing element namespace binding
991
printNamespaceAttr(XMLSymbols.EMPTY_STRING, value);
992                                 // case 4 does not apply here since attributes can't use
993
// default namespace
994
}
995                             continue;
996                         }
997
998                     }
999                     uri = fSymbolTable.addSymbol(uri);
1000
1001                    // find if for this prefix a URI was already declared
1002
String JavaDoc declaredURI = fNSBinder.getURI(prefix);
1003
1004                    if (prefix == XMLSymbols.EMPTY_STRING || declaredURI != uri) {
1005                        // attribute has no prefix (default namespace decl does not apply to attributes)
1006
// OR
1007
// attribute prefix is not declared
1008
// OR
1009
// conflict: attr URI does not match the prefix in scope
1010

1011                        name = attr.getNodeName();
1012                        // Find if any prefix for attributes namespace URI is available
1013
// in the scope
1014
String JavaDoc declaredPrefix = fNSBinder.getPrefix(uri);
1015
1016                        if (declaredPrefix !=null && declaredPrefix !=XMLSymbols.EMPTY_STRING) {
1017                            // use the prefix that was found
1018
prefix = declaredPrefix;
1019                            name=prefix+":"+localpart;
1020                        } else {
1021                            if (DEBUG) {
1022                                System.out.println("==> cound not find prefix for the attribute: " +prefix);
1023                            }
1024
1025                            if (prefix != XMLSymbols.EMPTY_STRING && fLocalNSBinder.getURI(prefix) == null) {
1026                                // the current prefix is not null and it has no in scope declaration
1027

1028                                // use this prefix
1029
} else {
1030                                // find a prefix following the pattern "NS" +index (starting at 1)
1031
// make sure this prefix is not declared in the current scope.
1032
int counter = 1;
1033                                prefix = fSymbolTable.addSymbol(PREFIX + counter++);
1034                                while (fLocalNSBinder.getURI(prefix)!=null) {
1035                                    prefix = fSymbolTable.addSymbol(PREFIX +counter++);
1036                                }
1037                                name=prefix+":"+localpart;
1038                            }
1039                            // add declaration for the new prefix
1040
printNamespaceAttr(prefix, uri);
1041                            value = fSymbolTable.addSymbol(value);
1042                            fLocalNSBinder.declarePrefix(prefix, value);
1043                            fNSBinder.declarePrefix(prefix, uri);
1044                        }
1045
1046                        // change prefix for this attribute
1047
}
1048
1049                    printAttribute (name, (value==null)?XMLSymbols.EMPTY_STRING:value, attr.getSpecified(), attr);
1050                } else { // attribute uri == null
1051
if (attr.getLocalName() == null) {
1052                        if (fDOMErrorHandler != null) {
1053                            String JavaDoc msg = DOMMessageFormatter.formatMessage(
1054                                DOMMessageFormatter.DOM_DOMAIN,
1055                                "NullLocalAttrName", new Object JavaDoc[]{attr.getNodeName()});
1056                            modifyDOMError(msg, DOMError.SEVERITY_ERROR, attr);
1057                            boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
1058                            if (!continueProcess) {
1059                                // stop the namespace fixup and validation
1060
throw new RuntimeException JavaDoc(
1061                                   DOMMessageFormatter.formatMessage(
1062                                   DOMMessageFormatter.SERIALIZER_DOMAIN,
1063                                   "SerializationStopped", null));
1064                            }
1065                        }
1066                        printAttribute (name, value, attr.getSpecified(), attr);
1067                    } else { // uri=null and no colon
1068

1069                        // no fix up is needed: default namespace decl does not
1070
// apply to attributes
1071
printAttribute (name, value, attr.getSpecified(), attr);
1072                    }
1073                }
1074            } // end loop for attributes
1075

1076        }// end namespace fixup algorithm
1077

1078
1079        // If element has children, then serialize them, otherwise
1080
// serialize en empty tag.
1081
if (elem.hasChildNodes()) {
1082            // Enter an element state, and serialize the children
1083
// one by one. Finally, end the element.
1084
state = enterElementState( null, null, tagName, fPreserveSpace );
1085            state.doCData = _format.isCDataElement( tagName );
1086            state.unescaped = _format.isNonEscapingElement( tagName );
1087            child = elem.getFirstChild();
1088            while (child != null) {
1089                serializeNode( child );
1090                child = child.getNextSibling();
1091            }
1092            if (fNamespaces) {
1093                fNSBinder.popContext();
1094            }
1095            endElementIO( null, null, tagName );
1096        } else {
1097            if (DEBUG) {
1098                System.out.println("==>endElement: " +elem.getNodeName());
1099            }
1100            if (fNamespaces) {
1101                fNSBinder.popContext();
1102            }
1103            _printer.unindent();
1104            _printer.printText( "/>" );
1105            // After element but parent element is no longer empty.
1106
state.afterElement = true;
1107            state.afterComment = false;
1108            state.empty = false;
1109            if (isDocumentState())
1110                _printer.flush();
1111        }
1112    }
1113
1114
1115
1116    /**
1117     * Serializes a namespace attribute with the given prefix and value for URI.
1118     * In case prefix is empty will serialize default namespace declaration.
1119     *
1120     * @param prefix
1121     * @param uri
1122     * @exception IOException
1123     */

1124
1125    private void printNamespaceAttr(String JavaDoc prefix, String JavaDoc uri) throws IOException JavaDoc{
1126        _printer.printSpace();
1127        if (prefix == XMLSymbols.EMPTY_STRING) {
1128            if (DEBUG) {
1129                System.out.println("=>add xmlns=\""+uri+"\" declaration");
1130            }
1131            _printer.printText( XMLSymbols.PREFIX_XMLNS );
1132        } else {
1133            if (DEBUG) {
1134                System.out.println("=>add xmlns:"+prefix+"=\""+uri+"\" declaration");
1135            }
1136            _printer.printText( "xmlns:"+prefix );
1137        }
1138        _printer.printText( "=\"" );
1139        printEscaped( uri );
1140        _printer.printText( '"' );
1141    }
1142
1143
1144
1145    /**
1146     * Prints attribute.
1147     * NOTE: xml:space attribute modifies output format
1148     *
1149     * @param name
1150     * @param value
1151     * @param isSpecified
1152     * @exception IOException
1153     */

1154    private void printAttribute (String JavaDoc name, String JavaDoc value, boolean isSpecified, Attr JavaDoc attr) throws IOException JavaDoc{
1155
1156        if (isSpecified || (features & DOMSerializerImpl.DISCARDDEFAULT) == 0) {
1157            if (fDOMFilter !=null &&
1158                (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)!= 0) {
1159                short code = fDOMFilter.acceptNode(attr);
1160                switch (code) {
1161                    case NodeFilter.FILTER_REJECT:
1162                    case NodeFilter.FILTER_SKIP: {
1163                        return;
1164                    }
1165                    default: {
1166                        // fall through
1167
}
1168                }
1169            }
1170            _printer.printSpace();
1171            _printer.printText( name );
1172            _printer.printText( "=\"" );
1173            printEscaped( value );
1174            _printer.printText( '"' );
1175        }
1176
1177        // If the attribute xml:space exists, determine whether
1178
// to preserve spaces in this and child nodes based on
1179
// its value.
1180
if (name.equals( "xml:space" )) {
1181            if (value.equals( "preserve" ))
1182                fPreserveSpace = true;
1183            else
1184                fPreserveSpace = _format.getPreserveSpace();
1185        }
1186    }
1187
1188    protected String JavaDoc getEntityRef( int ch ) {
1189        // Encode special XML characters into the equivalent character references.
1190
// These five are defined by default for all XML documents.
1191
switch (ch) {
1192        case '<':
1193            return "lt";
1194        case '>':
1195            return "gt";
1196        case '"':
1197            return "quot";
1198        case '\'':
1199            return "apos";
1200        case '&':
1201            return "amp";
1202        }
1203        return null;
1204    }
1205
1206
1207    /** Retrieve and remove the namespaces declarations from the list of attributes.
1208     *
1209     */

1210    private Attributes JavaDoc extractNamespaces( Attributes JavaDoc attrs )
1211    throws SAXException JavaDoc
1212    {
1213        AttributesImpl JavaDoc attrsOnly;
1214        String JavaDoc rawName;
1215        int i;
1216        int indexColon;
1217        String JavaDoc prefix;
1218        int length;
1219
1220        if (attrs == null) {
1221            return null;
1222        }
1223        length = attrs.getLength();
1224        attrsOnly = new AttributesImpl JavaDoc( attrs );
1225
1226        for (i = length - 1 ; i >= 0 ; --i) {
1227            rawName = attrsOnly.getQName( i );
1228
1229            //We have to exclude the namespaces declarations from the attributes
1230
//Append only when the feature http://xml.org/sax/features/namespace-prefixes"
1231
//is TRUE
1232
if (rawName.startsWith( "xmlns" )) {
1233                if (rawName.length() == 5) {
1234                    startPrefixMapping( "", attrs.getValue( i ) );
1235                    attrsOnly.removeAttribute( i );
1236                } else if (rawName.charAt(5) == ':') {
1237                    startPrefixMapping(rawName.substring(6), attrs.getValue(i));
1238                    attrsOnly.removeAttribute( i );
1239                }
1240            }
1241        }
1242        return attrsOnly;
1243    }
1244
1245    //
1246
// Printing attribute value
1247
//
1248
protected void printEscaped(String JavaDoc source) throws IOException JavaDoc {
1249        int length = source.length();
1250        for (int i = 0; i < length; ++i) {
1251            int ch = source.charAt(i);
1252            if (!XMLChar.isValid(ch)) {
1253                if (++i < length) {
1254                    surrogates(ch, source.charAt(i));
1255                } else {
1256                    fatalError("The character '" + (char) ch + "' is an invalid XML character");
1257                }
1258                continue;
1259            }
1260            // escape NL, CR, TAB
1261
if (ch == '\n' || ch == '\r' || ch == '\t') {
1262                printHex(ch);
1263            } else if (ch == '<') {
1264                _printer.printText("&lt;");
1265            } else if (ch == '&') {
1266                _printer.printText("&amp;");
1267            } else if (ch == '"') {
1268                _printer.printText("&quot;");
1269            } else if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch))) {
1270                _printer.printText((char) ch);
1271            } else {
1272                printHex(ch);
1273            }
1274        }
1275    }
1276
1277    /** print text data */
1278    protected void printXMLChar( int ch) throws IOException JavaDoc {
1279        if (ch == '\r') {
1280            printHex(ch);
1281        } else if ( ch == '<') {
1282            _printer.printText("&lt;");
1283        } else if (ch == '&') {
1284            _printer.printText("&amp;");
1285        } else if (ch == '>'){
1286            // character sequence "]]>" can't appear in content, therefore
1287
// we should escape '>'
1288
_printer.printText("&gt;");
1289        } else if ( ch == '\n' || ch == '\t' ||
1290                    ( ch >= ' ' && _encodingInfo.isPrintable((char)ch))) {
1291            _printer.printText((char)ch);
1292        } else {
1293            printHex(ch);
1294        }
1295    }
1296
1297    protected void printText( String JavaDoc text, boolean preserveSpace, boolean unescaped )
1298    throws IOException JavaDoc {
1299        int index;
1300        char ch;
1301        int length = text.length();
1302        if ( preserveSpace ) {
1303            // Preserving spaces: the text must print exactly as it is,
1304
// without breaking when spaces appear in the text and without
1305
// consolidating spaces. If a line terminator is used, a line
1306
// break will occur.
1307
for ( index = 0 ; index < length ; ++index ) {
1308                ch = text.charAt( index );
1309                if (!XMLChar.isValid(ch)) {
1310                    // check if it is surrogate
1311
if (++index <length) {
1312                        surrogates(ch, text.charAt(index));
1313                    } else {
1314                        fatalError("The character '"+(char)ch+"' is an invalid XML character");
1315                    }
1316                    continue;
1317                }
1318                if ( unescaped ) {
1319                    _printer.printText( ch );
1320                } else
1321                    printXMLChar( ch );
1322            }
1323        } else {
1324            // Not preserving spaces: print one part at a time, and
1325
// use spaces between parts to break them into different
1326
// lines. Spaces at beginning of line will be stripped
1327
// by printing mechanism. Line terminator is treated
1328
// no different than other text part.
1329
for ( index = 0 ; index < length ; ++index ) {
1330                ch = text.charAt( index );
1331                if (!XMLChar.isValid(ch)) {
1332                    // check if it is surrogate
1333
if (++index <length) {
1334                        surrogates(ch, text.charAt(index));
1335                    } else {
1336                        fatalError("The character '"+(char)ch+"' is an invalid XML character");
1337                    }
1338                    continue;
1339                }
1340
1341                if ( unescaped )
1342                    _printer.printText( ch );
1343                else
1344                    printXMLChar( ch);
1345            }
1346        }
1347    }
1348
1349
1350
1351    protected void printText( char[] chars, int start, int length,
1352                              boolean preserveSpace, boolean unescaped ) throws IOException JavaDoc {
1353        int index;
1354        char ch;
1355
1356        if ( preserveSpace ) {
1357            // Preserving spaces: the text must print exactly as it is,
1358
// without breaking when spaces appear in the text and without
1359
// consolidating spaces. If a line terminator is used, a line
1360
// break will occur.
1361
while ( length-- > 0 ) {
1362                ch = chars[ start ];
1363                ++start;
1364                if (!XMLChar.isValid(ch)) {
1365                    // check if it is surrogate
1366
if (++start <length) {
1367                        surrogates(ch, chars[start]);
1368                    } else {
1369                        fatalError("The character '"+(char)ch+"' is an invalid XML character");
1370                    }
1371                    continue;
1372                }
1373                if ( unescaped )
1374                    _printer.printText( ch );
1375                else
1376                    printXMLChar( ch );
1377            }
1378        } else {
1379            // Not preserving spaces: print one part at a time, and
1380
// use spaces between parts to break them into different
1381
// lines. Spaces at beginning of line will be stripped
1382
// by printing mechanism. Line terminator is treated
1383
// no different than other text part.
1384
while ( length-- > 0 ) {
1385                ch = chars[ start ];
1386                ++start;
1387
1388                if (!XMLChar.isValid(ch)) {
1389                    // check if it is surrogate
1390
if (++start <length) {
1391                        surrogates(ch, chars[start]);
1392                    } else {
1393                        fatalError("The character '"+(char)ch+"' is an invalid XML character");
1394                    }
1395                    continue;
1396                }
1397                if ( unescaped )
1398                    _printer.printText( ch );
1399                else
1400                    printXMLChar( ch );
1401            }
1402        }
1403    }
1404
1405
1406   /**
1407    * DOM Level 3:
1408    * Check a node to determine if it contains unbound namespace prefixes.
1409    *
1410    * @param node The node to check for unbound namespace prefices
1411    */

1412    protected void checkUnboundNamespacePrefixedNode (Node JavaDoc node) throws IOException JavaDoc{
1413
1414        if (fNamespaces) {
1415
1416            if (DEBUG) {
1417                System.out.println("==>serializeNode("+node.getNodeName()+") [Entity Reference - Namespaces on]");
1418                System.out.println("==>Declared Prefix Count: " + fNSBinder.getDeclaredPrefixCount());
1419                System.out.println("==>Node Name: " + node.getNodeName());
1420                System.out.println("==>First Child Node Name: " + node.getFirstChild().getNodeName());
1421                System.out.println("==>First Child Node Prefix: " + node.getFirstChild().getPrefix());
1422                System.out.println("==>First Child Node NamespaceURI: " + node.getFirstChild().getNamespaceURI());
1423            }
1424
1425        
1426            Node JavaDoc child, next;
1427            for (child = node.getFirstChild(); child != null; child = next) {
1428                next = child.getNextSibling();
1429                if (DEBUG) {
1430                    System.out.println("==>serializeNode("+child.getNodeName()+") [Child Node]");
1431                    System.out.println("==>serializeNode("+child.getPrefix()+") [Child Node Prefix]");
1432                }
1433    
1434                //If a NamespaceURI is not declared for the current
1435
//node's prefix, raise a fatal error.
1436
String JavaDoc prefix = child.getPrefix();
1437                if (fNSBinder.getURI(prefix) == null && prefix != null) {
1438                    fatalError("The replacement text of the entity node '"
1439                                + node.getNodeName()
1440                                + "' contains an element node '"
1441                                + child.getNodeName()
1442                                + "' with an undeclared prefix '"
1443                                + prefix + "'.");
1444                }
1445
1446                if (child.getNodeType() == Node.ELEMENT_NODE) {
1447                    
1448                    NamedNodeMap JavaDoc attrs = child.getAttributes();
1449                    
1450                    for (int i = 0; i< attrs.getLength(); i++ ) {
1451                        
1452                        String JavaDoc attrPrefix = attrs.item(i).getPrefix();
1453                        if (fNSBinder.getURI(attrPrefix) == null && attrPrefix != null) {
1454                            fatalError("The replacement text of the entity node '"
1455                                        + node.getNodeName()
1456                                        + "' contains an element node '"
1457                                        + child.getNodeName()
1458                                        + "' with an attribute '"
1459                                        + attrs.item(i).getNodeName()
1460                                        + "' an undeclared prefix '"
1461                                        + attrPrefix + "'.");
1462                        }
1463                        
1464                    }
1465
1466                }
1467                    
1468                if (child.hasChildNodes()) {
1469                    checkUnboundNamespacePrefixedNode(child);
1470                }
1471            }
1472        }
1473    }
1474
1475    public boolean reset() {
1476        super.reset();
1477        if (fNSBinder != null){
1478            fNSBinder.reset();
1479            // during serialization always have a mapping to empty string
1480
// so we assume there is a declaration.
1481
fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
1482        }
1483        return true;
1484    }
1485
1486}
1487
1488
1489
1490
1491
Popular Tags