KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xml > serialize > BaseMarkupSerializer


1 /*
2  * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17
18 // Sep 14, 2000:
19
// Fixed comments to preserve whitespaces and add a line break
20
// when indenting. Reported by Gervase Markham <gerv@gerv.net>
21
// Sep 14, 2000:
22
// Fixed serializer to report IO exception directly, instead at
23
// the end of document processing.
24
// Reported by Patrick Higgins <phiggins@transzap.com>
25
// Sep 13, 2000:
26
// CR in character data will print as &#0D;
27
// Aug 25, 2000:
28
// Fixed processing instruction printing inside element content
29
// to not escape content. Reported by Mikael Staldal
30
// <d96-mst@d.kth.se>
31
// Aug 25, 2000:
32
// Added ability to omit comments.
33
// Contributed by Anupam Bagchi <abagchi@jtcsv.com>
34
// Aug 26, 2000:
35
// Fixed bug in newline handling when preserving spaces.
36
// Contributed by Mike Dusseault <mdusseault@home.com>
37
// Aug 29, 2000:
38
// Fixed state.unescaped not being set to false when
39
// entering element state.
40
// Reported by Lowell Vaughn <lvaughn@agillion.com>
41

42
43 package org.apache.xml.serialize;
44
45
46 import java.io.IOException JavaDoc;
47 import java.io.OutputStream JavaDoc;
48 import java.io.Writer JavaDoc;
49 import java.util.Hashtable JavaDoc;
50 import java.util.Vector JavaDoc;
51
52 import org.apache.xerces.dom.DOMErrorImpl;
53 import org.apache.xerces.dom.DOMLocatorImpl;
54 import org.apache.xerces.dom.DOMMessageFormatter;
55 import org.apache.xerces.util.XMLChar;
56 import org.w3c.dom.DOMImplementation JavaDoc;
57 import org.w3c.dom.Document JavaDoc;
58 import org.w3c.dom.DocumentFragment JavaDoc;
59 import org.w3c.dom.DocumentType JavaDoc;
60 import org.w3c.dom.DOMError JavaDoc;
61 import org.w3c.dom.DOMErrorHandler JavaDoc;
62 import org.w3c.dom.Element JavaDoc;
63 import org.w3c.dom.Entity JavaDoc;
64 import org.w3c.dom.NamedNodeMap JavaDoc;
65 import org.w3c.dom.Node JavaDoc;
66 import org.w3c.dom.Notation JavaDoc;
67 import org.w3c.dom.ls.LSException JavaDoc;
68 import org.w3c.dom.ls.LSSerializerFilter JavaDoc;
69 import org.w3c.dom.traversal.NodeFilter;
70 import org.xml.sax.ContentHandler JavaDoc;
71 import org.xml.sax.DTDHandler JavaDoc;
72 import org.xml.sax.DocumentHandler JavaDoc;
73 import org.xml.sax.Locator JavaDoc;
74 import org.xml.sax.SAXException JavaDoc;
75 import org.xml.sax.ext.DeclHandler JavaDoc;
76 import org.xml.sax.ext.LexicalHandler JavaDoc;
77
78 /**
79  * Base class for a serializer supporting both DOM and SAX pretty
80  * serializing of XML/HTML/XHTML documents. Derives classes perform
81  * the method-specific serializing, this class provides the common
82  * serializing mechanisms.
83  * <p>
84  * The serializer must be initialized with the proper writer and
85  * output format before it can be used by calling {@link #setOutputCharStream}
86  * or {@link #setOutputByteStream} for the writer and {@link #setOutputFormat}
87  * for the output format.
88  * <p>
89  * The serializer can be reused any number of times, but cannot
90  * be used concurrently by two threads.
91  * <p>
92  * If an output stream is used, the encoding is taken from the
93  * output format (defaults to <tt>UTF-8</tt>). If a writer is
94  * used, make sure the writer uses the same encoding (if applies)
95  * as specified in the output format.
96  * <p>
97  * The serializer supports both DOM and SAX. DOM serializing is done
98  * by calling {@link #serialize(Document)} and SAX serializing is done by firing
99  * SAX events and using the serializer as a document handler.
100  * This also applies to derived class.
101  * <p>
102  * If an I/O exception occurs while serializing, the serializer
103  * will not throw an exception directly, but only throw it
104  * at the end of serializing (either DOM or SAX's {@link
105  * org.xml.sax.DocumentHandler#endDocument}.
106  * <p>
107  * For elements that are not specified as whitespace preserving,
108  * the serializer will potentially break long text lines at space
109  * boundaries, indent lines, and serialize elements on separate
110  * lines. Line terminators will be regarded as spaces, and
111  * spaces at beginning of line will be stripped.
112  * <p>
113  * When indenting, the serializer is capable of detecting seemingly
114  * element content, and serializing these elements indented on separate
115  * lines. An element is serialized indented when it is the first or
116  * last child of an element, or immediate following or preceding
117  * another element.
118  *
119  *
120  * @version $Revision: 1.57 $ $Date: 2005/05/02 21:58:58 $
121  * @author <a HREF="mailto:arkin@intalio.com">Assaf Arkin</a>
122  * @author <a HREF="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
123  * @author Elena Litani, IBM
124  * @see Serializer
125  * @see LSSerializer
126  */

