1   
2   
3   
4   
5   
6   
7   
8   package org.dom4j.io;
9   
10  import java.io.BufferedWriter;
11  import java.io.IOException;
12  import java.io.OutputStream;
13  import java.io.OutputStreamWriter;
14  import java.io.UnsupportedEncodingException;
15  import java.io.Writer;
16  import java.util.HashMap;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.Map;
20  import java.util.StringTokenizer;
21  
22  import org.dom4j.Attribute;
23  import org.dom4j.CDATA;
24  import org.dom4j.Comment;
25  import org.dom4j.Document;
26  import org.dom4j.DocumentType;
27  import org.dom4j.Element;
28  import org.dom4j.Entity;
29  import org.dom4j.Namespace;
30  import org.dom4j.Node;
31  import org.dom4j.ProcessingInstruction;
32  import org.dom4j.Text;
33  import org.dom4j.tree.NamespaceStack;
34  
35  import org.xml.sax.Attributes;
36  import org.xml.sax.InputSource;
37  import org.xml.sax.Locator;
38  import org.xml.sax.SAXException;
39  import org.xml.sax.SAXNotRecognizedException;
40  import org.xml.sax.SAXNotSupportedException;
41  import org.xml.sax.XMLReader;
42  import org.xml.sax.ext.LexicalHandler;
43  import org.xml.sax.helpers.XMLFilterImpl;
44  
45  /***
46   * <p>
47   * <code>XMLWriter</code> takes a DOM4J tree and formats it to a stream as
48   * XML. It can also take SAX events too so can be used by SAX clients as this
49   * object implements the {@link org.xml.sax.ContentHandler}and {@link
50   * LexicalHandler} interfaces. as well. This formatter performs typical document
51   * formatting. The XML declaration and processing instructions are always on
52   * their own lines. An {@link OutputFormat}object can be used to define how
53   * whitespace is handled when printing and allows various configuration options,
54   * such as to allow suppression of the XML declaration, the encoding declaration
55   * or whether empty documents are collapsed.
56   * </p>
57   * 
58   * <p>
59   * There are <code>write(...)</code> methods to print any of the standard
60   * DOM4J classes, including <code>Document</code> and <code>Element</code>,
61   * to either a <code>Writer</code> or an <code>OutputStream</code>.
62   * Warning: using your own <code>Writer</code> may cause the writer's
63   * preferred character encoding to be ignored. If you use encodings other than
64   * UTF8, we recommend using the method that takes an OutputStream instead.
65   * </p>
66   * 
67   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
68   * @author Joseph Bowbeer
69   * @version $Revision: 1.83.2.2 $
70   */
71  public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
72      private static final String PAD_TEXT = " ";
73  
74      protected static final String[] LEXICAL_HANDLER_NAMES = {
75              "http://xml.org/sax/properties/lexical-handler",
76              "http://xml.org/sax/handlers/LexicalHandler"};
77  
78      protected static final OutputFormat DEFAULT_FORMAT = new OutputFormat();
79  
80      /*** Should entityRefs by resolved when writing ? */
81      private boolean resolveEntityRefs = true;
82  
83      /***
84       * Stores the last type of node written so algorithms can refer to the
85       * previous node type
86       */
87      protected int lastOutputNodeType;
88  
89      /***
90       * Stores if the last written element node was a closing tag or an opening
91       * tag.
92       */
93      private boolean lastElementClosed = false;
94  
95      /*** Stores the xml:space attribute value of preserve for whitespace flag */
96      protected boolean preserve = false;
97  
98      /*** The Writer used to output to */
99      protected Writer writer;
100 
101     /*** The Stack of namespaceStack written so far */
102     private NamespaceStack namespaceStack = new NamespaceStack();
103 
104     /*** The format used by this writer */
105     private OutputFormat format;
106 
107     /*** whether we should escape text */
108     private boolean escapeText = true;
109 
110     /***
111      * The initial number of indentations (so you can print a whole document
112      * indented, if you like)
113      */
114     private int indentLevel = 0;
115 
116     /*** buffer used when escaping strings */
117     private StringBuffer buffer = new StringBuffer();
118 
119     /***
120      * whether we have added characters before from the same chunk of characters
121      */
122     private boolean charsAdded = false;
123 
124     private char lastChar;
125 
126     /*** Whether a flush should occur after writing a document */
127     private boolean autoFlush;
128 
129     /*** Lexical handler we should delegate to */
130     private LexicalHandler lexicalHandler;
131 
132     /***
133      * Whether comments should appear inside DTD declarations - defaults to
134      * false
135      */
136     private boolean showCommentsInDTDs;
137 
138     /*** Is the writer curerntly inside a DTD definition? */
139     private boolean inDTD;
140 
141     /*** The namespaces used for the current element when consuming SAX events */
142     private Map namespacesMap;
143 
144     /***
145      * what is the maximum allowed character code such as 127 in US-ASCII (7
146      * bit) or 255 in ISO- (8 bit) or -1 to not escape any characters (other
147      * than the special XML characters like < > &)
148      */
149     private int maximumAllowedCharacter;
150 
151     public XMLWriter(Writer writer) {
152         this(writer, DEFAULT_FORMAT);
153     }
154 
155     public XMLWriter(Writer writer, OutputFormat format) {
156         this.writer = writer;
157         this.format = format;
158         namespaceStack.push(Namespace.NO_NAMESPACE);
159     }
160 
161     public XMLWriter() {
162         this.format = DEFAULT_FORMAT;
163         this.writer = new BufferedWriter(new OutputStreamWriter(System.out));
164         this.autoFlush = true;
165         namespaceStack.push(Namespace.NO_NAMESPACE);
166     }
167 
168     public XMLWriter(OutputStream out) throws UnsupportedEncodingException {
169         this.format = DEFAULT_FORMAT;
170         this.writer = createWriter(out, format.getEncoding());
171         this.autoFlush = true;
172         namespaceStack.push(Namespace.NO_NAMESPACE);
173     }
174 
175     public XMLWriter(OutputStream out, OutputFormat format)
176             throws UnsupportedEncodingException {
177         this.format = format;
178         this.writer = createWriter(out, format.getEncoding());
179         this.autoFlush = true;
180         namespaceStack.push(Namespace.NO_NAMESPACE);
181     }
182 
183     public XMLWriter(OutputFormat format) throws UnsupportedEncodingException {
184         this.format = format;
185         this.writer = createWriter(System.out, format.getEncoding());
186         this.autoFlush = true;
187         namespaceStack.push(Namespace.NO_NAMESPACE);
188     }
189 
190     public void setWriter(Writer writer) {
191         this.writer = writer;
192         this.autoFlush = false;
193     }
194 
195     public void setOutputStream(OutputStream out)
196             throws UnsupportedEncodingException {
197         this.writer = createWriter(out, format.getEncoding());
198         this.autoFlush = true;
199     }
200 
201     /***
202      * DOCUMENT ME!
203      * 
204      * @return true if text thats output should be escaped. This is enabled by
205      *         default. It could be disabled if the output format is textual,
206      *         like in XSLT where we can have xml, html or text output.
207      */
208     public boolean isEscapeText() {
209         return escapeText;
210     }
211 
212     /***
213      * Sets whether text output should be escaped or not. This is enabled by
214      * default. It could be disabled if the output format is textual, like in
215      * XSLT where we can have xml, html or text output.
216      * 
217      * @param escapeText
218      *            DOCUMENT ME!
219      */
220     public void setEscapeText(boolean escapeText) {
221         this.escapeText = escapeText;
222     }
223 
224     /***
225      * Set the initial indentation level. This can be used to output a document
226      * (or, more likely, an element) starting at a given indent level, so it's
227      * not always flush against the left margin. Default: 0
228      * 
229      * @param indentLevel
230      *            the number of indents to start with
231      */
232     public void setIndentLevel(int indentLevel) {
233         this.indentLevel = indentLevel;
234     }
235 
236     /***
237      * Returns the maximum allowed character code that should be allowed
238      * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8
239      * bit).
240      * 
241      * @return DOCUMENT ME!
242      */
243     public int getMaximumAllowedCharacter() {
244         if (maximumAllowedCharacter == 0) {
245             maximumAllowedCharacter = defaultMaximumAllowedCharacter();
246         }
247 
248         return maximumAllowedCharacter;
249     }
250 
251     /***
252      * Sets the maximum allowed character code that should be allowed unescaped
253      * such as 127 in US-ASCII (7 bit) or 255 in ISO- (8 bit) or -1 to not
254      * escape any characters (other than the special XML characters like <
255      * > &) If this is not explicitly set then it is defaulted from the
256      * encoding.
257      * 
258      * @param maximumAllowedCharacter
259      *            The maximumAllowedCharacter to set
260      */
261     public void setMaximumAllowedCharacter(int maximumAllowedCharacter) {
262         this.maximumAllowedCharacter = maximumAllowedCharacter;
263     }
264 
265     /***
266      * Flushes the underlying Writer
267      * 
268      * @throws IOException
269      *             DOCUMENT ME!
270      */
271     public void flush() throws IOException {
272         writer.flush();
273     }
274 
275     /***
276      * Closes the underlying Writer
277      * 
278      * @throws IOException
279      *             DOCUMENT ME!
280      */
281     public void close() throws IOException {
282         writer.close();
283     }
284 
285     /***
286      * Writes the new line text to the underlying Writer
287      * 
288      * @throws IOException
289      *             DOCUMENT ME!
290      */
291     public void println() throws IOException {
292         writer.write(format.getLineSeparator());
293     }
294 
295     /***
296      * Writes the given {@link Attribute}.
297      * 
298      * @param attribute
299      *            <code>Attribute</code> to output.
300      * 
301      * @throws IOException
302      *             DOCUMENT ME!
303      */
304     public void write(Attribute attribute) throws IOException {
305         writeAttribute(attribute);
306 
307         if (autoFlush) {
308             flush();
309         }
310     }
311 
312     /***
313      * <p>
314      * This will print the <code>Document</code> to the current Writer.
315      * </p>
316      * 
317      * <p>
318      * Warning: using your own Writer may cause the writer's preferred character
319      * encoding to be ignored. If you use encodings other than UTF8, we
320      * recommend using the method that takes an OutputStream instead.
321      * </p>
322      * 
323      * <p>
324      * Note: as with all Writers, you may need to flush() yours after this
325      * method returns.
326      * </p>
327      * 
328      * @param doc
329      *            <code>Document</code> to format.
330      * 
331      * @throws IOException
332      *             if there's any problem writing.
333      */
334     public void write(Document doc) throws IOException {
335         writeDeclaration();
336 
337         if (doc.getDocType() != null) {
338             indent();
339             writeDocType(doc.getDocType());
340         }
341 
342         for (int i = 0, size = doc.nodeCount(); i < size; i++) {
343             Node node = doc.node(i);
344             writeNode(node);
345         }
346 
347         writePrintln();
348 
349         if (autoFlush) {
350             flush();
351         }
352     }
353 
354     /***
355      * <p>
356      * Writes the <code>{@link Element}</code>, including its <code>{@link
357      * Attribute}</code>
358      * s, and its value, and all its content (child nodes) to the current
359      * Writer.
360      * </p>
361      * 
362      * @param element
363      *            <code>Element</code> to output.
364      * 
365      * @throws IOException
366      *             DOCUMENT ME!
367      */
368     public void write(Element element) throws IOException {
369         writeElement(element);
370 
371         if (autoFlush) {
372             flush();
373         }
374     }
375 
376     /***
377      * Writes the given {@link CDATA}.
378      * 
379      * @param cdata
380      *            <code>CDATA</code> to output.
381      * 
382      * @throws IOException
383      *             DOCUMENT ME!
384      */
385     public void write(CDATA cdata) throws IOException {
386         writeCDATA(cdata.getText());
387 
388         if (autoFlush) {
389             flush();
390         }
391     }
392 
393     /***
394      * Writes the given {@link Comment}.
395      * 
396      * @param comment
397      *            <code>Comment</code> to output.
398      * 
399      * @throws IOException
400      *             DOCUMENT ME!
401      */
402     public void write(Comment comment) throws IOException {
403         writeComment(comment.getText());
404 
405         if (autoFlush) {
406             flush();
407         }
408     }
409 
410     /***
411      * Writes the given {@link DocumentType}.
412      * 
413      * @param docType
414      *            <code>DocumentType</code> to output.
415      * 
416      * @throws IOException
417      *             DOCUMENT ME!
418      */
419     public void write(DocumentType docType) throws IOException {
420         writeDocType(docType);
421 
422         if (autoFlush) {
423             flush();
424         }
425     }
426 
427     /***
428      * Writes the given {@link Entity}.
429      * 
430      * @param entity
431      *            <code>Entity</code> to output.
432      * 
433      * @throws IOException
434      *             DOCUMENT ME!
435      */
436     public void write(Entity entity) throws IOException {
437         writeEntity(entity);
438 
439         if (autoFlush) {
440             flush();
441         }
442     }
443 
444     /***
445      * Writes the given {@link Namespace}.
446      * 
447      * @param namespace
448      *            <code>Namespace</code> to output.
449      * 
450      * @throws IOException
451      *             DOCUMENT ME!
452      */
453     public void write(Namespace namespace) throws IOException {
454         writeNamespace(namespace);
455 
456         if (autoFlush) {
457             flush();
458         }
459     }
460 
461     /***
462      * Writes the given {@link ProcessingInstruction}.
463      * 
464      * @param processingInstruction
465      *            <code>ProcessingInstruction</code> to output.
466      * 
467      * @throws IOException
468      *             DOCUMENT ME!
469      */
470     public void write(ProcessingInstruction processingInstruction)
471             throws IOException {
472         writeProcessingInstruction(processingInstruction);
473 
474         if (autoFlush) {
475             flush();
476         }
477     }
478 
479     /***
480      * <p>
481      * Print out a {@link String}, Perfoms the necessary entity escaping and
482      * whitespace stripping.
483      * </p>
484      * 
485      * @param text
486      *            is the text to output
487      * 
488      * @throws IOException
489      *             DOCUMENT ME!
490      */
491     public void write(String text) throws IOException {
492         writeString(text);
493 
494         if (autoFlush) {
495             flush();
496         }
497     }
498 
499     /***
500      * Writes the given {@link Text}.
501      * 
502      * @param text
503      *            <code>Text</code> to output.
504      * 
505      * @throws IOException
506      *             DOCUMENT ME!
507      */
508     public void write(Text text) throws IOException {
509         writeString(text.getText());
510 
511         if (autoFlush) {
512             flush();
513         }
514     }
515 
516     /***
517      * Writes the given {@link Node}.
518      * 
519      * @param node
520      *            <code>Node</code> to output.
521      * 
522      * @throws IOException
523      *             DOCUMENT ME!
524      */
525     public void write(Node node) throws IOException {
526         writeNode(node);
527 
528         if (autoFlush) {
529             flush();
530         }
531     }
532 
533     /***
534      * Writes the given object which should be a String, a Node or a List of
535      * Nodes.
536      * 
537      * @param object
538      *            is the object to output.
539      * 
540      * @throws IOException
541      *             DOCUMENT ME!
542      */
543     public void write(Object object) throws IOException {
544         if (object instanceof Node) {
545             write((Node) object);
546         } else if (object instanceof String) {
547             write((String) object);
548         } else if (object instanceof List) {
549             List list = (List) object;
550 
551             for (int i = 0, size = list.size(); i < size; i++) {
552                 write(list.get(i));
553             }
554         } else if (object != null) {
555             throw new IOException("Invalid object: " + object);
556         }
557     }
558 
559     /***
560      * <p>
561      * Writes the opening tag of an {@link Element}, including its {@link
562      * Attribute}s but without its content.
563      * </p>
564      * 
565      * @param element
566      *            <code>Element</code> to output.
567      * 
568      * @throws IOException
569      *             DOCUMENT ME!
570      */
571     public void writeOpen(Element element) throws IOException {
572         writer.write("<");
573         writer.write(element.getQualifiedName());
574         writeAttributes(element);
575         writer.write(">");
576     }
577 
578     /***
579      * <p>
580      * Writes the closing tag of an {@link Element}
581      * </p>
582      * 
583      * @param element
584      *            <code>Element</code> to output.
585      * 
586      * @throws IOException
587      *             DOCUMENT ME!
588      */
589     public void writeClose(Element element) throws IOException {
590         writeClose(element.getQualifiedName());
591     }
592 
593     
594     
595     public void parse(InputSource source) throws IOException, SAXException {
596         installLexicalHandler();
597         super.parse(source);
598     }
599 
600     public void setProperty(String name, Object value)
601             throws SAXNotRecognizedException, SAXNotSupportedException {
602         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
603             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
604                 setLexicalHandler((LexicalHandler) value);
605 
606                 return;
607             }
608         }
609 
610         super.setProperty(name, value);
611     }
612 
613     public Object getProperty(String name) throws SAXNotRecognizedException,
614             SAXNotSupportedException {
615         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
616             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
617                 return getLexicalHandler();
618             }
619         }
620 
621         return super.getProperty(name);
622     }
623 
624     public void setLexicalHandler(LexicalHandler handler) {
625         if (handler == null) {
626             throw new NullPointerException("Null lexical handler");
627         } else {
628             this.lexicalHandler = handler;
629         }
630     }
631 
632     public LexicalHandler getLexicalHandler() {
633         return lexicalHandler;
634     }
635 
636     
637     
638     public void setDocumentLocator(Locator locator) {
639         super.setDocumentLocator(locator);
640     }
641 
642     public void startDocument() throws SAXException {
643         try {
644             writeDeclaration();
645             super.startDocument();
646         } catch (IOException e) {
647             handleException(e);
648         }
649     }
650 
651     public void endDocument() throws SAXException {
652         super.endDocument();
653 
654         if (autoFlush) {
655             try {
656                 flush();
657             } catch (IOException e) {
658             }
659         }
660     }
661 
662     public void startPrefixMapping(String prefix, String uri)
663             throws SAXException {
664         if (namespacesMap == null) {
665             namespacesMap = new HashMap();
666         }
667 
668         namespacesMap.put(prefix, uri);
669         super.startPrefixMapping(prefix, uri);
670     }
671 
672     public void endPrefixMapping(String prefix) throws SAXException {
673         super.endPrefixMapping(prefix);
674     }
675 
676     public void startElement(String namespaceURI, String localName,
677             String qName, Attributes attributes) throws SAXException {
678         try {
679             charsAdded = false;
680 
681             writePrintln();
682             indent();
683             writer.write("<");
684             writer.write(qName);
685             writeNamespaces();
686             writeAttributes(attributes);
687             writer.write(">");
688             ++indentLevel;
689             lastOutputNodeType = Node.ELEMENT_NODE;
690             lastElementClosed = false;
691 
692             super.startElement(namespaceURI, localName, qName, attributes);
693         } catch (IOException e) {
694             handleException(e);
695         }
696     }
697 
698     public void endElement(String namespaceURI, String localName, String qName)
699             throws SAXException {
700         try {
701             charsAdded = false;
702             --indentLevel;
703 
704             if (lastElementClosed) {
705                 writePrintln();
706                 indent();
707             }
708 
709             
710             
711             boolean hadContent = true;
712 
713             if (hadContent) {
714                 writeClose(qName);
715             } else {
716                 writeEmptyElementClose(qName);
717             }
718 
719             lastOutputNodeType = Node.ELEMENT_NODE;
720             lastElementClosed = true;
721 
722             super.endElement(namespaceURI, localName, qName);
723         } catch (IOException e) {
724             handleException(e);
725         }
726     }
727 
728     public void characters(char[] ch, int start, int length)
729             throws SAXException {
730         if ((ch == null) || (ch.length == 0) || (length <= 0)) {
731             return;
732         }
733 
734         try {
735             
736 
737 
738 
739 
740 
741             String string = String.valueOf(ch, start, length);
742 
743             if (escapeText) {
744                 string = escapeElementEntities(string);
745             }
746 
747             if (format.isTrimText()) {
748                 if ((lastOutputNodeType == Node.TEXT_NODE) && !charsAdded) {
749                     writer.write(' ');
750                 } else if (charsAdded && Character.isWhitespace(lastChar)) {
751                     writer.write(' ');
752                 } else if (lastOutputNodeType == Node.ELEMENT_NODE
753                         && format.isPadText() && lastElementClosed
754                         && Character.isWhitespace(ch[0])) {
755                     writer.write(PAD_TEXT);
756                 }
757 
758                 String delim = "";
759                 StringTokenizer tokens = new StringTokenizer(string);
760 
761                 while (tokens.hasMoreTokens()) {
762                     writer.write(delim);
763                     writer.write(tokens.nextToken());
764                     delim = " ";
765                 }
766             } else {
767                 writer.write(string);
768             }
769 
770             charsAdded = true;
771             lastChar = ch[(start + length) - 1];
772             lastOutputNodeType = Node.TEXT_NODE;
773 
774             super.characters(ch, start, length);
775         } catch (IOException e) {
776             handleException(e);
777         }
778     }
779 
780     public void ignorableWhitespace(char[] ch, int start, int length)
781             throws SAXException {
782         super.ignorableWhitespace(ch, start, length);
783     }
784 
785     public void processingInstruction(String target, String data)
786             throws SAXException {
787         try {
788             indent();
789             writer.write("<?");
790             writer.write(target);
791             writer.write(" ");
792             writer.write(data);
793             writer.write("?>");
794             writePrintln();
795             lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
796 
797             super.processingInstruction(target, data);
798         } catch (IOException e) {
799             handleException(e);
800         }
801     }
802 
803     
804     
805     public void notationDecl(String name, String publicID, String systemID)
806             throws SAXException {
807         super.notationDecl(name, publicID, systemID);
808     }
809 
810     public void unparsedEntityDecl(String name, String publicID,
811             String systemID, String notationName) throws SAXException {
812         super.unparsedEntityDecl(name, publicID, systemID, notationName);
813     }
814 
815     
816     
817     public void startDTD(String name, String publicID, String systemID)
818             throws SAXException {
819         inDTD = true;
820 
821         try {
822             writeDocType(name, publicID, systemID);
823         } catch (IOException e) {
824             handleException(e);
825         }
826 
827         if (lexicalHandler != null) {
828             lexicalHandler.startDTD(name, publicID, systemID);
829         }
830     }
831 
832     public void endDTD() throws SAXException {
833         inDTD = false;
834 
835         if (lexicalHandler != null) {
836             lexicalHandler.endDTD();
837         }
838     }
839 
840     public void startCDATA() throws SAXException {
841         try {
842             writer.write("<![CDATA[");
843         } catch (IOException e) {
844             handleException(e);
845         }
846 
847         if (lexicalHandler != null) {
848             lexicalHandler.startCDATA();
849         }
850     }
851 
852     public void endCDATA() throws SAXException {
853         try {
854             writer.write("]]>");
855         } catch (IOException e) {
856             handleException(e);
857         }
858 
859         if (lexicalHandler != null) {
860             lexicalHandler.endCDATA();
861         }
862     }
863 
864     public void startEntity(String name) throws SAXException {
865         try {
866             writeEntityRef(name);
867         } catch (IOException e) {
868             handleException(e);
869         }
870 
871         if (lexicalHandler != null) {
872             lexicalHandler.startEntity(name);
873         }
874     }
875 
876     public void endEntity(String name) throws SAXException {
877         if (lexicalHandler != null) {
878             lexicalHandler.endEntity(name);
879         }
880     }
881 
882     public void comment(char[] ch, int start, int length) throws SAXException {
883         if (showCommentsInDTDs || !inDTD) {
884             try {
885                 charsAdded = false;
886                 writeComment(new String(ch, start, length));
887             } catch (IOException e) {
888                 handleException(e);
889             }
890         }
891 
892         if (lexicalHandler != null) {
893             lexicalHandler.comment(ch, start, length);
894         }
895     }
896 
897     
898     
899     protected void writeElement(Element element) throws IOException {
900         int size = element.nodeCount();
901         String qualifiedName = element.getQualifiedName();
902 
903         writePrintln();
904         indent();
905 
906         writer.write("<");
907         writer.write(qualifiedName);
908 
909         int previouslyDeclaredNamespaces = namespaceStack.size();
910         Namespace ns = element.getNamespace();
911 
912         if (isNamespaceDeclaration(ns)) {
913             namespaceStack.push(ns);
914             writeNamespace(ns);
915         }
916 
917         
918         boolean textOnly = true;
919 
920         for (int i = 0; i < size; i++) {
921             Node node = element.node(i);
922 
923             if (node instanceof Namespace) {
924                 Namespace additional = (Namespace) node;
925 
926                 if (isNamespaceDeclaration(additional)) {
927                     namespaceStack.push(additional);
928                     writeNamespace(additional);
929                 }
930             } else if (node instanceof Element) {
931                 textOnly = false;
932             } else if (node instanceof Comment) {
933                 textOnly = false;
934             }
935         }
936 
937         writeAttributes(element);
938 
939         lastOutputNodeType = Node.ELEMENT_NODE;
940 
941         if (size <= 0) {
942             writeEmptyElementClose(qualifiedName);
943         } else {
944             writer.write(">");
945 
946             if (textOnly) {
947                 
948                 
949                 writeElementContent(element);
950             } else {
951                 
952                 ++indentLevel;
953 
954                 writeElementContent(element);
955 
956                 --indentLevel;
957 
958                 writePrintln();
959                 indent();
960             }
961 
962             writer.write("</");
963             writer.write(qualifiedName);
964             writer.write(">");
965         }
966 
967         
968         while (namespaceStack.size() > previouslyDeclaredNamespaces) {
969             namespaceStack.pop();
970         }
971 
972         lastOutputNodeType = Node.ELEMENT_NODE;
973     }
974 
975     /***
976      * Determines if element is a special case of XML elements where it contains
977      * an xml:space attribute of "preserve". If it does, then retain whitespace.
978      * 
979      * @param element
980      *            DOCUMENT ME!
981      * 
982      * @return DOCUMENT ME!
983      */
984     protected final boolean isElementSpacePreserved(Element element) {
985         final Attribute attr = (Attribute) element.attribute("space");
986         boolean preserveFound = preserve; 
987 
988         if (attr != null) {
989             if ("xml".equals(attr.getNamespacePrefix())
990                     && "preserve".equals(attr.getText())) {
991                 preserveFound = true;
992             } else {
993                 preserveFound = false;
994             }
995         }
996 
997         return preserveFound;
998     }
999 
1000     /***
1001      * Outputs the content of the given element. If whitespace trimming is
1002      * enabled then all adjacent text nodes are appended together before the
1003      * whitespace trimming occurs to avoid problems with multiple text nodes
1004      * being created due to text content that spans parser buffers in a SAX
1005      * parser.
1006      * 
1007      * @param element
1008      *            DOCUMENT ME!
1009      * 
1010      * @throws IOException
1011      *             DOCUMENT ME!
1012      */
1013     protected void writeElementContent(Element element) throws IOException {
1014         boolean trim = format.isTrimText();
1015         boolean oldPreserve = preserve;
1016 
1017         if (trim) { 
1018             preserve = isElementSpacePreserved(element);
1019             trim = !preserve;
1020         }
1021 
1022         if (trim) {
1023             
1024             
1025             Text lastTextNode = null;
1026             StringBuffer buff = null;
1027             boolean textOnly = true;
1028 
1029             for (int i = 0, size = element.nodeCount(); i < size; i++) {
1030                 Node node = element.node(i);
1031 
1032                 if (node instanceof Text) {
1033                     if (lastTextNode == null) {
1034                         lastTextNode = (Text) node;
1035                     } else {
1036                         if (buff == null) {
1037                             buff = new StringBuffer(lastTextNode.getText());
1038                         }
1039 
1040                         buff.append(((Text) node).getText());
1041                     }
1042                 } else {
1043                     if (!textOnly && format.isPadText()) {
1044                         
1045                         
1046                         char firstChar = 'a';
1047                         if (buff != null) {
1048                             firstChar = buff.charAt(0);
1049                         } else if (lastTextNode != null) {
1050                             firstChar = lastTextNode.getText().charAt(0);
1051                         }
1052 
1053                         if (Character.isWhitespace(firstChar)) {
1054                             writer.write(PAD_TEXT);
1055                         }
1056                     }
1057 
1058                     if (lastTextNode != null) {
1059                         if (buff != null) {
1060                             writeString(buff.toString());
1061                             buff = null;
1062                         } else {
1063                             writeString(lastTextNode.getText());
1064                         }
1065 
1066                         if (format.isPadText()) {
1067                             
1068                             
1069                             char lastTextChar = 'a';
1070                             if (buff != null) {
1071                                 lastTextChar = buff.charAt(buff.length() - 1);
1072                             } else if (lastTextNode != null) {
1073                                 String txt = lastTextNode.getText();
1074                                 lastTextChar = txt.charAt(txt.length() - 1);
1075                             }
1076 
1077                             if (Character.isWhitespace(lastTextChar)) {
1078                                 writer.write(PAD_TEXT);
1079                             }
1080                         }
1081 
1082                         lastTextNode = null;
1083                     }
1084 
1085                     textOnly = false;
1086                     writeNode(node);
1087                 }
1088             }
1089 
1090             if (lastTextNode != null) {
1091                 if (!textOnly && format.isPadText()) {
1092                     
1093                     
1094                     char firstChar = 'a';
1095                     if (buff != null) {
1096                         firstChar = buff.charAt(0);
1097                     } else {
1098                         firstChar = lastTextNode.getText().charAt(0);
1099                     }
1100 
1101                     if (Character.isWhitespace(firstChar)) {
1102                         writer.write(PAD_TEXT);
1103                     }
1104                 }
1105 
1106                 if (buff != null) {
1107                     writeString(buff.toString());
1108                     buff = null;
1109                 } else {
1110                     writeString(lastTextNode.getText());
1111                 }
1112 
1113                 lastTextNode = null;
1114             }
1115         } else {
1116             Node lastTextNode = null;
1117 
1118             for (int i = 0, size = element.nodeCount(); i < size; i++) {
1119                 Node node = element.node(i);
1120 
1121                 if (node instanceof Text) {
1122                     writeNode(node);
1123                     lastTextNode = node;
1124                 } else {
1125                     if ((lastTextNode != null) && format.isPadText()) {
1126                         
1127                         
1128                         String txt = lastTextNode.getText();
1129                         char lastTextChar = txt.charAt(txt.length() - 1);
1130 
1131                         if (Character.isWhitespace(lastTextChar)) {
1132                             writer.write(PAD_TEXT);
1133                         }
1134                     }
1135 
1136                     writeNode(node);
1137 
1138                     
1139                     
1140                     
1141 
1142                     lastTextNode = null;
1143                 }
1144             }
1145         }
1146 
1147         preserve = oldPreserve;
1148     }
1149 
1150     protected void writeCDATA(String text) throws IOException {
1151         writer.write("<![CDATA[");
1152 
1153         if (text != null) {
1154             writer.write(text);
1155         }
1156 
1157         writer.write("]]>");
1158 
1159         lastOutputNodeType = Node.CDATA_SECTION_NODE;
1160     }
1161 
1162     protected void writeDocType(DocumentType docType) throws IOException {
1163         if (docType != null) {
1164             docType.write(writer);
1165             writePrintln();
1166         }
1167     }
1168 
1169     protected void writeNamespace(Namespace namespace) throws IOException {
1170         if (namespace != null) {
1171             writeNamespace(namespace.getPrefix(), namespace.getURI());
1172         }
1173     }
1174 
1175     /***
1176      * Writes the SAX namepsaces
1177      * 
1178      * @throws IOException
1179      *             DOCUMENT ME!
1180      */
1181     protected void writeNamespaces() throws IOException {
1182         if (namespacesMap != null) {
1183             for (Iterator iter = namespacesMap.entrySet().iterator(); iter
1184                     .hasNext();) {
1185                 Map.Entry entry = (Map.Entry) iter.next();
1186                 String prefix = (String) entry.getKey();
1187                 String uri = (String) entry.getValue();
1188                 writeNamespace(prefix, uri);
1189             }
1190 
1191             namespacesMap = null;
1192         }
1193     }
1194 
1195     /***
1196      * Writes the SAX namepsaces
1197      * 
1198      * @param prefix
1199      *            the prefix
1200      * @param uri
1201      *            the namespace uri
1202      * 
1203      * @throws IOException
1204      */
1205     protected void writeNamespace(String prefix, String uri) 
1206             throws IOException {
1207         if ((prefix != null) && (prefix.length() > 0)) {
1208             writer.write(" xmlns:");
1209             writer.write(prefix);
1210             writer.write("=\"");
1211         } else {
1212             writer.write(" xmlns=\"");
1213         }
1214 
1215         writer.write(uri);
1216         writer.write("\"");
1217     }
1218 
1219     protected void writeProcessingInstruction(ProcessingInstruction pi)
1220             throws IOException {
1221         
1222         writer.write("<?");
1223         writer.write(pi.getName());
1224         writer.write(" ");
1225         writer.write(pi.getText());
1226         writer.write("?>");
1227         writePrintln();
1228 
1229         lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
1230     }
1231 
1232     protected void writeString(String text) throws IOException {
1233         if ((text != null) && (text.length() > 0)) {
1234             if (escapeText) {
1235                 text = escapeElementEntities(text);
1236             }
1237 
1238             
1239             
1240             
1241             
1242             
1243             if (format.isTrimText()) {
1244                 boolean first = true;
1245                 StringTokenizer tokenizer = new StringTokenizer(text);
1246 
1247                 while (tokenizer.hasMoreTokens()) {
1248                     String token = tokenizer.nextToken();
1249 
1250                     if (first) {
1251                         first = false;
1252 
1253                         if (lastOutputNodeType == Node.TEXT_NODE) {
1254                             writer.write(" ");
1255                         }
1256                     } else {
1257                         writer.write(" ");
1258                     }
1259 
1260                     writer.write(token);
1261                     lastOutputNodeType = Node.TEXT_NODE;
1262                     lastChar = token.charAt(token.length() - 1);
1263                 }
1264             } else {
1265                 lastOutputNodeType = Node.TEXT_NODE;
1266                 writer.write(text);
1267                 lastChar = text.charAt(text.length() - 1);
1268             }
1269         }
1270     }
1271 
1272     /***
1273      * This method is used to write out Nodes that contain text and still allow
1274      * for xml:space to be handled properly.
1275      * 
1276      * @param node
1277      *            DOCUMENT ME!
1278      * 
1279      * @throws IOException
1280      *             DOCUMENT ME!
1281      */
1282     protected void writeNodeText(Node node) throws IOException {
1283         String text = node.getText();
1284 
1285         if ((text != null) && (text.length() > 0)) {
1286             if (escapeText) {
1287                 text = escapeElementEntities(text);
1288             }
1289 
1290             lastOutputNodeType = Node.TEXT_NODE;
1291             writer.write(text);
1292             lastChar = text.charAt(text.length() - 1);
1293         }
1294     }
1295 
1296     protected void writeNode(Node node) throws IOException {
1297         int nodeType = node.getNodeType();
1298 
1299         switch (nodeType) {
1300             case Node.ELEMENT_NODE:
1301                 writeElement((Element) node);
1302 
1303                 break;
1304 
1305             case Node.ATTRIBUTE_NODE:
1306                 writeAttribute((Attribute) node);
1307 
1308                 break;
1309 
1310             case Node.TEXT_NODE:
1311                 writeNodeText(node);
1312 
1313                 
1314                 break;
1315 
1316             case Node.CDATA_SECTION_NODE:
1317                 writeCDATA(node.getText());
1318 
1319                 break;
1320 
1321             case Node.ENTITY_REFERENCE_NODE:
1322                 writeEntity((Entity) node);
1323 
1324                 break;
1325 
1326             case Node.PROCESSING_INSTRUCTION_NODE:
1327                 writeProcessingInstruction((ProcessingInstruction) node);
1328 
1329                 break;
1330 
1331             case Node.COMMENT_NODE:
1332                 writeComment(node.getText());
1333 
1334                 break;
1335 
1336             case Node.DOCUMENT_NODE:
1337                 write((Document) node);
1338 
1339                 break;
1340 
1341             case Node.DOCUMENT_TYPE_NODE:
1342                 writeDocType((DocumentType) node);
1343 
1344                 break;
1345 
1346             case Node.NAMESPACE_NODE:
1347 
1348                 
1349                 
1350                 break;
1351 
1352             default:
1353                 throw new IOException("Invalid node type: " + node);
1354         }
1355     }
1356 
1357     protected void installLexicalHandler() {
1358         XMLReader parent = getParent();
1359 
1360         if (parent == null) {
1361             throw new NullPointerException("No parent for filter");
1362         }
1363 
1364         
1365         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
1366             try {
1367                 parent.setProperty(LEXICAL_HANDLER_NAMES[i], this);
1368 
1369                 break;
1370             } catch (SAXNotRecognizedException ex) {
1371                 
1372             } catch (SAXNotSupportedException ex) {
1373                 
1374             }
1375         }
1376     }
1377 
1378     protected void writeDocType(String name, String publicID, String systemID)
1379             throws IOException {
1380         boolean hasPublic = false;
1381 
1382         writer.write("<!DOCTYPE ");
1383         writer.write(name);
1384 
1385         if ((publicID != null) && (!publicID.equals(""))) {
1386             writer.write(" PUBLIC \"");
1387             writer.write(publicID);
1388             writer.write("\"");
1389             hasPublic = true;
1390         }
1391 
1392         if ((systemID != null) && (!systemID.equals(""))) {
1393             if (!hasPublic) {
1394                 writer.write(" SYSTEM");
1395             }
1396 
1397             writer.write(" \"");
1398             writer.write(systemID);
1399             writer.write("\"");
1400         }
1401 
1402         writer.write(">");
1403         writePrintln();
1404     }
1405 
1406     protected void writeEntity(Entity entity) throws IOException {
1407         if (!resolveEntityRefs()) {
1408             writeEntityRef(entity.getName());
1409         } else {
1410             writer.write(entity.getText());
1411         }
1412     }
1413 
1414     protected void writeEntityRef(String name) throws IOException {
1415         writer.write("&");
1416         writer.write(name);
1417         writer.write(";");
1418 
1419         lastOutputNodeType = Node.ENTITY_REFERENCE_NODE;
1420     }
1421 
1422     protected void writeComment(String text) throws IOException {
1423         if (format.isNewlines()) {
1424             println();
1425             indent();
1426         }
1427 
1428         writer.write("<!--");
1429         writer.write(text);
1430         writer.write("-->");
1431 
1432         lastOutputNodeType = Node.COMMENT_NODE;
1433     }
1434 
1435     /***
1436      * Writes the attributes of the given element
1437      * 
1438      * @param element
1439      *            DOCUMENT ME!
1440      * 
1441      * @throws IOException
1442      *             DOCUMENT ME!
1443      */
1444     protected void writeAttributes(Element element) throws IOException {
1445         
1446         
1447         
1448         
1449         for (int i = 0, size = element.attributeCount(); i < size; i++) {
1450             Attribute attribute = element.attribute(i);
1451             Namespace ns = attribute.getNamespace();
1452 
1453             if ((ns != null) && (ns != Namespace.NO_NAMESPACE)
1454                     && (ns != Namespace.XML_NAMESPACE)) {
1455                 String prefix = ns.getPrefix();
1456                 String uri = namespaceStack.getURI(prefix);
1457 
1458                 if (!ns.getURI().equals(uri)) {
1459                     writeNamespace(ns);
1460                     namespaceStack.push(ns);
1461                 }
1462             }
1463 
1464             
1465             
1466             
1467             String attName = attribute.getName();
1468 
1469             if (attName.startsWith("xmlns:")) {
1470                 String prefix = attName.substring(6);
1471 
1472                 if (namespaceStack.getNamespaceForPrefix(prefix) == null) {
1473                     String uri = attribute.getValue();
1474                     namespaceStack.push(prefix, uri);
1475                     writeNamespace(prefix, uri);
1476                 }
1477             } else if (attName.equals("xmlns")) {
1478                 if (namespaceStack.getDefaultNamespace() == null) {
1479                     String uri = attribute.getValue();
1480                     namespaceStack.push(null, uri);
1481                     writeNamespace(null, uri);
1482                 }
1483             } else {
1484                 char quote = format.getAttributeQuoteCharacter();
1485                 writer.write(" ");
1486                 writer.write(attribute.getQualifiedName());
1487                 writer.write("=");
1488                 writer.write(quote);
1489                 writeEscapeAttributeEntities(attribute.getValue());
1490                 writer.write(quote);
1491             }
1492         }
1493     }
1494 
1495     protected void writeAttribute(Attribute attribute) throws IOException {
1496         writer.write(" ");
1497         writer.write(attribute.getQualifiedName());
1498         writer.write("=");
1499 
1500         char quote = format.getAttributeQuoteCharacter();
1501         writer.write(quote);
1502 
1503         writeEscapeAttributeEntities(attribute.getValue());
1504 
1505         writer.write(quote);
1506         lastOutputNodeType = Node.ATTRIBUTE_NODE;
1507     }
1508 
1509     protected void writeAttributes(Attributes attributes) throws IOException {
1510         for (int i = 0, size = attributes.getLength(); i < size; i++) {
1511             writeAttribute(attributes, i);
1512         }
1513     }
1514 
1515     protected void writeAttribute(Attributes attributes, int index)
1516             throws IOException {
1517         char quote = format.getAttributeQuoteCharacter();
1518         writer.write(" ");
1519         writer.write(attributes.getQName(index));
1520         writer.write("=");
1521         writer.write(quote);
1522         writeEscapeAttributeEntities(attributes.getValue(index));
1523         writer.write(quote);
1524     }
1525 
1526     protected void indent() throws IOException {
1527         String indent = format.getIndent();
1528 
1529         if ((indent != null) && (indent.length() > 0)) {
1530             for (int i = 0; i < indentLevel; i++) {
1531                 writer.write(indent);
1532             }
1533         }
1534     }
1535 
1536     /***
1537      * <p>
1538      * This will print a new line only if the newlines flag was set to true
1539      * </p>
1540      * 
1541      * @throws IOException
1542      *             DOCUMENT ME!
1543      */
1544     protected void writePrintln() throws IOException {
1545         if (format.isNewlines()) {
1546             String seperator = format.getLineSeparator();
1547             if (lastChar != seperator.charAt(seperator.length() - 1)) {
1548                 writer.write(format.getLineSeparator());
1549             }
1550         }
1551     }
1552 
1553     /***
1554      * Get an OutputStreamWriter, use preferred encoding.
1555      * 
1556      * @param outStream
1557      *            DOCUMENT ME!
1558      * @param encoding
1559      *            DOCUMENT ME!
1560      * 
1561      * @return DOCUMENT ME!
1562      * 
1563      * @throws UnsupportedEncodingException
1564      *             DOCUMENT ME!
1565      */
1566     protected Writer createWriter(OutputStream outStream, String encoding)
1567             throws UnsupportedEncodingException {
1568         return new BufferedWriter(new OutputStreamWriter(outStream, encoding));
1569     }
1570 
1571     /***
1572      * <p>
1573      * This will write the declaration to the given Writer. Assumes XML version
1574      * 1.0 since we don't directly know.
1575      * </p>
1576      * 
1577      * @throws IOException
1578      *             DOCUMENT ME!
1579      */
1580     protected void writeDeclaration() throws IOException {
1581         String encoding = format.getEncoding();
1582 
1583         
1584         if (!format.isSuppressDeclaration()) {
1585             
1586             if (encoding.equals("UTF8")) {
1587                 writer.write("<?xml version=\"1.0\"");
1588 
1589                 if (!format.isOmitEncoding()) {
1590                     writer.write(" encoding=\"UTF-8\"");
1591                 }
1592 
1593                 writer.write("?>");
1594             } else {
1595                 writer.write("<?xml version=\"1.0\"");
1596 
1597                 if (!format.isOmitEncoding()) {
1598                     writer.write(" encoding=\"" + encoding + "\"");
1599                 }
1600 
1601                 writer.write("?>");
1602             }
1603 
1604             if (format.isNewLineAfterDeclaration()) {
1605                 println();
1606             }
1607         }
1608     }
1609 
1610     protected void writeClose(String qualifiedName) throws IOException {
1611         writer.write("</");
1612         writer.write(qualifiedName);
1613         writer.write(">");
1614     }
1615 
1616     protected void writeEmptyElementClose(String qualifiedName)
1617             throws IOException {
1618         
1619         if (!format.isExpandEmptyElements()) {
1620             writer.write("/>");
1621         } else {
1622             writer.write("></");
1623             writer.write(qualifiedName);
1624             writer.write(">");
1625         }
1626     }
1627 
1628     protected boolean isExpandEmptyElements() {
1629         return format.isExpandEmptyElements();
1630     }
1631 
1632     /***
1633      * This will take the pre-defined entities in XML 1.0 and convert their
1634      * character representation to the appropriate entity reference, suitable
1635      * for XML attributes.
1636      * 
1637      * @param text
1638      *            DOCUMENT ME!
1639      * 
1640      * @return DOCUMENT ME!
1641      */
1642     protected String escapeElementEntities(String text) {
1643         char[] block = null;
1644         int i;
1645         int last = 0;
1646         int size = text.length();
1647 
1648         for (i = 0; i < size; i++) {
1649             String entity = null;
1650             char c = text.charAt(i);
1651 
1652             switch (c) {
1653                 case '<':
1654                     entity = "<";
1655 
1656                     break;
1657 
1658                 case '>':
1659                     entity = ">";
1660 
1661                     break;
1662 
1663                 case '&':
1664                     entity = "&";
1665 
1666                     break;
1667 
1668                 case '\t':
1669                 case '\n':
1670                 case '\r':
1671 
1672                     
1673                     if (preserve) {
1674                         entity = String.valueOf(c);
1675                     }
1676 
1677                     break;
1678 
1679                 default:
1680 
1681                     if ((c < 32) || shouldEncodeChar(c)) {
1682                         entity = "&#" + (int) c + ";";
1683                     }
1684 
1685                     break;
1686             }
1687 
1688             if (entity != null) {
1689                 if (block == null) {
1690                     block = text.toCharArray();
1691                 }
1692 
1693                 buffer.append(block, last, i - last);
1694                 buffer.append(entity);
1695                 last = i + 1;
1696             }
1697         }
1698 
1699         if (last == 0) {
1700             return text;
1701         }
1702 
1703         if (last < size) {
1704             if (block == null) {
1705                 block = text.toCharArray();
1706             }
1707 
1708             buffer.append(block, last, i - last);
1709         }
1710 
1711         String answer = buffer.toString();
1712         buffer.setLength(0);
1713 
1714         return answer;
1715     }
1716 
1717     protected void writeEscapeAttributeEntities(String txt) throws IOException {
1718         if (txt != null) {
1719             String escapedText = escapeAttributeEntities(txt);
1720             writer.write(escapedText);
1721         }
1722     }
1723 
1724     /***
1725      * This will take the pre-defined entities in XML 1.0 and convert their
1726      * character representation to the appropriate entity reference, suitable
1727      * for XML attributes.
1728      * 
1729      * @param text
1730      *            DOCUMENT ME!
1731      * 
1732      * @return DOCUMENT ME!
1733      */
1734     protected String escapeAttributeEntities(String text) {
1735         char quote = format.getAttributeQuoteCharacter();
1736 
1737         char[] block = null;
1738         int i;
1739         int last = 0;
1740         int size = text.length();
1741 
1742         for (i = 0; i < size; i++) {
1743             String entity = null;
1744             char c = text.charAt(i);
1745 
1746             switch (c) {
1747                 case '<':
1748                     entity = "<";
1749 
1750                     break;
1751 
1752                 case '>':
1753                     entity = ">";
1754 
1755                     break;
1756 
1757                 case '\'':
1758 
1759                     if (quote == '\'') {
1760                         entity = "'";
1761                     }
1762 
1763                     break;
1764 
1765                 case '\"':
1766 
1767                     if (quote == '\"') {
1768                         entity = """;
1769                     }
1770 
1771                     break;
1772 
1773                 case '&':
1774                     entity = "&";
1775 
1776                     break;
1777 
1778                 case '\t':
1779                 case '\n':
1780                 case '\r':
1781 
1782                     
1783                     break;
1784 
1785                 default:
1786 
1787                     if ((c < 32) || shouldEncodeChar(c)) {
1788                         entity = "&#" + (int) c + ";";
1789                     }
1790 
1791                     break;
1792             }
1793 
1794             if (entity != null) {
1795                 if (block == null) {
1796                     block = text.toCharArray();
1797                 }
1798 
1799                 buffer.append(block, last, i - last);
1800                 buffer.append(entity);
1801                 last = i + 1;
1802             }
1803         }
1804 
1805         if (last == 0) {
1806             return text;
1807         }
1808 
1809         if (last < size) {
1810             if (block == null) {
1811                 block = text.toCharArray();
1812             }
1813 
1814             buffer.append(block, last, i - last);
1815         }
1816 
1817         String answer = buffer.toString();
1818         buffer.setLength(0);
1819 
1820         return answer;
1821     }
1822 
1823     /***
1824      * Should the given character be escaped. This depends on the encoding of
1825      * the document.
1826      * 
1827      * @param c
1828      *            DOCUMENT ME!
1829      * 
1830      * @return boolean
1831      */
1832     protected boolean shouldEncodeChar(char c) {
1833         int max = getMaximumAllowedCharacter();
1834 
1835         return (max > 0) && (c > max);
1836     }
1837 
1838     /***
1839      * Returns the maximum allowed character code that should be allowed
1840      * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8
1841      * bit).
1842      * 
1843      * @return DOCUMENT ME!
1844      */
1845     protected int defaultMaximumAllowedCharacter() {
1846         String encoding = format.getEncoding();
1847 
1848         if (encoding != null) {
1849             if (encoding.equals("US-ASCII")) {
1850                 return 127;
1851             }
1852         }
1853 
1854         
1855         return -1;
1856     }
1857 
1858     protected boolean isNamespaceDeclaration(Namespace ns) {
1859         if ((ns != null) && (ns != Namespace.XML_NAMESPACE)) {
1860             String uri = ns.getURI();
1861 
1862             if (uri != null) {
1863                 if (!namespaceStack.contains(ns)) {
1864                     return true;
1865                 }
1866             }
1867         }
1868 
1869         return false;
1870     }
1871 
1872     protected void handleException(IOException e) throws SAXException {
1873         throw new SAXException(e);
1874     }
1875 
1876     
1877 
1878     /***
1879      * Lets subclasses get at the current format object, so they can call
1880      * setTrimText, setNewLines, etc. Put in to support the HTMLWriter, in the
1881      * way that it pushes the current newline/trim state onto a stack and
1882      * overrides the state within preformatted tags.
1883      * 
1884      * @return DOCUMENT ME!
1885      */
1886     protected OutputFormat getOutputFormat() {
1887         return format;
1888     }
1889 
1890     public boolean resolveEntityRefs() {
1891         return resolveEntityRefs;
1892     }
1893 
1894     public void setResolveEntityRefs(boolean resolve) {
1895         this.resolveEntityRefs = resolve;
1896     }
1897 }
1898 
1899 
1900 
1901 
1902 
1903 
1904 
1905 
1906 
1907 
1908 
1909 
1910 
1911 
1912 
1913 
1914 
1915 
1916 
1917 
1918 
1919 
1920 
1921 
1922 
1923 
1924 
1925 
1926 
1927 
1928 
1929 
1930 
1931 
1932 
1933 
1934