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