127 public abstract class BaseMarkupSerializer
128     implements ContentHandler JavaDoc, DocumentHandler JavaDoc, LexicalHandler JavaDoc,
129                DTDHandler JavaDoc, DeclHandler JavaDoc, DOMSerializer, Serializer
130 {
131
132     // DOM L3 implementation
133
protected short features = 0xFFFFFFFF;
134     protected DOMErrorHandler JavaDoc fDOMErrorHandler;
135     protected final DOMErrorImpl fDOMError = new DOMErrorImpl();
136     protected LSSerializerFilter JavaDoc fDOMFilter;
137
138     protected EncodingInfo _encodingInfo;
139
140
141     /**
142      * Holds array of all element states that have been entered.
143      * The array is automatically resized. When leaving an element,
144      * it's state is not removed but reused when later returning
145      * to the same nesting level.
146      */

147     private ElementState[] _elementStates;
148
149
150     /**
151      * The index of the next state to place in the array,
152      * or one plus the index of the current state. When zero,
153      * we are in no state.
154      */

155     private int _elementStateCount;
156
157
158     /**
159      * Vector holding comments and PIs that come before the root
160      * element (even after it), see {@link #serializePreRoot}.
161      */

162     private Vector JavaDoc _preRoot;
163
164
165     /**
166      * If the document has been started (header serialized), this
167      * flag is set to true so it's not started twice.
168      */

169     protected boolean _started;
170
171
172     /**
173      * True if the serializer has been prepared. This flag is set
174      * to false when the serializer is reset prior to using it,
175      * and to true after it has been prepared for usage.
176      */

177     private boolean _prepared;
178
179
180     /**
181      * Association between namespace URIs (keys) and prefixes (values).
182      * Accumulated here prior to starting an element and placing this
183      * list in the element state.
184      */

185     protected Hashtable JavaDoc _prefixes;
186
187
188     /**
189      * The system identifier of the document type, if known.
190      */

191     protected String JavaDoc _docTypePublicId;
192
193
194     /**
195      * The system identifier of the document type, if known.
196      */

197     protected String JavaDoc _docTypeSystemId;
198
199
200     /**
201      * The output format associated with this serializer. This will never
202      * be a null reference. If no format was passed to the constructor,
203      * the default one for this document type will be used. The format
204      * object is never changed by the serializer.
205      */

206     protected OutputFormat _format;
207
208
209     /**
210      * The printer used for printing text parts.
211      */

212     protected Printer _printer;
213
214
215     /**
216      * True if indenting printer.
217      */

218     protected boolean _indenting;
219
220     /** Temporary buffer to store character data */
221     protected final StringBuffer JavaDoc fStrBuffer = new StringBuffer JavaDoc(40);
222
223     /**
224      * The underlying writer.
225      */

226     private Writer JavaDoc _writer;
227
228
229     /**
230      * The output stream.
231      */

232     private OutputStream JavaDoc _output;
233
234     /** Current node that is being processed */
235     protected Node JavaDoc fCurrentNode = null;
236
237     
238
239     //--------------------------------//
240
// Constructor and initialization //
241
//--------------------------------//
242

243
244     /**
245      * Protected constructor can only be used by derived class.
246      * Must initialize the serializer before serializing any document,
247      * by calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
248          * first
249      */

250     protected BaseMarkupSerializer( OutputFormat format )
251     {
252         int i;
253
254         _elementStates = new ElementState[ 10 ];
255         for ( i = 0 ; i < _elementStates.length ; ++i )
256             _elementStates[ i ] = new ElementState();
257         _format = format;
258     }
259
260
261     public DocumentHandler JavaDoc asDocumentHandler()
262         throws IOException JavaDoc
263     {
264         prepare();
265         return this;
266     }
267
268
269     public ContentHandler JavaDoc asContentHandler()
270         throws IOException JavaDoc
271     {
272         prepare();
273         return this;
274     }
275
276
277     public DOMSerializer asDOMSerializer()
278         throws IOException JavaDoc
279     {
280         prepare();
281         return this;
282     }
283
284
285     public void setOutputByteStream( OutputStream JavaDoc output )
286     {
287         if ( output == null ) {
288             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
289                                                            "ArgumentIsNull", new Object JavaDoc[]{"output"});
290             throw new NullPointerException JavaDoc(msg);
291         }
292         _output = output;
293         _writer = null;
294         reset();
295     }
296
297
298     public void setOutputCharStream( Writer JavaDoc writer )
299     {
300         if ( writer == null ) {
301             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
302                                                            "ArgumentIsNull", new Object JavaDoc[]{"writer"});
303             throw new NullPointerException JavaDoc(msg);
304         }
305         _writer = writer;
306         _output = null;
307         reset();
308     }
309
310
311     public void setOutputFormat( OutputFormat format )
312     {
313         if ( format == null ) {
314             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
315                                                            "ArgumentIsNull", new Object JavaDoc[]{"format"});
316             throw new NullPointerException JavaDoc(msg);
317         }
318         _format = format;
319         reset();
320     }
321
322
323     public boolean reset()
324     {
325         if ( _elementStateCount > 1 ) {
326             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
327                                                            "ResetInMiddle", null);
328             throw new IllegalStateException JavaDoc(msg);
329         }
330         _prepared = false;
331         fCurrentNode = null;
332         fStrBuffer.setLength(0);
333         return true;
334     }
335
336
337     protected void prepare()
338         throws IOException JavaDoc
339     {
340         if ( _prepared )
341             return;
342
343         if ( _writer == null && _output == null ) {
344             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN,
345                                                            "NoWriterSupplied", null);
346             throw new IOException JavaDoc(msg);
347         }
348         // If the output stream has been set, use it to construct
349
// the writer. It is possible that the serializer has been
350
// reused with the same output stream and different encoding.
351

