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