KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > output > XMLOutputter


1 /*--
2
3  $Id: XMLOutputter.java,v 1.113 2004/12/11 01:31:50 jhunter Exp $
4
5  Copyright (C) 2000-2004 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 JavaDoc;
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.113 $, $Date: 2004/12/11 01:31:50 $
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 JavaDoc {
116
117     private static final String JavaDoc CVS_ID =
118       "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.113 $ $Date: 2004/12/11 01:31:50 $ $Name: $";
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 JavaDoc {
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 JavaDoc enc)
331                          throws java.io.UnsupportedEncodingException JavaDoc {
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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc publicID = docType.getPublicID();
707         String JavaDoc systemID = docType.getSystemID();
708         String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc previous; // Previous text printed
1035
Object JavaDoc node; // Next node to print
1036
String JavaDoc 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 JavaDoc("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 JavaDoc prefix = ns.getPrefix();
1108        String JavaDoc 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(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 new line only if the newlines flag was set to
1193     * true.
1194     *
1195     * @param out <code>Writer</code> to use
1196     */

1197    private void newline(Writer out) throws IOException {
1198        if (currentFormat.indent != null) {
1199            out.write(currentFormat.lineSeparator);
1200        }
1201    }
1202
1203    /**
1204     * This will print indents (only if the newlines flag was
1205     * set to <code>true</code>, and indent is non-null).
1206     *
1207     * @param out <code>Writer</code> to use
1208     * @param level current indent level (number of tabs)
1209     */

1210    private void indent(Writer out, int level) throws IOException {
1211        if (currentFormat.indent == null ||
1212            currentFormat.indent.equals("")) {
1213            return;
1214        }
1215
1216        for (int i = 0; i < level; i++) {
1217            out.write(currentFormat.indent);
1218        }
1219    }
1220
1221    // Returns the index of the first non-all-whitespace CDATA or Text,
1222
// index = content.size() is returned if content contains
1223
// all whitespace.
1224
// @param start index to begin search (inclusive)
1225
private int skipLeadingWhite(List content, int start) {
1226        if (start < 0) {
1227            start = 0;
1228        }
1229
1230        int index = start;
1231        int size = content.size();
1232        if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
1233                || currentFormat.mode == Format.TextMode.NORMALIZE
1234                || currentFormat.mode == Format.TextMode.TRIM) {
1235            while (index < size) {
1236                if (!isAllWhitespace(content.get(index))) {
1237                    return index;
1238                }
1239                index++;
1240            }
1241        }
1242        return index;
1243    }
1244
1245    // Return the index + 1 of the last non-all-whitespace CDATA or
1246
// Text node, index < 0 is returned
1247
// if content contains all whitespace.
1248
// @param start index to begin search (exclusive)
1249
private int skipTrailingWhite(List content, int start) {
1250        int size = content.size();
1251        if (start > size) {
1252            start = size;
1253        }
1254
1255        int index = start;
1256        if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
1257                || currentFormat.mode == Format.TextMode.NORMALIZE
1258                || currentFormat.mode == Format.TextMode.TRIM) {
1259            while (index >= 0) {
1260                if (!isAllWhitespace(content.get(index - 1)))
1261                    break;
1262                --index;
1263            }
1264        }
1265        return index;
1266    }
1267
1268    // Return the next non-CDATA, non-Text, or non-EntityRef node,
1269
// index = content.size() is returned if there is no more non-CDATA,
1270
// non-Text, or non-EntiryRef nodes
1271
// @param start index to begin search (inclusive)
1272
private static int nextNonText(List content, int start) {
1273        if (start < 0) {
1274            start = 0;
1275        }
1276
1277        int index = start;
1278        int size = content.size();
1279        while (index < size) {
1280            Object JavaDoc node = content.get(index);
1281            if (!((node instanceof Text) || (node instanceof EntityRef))) {
1282                return index;
1283            }
1284            index++;
1285        }
1286        return size;
1287    }
1288
1289    // Determine if a Object is all whitespace
1290
private boolean isAllWhitespace(Object JavaDoc obj) {
1291        String JavaDoc str = null;
1292
1293        if (obj instanceof String JavaDoc) {
1294            str = (String JavaDoc) obj;
1295        }
1296        else if (obj instanceof Text) {
1297            str = ((Text) obj).getText();
1298        }
1299        else if (obj instanceof EntityRef) {
1300            return false;
1301        }
1302        else {
1303            return false;
1304        }
1305
1306        for (int i = 0; i < str.length(); i++) {
1307            if (!Verifier.isXMLWhitespace(str.charAt(i)))
1308                return false;
1309        }
1310        return true;
1311    }
1312
1313    // Determine if a string starts with a XML whitespace.
1314
private boolean startsWithWhite(String JavaDoc str) {
1315        if ((str != null) &&
1316            (str.length() > 0) &&
1317            Verifier.isXMLWhitespace(str.charAt(0))) {
1318           return true;
1319        }
1320        return false;
1321    }
1322
1323    // Determine if a string ends with a XML whitespace.
1324
private boolean endsWithWhite(String JavaDoc str) {
1325        if ((str != null) &&
1326            (str.length() > 0) &&
1327            Verifier.isXMLWhitespace(str.charAt(str.length() - 1))) {
1328           return true;
1329        }
1330        return false;
1331    }
1332
1333    /**
1334     * This will take the pre-defined entities in XML 1.0 and
1335     * convert their character representation to the appropriate
1336     * entity reference, suitable for XML attributes. It does not convert
1337     * the single quote (') because it's not necessary as the outputter
1338     * writes attributes surrounded by double-quotes.
1339     *
1340     * @param str <code>String</code> input to escape.
1341     * @return <code>String</code> with escaped content.
1342     */

