KickJava   Java API By Example, From Geeks To Geeks.

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


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 comments to preserve whitespaces and add a line break
61
// when indenting. Reported by Gervase Markham <gerv@gerv.net>
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
// Sep 13, 2000:
67
// CR in character data will print as &#0D;
68
// Aug 25, 2000:
69
// Fixed processing instruction printing inside element content
70
// to not escape content. Reported by Mikael Staldal
71
// <d96-mst@d.kth.se>
72
// Aug 25, 2000:
73
// Added ability to omit comments.
74
// Contributed by Anupam Bagchi <abagchi@jtcsv.com>
75
// Aug 26, 2000:
76
// Fixed bug in newline handling when preserving spaces.
77
// Contributed by Mike Dusseault <mdusseault@home.com>
78
// Aug 29, 2000:
79
// Fixed state.unescaped not being set to false when
80
// entering element state.
81
// Reported by Lowell Vaughn <lvaughn@agillion.com>
82

83
84 package com.sun.org.apache.xml.internal.serialize;
85
86
87 import java.io.IOException JavaDoc;
88 import java.io.OutputStream JavaDoc;
89 import java.io.Writer JavaDoc;
90 import java.util.Hashtable JavaDoc;
91 import java.util.Vector JavaDoc;
92
93 import com.sun.org.apache.xerces.internal.dom.DOMErrorImpl;
94 import com.sun.org.apache.xerces.internal.dom.DOMLocatorImpl;
95 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter;
96 import com.sun.org.apache.xerces.internal.util.XMLChar;
97 import org.w3c.dom.DOMImplementation JavaDoc;
98 import org.w3c.dom.Document JavaDoc;
99 import org.w3c.dom.DocumentFragment JavaDoc;
100 import org.w3c.dom.DocumentType JavaDoc;
101 import org.w3c.dom.DOMError JavaDoc;
102 import org.w3c.dom.DOMErrorHandler JavaDoc;
103 import org.w3c.dom.Element JavaDoc;
104 import org.w3c.dom.Entity JavaDoc;
105 import org.w3c.dom.NamedNodeMap JavaDoc;
106 import org.w3c.dom.Node JavaDoc;
107 import org.w3c.dom.Notation JavaDoc;
108 import org.w3c.dom.ls.LSSerializerFilter JavaDoc;
109 import org.w3c.dom.traversal.NodeFilter;
110 import org.xml.sax.ContentHandler JavaDoc;
111 import org.xml.sax.DTDHandler JavaDoc;
112 import org.xml.sax.DocumentHandler JavaDoc;
113 import org.xml.sax.Locator JavaDoc;
114 import org.xml.sax.SAXException JavaDoc;
115 import org.xml.sax.ext.DeclHandler JavaDoc;
116 import org.xml.sax.ext.LexicalHandler JavaDoc;
117
118 /**
119  * Base class for a serializer supporting both DOM and SAX pretty
120  * serializing of XML/HTML/XHTML documents. Derives classes perform
121  * the method-specific serializing, this class provides the common
122  * serializing mechanisms.
123  * <p>
124  * The serializer must be initialized with the proper writer and
125  * output format before it can be used by calling {@link #setOutputCharStream}
126  * or {@link #setOutputByteStream} for the writer and {@link #setOutputFormat}
127  * for the output format.
128  * <p>
129  * The serializer can be reused any number of times, but cannot
130  * be used concurrently by two threads.
131  * <p>
132  * If an output stream is used, the encoding is taken from the
133  * output format (defaults to <tt>UTF-8</tt>). If a writer is
134  * used, make sure the writer uses the same encoding (if applies)
135  * as specified in the output format.
136  * <p>
137  * The serializer supports both DOM and SAX. DOM serializing is done
138  * by calling {@link #serialize(Document)} and SAX serializing is done by firing
139  * SAX events and using the serializer as a document handler.
140  * This also applies to derived class.
141  * <p>
142  * If an I/O exception occurs while serializing, the serializer
143  * will not throw an exception directly, but only throw it
144  * at the end of serializing (either DOM or SAX's {@link
145  * org.xml.sax.DocumentHandler#endDocument}.
146  * <p>
147  * For elements that are not specified as whitespace preserving,
148  * the serializer will potentially break long text lines at space
149  * boundaries, indent lines, and serialize elements on separate
150  * lines. Line terminators will be regarded as spaces, and
151  * spaces at beginning of line will be stripped.
152  * <p>
153  * When indenting, the serializer is capable of detecting seemingly
154  * element content, and serializing these elements indented on separate
155  * lines. An element is serialized indented when it is the first or
156  * last child of an element, or immediate following or preceding
157  * another element.
158  *
159  *
160  * @version $Revision: 1.51 $ $Date: 2004/02/12 16:56:07 $
161  * @author <a HREF="mailto:arkin@intalio.com">Assaf Arkin</a>
162  * @author <a HREF="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
163  * @author Elena Litani, IBM
164  * @see Serializer
165  * @see LSSerializer
166  */

