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 <empty/>, but this can be
134 * configured with <code>{@link Format#setExpandEmptyElements}</code> to cause
135 * them to be expanded to <empty></empty>.</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 & 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 "<description>JDOM is
263 * <b>fun>!</description>".
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 "<description>JDOM is
277 * <b>fun>!</description>".
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 "<description>JDOM is
437 * <b>fun>!</description>".
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 "<description>JDOM is
451 * <b>fun>!</description>".
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>&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 = "<";
1309 break;
1310 case '>':
1311 entity = ">";
1312 break;
1313 /*
1314 case '\'' :
1315 entity = "'";
1316 break;
1317 */
1318 case '\"':
1319 entity = """;
1320 break;
1321 case '&':
1322 entity = "&";
1323 break;
1324 case '\r':
1325 entity = "
";
1326 break;
1327 case '\t':
1328 entity = "	";
1329 break;
1330 case '\n':
1331 entity = "
";
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 = "<";
1391 break;
1392 case '>':
1393 entity = ">";
1394 break;
1395 case '&':
1396 entity = "&";
1397 break;
1398 case '\r':
1399 entity = "
";
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 }