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