167 public abstract class BaseMarkupSerializer
168     implements ContentHandler JavaDoc, DocumentHandler JavaDoc, LexicalHandler JavaDoc,
169                DTDHandler JavaDoc, DeclHandler JavaDoc, DOMSerializer, Serializer
170 {
171
172     // DOM L3 implementation
173
protected short features = 0xFFFFFFFF;
174     protected DOMErrorHandler JavaDoc fDOMErrorHandler;
175     protected final DOMErrorImpl fDOMError = new DOMErrorImpl();
176     protected LSSerializerFilter JavaDoc fDOMFilter;
177
178     protected EncodingInfo _encodingInfo;
179
180
181     /**
182      * Holds array of all element states that have been entered.
183      * The array is automatically resized. When leaving an element,
184      * it's state is not removed but reused when later returning
185      * to the same nesting level.
186      */

187     private ElementState[] _elementStates;
188
189
190     /**
191      * The index of the next state to place in the array,
192      * or one plus the index of the current state. When zero,
193      * we are in no state.
194      */

195     private int _elementStateCount;
196
197
198     /**
199      * Vector holding comments and PIs that come before the root
200      * element (even after it), see {@link #serializePreRoot}.
201      */

202     private Vector JavaDoc _preRoot;
203
204
205     /**
206      * If the document has been started (header serialized), this
207      * flag is set to true so it's not started twice.
208      */

209     protected boolean _started;
210
211
212     /**
213      * True if the serializer has been prepared. This flag is set
214      * to false when the serializer is reset prior to using it,
215      * and to true after it has been prepared for usage.
216      */

217     private boolean _prepared;
218
219
220     /**
221      * Association between namespace URIs (keys) and prefixes (values).
222      * Accumulated here prior to starting an element and placing this
223      * list in the element state.
224      */

225     protected Hashtable JavaDoc _prefixes;
226
227
228     /**
229      * The system identifier of the document type, if known.
230      */

231     protected String JavaDoc _docTypePublicId;
232
233
234     /**
235      * The system identifier of the document type, if known.
236      */

237     protected String JavaDoc _docTypeSystemId;
238
239
240     /**
241      * The output format associated with this serializer. This will never
242      * be a null reference. If no format was passed to the constructor,
243      * the default one for this document type will be used. The format
244      * object is never changed by the serializer.
245      */

246     protected OutputFormat _format;
247
248
249     /**
250      * The printer used for printing text parts.
251      */

252     protected Printer _printer;
253
254
255     /**
256      * True if indenting printer.
257      */

258     protected boolean _indenting;
259
260     /** Temporary buffer to store character data */
261     protected final StringBuffer JavaDoc fStrBuffer = new StringBuffer JavaDoc(40);
262
263     /**
264      * The underlying writer.
265      */

266     private Writer JavaDoc _writer;
267
268
269     /**
270      * The output stream.
271      */

272     private OutputStream JavaDoc _output;
273
274     /** Current node that is being processed */
275     protected Node JavaDoc fCurrentNode = null;
276
277     
278
279     //--------------------------------//
280
// Constructor and initialization //
281
//--------------------------------//
282

283
284     /**
285      * Protected constructor can only be used by derived class.
286      * Must initialize the serializer before serializing any document,
287      * by calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
288          * first
289      */

290     protected BaseMarkupSerializer( OutputFormat format )
291     {
292         int i;
293
294         _elementStates = new ElementState[ 10 ];
295         for ( i = 0 ; i < _elementStates.length ; ++i )
296             _elementStates[ i ] = new ElementState();
297         _format = format;
298     }
299
300
301     public DocumentHandler JavaDoc asDocumentHandler()
302         throws IOException JavaDoc
303     {
304         prepare();
305         return this;
306     }
307
308
309     public ContentHandler JavaDoc asContentHandler()
310         throws IOException JavaDoc
311     {
312         prepare();
313         return this;
314     }
315
316
317     public DOMSerializer asDOMSerializer()
318         throws IOException JavaDoc
319     {
320         prepare();
321         return this;
322     }
323
324
325     public void setOutputByteStream( OutputStream JavaDoc output )
326     {
327         if ( output == null ) {
328             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
329                                                            "ArgumentIsNull", new Object JavaDoc[]{"output"});
330             throw new NullPointerException JavaDoc(msg);
331         }
332         _output = output;
333         _writer = null;
334         reset();
335     }
336
337
338     public void setOutputCharStream( Writer JavaDoc writer )
339     {
340         if ( writer == null ) {
341             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
342                                                            "ArgumentIsNull", new Object JavaDoc[]{"writer"});
343             throw new NullPointerException JavaDoc(msg);
344         }
345         _writer = writer;
346         _output = null;
347         reset();
348     }
349
350
351     public void setOutputFormat( OutputFormat format )
352     {
353         if ( format == null ) {
354             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
355                                                            "ArgumentIsNull", new Object JavaDoc[]{"format"});
356             throw new NullPointerException JavaDoc(msg);
357         }
358         _format = format;
359         reset();
360     }
361
362
363     public boolean reset()
364     {
365         if ( _elementStateCount > 1 ) {
366             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
367                                                            "ResetInMiddle", null);
368             throw new IllegalStateException JavaDoc(msg);
369         }
370         _prepared = false;
371         fCurrentNode = null;
372         fStrBuffer.setLength(0);
373         return true;
374     }
375
376
377     protected void prepare()
378         throws IOException JavaDoc
379     {
380         if ( _prepared )
381             return;
382
383         if ( _writer == null && _output == null ) {
384             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
385                                                            "NoWriterSupplied", null);
386             throw new IOException JavaDoc(msg);
387         }
388         // If the output stream has been set, use it to construct
389
// the writer. It is possible that the serializer has been
390
// reused with the same output stream and different encoding.
391

392         _encodingInfo = _format.getEncodingInfo();
393
394         if ( _output != null ) {
395             _writer = _encodingInfo.getWriter(_output);
396         }
397
398         if ( _format.getIndenting() ) {
399             _indenting = true;
400             _printer = new IndentPrinter( _writer, _format );
401         } else {
402             _indenting = false;
403             _printer = new Printer( _writer, _format );
404         }
405
406         ElementState state;
407
408         _elementStateCount = 0;
409         state = _elementStates[ 0 ];
410         state.namespaceURI = null;
411         state.localName = null;
412         state.rawName = null;
413         state.preserveSpace = _format.getPreserveSpace();
414         state.empty = true;
415         state.afterElement = false;
416         state.afterComment = false;
417         state.doCData = state.inCData = false;
418         state.prefixes = null;
419
420         _docTypePublicId = _format.getDoctypePublic();
421         _docTypeSystemId = _format.getDoctypeSystem();
422         _started = false;
423         _prepared = true;
424     }
425
426
427
428     //----------------------------------//
429
// DOM document serializing methods //
430
//----------------------------------//
431

432
433     /**
434      * Serializes the DOM element using the previously specified
435      * writer and output format. Throws an exception only if
436      * an I/O exception occured while serializing.
437      *
438      * @param elem The element to serialize
439      * @throws IOException An I/O exception occured while
440      * serializing
441      */

442     public void serialize( Element JavaDoc elem )
443         throws IOException JavaDoc
444     {
445         reset();
446         prepare();
447         serializeNode( elem );
448         _printer.flush();
449         if ( _printer.getException() != null )
450             throw _printer.getException();
451     }
452
453
454     /**
455      * Serializes the DOM document fragmnt using the previously specified
456      * writer and output format. Throws an exception only if
457      * an I/O exception occured while serializing.
458      *
459      * @param elem The element to serialize
460      * @throws IOException An I/O exception occured while
461      * serializing
462      */

463     public void serialize( DocumentFragment JavaDoc frag )
464         throws IOException JavaDoc
465     {
466         reset();
467         prepare();
468         serializeNode( frag );
469         _printer.flush();
470         if ( _printer.getException() != null )
471             throw _printer.getException();
472     }
473
474
475     /**
476      * Serializes the DOM document using the previously specified
477      * writer and output format. Throws an exception only if
478      * an I/O exception occured while serializing.
479      *
480      * @param doc The document to serialize
481      * @throws IOException An I/O exception occured while
482      * serializing
483      */

484     public void serialize( Document JavaDoc doc )
485         throws IOException JavaDoc
486     {
487         reset();
488         prepare();
489         serializeNode( doc );
490         serializePreRoot();
491         _printer.flush();
492         if ( _printer.getException() != null )
493             throw _printer.getException();
494     }
495
496
497     //------------------------------------------//
498
// SAX document handler serializing methods //
499
//------------------------------------------//
500