1343    public String JavaDoc escapeAttributeEntities(String JavaDoc str) {
1344        StringBuffer JavaDoc buffer;
1345        char ch;
1346        String JavaDoc entity;
1347        EscapeStrategy strategy = currentFormat.escapeStrategy;
1348
1349        buffer = null;
1350        for (int i = 0; i < str.length(); i++) {
1351            ch = str.charAt(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                    if (strategy.shouldEscape(ch)) {
1381                        entity = "&#x" + Integer.toHexString(ch) + ";";
1382                    }
1383                    else {
1384                        entity = null;
1385                    }
1386                    break;
1387            }
1388            if (buffer == null) {
1389                if (entity != null) {
1390                    // An entity occurred, so we'll have to use StringBuffer
1391
// (allocate room for it plus a few more entities).
1392
buffer = new StringBuffer JavaDoc(str.length() + 20);
1393                    // Copy previous skipped characters and fall through
1394
// to pickup current character
1395
buffer.append(str.substring(0, i));
1396                    buffer.append(entity);
1397                }
1398            }
1399            else {
1400                if (entity == null) {
1401                    buffer.append(ch);
1402                }
1403                else {
1404                    buffer.append(entity);
1405                }
1406            }
1407        }
1408
1409        // If there were any entities, return the escaped characters
1410
// that we put in the StringBuffer. Otherwise, just return
1411
// the unmodified input string.
1412
return (buffer == null) ? str : buffer.toString();
1413    }
1414
1415
1416    /**
1417     * This will take the three pre-defined entities in XML 1.0
1418     * (used specifically in XML elements) and convert their character
1419     * representation to the appropriate entity reference, suitable for
1420     * XML element content.
1421     *
1422     * @param str <code>String</code> input to escape.
1423     * @return <code>String</code> with escaped content.
1424     */

1425    public String JavaDoc escapeElementEntities(String JavaDoc str) {
1426        if (escapeOutput == false) return str;
1427
1428        StringBuffer JavaDoc buffer;
1429        char ch;
1430        String JavaDoc entity;
1431        EscapeStrategy strategy = currentFormat.escapeStrategy;
1432
1433        buffer = null;
1434        for (int i = 0; i < str.length(); i++) {
1435            ch = str.charAt(i);
1436            switch(ch) {
1437                case '<' :
1438                    entity = "&lt;";
1439                    break;
1440                case '>' :
1441                    entity = "&gt;";
1442                    break;
1443                case '&' :
1444                    entity = "&amp;";
1445                    break;
1446                case '\r' :
1447                    entity = "&#xD;";
1448                    break;
1449                case '\n' :
1450                    entity = currentFormat.lineSeparator;
1451                    break;
1452                default :
1453                    if (strategy.shouldEscape(ch)) {
1454                        entity = "&#x" + Integer.toHexString(ch) + ";";
1455                    }
1456                    else {
1457                        entity = null;
1458                    }
1459                    break;
1460            }
1461            if (buffer == null) {
1462                if (entity != null) {
1463                    // An entity occurred, so we'll have to use StringBuffer
1464
// (allocate room for it plus a few more entities).
1465
buffer = new StringBuffer JavaDoc(str.length() + 20);
1466                    // Copy previous skipped characters and fall through
1467
// to pickup current character
1468
buffer.append(str.substring(0, i));
1469                    buffer.append(entity);
1470                }
1471            }
1472            else {
1473                if (entity == null) {
1474                    buffer.append(ch);
1475                }
1476                else {
1477                    buffer.append(entity);
1478                }
1479            }
1480        }
1481
1482        // If there were any entities, return the escaped characters
1483
// that we put in the StringBuffer. Otherwise, just return
1484
// the unmodified input string.
1485
return (buffer == null) ? str : buffer.toString();
1486    }
1487
1488    /**
1489     * Returns a copy of this XMLOutputter.
1490     */