352         _encodingInfo = _format.getEncodingInfo();
353
354         if ( _output != null ) {
355             _writer = _encodingInfo.getWriter(_output);
356         }
357
358         if ( _format.getIndenting() ) {
359             _indenting = true;
360             _printer = new IndentPrinter( _writer, _format );
361         } else {
362             _indenting = false;
363             _printer = new Printer( _writer, _format );
364         }
365
366         ElementState state;
367
368         _elementStateCount = 0;
369         state = _elementStates[ 0 ];
370         state.namespaceURI = null;
371         state.localName = null;
372         state.rawName = null;
373         state.preserveSpace = _format.getPreserveSpace();
374         state.empty = true;
375         state.afterElement = false;
376         state.afterComment = false;
377         state.doCData = state.inCData = false;
378         state.prefixes = null;
379
380         _docTypePublicId = _format.getDoctypePublic();
381         _docTypeSystemId = _format.getDoctypeSystem();
382         _started = false;
383         _prepared = true;
384     }
385
386
387
388     //----------------------------------//
389
// DOM document serializing methods //
390
//----------------------------------//
391

392
393     /**
394      * Serializes the DOM element using the previously specified
395      * writer and output format. Throws an exception only if
396      * an I/O exception occured while serializing.
397      *
398      * @param elem The element to serialize
399      * @throws IOException An I/O exception occured while
400      * serializing
401      */

402     public void serialize( Element JavaDoc elem )
403         throws IOException JavaDoc
404     {
405         reset();
406         prepare();
407         serializeNode( elem );
408         _printer.flush();
409         if ( _printer.getException() != null )
410             throw _printer.getException();
411     }
412
413
414     /**
415      * Serializes the DOM document fragmnt using the previously specified
416      * writer and output format. Throws an exception only if
417      * an I/O exception occured while serializing.
418      *
419      * @param elem The element to serialize
420      * @throws IOException An I/O exception occured while
421      * serializing
422      */

423     public void serialize( DocumentFragment JavaDoc frag )
424         throws IOException JavaDoc
425     {
426         reset();
427         prepare();
428         serializeNode( frag );
429         _printer.flush();
430         if ( _printer.getException() != null )
431             throw _printer.getException();
432     }
433
434
435     /**
436      * Serializes the DOM document using the previously specified
437      * writer and output format. Throws an exception only if
438      * an I/O exception occured while serializing.
439      *
440      * @param doc The document to serialize
441      * @throws IOException An I/O exception occured while
442      * serializing
443      */

444     public void serialize( Document JavaDoc doc )
445         throws IOException JavaDoc
446     {
447         reset();
448         prepare();
449         serializeNode( doc );
450         serializePreRoot();
451         _printer.flush();
452         if ( _printer.getException() != null )
453             throw _printer.getException();
454     }
455
456
457     //------------------------------------------//
458
// SAX document handler serializing methods //
459
//------------------------------------------//
460