501
502     public void startDocument()
503         throws SAXException JavaDoc
504     {
505         try {
506             prepare();
507         } catch ( IOException JavaDoc except ) {
508             throw new SAXException JavaDoc( except.toString() );
509         }
510         // Nothing to do here. All the magic happens in startDocument(String)
511
}
512     
513     
514     public void characters( char[] chars, int start, int length )
515         throws SAXException JavaDoc
516     {
517         ElementState state;
518
519         try {
520         state = content();
521
522         // Check if text should be print as CDATA section or unescaped
523
// based on elements listed in the output format (the element
524
// state) or whether we are inside a CDATA section or entity.
525

526         if ( state.inCData || state.doCData ) {
527             int saveIndent;
528
529             // Print a CDATA section. The text is not escaped, but ']]>'
530
// appearing in the code must be identified and dealt with.
531
// The contents of a text node is considered space preserving.
532
if ( ! state.inCData ) {
533                 _printer.printText( "<![CDATA[" );
534                 state.inCData = true;
535             }
536             saveIndent = _printer.getNextIndent();
537             _printer.setNextIndent( 0 );
538             char ch;
539             for ( int index = start ; index < length ; ++index ) {
540                 ch = chars[index];
541                 if ( ch == ']' && index + 2 < length &&
542                      chars[ index + 1 ] == ']' && chars[ index + 2 ] == '>' ) {
543                     _printer.printText("]]]]><![CDATA[>");
544                     index +=2;
545                     continue;
546                 }
547                 if (!XMLChar.isValid(ch)) {
548                     // check if it is surrogate
549
if (++index <length) {
550                         surrogates(ch, chars[index]);
551                     }
552                     else {
553                         fatalError("The character '"+(char)ch+"' is an invalid XML character");
554                     }
555                     continue;
556                 } else {
557                     if ( ( ch >= ' ' && _encodingInfo.isPrintable((char)ch) && ch != 0xF7 ) ||
558                         ch == '\n' || ch == '\r' || ch == '\t' ) {
559                         _printer.printText((char)ch);
560                     } else {
561                         // The character is not printable -- split CDATA section
562
_printer.printText("]]>&#x");
563                         _printer.printText(Integer.toHexString(ch));
564                         _printer.printText(";<![CDATA[");
565                     }
566                 }
567             }
568             _printer.setNextIndent( saveIndent );
569
570         } else {
571
572             int saveIndent;
573
574             if ( state.preserveSpace ) {
575                 // If preserving space then hold of indentation so no
576
// excessive spaces are printed at line breaks, escape
577
// the text content without replacing spaces and print
578
// the text breaking only at line breaks.
579
saveIndent = _printer.getNextIndent();
580                 _printer.setNextIndent( 0 );
581                 printText( chars, start, length, true, state.unescaped );
582                 _printer.setNextIndent( saveIndent );
583             } else {
584                 printText( chars, start, length, false, state.unescaped );
585             }
586         }
587         } catch ( IOException JavaDoc except ) {
588             throw new SAXException JavaDoc( except );
589         }
590     }
591
592
593     public void ignorableWhitespace( char[] chars, int start, int length )
594         throws SAXException JavaDoc
595     {
596         int i;
597
598         try {
599         content();
600
601         // Print ignorable whitespaces only when indenting, after
602
// all they are indentation. Cancel the indentation to
603
// not indent twice.
604
if ( _indenting ) {
605             _printer.setThisIndent( 0 );
606             for ( i = start ; length-- > 0 ; ++i )
607                 _printer.printText( chars[ i ] );
608         }
609         } catch ( IOException JavaDoc except ) {
610             throw new SAXException JavaDoc( except );
611         }
612     }
613
614
615     public final void processingInstruction( String JavaDoc target, String JavaDoc code )
616         throws SAXException JavaDoc
617     {
618         try {
619             processingInstructionIO( target, code );
620         } catch ( IOException JavaDoc except ) {
621         throw new SAXException JavaDoc( except );
622         }
623     }
624
625     public void processingInstructionIO( String JavaDoc target, String JavaDoc code )
626         throws IOException JavaDoc
627     {
628         int index;
629         ElementState state;
630
631         state = content();
632
633         // Create the processing instruction textual representation.
634
// Make sure we don't have '?>' inside either target or code.
635
index = target.indexOf( "?>" );
636         if ( index >= 0 )
637             fStrBuffer.append( "<?" ).append( target.substring( 0, index ) );
638         else
639             fStrBuffer.append( "<?" ).append( target );
640         if ( code != null ) {
641             fStrBuffer.append( ' ' );
642             index = code.indexOf( "?>" );
643             if ( index >= 0 )
644                 fStrBuffer.append( code.substring( 0, index ) );
645             else
646                 fStrBuffer.append( code );
647         }
648         fStrBuffer.append( "?>" );
649
650         // If before the root element (or after it), do not print
651
// the PI directly but place it in the pre-root vector.
652
if ( isDocumentState() ) {
653             if ( _preRoot == null )
654                 _preRoot = new Vector JavaDoc();
655             _preRoot.addElement( fStrBuffer.toString() );
656         } else {
657             _printer.indent();
658             printText( fStrBuffer.toString(), true, true );
659             _printer.unindent();
660             if ( _indenting )
661             state.afterElement = true;
662         }
663
664         fStrBuffer.setLength(0);
665     }
666
667
668     public void comment( char[] chars, int start, int length )
669         throws SAXException JavaDoc
670     {
671         try {
672         comment( new String JavaDoc( chars, start, length ) );
673         } catch ( IOException JavaDoc except ) {
674             throw new SAXException JavaDoc( except );
675     }
676     }
677
678
679     public void comment( String JavaDoc text )
680         throws IOException JavaDoc
681     {
682         int index;
683         ElementState state;
684         
685         if ( _format.getOmitComments() )
686             return;
687
688         state = content();
689         // Create the processing comment textual representation.
690
// Make sure we don't have '-->' inside the comment.
691
index = text.indexOf( "-->" );
692         if ( index >= 0 )
693             fStrBuffer.append( "<!--" ).append( text.substring( 0, index ) ).append( "-->" );
694         else
695             fStrBuffer.append( "<!--" ).append( text ).append( "-->" );
696
697         // If before the root element (or after it), do not print
698
// the comment directly but place it in the pre-root vector.
699
if ( isDocumentState() ) {
700             if ( _preRoot == null )
701                 _preRoot = new Vector JavaDoc();
702             _preRoot.addElement( fStrBuffer.toString() );
703         } else {
704             // Indent this element on a new line if the first
705
// content of the parent element or immediately
706
// following an element.
707
if ( _indenting && ! state.preserveSpace)
708                 _printer.breakLine();
709                         _printer.indent();
710             printText( fStrBuffer.toString(), true, true );
711                         _printer.unindent();
712             if ( _indenting )
713                 state.afterElement = true;
714         }
715
716         fStrBuffer.setLength(0);
717     state.afterComment = true;
718     state.afterElement = false;
719     }
720
721
722     public void startCDATA()
723     {
724         ElementState state;
725
726         state = getElementState();
727         state.doCData = true;
728     }
729
730
731     public void endCDATA()
732     {
733         ElementState state;
734
735         state = getElementState();
736         state.doCData = false;
737     }
738
739
740     public void startNonEscaping()
741     {
742         ElementState state;
743
744         state = getElementState();
745         state.unescaped = true;
746     }
747
748
749     public void endNonEscaping()
750     {
751         ElementState state;
752
753         state = getElementState();
754         state.unescaped = false;
755     }
756
757
758     public void startPreserving()
759     {
760         ElementState state;
761
762         state = getElementState();
763         state.preserveSpace = true;
764     }
765
766
767     public void endPreserving()
768     {
769         ElementState state;
770
771         state = getElementState();
772         state.preserveSpace = false;
773     }
774
775
776     /**
777      * Called at the end of the document to wrap it up.
778      * Will flush the output stream and throw an exception
779      * if any I/O error occured while serializing.
780      *
781      * @throws SAXException An I/O exception occured during
782      * serializing
783      */