1491    public Object JavaDoc clone() {
1492        // Implementation notes: Since all state of an XMLOutputter is
1493
// embodied in simple private instance variables, Object.clone
1494
// can be used. Note that since Object.clone is totally
1495
// broken, we must catch an exception that will never be
1496
// thrown.
1497
try {
1498            return super.clone();
1499        }
1500        catch (java.lang.CloneNotSupportedException JavaDoc e) {
1501            // even though this should never ever happen, it's still
1502
// possible to fool Java into throwing a
1503
// CloneNotSupportedException. If that happens, we
1504
// shouldn't swallow it.
1505
throw new RuntimeException JavaDoc(e.toString());
1506        }
1507    }
1508
1509    /**
1510     * Return a string listing of the settings for this
1511     * XMLOutputter instance.
1512     *
1513     * @return a string listing the settings for this XMLOutputter instance
1514     */

1515    public String JavaDoc toString() {
1516        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1517        for (int i = 0; i < userFormat.lineSeparator.length(); i++) {
1518            char ch = userFormat.lineSeparator.charAt(i);
1519            switch (ch) {
1520            case '\r': buffer.append("\\r");
1521                       break;
1522            case '\n': buffer.append("\\n");
1523                       break;
1524            case '\t': buffer.append("\\t");
1525                       break;
1526            default: buffer.append("[" + ((int)ch) + "]");
1527                       break;
1528            }
1529        }
1530
1531        return (
1532            "XMLOutputter[omitDeclaration = " + userFormat.omitDeclaration + ", " +
1533            "encoding = " + userFormat.encoding + ", " +
1534            "omitEncoding = " + userFormat.omitEncoding + ", " +
1535            "indent = '" + userFormat.indent + "'" + ", " +
1536            "expandEmptyElements = " + userFormat.expandEmptyElements + ", " +
1537            "lineSeparator = '" + buffer.toString() + "', " +
1538            "textMode = " + userFormat.mode + "]"
1539        );
1540    }
1541
1542    /**
1543     * Factory for making new NamespaceStack objects. The NamespaceStack
1544     * created is actually an inner class extending the package protected
1545     * NamespaceStack, as a way to make NamespaceStack "friendly" toward
1546     * subclassers.
1547     */

1548    private NamespaceStack createNamespaceStack() {
1549       // actually returns a XMLOutputter.NamespaceStack (see below)
1550
return new NamespaceStack();
1551    }
1552
1553    /**
1554     * Our own null subclass of NamespaceStack. This plays a little
1555     * trick with Java access protection. We want subclasses of
1556     * XMLOutputter to be able to override protected methods that
1557     * declare a NamespaceStack parameter, but we don't want to
1558     * declare the parent NamespaceStack class as public.
1559     */

1560    protected class NamespaceStack
1561        extends org.jdom.output.NamespaceStack
1562    {
1563    }
1564
1565    // Support method to print a name without using elt.getQualifiedName()
1566
// and thus avoiding a StringBuffer creation and memory churn
1567
private void printQualifiedName(Writer out, Element e) throws IOException {
1568        if (e.getNamespace().getPrefix().length() == 0) {
1569            out.write(e.getName());
1570        }
1571        else {
1572            out.write(e.getNamespace().getPrefix());
1573            out.write(':');
1574            out.write(e.getName());
1575        }
1576    }
1577
1578    // Support method to print a name without using att.getQualifiedName()
1579
// and thus avoiding a StringBuffer creation and memory churn
1580
private void printQualifiedName(Writer out, Attribute a) throws IOException {
1581        String JavaDoc prefix = a.getNamespace().getPrefix();
1582        if ((prefix != null) && (!prefix.equals(""))) {
1583            out.write(prefix);
1584            out.write(':');
1585            out.write(a.getName());
1586        }
1587        else {
1588            out.write(a.getName());
1589        }
1590    }
1591
1592    // * * * * * * * * * * Deprecated methods * * * * * * * * * *
1593

1594    /* The methods below here are deprecations of protected methods. We
1595     * don't usually deprecate protected methods, so they're commented out.
1596     * They're left here in case this mass deprecation causes people trouble.
1597     * Since we're getting close to 1.0 it's actually better for people to
1598     * raise issues early though.
1599     */

1600
1601}
1602
Popular Tags