461
462     public void startDocument()
463         throws SAXException JavaDoc
464     {
465         try {
466             prepare();
467         } catch ( IOException JavaDoc except ) {
468             throw new SAXException JavaDoc( except.toString() );
469         }
470         // Nothing to do here. All the magic happens in startDocument(String)
471
}
472     
473     
474     public void characters( char[] chars, int start, int length )
475         throws SAXException JavaDoc
476     {
477         ElementState state;
478
479         try {
480         state = content();
481
482         // Check if text should be print as CDATA section or unescaped
483
// based on elements listed in the output format (the element
484
// state) or whether we are inside a CDATA section or entity.
485

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

745     public void endDocument()
746         throws SAXException JavaDoc
747     {
748         try {
749         // Print all the elements accumulated outside of
750
// the root element.
751
serializePreRoot();
752         // Flush the output, this is necessary for fStrBuffered output.
753
_printer.flush();
754         } catch ( IOException JavaDoc except ) {
755             throw new SAXException JavaDoc( except );
756     }
757     }
758
759
760     public void startEntity( String JavaDoc name )
761     {
762         // ???
763
}
764
765
766     public void endEntity( String JavaDoc name )
767     {
768         // ???
769
}
770
771
772     public void setDocumentLocator( Locator JavaDoc locator )
773     {
774         // Nothing to do
775
}
776
777
778     //-----------------------------------------//
779
// SAX content handler serializing methods //
780
//-----------------------------------------//
781

782
783     public void skippedEntity ( String JavaDoc name )
784         throws SAXException JavaDoc
785     {
786         try {
787         endCDATA();
788         content();
789         _printer.printText( '&' );
790         _printer.printText( name );
791         _printer.printText( ';' );
792         } catch ( IOException JavaDoc except ) {
793             throw new SAXException JavaDoc( except );
794     }
795     }
796
797
798     public void startPrefixMapping( String JavaDoc prefix, String JavaDoc uri )
799         throws SAXException JavaDoc
800     {
801         if ( _prefixes == null )
802             _prefixes = new Hashtable JavaDoc();
803         _prefixes.put( uri, prefix == null ? "" : prefix );
804     }
805
806
807     public void endPrefixMapping( String JavaDoc prefix )
808         throws SAXException JavaDoc
809     {
810     }
811
812
813     //------------------------------------------//
814
// SAX DTD/Decl handler serializing methods //
815
//------------------------------------------//
816

817
818     public final void startDTD( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
819         throws SAXException JavaDoc
820     {
821         try {
822         _printer.enterDTD();
823         _docTypePublicId = publicId;
824         _docTypeSystemId = systemId;
825         } catch ( IOException JavaDoc except ) {
826             throw new SAXException JavaDoc( except );
827         }
828     }
829
830
831     public void endDTD()
832     {
833         // Nothing to do here, all the magic occurs in startDocument(String).
834
}
835
836
837     public void elementDecl( String JavaDoc name, String JavaDoc model )
838         throws SAXException JavaDoc
839     {
840         try {
841         _printer.enterDTD();
842         _printer.printText( "<!ELEMENT " );
843         _printer.printText( name );
844         _printer.printText( ' ' );
845         _printer.printText( model );
846         _printer.printText( '>' );
847         if ( _indenting )
848             _printer.breakLine();
849         } catch ( IOException JavaDoc except ) {
850             throw new SAXException JavaDoc( except );
851         }
852     }
853
854
855     public void attributeDecl( String JavaDoc eName, String JavaDoc aName, String JavaDoc type,
856                                String JavaDoc valueDefault, String JavaDoc value )
857         throws SAXException JavaDoc
858     {
859         try {
860         _printer.enterDTD();
861         _printer.printText( "<!ATTLIST " );
862         _printer.printText( eName );
863         _printer.printText( ' ' );
864         _printer.printText( aName );
865         _printer.printText( ' ' );
866         _printer.printText( type );
867         if ( valueDefault != null ) {
868             _printer.printText( ' ' );
869             _printer.printText( valueDefault );
870         }
871         if ( value != null ) {
872             _printer.printText( " \"" );
873             printEscaped( value );
874             _printer.printText( '"' );
875         }
876         _printer.printText( '>' );
877         if ( _indenting )
878             _printer.breakLine();
879         } catch ( IOException JavaDoc except ) {
880             throw new SAXException JavaDoc( except );
881     }
882     }
883
884
885     public void internalEntityDecl( String JavaDoc name, String JavaDoc value )
886         throws SAXException JavaDoc
887     {
888         try {
889         _printer.enterDTD();
890         _printer.printText( "<!ENTITY " );
891         _printer.printText( name );
892         _printer.printText( " \"" );
893         printEscaped( value );
894         _printer.printText( "\">" );
895         if ( _indenting )
896             _printer.breakLine();
897         } catch ( IOException JavaDoc except ) {
898             throw new SAXException JavaDoc( except );
899         }
900     }
901
902
903     public void externalEntityDecl( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
904         throws SAXException JavaDoc
905     {
906         try {
907         _printer.enterDTD();
908         unparsedEntityDecl( name, publicId, systemId, null );
909         } catch ( IOException JavaDoc except ) {
910             throw new SAXException JavaDoc( except );
911         }
912     }
913
914
915     public void unparsedEntityDecl( String JavaDoc name, String JavaDoc publicId,
916                                     String JavaDoc systemId, String JavaDoc notationName )
917         throws SAXException JavaDoc
918     {
919         try {
920         _printer.enterDTD();
921         if ( publicId == null ) {
922             _printer.printText( "<!ENTITY " );
923             _printer.printText( name );
924             _printer.printText( " SYSTEM " );
925             printDoctypeURL( systemId );
926         } else {
927             _printer.printText( "<!ENTITY " );
928             _printer.printText( name );
929             _printer.printText( " PUBLIC " );
930             printDoctypeURL( publicId );
931             _printer.printText( ' ' );
932             printDoctypeURL( systemId );
933         }
934         if ( notationName != null ) {
935             _printer.printText( " NDATA " );
936             _printer.printText( notationName );
937         }
938         _printer.printText( '>' );
939         if ( _indenting )
940             _printer.breakLine();
941         } catch ( IOException JavaDoc except ) {
942             throw new SAXException JavaDoc( except );
943     }
944     }
945
946
947     public void notationDecl( String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId )
948         throws SAXException JavaDoc
949     {
950         try {
951         _printer.enterDTD();
952         if ( publicId != null ) {
953             _printer.printText( "<!NOTATION " );
954             _printer.printText( name );
955             _printer.printText( " PUBLIC " );
956             printDoctypeURL( publicId );
957             if ( systemId != null ) {
958                 _printer.printText( ' ' );
959                 printDoctypeURL( systemId );
960             }
961         } else {
962             _printer.printText( "<!NOTATION " );
963             _printer.printText( name );
964             _printer.printText( " SYSTEM " );
965             printDoctypeURL( systemId );
966         }
967         _printer.printText( '>' );
968         if ( _indenting )
969             _printer.breakLine();
970         } catch ( IOException JavaDoc except ) {
971             throw new SAXException JavaDoc( except );
972         }
973     }
974
975
976     //------------------------------------------//
977
// Generic node serializing methods methods //
978
//------------------------------------------//
979

980
981     /**
982      * Serialize the DOM node. This method is shared across XML, HTML and XHTML
983      * serializers and the differences are masked out in a separate {@link
984      * #serializeElement}.
985      *
986      * @param node The node to serialize
987      * @see #serializeElement
988      * @throws IOException An I/O exception occured while
989      * serializing
990      */

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

1260    protected ElementState content()
1261        throws IOException JavaDoc
1262    {
1263        ElementState state;
1264
1265        state = getElementState();
1266        if ( ! isDocumentState() ) {
1267            // Need to close CData section first
1268
if ( state.inCData && ! state.doCData ) {
1269                _printer.printText( "]]>" );
1270                state.inCData = false;
1271            }
1272            // If this is the first content in the element,
1273
// change the state to not-empty and close the
1274
// opening element tag.
1275
if ( state.empty ) {
1276                _printer.printText( '>' );
1277                state.empty = false;
1278            }
1279            // Except for one content type, all of them
1280
// are not last element. That one content
1281
// type will take care of itself.
1282
state.afterElement = false;
1283            // Except for one content type, all of them
1284
// are not last comment. That one content
1285
// type will take care of itself.
1286
state.afterComment = false;
1287        }
1288        return state;
1289    }
1290
1291
1292    /**
1293     * Called to print the text contents in the prevailing element format.
1294     * Since this method is capable of printing text as CDATA, it is used
1295     * for that purpose as well. White space handling is determined by the
1296     * current element state. In addition, the output format can dictate
1297     * whether the text is printed as CDATA or unescaped.
1298     *
1299     * @param text The text to print
1300     * @param unescaped True is should print unescaped
1301     * @throws IOException An I/O exception occured while
1302     * serializing
1303     */

1304    protected void characters( String JavaDoc text )
1305        throws IOException JavaDoc
1306    {
1307        ElementState state;
1308
1309        state = content();
1310        // Check if text should be print as CDATA section or unescaped
1311
// based on elements listed in the output format (the element
1312
// state) or whether we are inside a CDATA section or entity.
1313

1314        if ( state.inCData || state.doCData ) {
1315            int index;
1316            int saveIndent;
1317
1318            // Print a CDATA section. The text is not escaped, but ']]>'
1319
// appearing in the code must be identified and dealt with.
1320
// The contents of a text node is considered space preserving.
1321
if ( ! state.inCData ) {
1322                _printer.printText("<![CDATA[");
1323                state.inCData = true;
1324            }
1325            saveIndent = _printer.getNextIndent();
1326            _printer.setNextIndent( 0 );
1327            printCDATAText( text);
1328            _printer.setNextIndent( saveIndent );
1329
1330        } else {
1331
1332            int saveIndent;
1333
1334            if ( state.preserveSpace ) {
1335                // If preserving space then hold of indentation so no
1336
// excessive spaces are printed at line breaks, escape
1337
// the text content without replacing spaces and print
1338
// the text breaking only at line breaks.
1339
saveIndent = _printer.getNextIndent();
1340                _printer.setNextIndent( 0 );
1341                printText( text, true, state.unescaped );
1342                _printer.setNextIndent( saveIndent );
1343            } else {
1344                printText( text, false, state.unescaped );
1345            }
1346        }
1347    }
1348
1349
1350    /**
1351     * Returns the suitable entity reference for this character value,
1352     * or null if no such entity exists. Calling this method with <tt>'&amp;'</tt>
1353     * will return <tt>"&amp;amp;"</tt>.
1354     *
1355     * @param ch Character value
1356     * @return Character entity name, or null
1357     */

1358    protected abstract String JavaDoc getEntityRef( int ch );
1359
1360
1361    /**
1362     * Called to serializee the DOM element. The element is serialized based on
1363     * the serializer's method (XML, HTML, XHTML).
1364     *
1365     * @param elem The element to serialize
1366     * @throws IOException An I/O exception occured while
1367     * serializing
1368     */

1369    protected abstract void serializeElement( Element JavaDoc elem )
1370        throws IOException JavaDoc;
1371
1372
1373    /**
1374     * Comments and PIs cannot be serialized before the root element,
1375     * because the root element serializes the document type, which
1376     * generally comes first. Instead such PIs and comments are
1377     * accumulated inside a vector and serialized by calling this
1378     * method. Will be called when the root element is serialized
1379     * and when the document finished serializing.
1380     *
1381     * @throws IOException An I/O exception occured while
1382     * serializing
1383     */

1384    protected void serializePreRoot()
1385        throws IOException JavaDoc
1386    {
1387        int i;
1388
1389        if ( _preRoot != null ) {
1390            for ( i = 0 ; i < _preRoot.size() ; ++i ) {
1391                printText( (String JavaDoc) _preRoot.elementAt( i ), true, true );
1392                if ( _indenting )
1393                _printer.breakLine();
1394            }
1395            _preRoot.removeAllElements();
1396        }
1397    }
1398
1399
1400    //---------------------------------------------//
1401
// Text pretty printing and formatting methods //
1402
//---------------------------------------------//
1403

1404    protected void printCDATAText( String JavaDoc text ) throws IOException JavaDoc {
1405        int length = text.length();
1406        char ch;
1407
1408        for ( int index = 0 ; index < length; ++index ) {
1409            ch = text.charAt( index );
1410            if (ch == ']'
1411                && index + 2 < length
1412                && text.charAt(index + 1) == ']'
1413                && text.charAt(index + 2) == '>') { // check for ']]>'
1414
if (fDOMErrorHandler != null) {
1415                    // REVISIT: this means that if DOM Error handler is not registered we don't report any
1416
// fatal errors and might serialize not wellformed document
1417
if ((features & DOMSerializerImpl.SPLITCDATA) == 0) {
1418                        String JavaDoc msg = DOMMessageFormatter.formatMessage(
1419                            DOMMessageFormatter.SERIALIZER_DOMAIN,
1420                            "EndingCDATA",
1421                            null);
1422                        if ((features & DOMSerializerImpl.WELLFORMED) != 0) {
1423                            // issue fatal error
1424
modifyDOMError(msg, DOMError.SEVERITY_FATAL_ERROR, "wf-invalid-character", fCurrentNode);
1425                            fDOMErrorHandler.handleError(fDOMError);
1426                            throw new LSException JavaDoc(LSException.SERIALIZE_ERR, msg);
1427                        }
1428                        else {
1429                            // issue error
1430
modifyDOMError(msg, DOMError.SEVERITY_ERROR, "cdata-section-not-splitted", fCurrentNode);
1431                            if (!fDOMErrorHandler.handleError(fDOMError)) {
1432                                throw new LSException JavaDoc(LSException.SERIALIZE_ERR, msg);
1433                            }
1434                        }
1435                    } else {
1436                        // issue warning
1437
String JavaDoc msg =
1438                            DOMMessageFormatter.formatMessage(
1439                                DOMMessageFormatter.SERIALIZER_DOMAIN,
1440                                "SplittingCDATA",
1441                                null);
1442                        modifyDOMError(
1443                            msg,
1444                            DOMError.SEVERITY_WARNING,
1445                            null, fCurrentNode);
1446                        fDOMErrorHandler.handleError(fDOMError);
1447                    }
1448                }
1449                // split CDATA section
1450
_printer.printText("]]]]><![CDATA[>");
1451                index += 2;
1452                continue;
1453            }
1454            
1455            if (!XMLChar.isValid(ch)) {
1456                // check if it is surrogate
1457
if (++index <length) {
1458                    surrogates(ch, text.charAt(index));
1459                }
1460                else {
1461                    fatalError("The character '"+(char)ch+"' is an invalid XML character");
1462                }
1463                continue;
1464            } else {
1465                if ( ( ch >= ' ' && _encodingInfo.isPrintable((char)ch) && ch != 0xF7 ) ||
1466                     ch == '\n' || ch == '\r' || ch == '\t' ) {
1467                    _printer.printText((char)ch);
1468                } else {
1469
1470                    // The character is not printable -- split CDATA section
1471
_printer.printText("]]>&#x");
1472                    _printer.printText(Integer.toHexString(ch));
1473                    _printer.printText(";<![CDATA[");
1474                }
1475            }
1476        }
1477    }
1478
1479
1480    protected void surrogates(int high, int low) throws IOException JavaDoc{
1481        if (XMLChar.isHighSurrogate(high)) {
1482            if (!XMLChar.isLowSurrogate(low)) {
1483                //Invalid XML
1484
fatalError("The character '"+(char)low+"' is an invalid XML character");
1485            }
1486            else {
1487                int supplemental = XMLChar.supplemental((char)high, (char)low);
1488                if (!XMLChar.isValid(supplemental)) {
1489                    //Invalid XML
1490
fatalError("The character '"+(char)supplemental+"' is an invalid XML character");
1491                }
1492                else {
1493                    if (content().inCData ) {
1494                        _printer.printText("]]>&#x");
1495                        _printer.printText(Integer.toHexString(supplemental));
1496                        _printer.printText(";<![CDATA[");
1497                    }
1498                    else {
1499                        printHex(supplemental);
1500                    }
1501                }
1502            }
1503        } else {
1504            fatalError("The character '"+(char)high+"' is an invalid XML character");
1505        }
1506
1507    }
1508
1509    /**
1510     * Called to print additional text with whitespace handling.
1511     * If spaces are preserved, the text is printed as if by calling
1512     * {@link #printText(String,boolean,boolean)} with a call to {@link Printer#breakLine}
1513     * for each new line. If spaces are not preserved, the text is
1514     * broken at space boundaries if longer than the line width;
1515     * Multiple spaces are printed as such, but spaces at beginning
1516     * of line are removed.
1517     *
1518     * @param text The text to print
1519     * @param preserveSpace Space preserving flag
1520     * @param unescaped Print unescaped
1521     */

1522    protected void printText( char[] chars, int start, int length,
1523                                    boolean preserveSpace, boolean unescaped )
1524        throws IOException JavaDoc
1525    {
1526        int index;
1527        char ch;
1528
1529        if ( preserveSpace ) {
1530            // Preserving spaces: the text must print exactly as it is,
1531
// without breaking when spaces appear in the text and without
1532
// consolidating spaces. If a line terminator is used, a line
1533
// break will occur.
1534
while ( length-- > 0 ) {
1535                ch = chars[ start ];
1536                ++start;
1537                if ( ch == '\n' || ch == '\r' || unescaped )
1538                    _printer.printText( ch );
1539                else
1540                    printEscaped( ch );
1541            }
1542        } else {
1543            // Not preserving spaces: print one part at a time, and
1544
// use spaces between parts to break them into different
1545
// lines. Spaces at beginning of line will be stripped
1546
// by printing mechanism. Line terminator is treated
1547
// no different than other text part.
1548
while ( length-- > 0 ) {
1549                ch = chars[ start ];
1550                ++start;
1551                if ( ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r' )
1552                    _printer.printSpace();
1553                else if ( unescaped )
1554                    _printer.printText( ch );
1555                else
1556                    printEscaped( ch );
1557            }
1558        }
1559    }
1560
1561
1562    protected void printText( String JavaDoc text, boolean preserveSpace, boolean unescaped )
1563        throws IOException JavaDoc
1564    {
1565        int index;
1566        char ch;
1567
1568        if ( preserveSpace ) {
1569            // Preserving spaces: the text must print exactly as it is,
1570
// without breaking when spaces appear in the text and without
1571
// consolidating spaces. If a line terminator is used, a line
1572
// break will occur.
1573
for ( index = 0 ; index < text.length() ; ++index ) {
1574                ch = text.charAt( index );
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
for ( index = 0 ; index < text.length() ; ++index ) {
1587                ch = text.charAt( index );
1588                if ( ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r' )
1589                    _printer.printSpace();
1590                else if ( unescaped )
1591                    _printer.printText( ch );
1592                else
1593                    printEscaped( ch );
1594            }
1595        }
1596    }
1597
1598
1599    /**
1600     * Print a document type public or system identifier URL.
1601     * Encapsulates the URL in double quotes, escapes non-printing
1602     * characters and print it equivalent to {@link #printText}.
1603     *
1604     * @param url The document type url to print
1605     */

1606    protected void printDoctypeURL( String JavaDoc url )
1607        throws IOException JavaDoc
1608    {
1609        int i;
1610
1611        _printer.printText( '"' );
1612        for( i = 0 ; i < url.length() ; ++i ) {
1613            if ( url.charAt( i ) == '"' || url.charAt( i ) < 0x20 || url.charAt( i ) > 0x7F ) {
1614                _printer.printText( '%' );
1615                _printer.printText( Integer.toHexString( url.charAt( i ) ) );
1616            } else
1617                _printer.printText( url.charAt( i ) );
1618        }
1619        _printer.printText( '"' );
1620    }
1621
1622
1623    protected void printEscaped( int ch )
1624        throws IOException JavaDoc
1625    {
1626        String JavaDoc charRef;
1627        // If there is a suitable entity reference for this
1628
// character, print it. The list of available entity
1629
// references is almost but not identical between
1630
// XML and HTML.
1631
charRef = getEntityRef( ch );
1632        if ( charRef != null ) {
1633            _printer.printText( '&' );
1634            _printer.printText( charRef );
1635            _printer.printText( ';' );
1636        } else if ( ( ch >= ' ' && _encodingInfo.isPrintable((char)ch) && ch != 0xF7 ) ||
1637                    ch == '\n' || ch == '\r' || ch == '\t' ) {
1638            // Non printables are below ASCII space but not tab or line
1639
// terminator, ASCII delete, or above a certain Unicode threshold.
1640
if (ch < 0x10000) {
1641                _printer.printText((char)ch );
1642            } else {
1643                _printer.printText((char)(((ch-0x10000)>>10)+0xd800));
1644                _printer.printText((char)(((ch-0x10000)&0x3ff)+0xdc00));
1645            }
1646        } else {
1647            printHex(ch);
1648        }
1649    }
1650    
1651    /**
1652     * Escapes chars
1653     */

1654     final void printHex( int ch) throws IOException JavaDoc {
1655         _printer.printText( "&#x" );
1656         _printer.printText(Integer.toHexString(ch));
1657         _printer.printText( ';' );
1658        
1659     }
1660
1661
1662    /**
1663     * Escapes a string so it may be printed as text content or attribute
1664     * value. Non printable characters are escaped using character references.
1665     * Where the format specifies a deault entity reference, that reference
1666     * is used (e.g. <tt>&amp;lt;</tt>).
1667     *
1668     * @param source The string to escape
1669     */

1670    protected void printEscaped( String JavaDoc source )
1671        throws IOException JavaDoc
1672    {
1673        for ( int i = 0 ; i < source.length() ; ++i ) {
1674            int ch = source.charAt(i);
1675            if ((ch & 0xfc00) == 0xd800 && i+1 < source.length()) {
1676                int lowch = source.charAt(i+1);
1677                if ((lowch & 0xfc00) == 0xdc00) {
1678                    ch = 0x10000 + ((ch-0xd800)<<10) + lowch-0xdc00;
1679                    i++;
1680                }
1681            }
1682            printEscaped(ch);
1683        }
1684    }
1685
1686
1687    //--------------------------------//
1688
// Element state handling methods //
1689
//--------------------------------//
1690

1691
1692    /**
1693     * Return the state of the current element.
1694     *
1695     * @return Current element state
1696     */

1697    protected ElementState getElementState()
1698    {
1699        return _elementStates[ _elementStateCount ];
1700    }
1701
1702
1703    /**
1704     * Enter a new element state for the specified element.
1705     * Tag name and space preserving is specified, element
1706     * state is initially empty.
1707     *
1708     * @return Current element state, or null
1709     */

1710    protected ElementState enterElementState( String JavaDoc namespaceURI, String JavaDoc localName,
1711                                              String JavaDoc rawName, boolean preserveSpace )
1712    {
1713        ElementState state;
1714
1715        if ( _elementStateCount + 1 == _elementStates.length ) {
1716            ElementState[] newStates;
1717
1718            // Need to create a larger array of states. This does not happen
1719
// often, unless the document is really deep.
1720
newStates = new ElementState[ _elementStates.length + 10 ];
1721            for ( int i = 0 ; i < _elementStates.length ; ++i )
1722                newStates[ i ] = _elementStates[ i ];
1723            for ( int i = _elementStates.length ; i < newStates.length ; ++i )
1724                newStates[ i ] = new ElementState();
1725            _elementStates = newStates;
1726        }
1727
1728        ++_elementStateCount;
1729        state = _elementStates[ _elementStateCount ];
1730        state.namespaceURI = namespaceURI;
1731        state.localName = localName;
1732        state.rawName = rawName;
1733        state.preserveSpace = preserveSpace;
1734        state.empty = true;
1735        state.afterElement = false;
1736        state.afterComment = false;
1737        state.doCData = state.inCData = false;
1738        state.unescaped = false;
1739        state.prefixes = _prefixes;
1740
1741        _prefixes = null;
1742        return state;
1743    }
1744
1745
1746    /**
1747     * Leave the current element state and return to the
1748     * state of the parent element. If this was the root
1749     * element, return to the state of the document.
1750     *
1751     * @return Previous element state
1752     */

1753    protected ElementState leaveElementState()
1754    {
1755        if ( _elementStateCount > 0 ) {
1756            /*Corrected by David Blondeau (blondeau@intalio.com)*/
1757        _prefixes = null;
1758        //_prefixes = _elementStates[ _elementStateCount ].prefixes;
1759
-- _elementStateCount;
1760            return _elementStates[ _elementStateCount ];
1761        } else {
1762            String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "Internal", null);
1763            throw new IllegalStateException JavaDoc(msg);
1764        }
1765    }
1766
1767
1768    /**
1769     * Returns true if in the state of the document.
1770     * Returns true before entering any element and after
1771     * leaving the root element.
1772     *
1773     * @return True if in the state of the document
1774     */

1775    protected boolean isDocumentState()
1776    {
1777        return _elementStateCount == 0;
1778    }
1779
1780
1781    /**
1782     * Returns the namespace prefix for the specified URI.
1783     * If the URI has been mapped to a prefix, returns the
1784     * prefix, otherwise returns null.
1785     *
1786     * @param namespaceURI The namespace URI
1787     * @return The namespace prefix if known, or null
1788     */

1789    protected String JavaDoc getPrefix( String JavaDoc namespaceURI )
1790    {
1791        String JavaDoc prefix;
1792
1793        if ( _prefixes != null ) {
1794            prefix = (String JavaDoc) _prefixes.get( namespaceURI );
1795            if ( prefix != null )
1796                return prefix;
1797        }
1798        if ( _elementStateCount == 0 )
1799            return null;
1800        else {
1801            for ( int i = _elementStateCount ; i > 0 ; --i ) {
1802                if ( _elementStates[ i ].prefixes != null ) {
1803                    prefix = (String JavaDoc) _elementStates[ i ].prefixes.get( namespaceURI );
1804                    if ( prefix != null )
1805                        return prefix;
1806                }
1807            }
1808        }
1809        return null;
1810    }
1811
1812    /**
1813     * The method modifies global DOM error object
1814     *
1815     * @param message
1816     * @param severity
1817     * @param type
1818     * @return a DOMError
1819     */

1820    protected DOMError JavaDoc modifyDOMError(String JavaDoc message, short severity, String JavaDoc type, Node JavaDoc node){
1821            fDOMError.reset();
1822            fDOMError.fMessage = message;
1823            fDOMError.fType = type;
1824            fDOMError.fSeverity = severity;
1825            fDOMError.fLocator = new DOMLocatorImpl(-1, -1, -1, node, null);
1826            return fDOMError;
1827        
1828    }
1829
1830
1831    protected void fatalError(String JavaDoc message) throws IOException JavaDoc{
1832        if (fDOMErrorHandler != null) {
1833            modifyDOMError(message, DOMError.SEVERITY_FATAL_ERROR, null, fCurrentNode);
1834            fDOMErrorHandler.handleError(fDOMError);
1835        }
1836        else {
1837            throw new IOException JavaDoc(message);
1838        }
1839    }
1840    
1841    /**
1842     * DOM level 3:
1843     * Check a node to determine if it contains unbound namespace prefixes.
1844     *
1845     * @param node The node to check for unbound namespace prefices
1846     */

1847     protected void checkUnboundNamespacePrefixedNode (Node JavaDoc node) throws IOException JavaDoc{
1848        
1849     }
1850}
1851
Popular Tags