784     public void endDocument()
785         throws SAXException JavaDoc
786     {
787         try {
788         // Print all the elements accumulated outside of
789
// the root element.
790
serializePreRoot();
791         // Flush the output, this is necessary for fStrBuffered output.
792
_printer.flush();
793         } catch ( IOException JavaDoc except ) {
794             throw new SAXException JavaDoc( except );
795     }
796     }
797
798
799     public void startEntity( String JavaDoc name )
800     {
801         // ???
802
}
803
804
805     public void endEntity( String JavaDoc name )
806     {
807         // ???
808
}
809
810
811     public void setDocumentLocator( Locator JavaDoc locator )
812     {
813         // Nothing to do
814
}
815
816
817     //-----------------------------------------//
818
// SAX content handler serializing methods //
819
//-----------------------------------------//
820

821
822     public void skippedEntity ( String JavaDoc name )
823         throws SAXException JavaDoc
824     {
825         try {
826         endCDATA();
827         content();
828         _printer.printText( '&' );
829         _printer.printText( name );
830         _printer.printText( ';' );
831         } catch ( IOException JavaDoc except ) {
832             throw new SAXException JavaDoc( except );
833     }
834     }
835
836
837     public void startPrefixMapping( String JavaDoc prefix, String JavaDoc uri )
838         throws SAXException JavaDoc
839     {
840         if ( _prefixes == null )
841             _prefixes = new Hashtable JavaDoc();
842         _prefixes.put( uri, prefix == null ? "" : prefix );
843     }
844
845
846     public void endPrefixMapping( String JavaDoc prefix )
847         throws SAXException JavaDoc
848     {
849     }
850
851
852     //------------------------------------------//
853
// SAX DTD/Decl handler serializing methods //
854
//------------------------------------------//
855

