comparison NGSrich_0.5.5/src/org/jdom/output/XMLOutputter.java @ 0:89ad0a9cca52 default tip

Uploaded
author pfrommolt
date Mon, 21 Nov 2011 08:12:19 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:89ad0a9cca52
1 /*--
2
3 $Id: XMLOutputter.java,v 1.117 2009/07/23 05:54:23 jhunter Exp $
4
5 Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 1. Redistributions of source code must retain the above copyright
13 notice, this list of conditions, and the following disclaimer.
14
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions, and the disclaimer that follows
17 these conditions in the documentation and/or other materials
18 provided with the distribution.
19
20 3. The name "JDOM" must not be used to endorse or promote products
21 derived from this software without prior written permission. For
22 written permission, please contact <request_AT_jdom_DOT_org>.
23
24 4. Products derived from this software may not be called "JDOM", nor
25 may "JDOM" appear in their name, without prior written permission
26 from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28 In addition, we request (but do not require) that you include in the
29 end-user documentation provided with the redistribution and/or in the
30 software itself an acknowledgement equivalent to the following:
31 "This product includes software developed by the
32 JDOM Project (http://www.jdom.org/)."
33 Alternatively, the acknowledgment may be graphical using the logos
34 available at http://www.jdom.org/images/logos.
35
36 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 SUCH DAMAGE.
48
49 This software consists of voluntary contributions made by many
50 individuals on behalf of the JDOM Project and was originally
51 created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52 Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53 on the JDOM Project, please see <http://www.jdom.org/>.
54
55 */
56
57 package org.jdom.output;
58
59 import java.io.*;
60 import java.util.*;
61
62 import javax.xml.transform.Result;
63
64 import org.jdom.*;
65
66 /**
67 * Outputs a JDOM document as a stream of bytes. The outputter can manage many
68 * styles of document formatting, from untouched to pretty printed. The default
69 * is to output the document content exactly as created, but this can be changed
70 * by setting a new Format object. For pretty-print output, use
71 * <code>{@link Format#getPrettyFormat()}</code>. For whitespace-normalized
72 * output, use <code>{@link Format#getCompactFormat()}</code>.
73 * <p>
74 * There are <code>{@link #output output(...)}</code> methods to print any of
75 * the standard JDOM classes, including Document and Element, to either a Writer
76 * or an OutputStream. <b>Warning</b>: When outputting to a Writer, make sure
77 * the writer's encoding matches the encoding setting in the Format object. This
78 * ensures the encoding in which the content is written (controlled by the
79 * Writer configuration) matches the encoding placed in the document's XML
80 * declaration (controlled by the XMLOutputter). Because a Writer cannot be
81 * queried for its encoding, the information must be passed to the Format
82 * manually in its constructor or via the
83 * <code>{@link Format#setEncoding}</code> method. The default encoding is
84 * UTF-8.
85 * <p>
86 * The methods <code>{@link #outputString outputString(...)}</code> are for
87 * convenience only; for top performance you should call one of the <code>{@link
88 * #output output(...)}</code> methods and pass in your own Writer or
89 * OutputStream if possible.
90 * <p>
91 * XML declarations are always printed on their own line followed by a line
92 * seperator (this doesn't change the semantics of the document). To omit
93 * printing of the declaration use
94 * <code>{@link Format#setOmitDeclaration}</code>. To omit printing of the
95 * encoding in the declaration use <code>{@link Format#setOmitEncoding}</code>.
96 * Unfortunatly there is currently no way to know the original encoding of the
97 * document.
98 * <p>
99 * Empty elements are by default printed as &lt;empty/&gt;, but this can be
100 * configured with <code>{@link Format#setExpandEmptyElements}</code> to cause
101 * them to be expanded to &lt;empty&gt;&lt;/empty&gt;.
102 *
103 * @version $Revision: 1.117 $, $Date: 2009/07/23 05:54:23 $
104 * @author Brett McLaughlin
105 * @author Jason Hunter
106 * @author Jason Reid
107 * @author Wolfgang Werner
108 * @author Elliotte Rusty Harold
109 * @author David &amp; Will (from Post Tool Design)
110 * @author Dan Schaffer
111 * @author Alex Chaffee
112 * @author Bradley S. Huffman
113 */
114
115 public class XMLOutputter implements Cloneable {
116
117 private static final String CVS_ID =
118 "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.117 $ $Date: 2009/07/23 05:54:23 $ $Name: jdom_1_1_1 $";
119
120 // For normal output
121 private Format userFormat = Format.getRawFormat();
122
123 // For xml:space="preserve"
124 protected static final Format preserveFormat = Format.getRawFormat();
125
126 // What's currently in use
127 protected Format currentFormat = userFormat;
128
129 /** Whether output escaping is enabled for the being processed
130 * Element - default is <code>true</code> */
131 private boolean escapeOutput = true;
132
133 // * * * * * * * * * * Constructors * * * * * * * * * *
134 // * * * * * * * * * * Constructors * * * * * * * * * *
135
136 /**
137 * This will create an <code>XMLOutputter</code> with the default
138 * {@link Format} matching {@link Format#getRawFormat}.
139 */
140 public XMLOutputter() {
141 }
142
143 /**
144 * This will create an <code>XMLOutputter</code> with the specified
145 * format characteristics. Note the format object is cloned internally
146 * before use.
147 */
148 public XMLOutputter(Format format) {
149 userFormat = (Format) format.clone();
150 currentFormat = userFormat;
151 }
152
153 /**
154 * This will create an <code>XMLOutputter</code> with all the
155 * options as set in the given <code>XMLOutputter</code>. Note
156 * that <code>XMLOutputter two = (XMLOutputter)one.clone();</code>
157 * would work equally well.
158 *
159 * @param that the XMLOutputter to clone
160 */
161 public XMLOutputter(XMLOutputter that) {
162 this.userFormat = (Format) that.userFormat.clone();
163 currentFormat = userFormat;
164 }
165
166 // * * * * * * * * * * Set parameters methods * * * * * * * * * *
167 // * * * * * * * * * * Set parameters methods * * * * * * * * * *
168
169 /**
170 * Sets the new format logic for the outputter. Note the Format
171 * object is cloned internally before use.
172 *
173 * @param newFormat the format to use for output
174 */
175 public void setFormat(Format newFormat) {
176 this.userFormat = (Format) newFormat.clone();
177 this.currentFormat = userFormat;
178 }
179
180 /**
181 * Returns the current format in use by the outputter. Note the
182 * Format object returned is a clone of the one used internally.
183 */
184 public Format getFormat() {
185 return (Format) userFormat.clone();
186 }
187
188 // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
189 // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
190
191 /**
192 * This will print the <code>Document</code> to the given output stream.
193 * The characters are printed using the encoding specified in the
194 * constructor, or a default of UTF-8.
195 *
196 * @param doc <code>Document</code> to format.
197 * @param out <code>OutputStream</code> to use.
198 * @throws IOException - if there's any problem writing.
199 */
200 public void output(Document doc, OutputStream out)
201 throws IOException {
202 Writer writer = makeWriter(out);
203 output(doc, writer); // output() flushes
204 }
205
206 /**
207 * Print out the <code>{@link DocType}</code>.
208 *
209 * @param doctype <code>DocType</code> to output.
210 * @param out <code>OutputStream</code> to use.
211 */
212 public void output(DocType doctype, OutputStream out) throws IOException {
213 Writer writer = makeWriter(out);
214 output(doctype, writer); // output() flushes
215 }
216
217 /**
218 * Print out an <code>{@link Element}</code>, including
219 * its <code>{@link Attribute}</code>s, and all
220 * contained (child) elements, etc.
221 *
222 * @param element <code>Element</code> to output.
223 * @param out <code>Writer</code> to use.
224 */
225 public void output(Element element, OutputStream out) throws IOException {
226 Writer writer = makeWriter(out);
227 output(element, writer); // output() flushes
228 }
229
230 /**
231 * This will handle printing out an <code>{@link
232 * Element}</code>'s content only, not including its tag, and
233 * attributes. This can be useful for printing the content of an
234 * element that contains HTML, like "&lt;description&gt;JDOM is
235 * &lt;b&gt;fun&gt;!&lt;/description&gt;".
236 *
237 * @param element <code>Element</code> to output.
238 * @param out <code>OutputStream</code> to use.
239 */
240 public void outputElementContent(Element element, OutputStream out)
241 throws IOException {
242 Writer writer = makeWriter(out);
243 outputElementContent(element, writer); // output() flushes
244 }
245
246 /**
247 * This will handle printing out a list of nodes.
248 * This can be useful for printing the content of an element that
249 * contains HTML, like "&lt;description&gt;JDOM is
250 * &lt;b&gt;fun&gt;!&lt;/description&gt;".
251 *
252 * @param list <code>List</code> of nodes.
253 * @param out <code>OutputStream</code> to use.
254 */
255 public void output(List list, OutputStream out)
256 throws IOException {
257 Writer writer = makeWriter(out);
258 output(list, writer); // output() flushes
259 }
260
261 /**
262 * Print out a <code>{@link CDATA}</code> node.
263 *
264 * @param cdata <code>CDATA</code> to output.
265 * @param out <code>OutputStream</code> to use.
266 */
267 public void output(CDATA cdata, OutputStream out) throws IOException {
268 Writer writer = makeWriter(out);
269 output(cdata, writer); // output() flushes
270 }
271
272 /**
273 * Print out a <code>{@link Text}</code> node. Perfoms
274 * the necessary entity escaping and whitespace stripping.
275 *
276 * @param text <code>Text</code> to output.
277 * @param out <code>OutputStream</code> to use.
278 */
279 public void output(Text text, OutputStream out) throws IOException {
280 Writer writer = makeWriter(out);
281 output(text, writer); // output() flushes
282 }
283
284 /**
285 * Print out a <code>{@link Comment}</code>.
286 *
287 * @param comment <code>Comment</code> to output.
288 * @param out <code>OutputStream</code> to use.
289 */
290 public void output(Comment comment, OutputStream out) throws IOException {
291 Writer writer = makeWriter(out);
292 output(comment, writer); // output() flushes
293 }
294
295 /**
296 * Print out a <code>{@link ProcessingInstruction}</code>.
297 *
298 * @param pi <code>ProcessingInstruction</code> to output.
299 * @param out <code>OutputStream</code> to use.
300 */
301 public void output(ProcessingInstruction pi, OutputStream out)
302 throws IOException {
303 Writer writer = makeWriter(out);
304 output(pi, writer); // output() flushes
305 }
306
307 /**
308 * Print out a <code>{@link EntityRef}</code>.
309 *
310 * @param entity <code>EntityRef</code> to output.
311 * @param out <code>OutputStream</code> to use.
312 */
313 public void output(EntityRef entity, OutputStream out) throws IOException {
314 Writer writer = makeWriter(out);
315 output(entity, writer); // output() flushes
316 }
317
318 /**
319 * Get an OutputStreamWriter, using prefered encoding
320 * (see {@link Format#setEncoding}).
321 */
322 private Writer makeWriter(OutputStream out)
323 throws java.io.UnsupportedEncodingException {
324 return makeWriter(out, userFormat.encoding);
325 }
326
327 /**
328 * Get an OutputStreamWriter, use specified encoding.
329 */
330 private static Writer makeWriter(OutputStream out, String enc)
331 throws java.io.UnsupportedEncodingException {
332 // "UTF-8" is not recognized before JDK 1.1.6, so we'll translate
333 // into "UTF8" which works with all JDKs.
334 if ("UTF-8".equals(enc)) {
335 enc = "UTF8";
336 }
337
338 Writer writer = new BufferedWriter(
339 (new OutputStreamWriter(
340 new BufferedOutputStream(out), enc)
341 ));
342 return writer;
343 }
344
345 // * * * * * * * * * * Output to a Writer * * * * * * * * * *
346 // * * * * * * * * * * Output to a Writer * * * * * * * * * *
347
348 /**
349 * This will print the <code>Document</code> to the given Writer.
350 *
351 * <p>
352 * Warning: using your own Writer may cause the outputter's
353 * preferred character encoding to be ignored. If you use
354 * encodings other than UTF-8, we recommend using the method that
355 * takes an OutputStream instead.
356 * </p>
357 *
358 * @param doc <code>Document</code> to format.
359 * @param out <code>Writer</code> to use.
360 * @throws IOException - if there's any problem writing.
361 */
362 public void output(Document doc, Writer out) throws IOException {
363
364 printDeclaration(out, doc, userFormat.encoding);
365
366 // Print out root element, as well as any root level
367 // comments and processing instructions,
368 // starting with no indentation
369 List content = doc.getContent();
370 int size = content.size();
371 for (int i = 0; i < size; i++) {
372 Object obj = content.get(i);
373
374 if (obj instanceof Element) {
375 printElement(out, doc.getRootElement(), 0,
376 createNamespaceStack());
377 }
378 else if (obj instanceof Comment) {
379 printComment(out, (Comment) obj);
380 }
381 else if (obj instanceof ProcessingInstruction) {
382 printProcessingInstruction(out, (ProcessingInstruction) obj);
383 }
384 else if (obj instanceof DocType) {
385 printDocType(out, doc.getDocType());
386 // Always print line separator after declaration, helps the
387 // output look better and is semantically inconsequential
388 out.write(currentFormat.lineSeparator);
389 }
390 else {
391 // XXX if we get here then we have a illegal content, for
392 // now we'll just ignore it
393 }
394
395 newline(out);
396 indent(out, 0);
397 }
398
399 // Output final line separator
400 // We output this no matter what the newline flags say
401 out.write(currentFormat.lineSeparator);
402
403 out.flush();
404 }
405
406 /**
407 * Print out the <code>{@link DocType}</code>.
408 *
409 * @param doctype <code>DocType</code> to output.
410 * @param out <code>Writer</code> to use.
411 */
412 public void output(DocType doctype, Writer out) throws IOException {
413 printDocType(out, doctype);
414 out.flush();
415 }
416
417 /**
418 * Print out an <code>{@link Element}</code>, including
419 * its <code>{@link Attribute}</code>s, and all
420 * contained (child) elements, etc.
421 *
422 * @param element <code>Element</code> to output.
423 * @param out <code>Writer</code> to use.
424 */
425 public void output(Element element, Writer out) throws IOException {
426 // If this is the root element we could pre-initialize the
427 // namespace stack with the namespaces
428 printElement(out, element, 0, createNamespaceStack());
429 out.flush();
430 }
431
432 /**
433 * This will handle printing out an <code>{@link
434 * Element}</code>'s content only, not including its tag, and
435 * attributes. This can be useful for printing the content of an
436 * element that contains HTML, like "&lt;description&gt;JDOM is
437 * &lt;b&gt;fun&gt;!&lt;/description&gt;".
438 *
439 * @param element <code>Element</code> to output.
440 * @param out <code>Writer</code> to use.
441 */
442 public void outputElementContent(Element element, Writer out)
443 throws IOException {
444 List content = element.getContent();
445 printContentRange(out, content, 0, content.size(),
446 0, createNamespaceStack());
447 out.flush();
448 }
449
450 /**
451 * This will handle printing out a list of nodes.
452 * This can be useful for printing the content of an element that
453 * contains HTML, like "&lt;description&gt;JDOM is
454 * &lt;b&gt;fun&gt;!&lt;/description&gt;".
455 *
456 * @param list <code>List</code> of nodes.
457 * @param out <code>Writer</code> to use.
458 */
459 public void output(List list, Writer out)
460 throws IOException {
461 printContentRange(out, list, 0, list.size(),
462 0, createNamespaceStack());
463 out.flush();
464 }
465
466 /**
467 * Print out a <code>{@link CDATA}</code> node.
468 *
469 * @param cdata <code>CDATA</code> to output.
470 * @param out <code>Writer</code> to use.
471 */
472 public void output(CDATA cdata, Writer out) throws IOException {
473 printCDATA(out, cdata);
474 out.flush();
475 }
476
477 /**
478 * Print out a <code>{@link Text}</code> node. Perfoms
479 * the necessary entity escaping and whitespace stripping.
480 *
481 * @param text <code>Text</code> to output.
482 * @param out <code>Writer</code> to use.
483 */
484 public void output(Text text, Writer out) throws IOException {
485 printText(out, text);
486 out.flush();
487 }
488
489 /**
490 * Print out a <code>{@link Comment}</code>.
491 *
492 * @param comment <code>Comment</code> to output.
493 * @param out <code>Writer</code> to use.
494 */
495 public void output(Comment comment, Writer out) throws IOException {
496 printComment(out, comment);
497 out.flush();
498 }
499
500 /**
501 * Print out a <code>{@link ProcessingInstruction}</code>.
502 *
503 * @param pi <code>ProcessingInstruction</code> to output.
504 * @param out <code>Writer</code> to use.
505 */
506 public void output(ProcessingInstruction pi, Writer out)
507 throws IOException {
508 boolean currentEscapingPolicy = currentFormat.ignoreTrAXEscapingPIs;
509
510 // Output PI verbatim, disregarding TrAX escaping PIs.
511 currentFormat.setIgnoreTrAXEscapingPIs(true);
512 printProcessingInstruction(out, pi);
513 currentFormat.setIgnoreTrAXEscapingPIs(currentEscapingPolicy);
514
515 out.flush();
516 }
517
518 /**
519 * Print out a <code>{@link EntityRef}</code>.
520 *
521 * @param entity <code>EntityRef</code> to output.
522 * @param out <code>Writer</code> to use.
523 */
524 public void output(EntityRef entity, Writer out) throws IOException {
525 printEntityRef(out, entity);
526 out.flush();
527 }
528
529 // * * * * * * * * * * Output to a String * * * * * * * * * *
530 // * * * * * * * * * * Output to a String * * * * * * * * * *
531
532 /**
533 * Return a string representing a document. Uses an internal
534 * StringWriter. Warning: a String is Unicode, which may not match
535 * the outputter's specified encoding.
536 *
537 * @param doc <code>Document</code> to format.
538 */
539 public String outputString(Document doc) {
540 StringWriter out = new StringWriter();
541 try {
542 output(doc, out); // output() flushes
543 } catch (IOException e) { }
544 return out.toString();
545 }
546
547 /**
548 * Return a string representing a DocType. Warning: a String is
549 * Unicode, which may not match the outputter's specified
550 * encoding.
551 *
552 * @param doctype <code>DocType</code> to format.
553 */
554 public String outputString(DocType doctype) {
555 StringWriter out = new StringWriter();
556 try {
557 output(doctype, out); // output() flushes
558 } catch (IOException e) { }
559 return out.toString();
560 }
561
562 /**
563 * Return a string representing an element. Warning: a String is
564 * Unicode, which may not match the outputter's specified
565 * encoding.
566 *
567 * @param element <code>Element</code> to format.
568 */
569 public String outputString(Element element) {
570 StringWriter out = new StringWriter();
571 try {
572 output(element, out); // output() flushes
573 } catch (IOException e) { }
574 return out.toString();
575 }
576
577 /**
578 * Return a string representing a list of nodes. The list is
579 * assumed to contain legal JDOM nodes.
580 *
581 * @param list <code>List</code> to format.
582 */
583 public String outputString(List list) {
584 StringWriter out = new StringWriter();
585 try {
586 output(list, out); // output() flushes
587 } catch (IOException e) { }
588 return out.toString();
589 }
590
591 /**
592 * Return a string representing a CDATA node. Warning: a String is
593 * Unicode, which may not match the outputter's specified
594 * encoding.
595 *
596 * @param cdata <code>CDATA</code> to format.
597 */
598 public String outputString(CDATA cdata) {
599 StringWriter out = new StringWriter();
600 try {
601 output(cdata, out); // output() flushes
602 } catch (IOException e) { }
603 return out.toString();
604 }
605
606 /**
607 * Return a string representing a Text node. Warning: a String is
608 * Unicode, which may not match the outputter's specified
609 * encoding.
610 *
611 * @param text <code>Text</code> to format.
612 */
613 public String outputString(Text text) {
614 StringWriter out = new StringWriter();
615 try {
616 output(text, out); // output() flushes
617 } catch (IOException e) { }
618 return out.toString();
619 }
620
621
622 /**
623 * Return a string representing a comment. Warning: a String is
624 * Unicode, which may not match the outputter's specified
625 * encoding.
626 *
627 * @param comment <code>Comment</code> to format.
628 */
629 public String outputString(Comment comment) {
630 StringWriter out = new StringWriter();
631 try {
632 output(comment, out); // output() flushes
633 } catch (IOException e) { }
634 return out.toString();
635 }
636
637 /**
638 * Return a string representing a PI. Warning: a String is
639 * Unicode, which may not match the outputter's specified
640 * encoding.
641 *
642 * @param pi <code>ProcessingInstruction</code> to format.
643 */
644 public String outputString(ProcessingInstruction pi) {
645 StringWriter out = new StringWriter();
646 try {
647 output(pi, out); // output() flushes
648 } catch (IOException e) { }
649 return out.toString();
650 }
651
652 /**
653 * Return a string representing an entity. Warning: a String is
654 * Unicode, which may not match the outputter's specified
655 * encoding.
656 *
657 * @param entity <code>EntityRef</code> to format.
658 */
659 public String outputString(EntityRef entity) {
660 StringWriter out = new StringWriter();
661 try {
662 output(entity, out); // output() flushes
663 } catch (IOException e) { }
664 return out.toString();
665 }
666
667 // * * * * * * * * * * Internal printing methods * * * * * * * * * *
668 // * * * * * * * * * * Internal printing methods * * * * * * * * * *
669
670 /**
671 * This will handle printing of the declaration.
672 * Assumes XML version 1.0 since we don't directly know.
673 *
674 * @param doc <code>Document</code> whose declaration to write.
675 * @param out <code>Writer</code> to use.
676 * @param encoding The encoding to add to the declaration
677 */
678 protected void printDeclaration(Writer out, Document doc,
679 String encoding) throws IOException {
680
681 // Only print the declaration if it's not being omitted
682 if (!userFormat.omitDeclaration) {
683 // Assume 1.0 version
684 out.write("<?xml version=\"1.0\"");
685 if (!userFormat.omitEncoding) {
686 out.write(" encoding=\"" + encoding + "\"");
687 }
688 out.write("?>");
689
690 // Print new line after decl always, even if no other new lines
691 // Helps the output look better and is semantically
692 // inconsequential
693 out.write(currentFormat.lineSeparator);
694 }
695 }
696
697 /**
698 * This handle printing the DOCTYPE declaration if one exists.
699 *
700 * @param docType <code>Document</code> whose declaration to write.
701 * @param out <code>Writer</code> to use.
702 */
703 protected void printDocType(Writer out, DocType docType)
704 throws IOException {
705
706 String publicID = docType.getPublicID();
707 String systemID = docType.getSystemID();
708 String internalSubset = docType.getInternalSubset();
709 boolean hasPublic = false;
710
711 out.write("<!DOCTYPE ");
712 out.write(docType.getElementName());
713 if (publicID != null) {
714 out.write(" PUBLIC \"");
715 out.write(publicID);
716 out.write("\"");
717 hasPublic = true;
718 }
719 if (systemID != null) {
720 if (!hasPublic) {
721 out.write(" SYSTEM");
722 }
723 out.write(" \"");
724 out.write(systemID);
725 out.write("\"");
726 }
727 if ((internalSubset != null) && (!internalSubset.equals(""))) {
728 out.write(" [");
729 out.write(currentFormat.lineSeparator);
730 out.write(docType.getInternalSubset());
731 out.write("]");
732 }
733 out.write(">");
734 }
735
736 /**
737 * This will handle printing of comments.
738 *
739 * @param comment <code>Comment</code> to write.
740 * @param out <code>Writer</code> to use.
741 */
742 protected void printComment(Writer out, Comment comment)
743 throws IOException {
744 out.write("<!--");
745 out.write(comment.getText());
746 out.write("-->");
747 }
748
749 /**
750 * This will handle printing of processing instructions.
751 *
752 * @param pi <code>ProcessingInstruction</code> to write.
753 * @param out <code>Writer</code> to use.
754 */
755 protected void printProcessingInstruction(Writer out, ProcessingInstruction pi
756 ) throws IOException {
757 String target = pi.getTarget();
758 boolean piProcessed = false;
759
760 if (currentFormat.ignoreTrAXEscapingPIs == false) {
761 if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING)) {
762 escapeOutput = false;
763 piProcessed = true;
764 }
765 else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING)) {
766 escapeOutput = true;
767 piProcessed = true;
768 }
769 }
770 if (piProcessed == false) {
771 String rawData = pi.getData();
772
773 // Write <?target data?> or if no data then just <?target?>
774 if (!"".equals(rawData)) {
775 out.write("<?");
776 out.write(target);
777 out.write(" ");
778 out.write(rawData);
779 out.write("?>");
780 }
781 else {
782 out.write("<?");
783 out.write(target);
784 out.write("?>");
785 }
786 }
787 }
788
789 /**
790 * This will handle printing a <code>{@link EntityRef}</code>.
791 * Only the entity reference such as <code>&amp;entity;</code>
792 * will be printed. However, subclasses are free to override
793 * this method to print the contents of the entity instead.
794 *
795 * @param entity <code>EntityRef</code> to output.
796 * @param out <code>Writer</code> to use. */
797 protected void printEntityRef(Writer out, EntityRef entity)
798 throws IOException {
799 out.write("&");
800 out.write(entity.getName());
801 out.write(";");
802 }
803
804 /**
805 * This will handle printing of <code>{@link CDATA}</code> text.
806 *
807 * @param cdata <code>CDATA</code> to output.
808 * @param out <code>Writer</code> to use.
809 */
810 protected void printCDATA(Writer out, CDATA cdata) throws IOException {
811 String str = (currentFormat.mode == Format.TextMode.NORMALIZE)
812 ? cdata.getTextNormalize()
813 : ((currentFormat.mode == Format.TextMode.TRIM) ?
814 cdata.getText().trim() : cdata.getText());
815 out.write("<![CDATA[");
816 out.write(str);
817 out.write("]]>");
818 }
819
820 /**
821 * This will handle printing of <code>{@link Text}</code> strings.
822 *
823 * @param text <code>Text</code> to write.
824 * @param out <code>Writer</code> to use.
825 */
826 protected void printText(Writer out, Text text) throws IOException {
827 String str = (currentFormat.mode == Format.TextMode.NORMALIZE)
828 ? text.getTextNormalize()
829 : ((currentFormat.mode == Format.TextMode.TRIM) ?
830 text.getText().trim() : text.getText());
831 out.write(escapeElementEntities(str));
832 }
833
834 /**
835 * This will handle printing a string. Escapes the element entities,
836 * trims interior whitespace, etc. if necessary.
837 */
838 private void printString(Writer out, String str) throws IOException {
839 if (currentFormat.mode == Format.TextMode.NORMALIZE) {
840 str = Text.normalizeString(str);
841 }
842 else if (currentFormat.mode == Format.TextMode.TRIM) {
843 str = str.trim();
844 }
845 out.write(escapeElementEntities(str));
846 }
847
848 /**
849 * This will handle printing of a <code>{@link Element}</code>,
850 * its <code>{@link Attribute}</code>s, and all contained (child)
851 * elements, etc.
852 *
853 * @param element <code>Element</code> to output.
854 * @param out <code>Writer</code> to use.
855 * @param level <code>int</code> level of indention.
856 * @param namespaces <code>List</code> stack of Namespaces in scope.
857 */
858 protected void printElement(Writer out, Element element,
859 int level, NamespaceStack namespaces)
860 throws IOException {
861
862 List attributes = element.getAttributes();
863 List content = element.getContent();
864
865 // Check for xml:space and adjust format settings
866 String space = null;
867 if (attributes != null) {
868 space = element.getAttributeValue("space",
869 Namespace.XML_NAMESPACE);
870 }
871
872 Format previousFormat = currentFormat;
873
874 if ("default".equals(space)) {
875 currentFormat = userFormat;
876 }
877 else if ("preserve".equals(space)) {
878 currentFormat = preserveFormat;
879 }
880
881 // Print the beginning of the tag plus attributes and any
882 // necessary namespace declarations
883 out.write("<");
884 printQualifiedName(out, element);
885
886 // Mark our namespace starting point
887 int previouslyDeclaredNamespaces = namespaces.size();
888
889 // Print the element's namespace, if appropriate
890 printElementNamespace(out, element, namespaces);
891
892 // Print out additional namespace declarations
893 printAdditionalNamespaces(out, element, namespaces);
894
895 // Print out attributes
896 if (attributes != null)
897 printAttributes(out, attributes, element, namespaces);
898
899 // Depending on the settings (newlines, textNormalize, etc), we may
900 // or may not want to print all of the content, so determine the
901 // index of the start of the content we're interested
902 // in based on the current settings.
903
904 int start = skipLeadingWhite(content, 0);
905 int size = content.size();
906 if (start >= size) {
907 // Case content is empty or all insignificant whitespace
908 if (currentFormat.expandEmptyElements) {
909 out.write("></");
910 printQualifiedName(out, element);
911 out.write(">");
912 }
913 else {
914 out.write(" />");
915 }
916 }
917 else {
918 out.write(">");
919
920 // For a special case where the content is only CDATA
921 // or Text we don't want to indent after the start or
922 // before the end tag.
923
924 if (nextNonText(content, start) < size) {
925 // Case Mixed Content - normal indentation
926 newline(out);
927 printContentRange(out, content, start, size,
928 level + 1, namespaces);
929 newline(out);
930 indent(out, level);
931 }
932 else {
933 // Case all CDATA or Text - no indentation
934 printTextRange(out, content, start, size);
935 }
936 out.write("</");
937 printQualifiedName(out, element);
938 out.write(">");
939 }
940
941 // remove declared namespaces from stack
942 while (namespaces.size() > previouslyDeclaredNamespaces) {
943 namespaces.pop();
944 }
945
946 // Restore our format settings
947 currentFormat = previousFormat;
948 }
949
950 /**
951 * This will handle printing of content within a given range.
952 * The range to print is specified in typical Java fashion; the
953 * starting index is inclusive, while the ending index is
954 * exclusive.
955 *
956 * @param content <code>List</code> of content to output
957 * @param start index of first content node (inclusive.
958 * @param end index of last content node (exclusive).
959 * @param out <code>Writer</code> to use.
960 * @param level <code>int</code> level of indentation.
961 * @param namespaces <code>List</code> stack of Namespaces in scope.
962 */
963 private void printContentRange(Writer out, List content,
964 int start, int end, int level,
965 NamespaceStack namespaces)
966 throws IOException {
967 boolean firstNode; // Flag for 1st node in content
968 Object next; // Node we're about to print
969 int first, index; // Indexes into the list of content
970
971 index = start;
972 while (index < end) {
973 firstNode = (index == start) ? true : false;
974 next = content.get(index);
975
976 //
977 // Handle consecutive CDATA, Text, and EntityRef nodes all at once
978 //
979 if ((next instanceof Text) || (next instanceof EntityRef)) {
980 first = skipLeadingWhite(content, index);
981 // Set index to next node for loop
982 index = nextNonText(content, first);
983
984 // If it's not all whitespace - print it!
985 if (first < index) {
986 if (!firstNode)
987 newline(out);
988 indent(out, level);
989 printTextRange(out, content, first, index);
990 }
991 continue;
992 }
993
994 //
995 // Handle other nodes
996 //
997 if (!firstNode) {
998 newline(out);
999 }
1000
1001 indent(out, level);
1002
1003 if (next instanceof Comment) {
1004 printComment(out, (Comment)next);
1005 }
1006 else if (next instanceof Element) {
1007 printElement(out, (Element)next, level, namespaces);
1008 }
1009 else if (next instanceof ProcessingInstruction) {
1010 printProcessingInstruction(out, (ProcessingInstruction)next);
1011 }
1012 else {
1013 // XXX if we get here then we have a illegal content, for
1014 // now we'll just ignore it (probably should throw
1015 // a exception)
1016 }
1017
1018 index++;
1019 } /* while */
1020 }
1021
1022 /**
1023 * This will handle printing of a sequence of <code>{@link CDATA}</code>
1024 * or <code>{@link Text}</code> nodes. It is an error to have any other
1025 * pass this method any other type of node.
1026 *
1027 * @param content <code>List</code> of content to output
1028 * @param start index of first content node (inclusive).
1029 * @param end index of last content node (exclusive).
1030 * @param out <code>Writer</code> to use.
1031 */
1032 private void printTextRange(Writer out, List content, int start, int end
1033 ) throws IOException {
1034 String previous; // Previous text printed
1035 Object node; // Next node to print
1036 String next; // Next text to print
1037
1038 previous = null;
1039
1040 // Remove leading whitespace-only nodes
1041 start = skipLeadingWhite(content, start);
1042
1043 int size = content.size();
1044 if (start < size) {
1045 // And remove trialing whitespace-only nodes
1046 end = skipTrailingWhite(content, end);
1047
1048 for (int i = start; i < end; i++) {
1049 node = content.get(i);
1050
1051 // Get the unmangled version of the text
1052 // we are about to print
1053 if (node instanceof Text) {
1054 next = ((Text) node).getText();
1055 }
1056 else if (node instanceof EntityRef) {
1057 next = "&" + ((EntityRef) node).getValue() + ";";
1058 }
1059 else {
1060 throw new IllegalStateException("Should see only " +
1061 "CDATA, Text, or EntityRef");
1062 }
1063
1064 // This may save a little time
1065 if (next == null || "".equals(next)) {
1066 continue;
1067 }
1068
1069 // Determine if we need to pad the output (padding is
1070 // only need in trim or normalizing mode)
1071 if (previous != null) { // Not 1st node
1072 if (currentFormat.mode == Format.TextMode.NORMALIZE ||
1073 currentFormat.mode == Format.TextMode.TRIM) {
1074 if ((endsWithWhite(previous)) ||
1075 (startsWithWhite(next))) {
1076 out.write(" ");
1077 }
1078 }
1079 }
1080
1081 // Print the node
1082 if (node instanceof CDATA) {
1083 printCDATA(out, (CDATA) node);
1084 }
1085 else if (node instanceof EntityRef) {
1086 printEntityRef(out, (EntityRef) node);
1087 }
1088 else {
1089 printString(out, next);
1090 }
1091
1092 previous = next;
1093 }
1094 }
1095 }
1096
1097 /**
1098 * This will handle printing of any needed <code>{@link Namespace}</code>
1099 * declarations.
1100 *
1101 * @param ns <code>Namespace</code> to print definition of
1102 * @param out <code>Writer</code> to use.
1103 */
1104 private void printNamespace(Writer out, Namespace ns,
1105 NamespaceStack namespaces)
1106 throws IOException {
1107 String prefix = ns.getPrefix();
1108 String uri = ns.getURI();
1109
1110 // Already printed namespace decl?
1111 if (uri.equals(namespaces.getURI(prefix))) {
1112 return;
1113 }
1114
1115 out.write(" xmlns");
1116 if (!prefix.equals("")) {
1117 out.write(":");
1118 out.write(prefix);
1119 }
1120 out.write("=\"");
1121 out.write(escapeAttributeEntities(uri));
1122 out.write("\"");
1123 namespaces.push(ns);
1124 }
1125
1126 /**
1127 * This will handle printing of a <code>{@link Attribute}</code> list.
1128 *
1129 * @param attributes <code>List</code> of Attribute objcts
1130 * @param out <code>Writer</code> to use
1131 */
1132 protected void printAttributes(Writer out, List attributes, Element parent,
1133 NamespaceStack namespaces)
1134 throws IOException {
1135
1136 // I do not yet handle the case where the same prefix maps to
1137 // two different URIs. For attributes on the same element
1138 // this is illegal; but as yet we don't throw an exception
1139 // if someone tries to do this
1140 // Set prefixes = new HashSet();
1141 for (int i = 0; i < attributes.size(); i++) {
1142 Attribute attribute = (Attribute) attributes.get(i);
1143 Namespace ns = attribute.getNamespace();
1144 if ((ns != Namespace.NO_NAMESPACE) &&
1145 (ns != Namespace.XML_NAMESPACE)) {
1146 printNamespace(out, ns, namespaces);
1147 }
1148
1149 out.write(" ");
1150 printQualifiedName(out, attribute);
1151 out.write("=");
1152
1153 out.write("\"");
1154 out.write(escapeAttributeEntities(attribute.getValue()));
1155 out.write("\"");
1156 }
1157 }
1158
1159 private void printElementNamespace(Writer out, Element element,
1160 NamespaceStack namespaces)
1161 throws IOException {
1162 // Add namespace decl only if it's not the XML namespace and it's
1163 // not the NO_NAMESPACE with the prefix "" not yet mapped
1164 // (we do output xmlns="" if the "" prefix was already used and we
1165 // need to reclaim it for the NO_NAMESPACE)
1166 Namespace ns = element.getNamespace();
1167 if (ns == Namespace.XML_NAMESPACE) {
1168 return;
1169 }
1170 if ( !((ns == Namespace.NO_NAMESPACE) &&
1171 (namespaces.getURI("") == null))) {
1172 printNamespace(out, ns, namespaces);
1173 }
1174 }
1175
1176 private void printAdditionalNamespaces(Writer out, Element element,
1177 NamespaceStack namespaces)
1178 throws IOException {
1179 List list = element.getAdditionalNamespaces();
1180 if (list != null) {
1181 for (int i = 0; i < list.size(); i++) {
1182 Namespace additional = (Namespace)list.get(i);
1183 printNamespace(out, additional, namespaces);
1184 }
1185 }
1186 }
1187
1188 // * * * * * * * * * * Support methods * * * * * * * * * *
1189 // * * * * * * * * * * Support methods * * * * * * * * * *
1190
1191 /**
1192 * This will print a newline only if indent is not null.
1193 *
1194 * @param out <code>Writer</code> to use
1195 */
1196 private void newline(Writer out) throws IOException {
1197 if (currentFormat.indent != null) {
1198 out.write(currentFormat.lineSeparator);
1199 }
1200 }
1201
1202 /**
1203 * This will print indents only if indent is not null or the empty string.
1204 *
1205 * @param out <code>Writer</code> to use
1206 * @param level current indent level
1207 */
1208 private void indent(Writer out, int level) throws IOException {
1209 if (currentFormat.indent == null ||
1210 currentFormat.indent.equals("")) {
1211 return;
1212 }
1213
1214 for (int i = 0; i < level; i++) {
1215 out.write(currentFormat.indent);
1216 }
1217 }
1218
1219 // Returns the index of the first non-all-whitespace CDATA or Text,
1220 // index = content.size() is returned if content contains
1221 // all whitespace.
1222 // @param start index to begin search (inclusive)
1223 private int skipLeadingWhite(List content, int start) {
1224 if (start < 0) {
1225 start = 0;
1226 }
1227
1228 int index = start;
1229 int size = content.size();
1230 if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
1231 || currentFormat.mode == Format.TextMode.NORMALIZE
1232 || currentFormat.mode == Format.TextMode.TRIM) {
1233 while (index < size) {
1234 if (!isAllWhitespace(content.get(index))) {
1235 return index;
1236 }
1237 index++;
1238 }
1239 }
1240 return index;
1241 }
1242
1243 // Return the index + 1 of the last non-all-whitespace CDATA or
1244 // Text node, index < 0 is returned
1245 // if content contains all whitespace.
1246 // @param start index to begin search (exclusive)
1247 private int skipTrailingWhite(List content, int start) {
1248 int size = content.size();
1249 if (start > size) {
1250 start = size;
1251 }
1252
1253 int index = start;
1254 if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
1255 || currentFormat.mode == Format.TextMode.NORMALIZE
1256 || currentFormat.mode == Format.TextMode.TRIM) {
1257 while (index >= 0) {
1258 if (!isAllWhitespace(content.get(index - 1)))
1259 break;
1260 --index;
1261 }
1262 }
1263 return index;
1264 }
1265
1266 // Return the next non-CDATA, non-Text, or non-EntityRef node,
1267 // index = content.size() is returned if there is no more non-CDATA,
1268 // non-Text, or non-EntiryRef nodes
1269 // @param start index to begin search (inclusive)
1270 private static int nextNonText(List content, int start) {
1271 if (start < 0) {
1272 start = 0;
1273 }
1274
1275 int index = start;
1276 int size = content.size();
1277 while (index < size) {
1278 Object node = content.get(index);
1279 if (!((node instanceof Text) || (node instanceof EntityRef))) {
1280 return index;
1281 }
1282 index++;
1283 }
1284 return size;
1285 }
1286
1287 // Determine if a Object is all whitespace
1288 private boolean isAllWhitespace(Object obj) {
1289 String str = null;
1290
1291 if (obj instanceof String) {
1292 str = (String) obj;
1293 }
1294 else if (obj instanceof Text) {
1295 str = ((Text) obj).getText();
1296 }
1297 else if (obj instanceof EntityRef) {
1298 return false;
1299 }
1300 else {
1301 return false;
1302 }
1303
1304 for (int i = 0; i < str.length(); i++) {
1305 if (!Verifier.isXMLWhitespace(str.charAt(i)))
1306 return false;
1307 }
1308 return true;
1309 }
1310
1311 // Determine if a string starts with a XML whitespace.
1312 private boolean startsWithWhite(String str) {
1313 if ((str != null) &&
1314 (str.length() > 0) &&
1315 Verifier.isXMLWhitespace(str.charAt(0))) {
1316 return true;
1317 }
1318 return false;
1319 }
1320
1321 // Determine if a string ends with a XML whitespace.
1322 private boolean endsWithWhite(String str) {
1323 if ((str != null) &&
1324 (str.length() > 0) &&
1325 Verifier.isXMLWhitespace(str.charAt(str.length() - 1))) {
1326 return true;
1327 }
1328 return false;
1329 }
1330
1331 /**
1332 * This will take the pre-defined entities in XML 1.0 and
1333 * convert their character representation to the appropriate
1334 * entity reference, suitable for XML attributes. It does not convert
1335 * the single quote (') because it's not necessary as the outputter
1336 * writes attributes surrounded by double-quotes.
1337 *
1338 * @param str <code>String</code> input to escape.
1339 * @return <code>String</code> with escaped content.
1340 * @throws IllegalArgumentException if an entity can not be escaped
1341 */
1342 public String escapeAttributeEntities(String str) {
1343 StringBuffer buffer;
1344 int ch, pos;
1345 String entity;
1346 EscapeStrategy strategy = currentFormat.escapeStrategy;
1347
1348 buffer = null;
1349 for (int i = 0; i < str.length(); i++) {
1350 ch = str.charAt(i);
1351 pos = i;
1352 switch(ch) {
1353 case '<' :
1354 entity = "&lt;";
1355 break;
1356 case '>' :
1357 entity = "&gt;";
1358 break;
1359 /*
1360 case '\'' :
1361 entity = "&apos;";
1362 break;
1363 */
1364 case '\"' :
1365 entity = "&quot;";
1366 break;
1367 case '&' :
1368 entity = "&amp;";
1369 break;
1370 case '\r' :
1371 entity = "&#xD;";
1372 break;
1373 case '\t' :
1374 entity = "&#x9;";
1375 break;
1376 case '\n' :
1377 entity = "&#xA;";
1378 break;
1379 default :
1380
1381 if (strategy.shouldEscape((char) ch)) {
1382 // Make sure what we are escaping is not the
1383 // Beginning of a multi-byte character.
1384 if (Verifier.isHighSurrogate((char) ch)) {
1385 // This is a the high of a surrogate pair
1386 i++;
1387 if (i < str.length()) {
1388 char low = str.charAt(i);
1389 if(!Verifier.isLowSurrogate(low)) {
1390 throw new IllegalDataException("Could not decode surrogate pair 0x" +
1391 Integer.toHexString(ch) + " / 0x" + Integer.toHexString(low));
1392 }
1393 ch = Verifier.decodeSurrogatePair((char) ch, low);
1394 } else {
1395 throw new IllegalDataException("Surrogate pair 0x" +
1396 Integer.toHexString(ch) + " truncated");
1397 }
1398 }
1399 entity = "&#x" + Integer.toHexString(ch) + ";";
1400 }
1401 else {
1402 entity = null;
1403 }
1404 break;
1405 }
1406 if (buffer == null) {
1407 if (entity != null) {
1408 // An entity occurred, so we'll have to use StringBuffer
1409 // (allocate room for it plus a few more entities).
1410 buffer = new StringBuffer(str.length() + 20);
1411 // Copy previous skipped characters and fall through
1412 // to pickup current character
1413 buffer.append(str.substring(0, pos));
1414 buffer.append(entity);
1415 }
1416 }
1417 else {
1418 if (entity == null) {
1419 buffer.append((char) ch);
1420 }
1421 else {
1422 buffer.append(entity);
1423 }
1424 }
1425 }
1426
1427 // If there were any entities, return the escaped characters
1428 // that we put in the StringBuffer. Otherwise, just return
1429 // the unmodified input string.
1430 return (buffer == null) ? str : buffer.toString();
1431 }
1432
1433
1434 /**
1435 * This will take the three pre-defined entities in XML 1.0
1436 * (used specifically in XML elements) and convert their character
1437 * representation to the appropriate entity reference, suitable for
1438 * XML element content.
1439 *
1440 * @param str <code>String</code> input to escape.
1441 * @return <code>String</code> with escaped content.
1442 * @throws IllegalArgumentException if an entity can not be escaped
1443 */
1444 public String escapeElementEntities(String str) {
1445 if (escapeOutput == false) return str;
1446
1447 StringBuffer buffer;
1448 int ch, pos;
1449 String entity;
1450 EscapeStrategy strategy = currentFormat.escapeStrategy;
1451
1452 buffer = null;
1453 for (int i = 0; i < str.length(); i++) {
1454 ch = str.charAt(i);
1455 pos = i;
1456 switch(ch) {
1457 case '<' :
1458 entity = "&lt;";
1459 break;
1460 case '>' :
1461 entity = "&gt;";
1462 break;
1463 case '&' :
1464 entity = "&amp;";
1465 break;
1466 case '\r' :
1467 entity = "&#xD;";
1468 break;
1469 case '\n' :
1470 entity = currentFormat.lineSeparator;
1471 break;
1472 default :
1473
1474 if (strategy.shouldEscape((char) ch)) {
1475
1476 //make sure what we are escaping is not the
1477 //beginning of a multi-byte character.
1478 if(Verifier.isHighSurrogate((char) ch)) {
1479 //this is a the high of a surrogate pair
1480 i++;
1481 if (i < str.length()) {
1482 char low = str.charAt(i);
1483 if(!Verifier.isLowSurrogate(low)) {
1484 throw new IllegalDataException("Could not decode surrogate pair 0x" +
1485 Integer.toHexString(ch) + " / 0x" + Integer.toHexString(low));
1486 }
1487 ch = Verifier.decodeSurrogatePair((char) ch, low);
1488 } else {
1489 throw new IllegalDataException("Surrogate pair 0x" +
1490 Integer.toHexString(ch) + " truncated");
1491 }
1492 }
1493 entity = "&#x" + Integer.toHexString(ch) + ";";
1494 }
1495 else {
1496 entity = null;
1497 }
1498 break;
1499 }
1500 if (buffer == null) {
1501 if (entity != null) {
1502 // An entity occurred, so we'll have to use StringBuffer
1503 // (allocate room for it plus a few more entities).
1504 buffer = new StringBuffer(str.length() + 20);
1505 // Copy previous skipped characters and fall through
1506 // to pickup current character
1507 buffer.append(str.substring(0, pos));
1508 buffer.append(entity);
1509 }
1510 }
1511 else {
1512 if (entity == null) {
1513 buffer.append((char) ch);
1514 }
1515 else {
1516 buffer.append(entity);
1517 }
1518 }
1519 }
1520
1521 // If there were any entities, return the escaped characters
1522 // that we put in the StringBuffer. Otherwise, just return
1523 // the unmodified input string.
1524 return (buffer == null) ? str : buffer.toString();
1525 }
1526
1527 /**
1528 * Returns a copy of this XMLOutputter.
1529 */
1530 public Object clone() {
1531 // Implementation notes: Since all state of an XMLOutputter is
1532 // embodied in simple private instance variables, Object.clone
1533 // can be used. Note that since Object.clone is totally
1534 // broken, we must catch an exception that will never be
1535 // thrown.
1536 try {
1537 return super.clone();
1538 }
1539 catch (java.lang.CloneNotSupportedException e) {
1540 // even though this should never ever happen, it's still
1541 // possible to fool Java into throwing a
1542 // CloneNotSupportedException. If that happens, we
1543 // shouldn't swallow it.
1544 throw new RuntimeException(e.toString());
1545 }
1546 }
1547
1548 /**
1549 * Return a string listing of the settings for this
1550 * XMLOutputter instance.
1551 *
1552 * @return a string listing the settings for this XMLOutputter instance
1553 */
1554 public String toString() {
1555 StringBuffer buffer = new StringBuffer();
1556 for (int i = 0; i < userFormat.lineSeparator.length(); i++) {
1557 char ch = userFormat.lineSeparator.charAt(i);
1558 switch (ch) {
1559 case '\r': buffer.append("\\r");
1560 break;
1561 case '\n': buffer.append("\\n");
1562 break;
1563 case '\t': buffer.append("\\t");
1564 break;
1565 default: buffer.append("[" + ((int)ch) + "]");
1566 break;
1567 }
1568 }
1569
1570 return (
1571 "XMLOutputter[omitDeclaration = " + userFormat.omitDeclaration + ", " +
1572 "encoding = " + userFormat.encoding + ", " +
1573 "omitEncoding = " + userFormat.omitEncoding + ", " +
1574 "indent = '" + userFormat.indent + "'" + ", " +
1575 "expandEmptyElements = " + userFormat.expandEmptyElements + ", " +
1576 "lineSeparator = '" + buffer.toString() + "', " +
1577 "textMode = " + userFormat.mode + "]"
1578 );
1579 }
1580
1581 /**
1582 * Factory for making new NamespaceStack objects. The NamespaceStack
1583 * created is actually an inner class extending the package protected
1584 * NamespaceStack, as a way to make NamespaceStack "friendly" toward
1585 * subclassers.
1586 */
1587 private NamespaceStack createNamespaceStack() {
1588 // actually returns a XMLOutputter.NamespaceStack (see below)
1589 return new NamespaceStack();
1590 }
1591
1592 /**
1593 * Our own null subclass of NamespaceStack. This plays a little
1594 * trick with Java access protection. We want subclasses of
1595 * XMLOutputter to be able to override protected methods that
1596 * declare a NamespaceStack parameter, but we don't want to
1597 * declare the parent NamespaceStack class as public.
1598 */
1599 protected class NamespaceStack
1600 extends org.jdom.output.NamespaceStack
1601 {
1602 }
1603
1604 // Support method to print a name without using elt.getQualifiedName()
1605 // and thus avoiding a StringBuffer creation and memory churn
1606 private void printQualifiedName(Writer out, Element e) throws IOException {
1607 if (e.getNamespace().getPrefix().length() == 0) {
1608 out.write(e.getName());
1609 }
1610 else {
1611 out.write(e.getNamespace().getPrefix());
1612 out.write(':');
1613 out.write(e.getName());
1614 }
1615 }
1616
1617 // Support method to print a name without using att.getQualifiedName()
1618 // and thus avoiding a StringBuffer creation and memory churn
1619 private void printQualifiedName(Writer out, Attribute a) throws IOException {
1620 String prefix = a.getNamespace().getPrefix();
1621 if ((prefix != null) && (!prefix.equals(""))) {
1622 out.write(prefix);
1623 out.write(':');
1624 out.write(a.getName());
1625 }
1626 else {
1627 out.write(a.getName());
1628 }
1629 }
1630
1631 // * * * * * * * * * * Deprecated methods * * * * * * * * * *
1632
1633 /* The methods below here are deprecations of protected methods. We
1634 * don't usually deprecate protected methods, so they're commented out.
1635 * They're left here in case this mass deprecation causes people trouble.
1636 * Since we're getting close to 1.0 it's actually better for people to
1637 * raise issues early though.
1638 */
1639
1640 }