856
857     public final void startDTD( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
858         throws SAXException JavaDoc
859     {
860         try {
861         _printer.enterDTD();
862         _docTypePublicId = publicId;
863         _docTypeSystemId = systemId;
864         } catch ( IOException JavaDoc except ) {
865             throw new SAXException JavaDoc( except );
866         }
867     }
868
869
870     public void endDTD()
871     {
872         // Nothing to do here, all the magic occurs in startDocument(String).
873
}
874
875
876     public void elementDecl( String JavaDoc name, String JavaDoc model )
877         throws SAXException JavaDoc
878     {
879         try {
880         _printer.enterDTD();
881         _printer.printText( "<!ELEMENT " );
882         _printer.printText( name );
883         _printer.printText( ' ' );
884         _printer.printText( model );
885         _printer.printText( '>' );
886         if ( _indenting )
887             _printer.breakLine();
888         } catch ( IOException JavaDoc except ) {
889             throw new SAXException JavaDoc( except );
890         }
891     }
892
893
894     public void attributeDecl( String JavaDoc eName, String JavaDoc aName, String JavaDoc type,
895                                String JavaDoc valueDefault, String JavaDoc value )
896         throws SAXException JavaDoc
897     {
898         try {
899         _printer.enterDTD();
900         _printer.printText( "<!ATTLIST " );
901         _printer.printText( eName );
902         _printer.printText( ' ' );
903         _printer.printText( aName );
904         _printer.printText( ' ' );
905         _printer.printText( type );
906         if ( valueDefault != null ) {
907             _printer.printText( ' ' );
908             _printer.printText( valueDefault );
909         }
910         if ( value != null ) {
911             _printer.printText( " \"" );
912             printEscaped( value );
913             _printer.printText( '"' );
914         }
915         _printer.printText( '>' );
916         if ( _indenting )
917             _printer.breakLine();
918         } catch ( IOException JavaDoc except ) {
919             throw new SAXException JavaDoc( except );
920     }
921     }
922
923
924     public void internalEntityDecl( String JavaDoc name, String JavaDoc value )
925         throws SAXException JavaDoc
926     {
927         try {
928         _printer.enterDTD();
929         _printer.printText( "<!ENTITY " );
930         _printer.printText( name );
931         _printer.printText( " \"" );
932         printEscaped( value );
933         _printer.printText( "\">" );
934         if ( _indenting )
935             _printer.breakLine();
936         } catch ( IOException JavaDoc except ) {
937             throw new SAXException JavaDoc( except );
938         }
939     }
940
941
942     public void externalEntityDecl( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
943         throws SAXException JavaDoc
944     {
945         try {
946         _printer.enterDTD();
947         unparsedEntityDecl( name, publicId, systemId, null );
948         } catch ( IOException JavaDoc except ) {
949             throw new SAXException JavaDoc( except );
950         }
951     }
952
953
954     public void unparsedEntityDecl( String JavaDoc name, String JavaDoc publicId,
955                                     String JavaDoc systemId, String JavaDoc notationName )
956         throws SAXException JavaDoc
957     {
958         try {
959         _printer.enterDTD();
960         if ( publicId == null ) {
961             _printer.printText( "<!ENTITY " );
962             _printer.printText( name );
963             _printer.printText( " SYSTEM " );
964             printDoctypeURL( systemId );
965         } else {
966             _printer.printText( "<!ENTITY " );
967             _printer.printText( name );
968             _printer.printText( " PUBLIC " );
969             printDoctypeURL( publicId );
970             _printer.printText( ' ' );
971             printDoctypeURL( systemId );
972         }
973         if ( notationName != null ) {
974             _printer.printText( " NDATA " );
975             _printer.printText( notationName );
976         }
977         _printer.printText( '>' );
978         if ( _indenting )
979             _printer.breakLine();
980         } catch ( IOException JavaDoc except ) {
981             throw new SAXException JavaDoc( except );
982     }
983     }
984
985
986     public void notationDecl( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
987         throws SAXException JavaDoc
988     {
989         try {
990         _printer.enterDTD();
991         if ( publicId != null ) {
992             _printer.printText( "<!NOTATION " );
993             _printer.printText( name );
994             _printer.printText( " PUBLIC " );
995             printDoctypeURL( publicId );
996             if ( systemId != null ) {
997                 _printer.printText( ' ' );
998                 printDoctypeURL( systemId );
999             }
1000        } else {
1001            _printer.printText( "<!NOTATION " );
1002            _printer.printText( name );
1003            _printer.printText( " SYSTEM " );
1004            printDoctypeURL( systemId );
1005        }
1006        _printer.printText( '>' );
1007        if ( _indenting )
1008            _printer.breakLine();
1009        } catch ( IOException JavaDoc except ) {
1010            throw new SAXException JavaDoc( except );
1011        }
1012    }
1013
1014
1015    //------------------------------------------//
1016
// Generic node serializing methods methods //
1017
//------------------------------------------//
1018

1019
1020    /**
1021     * Serialize the DOM node. This method is shared across XML, HTML and XHTML
1022     * serializers and the differences are masked out in a separate {@link
1023     * #serializeElement}.
1024     *
1025     * @param node The node to serialize
1026     * @see #serializeElement
1027     * @throws IOException An I/O exception occured while
1028     * serializing
1029     */

1030    protected void serializeNode( Node JavaDoc node )
1031        throws IOException JavaDoc
1032    {
1033        fCurrentNode = node;
1034
1035        // Based on the node type call the suitable SAX handler.
1036
// Only comments entities and documents which are not
1037
// handled by SAX are serialized directly.
1038
switch ( node.getNodeType() ) {
1039        case Node.TEXT_NODE : {
1040            String JavaDoc text;
1041
1042            text = node.getNodeValue();
1043            if ( text != null ) {
1044                if (fDOMFilter !=null &&
1045                    (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_TEXT)!= 0) {
1046                    short code = fDOMFilter.acceptNode(node);
1047                    switch (code) {
1048                        case NodeFilter.FILTER_REJECT:
1049                        case NodeFilter.FILTER_SKIP: {
1050                            break;
1051                        }
1052                        default: {
1053                            characters(text);
1054                        }
1055                    }
1056                }
1057                else if ( !_indenting || getElementState().preserveSpace
1058                     || (text.replace('\n',' ').trim().length() != 0))
1059                    characters( text );
1060            
1061            }
1062            break;
1063        }
1064
1065        case Node.CDATA_SECTION_NODE : {
1066            String JavaDoc text = node.getNodeValue();
1067            if ((features & DOMSerializerImpl.CDATA) != 0) {
1068                if (text != null) {
1069                    if (fDOMFilter != null
1070                        && (fDOMFilter.getWhatToShow()
1071                            & NodeFilter.SHOW_CDATA_SECTION)
1072                            != 0) {
1073                        short code = fDOMFilter.acceptNode(node);
1074                        switch (code) {
1075                            case NodeFilter.FILTER_REJECT :
1076                            case NodeFilter.FILTER_SKIP :
1077                                {
1078                                    // skip the CDATA node
1079
return;
1080                                }
1081                            default :
1082                                {
1083                                    //fall through..
1084
}
1085                        }
1086                    }
1087                    startCDATA();
1088                    characters(text);
1089                    endCDATA();
1090                }
1091            } else {
1092                // transform into a text node
1093
characters(text);
1094            }
1095            break;
1096        }
1097        case Node.COMMENT_NODE : {
1098            String JavaDoc text;
1099
1100            if ( ! _format.getOmitComments() ) {
1101                text = node.getNodeValue();
1102                if ( text != null ) {
1103                
1104                    if (fDOMFilter !=null &&
1105                          (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_COMMENT)!= 0) {
1106                          short code = fDOMFilter.acceptNode(node);
1107                          switch (code) {
1108                              case NodeFilter.FILTER_REJECT:
1109                              case NodeFilter.FILTER_SKIP: {
1110                                  // skip the comment node
1111
return;
1112                              }
1113                              default: {
1114                                   // fall through
1115
}
1116                          }
1117                    }
1118                    comment( text );
1119                }
1120            }
1121            break;
1122        }
1123
1124        case Node.ENTITY_REFERENCE_NODE : {
1125            Node JavaDoc child;
1126
1127            endCDATA();
1128            content();
1129            
1130            if (((features & DOMSerializerImpl.ENTITIES) != 0)
1131                || (node.getFirstChild() == null)) {
1132                if (fDOMFilter !=null &&
1133                      (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ENTITY_REFERENCE)!= 0) {
1134                      short code = fDOMFilter.acceptNode(node);
1135                      switch (code) {
1136                        case NodeFilter.FILTER_REJECT:{
1137                            return; // remove the node
1138
}
1139                          case NodeFilter.FILTER_SKIP: {
1140                              child = node.getFirstChild();
1141                              while ( child != null ) {
1142                                  serializeNode( child );
1143                                  child = child.getNextSibling();
1144                              }
1145                              return;
1146                          }
1147
1148                          default: {
1149                               // fall through
1150
}
1151                      }
1152                  }
1153                checkUnboundNamespacePrefixedNode(node);
1154              
1155                _printer.printText("&");
1156                _printer.printText(node.getNodeName());
1157                _printer.printText(";");
1158            }
1159            else {
1160                child = node.getFirstChild();
1161                while ( child != null ) {
1162                    serializeNode( child );
1163                    child = child.getNextSibling();
1164                }
1165            }
1166
1167            break;
1168        }
1169
1170        case Node.PROCESSING_INSTRUCTION_NODE : {
1171        
1172            if (fDOMFilter !=null &&
1173                  (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_PROCESSING_INSTRUCTION)!= 0) {
1174                  short code = fDOMFilter.acceptNode(node);
1175                  switch (code) {
1176                    case NodeFilter.FILTER_REJECT:
1177                    case NodeFilter.FILTER_SKIP: {
1178                          return; // skip this node
1179
}
1180                    default: { // fall through
1181
}
1182                  }
1183            }
1184            processingInstructionIO( node.getNodeName(), node.getNodeValue() );
1185            break;
1186        }
1187        case Node.ELEMENT_NODE : {
1188
1189            if (fDOMFilter !=null &&
1190                  (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ELEMENT)!= 0) {
1191                  short code = fDOMFilter.acceptNode(node);
1192                  switch (code) {
1193                    case NodeFilter.FILTER_REJECT: {
1194                        return;
1195                    }
1196                    case NodeFilter.FILTER_SKIP: {
1197                        Node JavaDoc child = node.getFirstChild();
1198                        while ( child != null ) {
1199                            serializeNode( child );
1200                            child = child.getNextSibling();
1201                        }
1202                        return; // skip this node
1203
}
1204
1205                    default: { // fall through
1206
}
1207                  }
1208            }
1209            serializeElement( (Element JavaDoc) node );
1210            break;
1211        }
1212        case Node.DOCUMENT_NODE : {
1213            DocumentType JavaDoc docType;
1214            DOMImplementation JavaDoc domImpl;
1215            NamedNodeMap JavaDoc map;
1216            Entity JavaDoc entity;
1217            Notation JavaDoc notation;
1218            int i;
1219            
1220            // If there is a document type, use the SAX events to
1221
// serialize it.
1222
docType = ( (Document JavaDoc) node ).getDoctype();
1223            if (docType != null) {
1224                // DOM Level 2 (or higher)
1225
domImpl = ( (Document JavaDoc) node ).getImplementation();
1226                try {
1227                    String JavaDoc internal;
1228
1229                    _printer.enterDTD();
1230                    _docTypePublicId = docType.getPublicId();
1231                    _docTypeSystemId = docType.getSystemId();
1232                    internal = docType.getInternalSubset();
1233                    if ( internal != null && internal.length() > 0 )
1234                        _printer.printText( internal );
1235                    endDTD();
1236                }
1237                // DOM Level 1 -- does implementation have methods?
1238
catch (NoSuchMethodError JavaDoc nsme) {
1239                    Class JavaDoc docTypeClass = docType.getClass();
1240
1241                    String JavaDoc docTypePublicId = null;
1242                    String JavaDoc docTypeSystemId = null;
1243                    try {
1244                        java.lang.reflect.Method JavaDoc getPublicId = docTypeClass.getMethod("getPublicId", null);
1245                        if (getPublicId.getReturnType().equals(String JavaDoc.class)) {
1246                            docTypePublicId = (String JavaDoc)getPublicId.invoke(docType, null);
1247                        }
1248                    }
1249                    catch (Exception JavaDoc e) {
1250                        // ignore
1251
}
1252                    try {
1253                        java.lang.reflect.Method JavaDoc getSystemId = docTypeClass.getMethod("getSystemId", null);
1254                        if (getSystemId.getReturnType().equals(String JavaDoc.class)) {
1255                            docTypeSystemId = (String JavaDoc)getSystemId.invoke(docType, null);
1256                        }
1257                    }
1258                    catch (Exception JavaDoc e) {
1259                        // ignore
1260
}
1261                    _printer.enterDTD();
1262                    _docTypePublicId = docTypePublicId;
1263                    _docTypeSystemId = docTypeSystemId;
1264                    endDTD();
1265                }
1266            }
1267            // !! Fall through
1268
}
1269        case Node.DOCUMENT_FRAGMENT_NODE : {
1270            Node JavaDoc child;
1271
1272            // By definition this will happen if the node is a document,
1273
// document fragment, etc. Just serialize its contents. It will
1274
// work well for other nodes that we do not know how to serialize.
1275
child = node.getFirstChild();
1276            while ( child != null ) {
1277                serializeNode( child );
1278                child = child.getNextSibling();
1279            }
1280            break;
1281        }
1282
1283        default:
1284            break;
1285        }
1286    }
1287
1288
1289    /**
1290     * Must be called by a method about to print any type of content.
1291     * If the element was just opened, the opening tag is closed and
1292     * will be matched to a closing tag. Returns the current element
1293     * state with <tt>empty</tt> and <tt>afterElement</tt> set to false.
1294     *
1295     * @return The current element state
1296     * @throws IOException An I/O exception occured while
1297     * serializing
1298     */

1299    protected ElementState content()
1300        throws IOException JavaDoc
1301    {
1302        ElementState state;
1303
1304        state = getElementState();
1305        if ( ! isDocumentState() ) {
1306            // Need to close CData section first
1307
if ( state.inCData && ! state.doCData ) {
1308                _printer.printText( "]]>" );
1309                state.inCData = false;
1310            }
1311            // If this is the first content in the element,
1312
// change the state to not-empty and close the
1313
// opening element tag.
1314
if ( state.empty ) {
1315                _printer.printText( '>' );
1316                state.empty = false;
1317            }
1318            // Except for one content type, all of them
1319
// are not last element. That one content
1320
// type will take care of itself.
1321
state.afterElement = false;
1322            // Except for one content type, all of them
1323
// are not last comment. That one content
1324
// type will take care of itself.
1325
state.afterComment = false;
1326        }
1327        return state;
1328    }
1329
1330
1331    /**
1332     * Called to print the text contents in the prevailing element format.
1333     * Since this method is capable of printing text as CDATA, it is used
1334     * for that purpose as well. White space handling is determined by the
1335     * current element state. In addition, the output format can dictate
1336     * whether the text is printed as CDATA or unescaped.
1337     *
1338     * @param text The text to print
1339     * @param unescaped True is should print unescaped
1340     * @throws IOException An I/O exception occured while
1341     * serializing
1342     */

1343    protected void characters( String JavaDoc text )
1344        throws IOException JavaDoc
1345    {
1346        ElementState state;
1347
1348        state = content();
1349        // Check if text should be print as CDATA section or unescaped
1350
// based on elements listed in the output format (the element
1351
// state) or whether we are inside a CDATA section or entity.
1352

1353        if ( state.inCData || state.doCData ) {
1354            int index;
1355            int saveIndent;
1356
1357            // Print a CDATA section. The text is not escaped, but ']]>'
1358
// appearing in the code must be identified and dealt with.
1359
// The contents of a text node is considered space preserving.
1360
if ( ! state.inCData ) {
1361                _printer.printText("<![CDATA[");
1362                state.inCData = true;
1363            }
1364            saveIndent = _printer.getNextIndent();
1365            _printer.setNextIndent( 0 );
1366            printCDATAText( text);
1367            _printer.setNextIndent( saveIndent );
1368
1369        } else {
1370
1371            int saveIndent;
1372
1373            if ( state.preserveSpace ) {
1374                // If preserving space then hold of indentation so no
1375
// excessive spaces are printed at line breaks, escape
1376
// the text content without replacing spaces and print
1377
// the text breaking only at line breaks.
1378
saveIndent = _printer.getNextIndent();
1379                _printer.setNextIndent( 0 );
1380                printText( text, true, state.unescaped );
1381                _printer.setNextIndent( saveIndent );
1382            } else {
1383                printText( text, false, state.unescaped );
1384            }
1385        }
1386    }
1387
1388
1389    /**
1390     * Returns the suitable entity reference for this character value,
1391     * or null if no such entity exists. Calling this method with <tt>'&amp;'</tt>
1392     * will return <tt>"&amp;amp;"</tt>.
1393     *
1394     * @param ch Character value
1395     * @return Character entity name, or null
1396     */

1397    protected abstract String JavaDoc getEntityRef( int ch );
1398
1399
1400    /**
1401     * Called to serializee the DOM element. The element is serialized based on
1402     * the serializer's method (XML, HTML, XHTML).
1403     *
1404     * @param elem The element to serialize
1405     * @throws IOException An I/O exception occured while
1406     * serializing
1407     */

1408    protected abstract void serializeElement( Element JavaDoc elem )
1409        throws IOException JavaDoc;
1410
1411
1412    /**
1413     * Comments and PIs cannot be serialized before the root element,
1414     * because the root element serializes the document type, which
1415     * generally comes first. Instead such PIs and comments are
1416     * accumulated inside a vector and serialized by calling this
1417     * method. Will be called when the root element is serialized
1418     * and when the document finished serializing.
1419     *
1420     * @throws IOException An I/O exception occured while
1421     * serializing
1422     */

1423    protected void serializePreRoot()
1424        throws IOException JavaDoc
1425    {
1426        int i;
1427
1428        if ( _preRoot != null ) {
1429            for ( i = 0 ; i < _preRoot.size() ; ++i ) {
1430                printText( (String JavaDoc) _preRoot.elementAt( i ), true, true );
1431                if ( _indenting )
1432                _printer.breakLine();
1433            }
1434            _preRoot.removeAllElements();
1435        }
1436    }
1437
1438
1439    //---------------------------------------------//
1440
// Text pretty printing and formatting methods //
1441
//---------------------------------------------//
1442

1443    protected void printCDATAText( String JavaDoc text ) throws IOException JavaDoc {
1444        int length = text.length();
1445        char ch;
1446
1447        for ( int index = 0 ; index < length; ++index ) {
1448            ch = text.charAt( index );
1449            if (ch == ']'
1450                && index + 2 < length
1451                && text.charAt(index + 1) == ']'
1452                && text.charAt(index + 2) == '>') { // check for ']]>'
1453
if (fDOMErrorHandler != null) {
1454                    // REVISIT: this means that if DOM Error handler is not registered we don't report any
1455
// fatal errors and might serialize not wellformed document
1456
if ((features & DOMSerializerImpl.SPLITCDATA) == 0
1457                        && (features & DOMSerializerImpl.WELLFORMED) == 0) {
1458                        // issue fatal error
1459
String JavaDoc msg =
1460                            DOMMessageFormatter.formatMessage(
1461                                DOMMessageFormatter.SERIALIZER_DOMAIN,
1462                                "EndingCDATA",
1463                                null);
1464                        modifyDOMError(
1465                            msg,
1466                            DOMError.SEVERITY_FATAL_ERROR,
1467                            fCurrentNode);
1468                        boolean continueProcess =
1469                            fDOMErrorHandler.handleError(fDOMError);
1470                        if (!continueProcess) {
1471                            throw new IOException JavaDoc();
1472                        }
1473                    } else {
1474                        // issue warning
1475
String JavaDoc msg =
1476                            DOMMessageFormatter.formatMessage(
1477                                DOMMessageFormatter.SERIALIZER_DOMAIN,
1478                                "SplittingCDATA",
1479                                null);
1480                        modifyDOMError(
1481                            msg,
1482                            DOMError.SEVERITY_WARNING,
1483                            fCurrentNode);
1484                        fDOMErrorHandler.handleError(fDOMError);
1485                    }
1486                }
1487                // split CDATA section
1488
_printer.printText("]]]]><![CDATA[>");
1489                index += 2;
1490                continue;
1491            }
1492            
1493            if (!XMLChar.isValid(ch)) {
1494                // check if it is surrogate
1495
if (++index <length) {
1496                    surrogates(ch, text.charAt(index));
1497                }
1498                else {
1499                    fatalError("The character '"+(char)ch+"' is an invalid XML character");
1500                }
1501                continue;
1502            } else {
1503                if ( ( ch >= ' ' && _encodingInfo.isPrintable((char)ch) && ch != 0xF7 ) ||
1504                     ch == '\n' || ch == '\r' || ch == '\t' ) {
1505                    _printer.printText((char)ch);
1506                } else {
1507
1508                    // The character is not printable -- split CDATA section
1509
_printer.printText("]]>&#x");
1510                    _printer.printText(Integer.toHexString(ch));
1511                    _printer.printText(";<![CDATA[");
1512                }
1513            }
1514        }
1515    }
1516
1517
1518    protected void surrogates(int high, int low) throws IOException JavaDoc{
1519        if (XMLChar.isHighSurrogate(high)) {
1520            if (!XMLChar.isLowSurrogate(low)) {
1521                //Invalid XML
1522
fatalError("The character '"+(char)low+"' is an invalid XML character");
1523            }
1524            else {
1525                int supplemental = XMLChar.supplemental((char)high, (char)low);
1526                if (!XMLChar.isValid(supplemental)) {
1527                    //Invalid XML
1528
fatalError("The character '"+(char)supplemental+"' is an invalid XML character");
1529                }
1530                else {
1531                    if (content().inCData ) {
1532                        _printer.printText("]]>&#x");
1533                        _printer.printText(Integer.toHexString(supplemental));
1534                        _printer.printText(";<![CDATA[");
1535                    }
1536                    else {
1537                        printHex(supplemental);
1538                    }
1539                }
1540            }
1541        } else {
1542            fatalError("The character '"+(char)high+"' is an invalid XML character");
1543        }
1544
1545    }
1546
1547    /**
1548     * Called to print additional text with whitespace handling.
1549     * If spaces are preserved, the text is printed as if by calling
1550     * {@link #printText(String,boolean,boolean)} with a call to {@link Printer#breakLine}
1551     * for each new line. If spaces are not preserved, the text is
1552     * broken at space boundaries if longer than the line width;
1553     * Multiple spaces are printed as such, but spaces at beginning
1554     * of line are removed.
1555     *
1556     * @param text The text to print
1557     * @param preserveSpace Space preserving flag
1558     * @param unescaped Print unescaped
1559     */

1560    protected void printText( char[] chars, int start, int length,
1561                                    boolean preserveSpace, boolean unescaped )
1562        throws IOException JavaDoc
1563    {
1564        int index;
1565        char ch;
1566
1567        if ( preserveSpace ) {
1568            // Preserving spaces: the text must print exactly as it is,
1569
// without breaking when spaces appear in the text and without
1570
// consolidating spaces. If a line terminator is used, a line
1571
// break will occur.
1572
while ( length-- > 0 ) {
1573                ch = chars[ start ];
1574                ++start;
1575                if ( ch == '\n' || ch == '\r' || unescaped )
1576                    _printer.printText( ch );
1577                else
1578                    printEscaped( ch );
1579            }
1580        } else {
1581            // Not preserving spaces: print one part at a time, and
1582
// use spaces between parts to break them into different
1583
// lines. Spaces at beginning of line will be stripped
1584
// by printing mechanism. Line terminator is treated
1585
// no different than other text part.
1586
while ( length-- > 0 ) {
1587                ch = chars[ start ];
1588                ++start;
1589                if ( ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r' )
1590                    _printer.printSpace();
1591                else if ( unescaped )
1592                    _printer.printText( ch );
1593                else
1594                    printEscaped( ch );
1595            }
1596        }
1597    }
1598
1599
1600    protected void printText( String JavaDoc text, boolean preserveSpace, boolean unescaped )
1601        throws IOException JavaDoc
1602    {
1603        int index;
1604        char ch;
1605
1606        if ( preserveSpace ) {
1607            // Preserving spaces: the text must print exactly as it is,
1608
// without breaking when spaces appear in the text and without
1609
// consolidating spaces. If a line terminator is used, a line
1610
// break will occur.
1611
for ( index = 0 ; index < text.length() ; ++index ) {
1612                ch = text.charAt( index );
1613                if ( ch == '\n' || ch == '\r' || unescaped )
1614                    _printer.printText( ch );
1615                else
1616                    printEscaped( ch );
1617            }
1618        } else {
1619            // Not preserving spaces: print one part at a time, and
1620
// use spaces between parts to break them into different
1621
// lines. Spaces at beginning of line will be stripped
1622
// by printing mechanism. Line terminator is treated
1623
// no different than other text part.
1624
for ( index = 0 ; index < text.length() ; ++index ) {
1625                ch = text.charAt( index );
1626                if ( ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r' )
1627                    _printer.printSpace();
1628                else if ( unescaped )
1629                    _printer.printText( ch );
1630                else
1631                    printEscaped( ch );
1632            }
1633        }
1634    }
1635
1636
1637    /**
1638     * Print a document type public or system identifier URL.
1639     * Encapsulates the URL in double quotes, escapes non-printing
1640     * characters and print it equivalent to {@link #printText}.
1641     *
1642     * @param url The document type url to print
1643     */

1644    protected void printDoctypeURL( String JavaDoc url )
1645        throws IOException JavaDoc
1646    {
1647        int i;
1648
1649        _printer.printText( '"' );
1650        for( i = 0 ; i < url.length() ; ++i ) {
1651            if ( url.charAt( i ) == '"' || url.charAt( i ) < 0x20 || url.charAt( i ) > 0x7F ) {
1652                _printer.printText( '%' );
1653                _printer.printText( Integer.toHexString( url.charAt( i ) ) );
1654            } else
1655                _printer.printText( url.charAt( i ) );
1656        }
1657        _printer.printText( '"' );
1658    }
1659
1660
1661    protected void printEscaped( int ch )
1662        throws IOException JavaDoc
1663    {
1664        String JavaDoc charRef;
1665        // If there is a suitable entity reference for this
1666
// character, print it. The list of available entity
1667
// references is almost but not identical between
1668
// XML and HTML.
1669
charRef = getEntityRef( ch );
1670        if ( charRef != null ) {
1671            _printer.printText( '&' );
1672            _printer.printText( charRef );
1673            _printer.printText( ';' );
1674        } else if ( ( ch >= ' ' && _encodingInfo.isPrintable((char)ch) && ch != 0xF7 ) ||
1675                    ch == '\n' || ch == '\r' || ch == '\t' ) {
1676            // Non printables are below ASCII space but not tab or line
1677
// terminator, ASCII delete, or above a certain Unicode threshold.
1678
if (ch < 0x10000) {
1679                _printer.printText((char)ch );
1680            } else {
1681                _printer.printText((char)(((ch-0x10000)>>10)+0xd800));
1682                _printer.printText((char)(((ch-0x10000)&0x3ff)+0xdc00));
1683            }
1684        } else {
1685            printHex(ch);
1686        }
1687    }
1688    
1689    /**
1690     * Escapes chars
1691     */

1692     final void printHex( int ch) throws IOException JavaDoc {
1693         _printer.printText( "&#x" );
1694         _printer.printText(Integer.toHexString(ch));
1695         _printer.printText( ';' );
1696        
1697     }
1698
1699
1700    /**
1701     * Escapes a string so it may be printed as text content or attribute
1702     * value. Non printable characters are escaped using character references.
1703     * Where the format specifies a deault entity reference, that reference
1704     * is used (e.g. <tt>&amp;lt;</tt>).
1705     *
1706     * @param source The string to escape
1707     */

1708    protected void printEscaped( String JavaDoc source )
1709        throws IOException JavaDoc
1710    {
1711        for ( int i = 0 ; i < source.length() ; ++i ) {
1712            int ch = source.charAt(i);
1713            if ((ch & 0xfc00) == 0xd800 && i+1 < source.length()) {
1714                int lowch = source.charAt(i+1);
1715                if ((lowch & 0xfc00) == 0xdc00) {
1716                    ch = 0x10000 + ((ch-0xd800)<<10) + lowch-0xdc00;
1717                    i++;
1718                }
1719            }
1720            printEscaped(ch);
1721        }
1722    }
1723
1724
1725    //--------------------------------//
1726
// Element state handling methods //
1727
//--------------------------------//
1728

1729
1730    /**
1731     * Return the state of the current element.
1732     *
1733     * @return Current element state
1734     */

1735    protected ElementState getElementState()
1736    {
1737        return _elementStates[ _elementStateCount ];
1738    }
1739
1740
1741    /**
1742     * Enter a new element state for the specified element.
1743     * Tag name and space preserving is specified, element
1744     * state is initially empty.
1745     *
1746     * @return Current element state, or null
1747     */

1748    protected ElementState enterElementState( String JavaDoc namespaceURI, String JavaDoc localName,
1749                                              String JavaDoc rawName, boolean preserveSpace )
1750    {
1751        ElementState state;
1752
1753        if ( _elementStateCount + 1 == _elementStates.length ) {
1754            ElementState[] newStates;
1755
1756            // Need to create a larger array of states. This does not happen
1757
// often, unless the document is really deep.
1758
newStates = new ElementState[ _elementStates.length + 10 ];
1759            for ( int i = 0 ; i < _elementStates.length ; ++i )
1760                newStates[ i ] = _elementStates[ i ];
1761            for ( int i = _elementStates.length ; i < newStates.length ; ++i )
1762                newStates[ i ] = new ElementState();
1763            _elementStates = newStates;
1764        }
1765
1766        ++_elementStateCount;
1767        state = _elementStates[ _elementStateCount ];
1768        state.namespaceURI = namespaceURI;
1769        state.localName = localName;
1770        state.rawName = rawName;
1771        state.preserveSpace = preserveSpace;
1772        state.empty = true;
1773        state.afterElement = false;
1774        state.afterComment = false;
1775        state.doCData = state.inCData = false;
1776        state.unescaped = false;
1777        state.prefixes = _prefixes;
1778
1779        _prefixes = null;
1780        return state;
1781    }
1782
1783
1784    /**
1785     * Leave the current element state and return to the
1786     * state of the parent element. If this was the root
1787     * element, return to the state of the document.
1788     *
1789     * @return Previous element state
1790     */

1791    protected ElementState leaveElementState()
1792    {
1793        if ( _elementStateCount > 0 ) {
1794            /*Corrected by David Blondeau (blondeau@intalio.com)*/
1795        _prefixes = null;
1796        //_prefixes = _elementStates[ _elementStateCount ].prefixes;
1797
-- _elementStateCount;
1798            return _elementStates[ _elementStateCount ];
1799        } else {
1800            String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "Internal", null);
1801            throw new IllegalStateException JavaDoc(msg);
1802        }
1803    }
1804
1805
1806    /**
1807     * Returns true if in the state of the document.
1808     * Returns true before entering any element and after
1809     * leaving the root element.
1810     *
1811     * @return True if in the state of the document
1812     */

1813    protected boolean isDocumentState()
1814    {
1815        return _elementStateCount == 0;
1816    }
1817
1818
1819    /**
1820     * Returns the namespace prefix for the specified URI.
1821     * If the URI has been mapped to a prefix, returns the
1822     * prefix, otherwise returns null.
1823     *
1824     * @param namespaceURI The namespace URI
1825     * @return The namespace prefix if known, or null
1826     */

1827    protected String JavaDoc getPrefix( String JavaDoc namespaceURI )
1828    {
1829        String JavaDoc prefix;
1830
1831        if ( _prefixes != null ) {
1832            prefix = (String JavaDoc) _prefixes.get( namespaceURI );
1833            if ( prefix != null )
1834                return prefix;
1835        }
1836        if ( _elementStateCount == 0 )
1837            return null;
1838        else {
1839            for ( int i = _elementStateCount ; i > 0 ; --i ) {
1840                if ( _elementStates[ i ].prefixes != null ) {
1841                    prefix = (String JavaDoc) _elementStates[ i ].prefixes.get( namespaceURI );
1842                    if ( prefix != null )
1843                        return prefix;
1844                }
1845            }
1846        }
1847        return null;
1848    }
1849
1850    /**
1851     * The method modifies global DOM error object
1852     *
1853     * @param message
1854     * @param severity
1855     * @return a DOMError
1856     */

1857    protected DOMError JavaDoc modifyDOMError(String JavaDoc message, short severity, Node JavaDoc node){
1858            fDOMError.reset();
1859            fDOMError.fMessage = message;
1860            fDOMError.fSeverity = severity;
1861            fDOMError.fLocator = new DOMLocatorImpl(-1, -1, -1, node, null);
1862            return fDOMError;
1863        
1864    }
1865
1866
1867    protected void fatalError(String JavaDoc message) throws IOException JavaDoc{
1868        if (fDOMErrorHandler != null) {
1869            modifyDOMError(message, DOMError.SEVERITY_FATAL_ERROR, fCurrentNode);
1870            fDOMErrorHandler.handleError(fDOMError);
1871        }
1872        else {
1873            throw new IOException JavaDoc(message);
1874        }
1875    }
1876    
1877    /**
1878     * DOM level 3:
1879     * Check a node to determine if it contains unbound namespace prefixes.
1880     *
1881     * @param node The node to check for unbound namespace prefices
1882     */

1883     protected void checkUnboundNamespacePrefixedNode (Node JavaDoc node) throws IOException JavaDoc{
1884        
1885     }
1886}
1887
Popular Tags