1 // XMLWriter.java - serialize an XML document.
2 // Written by David Megginson, david@megginson.com
3 // NO WARRANTY! This class is in the public domain.
4
5 // $Id:XMLWriter.java 378 2007-01-24 01:18:35Z mrohrmoser $
6
7 package com.megginson.sax;
8
9 import java.io.IOException;
10 import java.io.OutputStreamWriter;
11 import java.io.Writer;
12 import java.util.Enumeration;
13 import java.util.Hashtable;
14
15 import org.xml.sax.Attributes;
16 import org.xml.sax.SAXException;
17 import org.xml.sax.XMLReader;
18 import org.xml.sax.helpers.AttributesImpl;
19 import org.xml.sax.helpers.NamespaceSupport;
20 import org.xml.sax.helpers.XMLFilterImpl;
21
22 /**
23 * Filter to write an XML document from a SAX event stream.
24 *
25 * <p>
26 * This class can be used by itself or as part of a SAX event stream: it takes
27 * as input a series of SAX2 ContentHandler events and uses the information in
28 * those events to write an XML document. Since this class is a filter, it can
29 * also pass the events on down a filter chain for further processing (you can
30 * use the XMLWriter to take a snapshot of the current state at any point in a
31 * filter chain), and it can be used directly as a ContentHandler for a SAX2
32 * XMLReader.
33 * </p>
34 *
35 * <p>
36 * The client creates a document by invoking the methods for standard SAX2
37 * events, always beginning with the {@link #startDocument startDocument} method
38 * and ending with the {@link #endDocument endDocument} method. There are
39 * convenience methods provided so that clients to not have to create empty
40 * attribute lists or provide empty strings as parameters; for example, the
41 * method invocation
42 * </p>
43 *
44 * <pre>
45 * w.startElement("foo");
46 * </pre>
47 *
48 * <p>
49 * is equivalent to the regular SAX2 ContentHandler method
50 * </p>
51 *
52 * <pre>
53 * w.startElement("", "foo", "", new AttributesImpl());
54 * </pre>
55 *
56 * <p>
57 * Except that it is more efficient because it does not allocate a new empty
58 * attribute list each time. The following code will send a simple XML document
59 * to standard output:
60 * </p>
61 *
62 * <pre>
63 * XMLWriter w = new XMLWriter();
64 *
65 * w.startDocument();
66 * w.startElement("greeting");
67 * w.characters("Hello, world!");
68 * w.endElement("greeting");
69 * w.endDocument();
70 * </pre>
71 *
72 * <p>
73 * The resulting document will look like this:
74 * </p>
75 *
76 * <pre>
77 * <?xml version="1.0" standalone="yes"?>
78 *
79 * <greeting>Hello, world!</greeting>
80 * </pre>
81 *
82 * <p>
83 * In fact, there is an even simpler convenience method, <var>dataElement</var>,
84 * designed for writing elements that contain only character data, so the code
85 * to generate the document could be shortened to
86 * </p>
87 *
88 * <pre>
89 * XMLWriter w = new XMLWriter();
90 *
91 * w.startDocument();
92 * w.dataElement("greeting", "Hello, world!");
93 * w.endDocument();
94 * </pre>
95 *
96 * <h2>Whitespace</h2>
97 *
98 * <p>
99 * According to the XML Recommendation, <em>all</em> whitespace in an XML
100 * document is potentially significant to an application, so this class never
101 * adds newlines or indentation. If you insert three elements in a row, as in
102 * </p>
103 *
104 * <pre>
105 * w.dataElement("item", "1");
106 * w.dataElement("item", "2");
107 * w.dataElement("item", "3");
108 * </pre>
109 *
110 * <p>
111 * you will end up with
112 * </p>
113 *
114 * <pre>
115 * <item>1</item><item>3</item><item>3</item>
116 * </pre>
117 *
118 * <p>
119 * You need to invoke one of the <var>characters</var> methods explicitly to
120 * add newlines or indentation. Alternatively, you can use
121 * {@link com.megginson.sax.DataWriter DataWriter}, which is derived from this
122 * class -- it is optimized for writing purely data-oriented (or field-oriented)
123 * XML, and does automatic linebreaks and indentation (but does not support
124 * mixed content properly).
125 * </p>
126 *
127 *
128 * <h2>Namespace Support</h2>
129 *
130 * <p>
131 * The writer contains extensive support for XML Namespaces, so that a client
132 * application does not have to keep track of prefixes and supply <var>xmlns</var>
133 * attributes. By default, the XML writer will generate Namespace declarations
134 * in the form _NS1, _NS2, etc., wherever they are needed, as in the following
135 * example:
136 * </p>
137 *
138 * <pre>
139 * w.startDocument();
140 * w.emptyElement("http://www.foo.com/ns/", "foo");
141 * w.endDocument();
142 * </pre>
143 *
144 * <p>
145 * The resulting document will look like this:
146 * </p>
147 *
148 * <pre>
149 * <?xml version="1.0" standalone="yes"?>
150 *
151 * <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
152 * </pre>
153 *
154 * <p>
155 * In many cases, document authors will prefer to choose their own prefixes
156 * rather than using the (ugly) default names. The XML writer allows two methods
157 * for selecting prefixes:
158 * </p>
159 *
160 * <ol>
161 * <li>the qualified name</li>
162 * <li>the {@link #setPrefix setPrefix} method.</li>
163 * </ol>
164 *
165 * <p>
166 * Whenever the XML writer finds a new Namespace URI, it checks to see if a
167 * qualified (prefixed) name is also available; if so it attempts to use the
168 * name's prefix (as long as the prefix is not already in use for another
169 * Namespace URI).
170 * </p>
171 *
172 * <p>
173 * Before writing a document, the client can also pre-map a prefix to a
174 * Namespace URI with the setPrefix method:
175 * </p>
176 *
177 * <pre>
178 * w.setPrefix("http://www.foo.com/ns/", "foo");
179 * w.startDocument();
180 * w.emptyElement("http://www.foo.com/ns/", "foo");
181 * w.endDocument();
182 * </pre>
183 *
184 * <p>
185 * The resulting document will look like this:
186 * </p>
187 *
188 * <pre>
189 * <?xml version="1.0" standalone="yes"?>
190 *
191 * <foo:foo xmlns:foo="http://www.foo.com/ns/"/>
192 * </pre>
193 *
194 * <p>
195 * The default Namespace simply uses an empty string as the prefix:
196 * </p>
197 *
198 * <pre>
199 * w.setPrefix("http://www.foo.com/ns/", "");
200 * w.startDocument();
201 * w.emptyElement("http://www.foo.com/ns/", "foo");
202 * w.endDocument();
203 * </pre>
204 *
205 * <p>
206 * The resulting document will look like this:
207 * </p>
208 *
209 * <pre>
210 * <?xml version="1.0" standalone="yes"?>
211 *
212 * <foo xmlns="http://www.foo.com/ns/"/>
213 * </pre>
214 *
215 * <p>
216 * By default, the XML writer will not declare a Namespace until it is actually
217 * used. Sometimes, this approach will create a large number of Namespace
218 * declarations, as in the following example:
219 * </p>
220 *
221 * <pre>
222 * <xml version="1.0" standalone="yes"?>
223 *
224 * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
225 * <rdf:Description about="http://www.foo.com/ids/books/12345">
226 * <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title>
227 * <dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith</dc:title>
228 * <dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09</dc:title>
229 * </rdf:Description>
230 * </rdf:RDF>
231 * </pre>
232 *
233 * <p>
234 * The "rdf" prefix is declared only once, because the RDF Namespace is used by
235 * the root element and can be inherited by all of its descendants; the "dc"
236 * prefix, on the other hand, is declared three times, because no higher element
237 * uses the Namespace. To solve this problem, you can instruct the XML writer to
238 * predeclare Namespaces on the root element even if they are not used there:
239 * </p>
240 *
241 * <pre>
242 * w.forceNSDecl("http://www.purl.org/dc/");
243 * </pre>
244 *
245 * <p>
246 * Now, the "dc" prefix will be declared on the root element even though it's
247 * not needed there, and can be inherited by its descendants:
248 * </p>
249 *
250 * <pre>
251 * <xml version="1.0" standalone="yes"?>
252 *
253 * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
254 * xmlns:dc="http://www.purl.org/dc/">
255 * <rdf:Description about="http://www.foo.com/ids/books/12345">
256 * <dc:title>A Dark Night</dc:title>
257 * <dc:creator>Jane Smith</dc:title>
258 * <dc:date>2000-09-09</dc:title>
259 * </rdf:Description>
260 * </rdf:RDF>
261 * </pre>
262 *
263 * <p>
264 * This approach is also useful for declaring Namespace prefixes that be used by
265 * qualified names appearing in attribute values or character data.
266 * </p>
267 *
268 * @author David Megginson, david@megginson.com
269 * @version 0.2
270 * @see org.xml.sax.XMLFilter
271 * @see org.xml.sax.ContentHandler
272 */
273 class XMLWriter extends XMLFilterImpl {
274
275 // //////////////////////////////////////////////////////////////////
276 // Constructors.
277 // //////////////////////////////////////////////////////////////////
278
279 private Hashtable doneDeclTable;
280
281 private int elementLevel = 0;
282
283 private final Attributes EMPTY_ATTS = new AttributesImpl();
284
285 private Hashtable forcedDeclTable;
286
287 private NamespaceSupport nsSupport;
288
289 // //////////////////////////////////////////////////////////////////
290 // Public methods.
291 // //////////////////////////////////////////////////////////////////
292
293 private Writer output;
294
295 private int prefixCounter = 0;
296
297 private Hashtable prefixTable;
298
299 /**
300 * Create a new XML writer.
301 *
302 * <p>
303 * Write to standard output.
304 * </p>
305 */
306 public XMLWriter() {
307 init(null);
308 }
309
310 /**
311 * Create a new XML writer.
312 *
313 * <p>
314 * Write to the writer provided.
315 * </p>
316 *
317 * @param writer
318 * The output destination, or null to use standard output.
319 */
320 public XMLWriter(final Writer writer) {
321 init(writer);
322 }
323
324 /**
325 * Create a new XML writer.
326 *
327 * <p>
328 * Use the specified XML reader as the parent.
329 * </p>
330 *
331 * @param xmlreader
332 * The parent in the filter chain, or null for no parent.
333 */
334 public XMLWriter(final XMLReader xmlreader) {
335 super(xmlreader);
336 init(null);
337 }
338
339 /**
340 * Create a new XML writer.
341 *
342 * <p>
343 * Use the specified XML reader as the parent, and write to the specified
344 * writer.
345 * </p>
346 *
347 * @param xmlreader
348 * The parent in the filter chain, or null for no parent.
349 * @param writer
350 * The output destination, or null to use standard output.
351 */
352 public XMLWriter(final XMLReader xmlreader, final Writer writer) {
353 super(xmlreader);
354 init(writer);
355 }
356
357 // //////////////////////////////////////////////////////////////////
358 // Methods from org.xml.sax.ContentHandler.
359 // //////////////////////////////////////////////////////////////////
360
361 /**
362 * Write character data.
363 *
364 * Pass the event on down the filter chain for further processing.
365 *
366 * @param ch
367 * The array of characters to write.
368 * @param start
369 * The starting position in the array.
370 * @param len
371 * The number of characters to write.
372 * @exception org.xml.sax.SAXException
373 * If there is an error writing the characters, or if a
374 * handler further down the filter chain raises an exception.
375 * @see org.xml.sax.ContentHandler#characters
376 */
377 @Override
378 public void characters(final char ch[], final int start, final int len)
379 throws SAXException {
380 writeEsc(ch, start, len, false);
381 super.characters(ch, start, len);
382 }
383
384 /**
385 * Write a string of character data, with XML escaping.
386 *
387 * <p>
388 * This is a convenience method that takes an XML String, converts it to a
389 * character array, then invokes {@link #characters(char[], int, int)}.
390 * </p>
391 *
392 * @param data
393 * The character data.
394 * @exception org.xml.sax.SAXException
395 * If there is an error writing the string, or if a handler
396 * further down the filter chain raises an exception.
397 * @see #characters(char[], int, int)
398 */
399 public void characters(final String data) throws SAXException {
400 final char ch[] = data.toCharArray();
401 this.characters(ch, 0, ch.length);
402 }
403
404 /**
405 * Write an element with character data content but no attributes or
406 * Namespace URI.
407 *
408 * <p>
409 * This is a convenience method to write a complete element with character
410 * data content, including the start tag and end tag. The method provides an
411 * empty string for the Namespace URI, and empty string for the qualified
412 * name, and an empty attribute list.
413 * </p>
414 *
415 * <p>
416 * This method invokes
417 * {@link #startElement(String, String, String, Attributes)}, followed by
418 * {@link #characters(String)}, followed by
419 * {@link #endElement(String, String, String)}.
420 * </p>
421 *
422 * @param localName
423 * The element's local name.
424 * @param content
425 * The character data content.
426 * @exception org.xml.sax.SAXException
427 * If there is an error writing the empty tag, or if a
428 * handler further down the filter chain raises an exception.
429 * @see #startElement(String, String, String, Attributes)
430 * @see #characters(String)
431 * @see #endElement(String, String, String)
432 */
433 public void dataElement(final String localName, final String content)
434 throws SAXException {
435 this.dataElement("", localName, "", EMPTY_ATTS, content);
436 }
437
438 /**
439 * Write an element with character data content but no attributes.
440 *
441 * <p>
442 * This is a convenience method to write a complete element with character
443 * data content, including the start tag and end tag. This method provides
444 * an empty string for the qname and an empty attribute list.
445 * </p>
446 *
447 * <p>
448 * This method invokes
449 * {@link #startElement(String, String, String, Attributes)}, followed by
450 * {@link #characters(String)}, followed by
451 * {@link #endElement(String, String, String)}.
452 * </p>
453 *
454 * @param uri
455 * The element's Namespace URI.
456 * @param localName
457 * The element's local name.
458 * @param content
459 * The character data content.
460 * @exception org.xml.sax.SAXException
461 * If there is an error writing the empty tag, or if a
462 * handler further down the filter chain raises an exception.
463 * @see #startElement(String, String, String, Attributes)
464 * @see #characters(String)
465 * @see #endElement(String, String, String)
466 */
467 public void dataElement(final String uri, final String localName,
468 final String content) throws SAXException {
469 this.dataElement(uri, localName, "", EMPTY_ATTS, content);
470 }
471
472 /**
473 * Write an element with character data content.
474 *
475 * <p>
476 * This is a convenience method to write a complete element with character
477 * data content, including the start tag and end tag.
478 * </p>
479 *
480 * <p>
481 * This method invokes
482 * {@link #startElement(String, String, String, Attributes)}, followed by
483 * {@link #characters(String)}, followed by
484 * {@link #endElement(String, String, String)}.
485 * </p>
486 *
487 * @param uri
488 * The element's Namespace URI.
489 * @param localName
490 * The element's local name.
491 * @param qName
492 * The element's default qualified name.
493 * @param atts
494 * The element's attributes.
495 * @param content
496 * The character data content.
497 * @exception org.xml.sax.SAXException
498 * If there is an error writing the empty tag, or if a
499 * handler further down the filter chain raises an exception.
500 * @see #startElement(String, String, String, Attributes)
501 * @see #characters(String)
502 * @see #endElement(String, String, String)
503 */
504 public void dataElement(final String uri, final String localName,
505 final String qName, final Attributes atts, final String content)
506 throws SAXException {
507 this.startElement(uri, localName, qName, atts);
508 this.characters(content);
509 this.endElement(uri, localName, qName);
510 }
511
512 /**
513 * Determine the prefix for an element or attribute name.
514 *
515 * TODO: this method probably needs some cleanup.
516 *
517 * @param uri
518 * The Namespace URI.
519 * @param qName
520 * The qualified name (optional); this will be used to indicate
521 * the preferred prefix if none is currently bound.
522 * @param isElement
523 * true if this is an element name, false if it is an attribute
524 * name (which cannot use the default Namespace).
525 * @return ???
526 */
527 private String doPrefix(final String uri, final String qName,
528 boolean isElement) {
529 final String defaultNS = nsSupport.getURI("");
530 if ("".equals(uri)) {
531 if (isElement && defaultNS != null)
532 nsSupport.declarePrefix("", "");
533 return null;
534 }
535 String prefix;
536 if (isElement && defaultNS != null && uri.equals(defaultNS))
537 prefix = "";
538 else
539 prefix = nsSupport.getPrefix(uri);
540 if (prefix != null)
541 return prefix;
542 prefix = (String) doneDeclTable.get(uri);
543 if (prefix != null
544 && ((!isElement || defaultNS != null) && "".equals(prefix) || nsSupport
545 .getURI(prefix) != null))
546 prefix = null;
547 if (prefix == null) {
548 prefix = (String) prefixTable.get(uri);
549 if (prefix != null
550 && ((!isElement || defaultNS != null) && "".equals(prefix) || nsSupport
551 .getURI(prefix) != null))
552 prefix = null;
553 }
554 if (prefix == null && qName != null && !"".equals(qName)) {
555 final int i = qName.indexOf(':');
556 if (i == -1) {
557 if (isElement && defaultNS == null)
558 prefix = "";
559 } else
560 prefix = qName.substring(0, i);
561 }
562 for (; prefix == null || nsSupport.getURI(prefix) != null; prefix = "__NS"
563 + ++prefixCounter)
564 ;
565 nsSupport.declarePrefix(prefix, uri);
566 doneDeclTable.put(uri, prefix);
567 return prefix;
568 }
569
570 /**
571 * Add an empty element without a Namespace URI, qname or attributes.
572 *
573 * <p>
574 * This method will supply an empty string for the qname, and empty string
575 * for the Namespace URI, and an empty attribute list. It invokes
576 * {@link #emptyElement(String, String, String, Attributes)} directly.
577 * </p>
578 *
579 * @param localName
580 * The element's local name.
581 * @exception org.xml.sax.SAXException
582 * If there is an error writing the empty tag, or if a
583 * handler further down the filter chain raises an exception.
584 * @see #emptyElement(String, String, String, Attributes)
585 */
586 public void emptyElement(final String localName) throws SAXException {
587 this.emptyElement("", localName, "", EMPTY_ATTS);
588 }
589
590 // //////////////////////////////////////////////////////////////////
591 // Additional markup.
592 // //////////////////////////////////////////////////////////////////
593
594 /**
595 * Add an empty element without a qname or attributes.
596 *
597 * <p>
598 * This method will supply an empty string for the qname and an empty
599 * attribute list. It invokes
600 * {@link #emptyElement(String, String, String, Attributes)} directly.
601 * </p>
602 *
603 * @param uri
604 * The element's Namespace URI.
605 * @param localName
606 * The element's local name.
607 * @exception org.xml.sax.SAXException
608 * If there is an error writing the empty tag, or if a
609 * handler further down the filter chain raises an exception.
610 * @see #emptyElement(String, String, String, Attributes)
611 */
612 public void emptyElement(final String uri, final String localName)
613 throws SAXException {
614 this.emptyElement(uri, localName, "", EMPTY_ATTS);
615 }
616
617 // //////////////////////////////////////////////////////////////////
618 // Convenience methods.
619 // //////////////////////////////////////////////////////////////////
620
621 /**
622 * Write an empty element.
623 *
624 * This method writes an empty element tag rather than a start tag followed
625 * by an end tag. Both a {@link #startElement(String)} and an
626 * {@link #endElement(String) } event will be passed on down the filter
627 * chain.
628 *
629 * @param uri
630 * The element's Namespace URI, or the empty string if the
631 * element has no Namespace or if Namespace processing is not
632 * being performed.
633 * @param localName
634 * The element's local name (without prefix). This parameter must
635 * be provided.
636 * @param qName
637 * The element's qualified name (with prefix), or the empty
638 * string if none is available. This parameter is strictly
639 * advisory: the writer may or may not use the prefix attached.
640 * @param atts
641 * The element's attribute list.
642 * @exception org.xml.sax.SAXException
643 * If there is an error writing the empty tag, or if a
644 * handler further down the filter chain raises an exception.
645 * @see #startElement(String, String, String, Attributes)
646 * @see #endElement(String, String, String)
647 */
648 public void emptyElement(final String uri, final String localName,
649 final String qName, final Attributes atts) throws SAXException {
650 nsSupport.pushContext();
651 this.write('<');
652 writeName(uri, localName, qName, true);
653 writeAttributes(atts);
654 if (elementLevel == 1)
655 forceNSDecls();
656 writeNSDecls();
657 this.write("/>");
658 super.startElement(uri, localName, qName, atts);
659 super.endElement(uri, localName, qName);
660 }
661
662 /**
663 * Write a newline at the end of the document.
664 *
665 * Pass the event on down the filter chain for further processing.
666 *
667 * @exception org.xml.sax.SAXException
668 * If there is an error writing the newline, or if a handler
669 * further down the filter chain raises an exception.
670 * @see org.xml.sax.ContentHandler#endDocument
671 */
672 @Override
673 public void endDocument() throws SAXException {
674 this.write('\n');
675 super.endDocument();
676 try {
677 flush();
678 } catch (final IOException e) {
679 throw new SAXException(e);
680 }
681 }
682
683 /**
684 * End an element without a Namespace URI or qname.
685 *
686 * <p>
687 * This method will supply an empty string for the qName and an empty string
688 * for the Namespace URI. It invokes
689 * {@link #endElement(String, String, String)} directly.
690 * </p>
691 *
692 * @param localName
693 * The element's local name.
694 * @exception org.xml.sax.SAXException
695 * If there is an error writing the end tag, or if a handler
696 * further down the filter chain raises an exception.
697 * @see #endElement(String, String, String)
698 */
699 public void endElement(final String localName) throws SAXException {
700 this.endElement("", localName, "");
701 }
702
703 /**
704 * End an element without a qname.
705 *
706 * <p>
707 * This method will supply an empty string for the qName. It invokes
708 * {@link #endElement(String, String, String)} directly.
709 * </p>
710 *
711 * @param uri
712 * The element's Namespace URI.
713 * @param localName
714 * The element's local name.
715 * @exception org.xml.sax.SAXException
716 * If there is an error writing the end tag, or if a handler
717 * further down the filter chain raises an exception.
718 * @see #endElement(String, String, String)
719 */
720 public void endElement(final String uri, final String localName)
721 throws SAXException {
722 this.endElement(uri, localName, "");
723 }
724
725 /**
726 * Write an end tag.
727 *
728 * Pass the event on down the filter chain for further processing.
729 *
730 * @param uri
731 * The Namespace URI, or the empty string if none is available.
732 * @param localName
733 * The element's local (unprefixed) name (required).
734 * @param qName
735 * The element's qualified (prefixed) name, or the empty string
736 * is none is available. This method will use the qName as a
737 * template for generating a prefix if necessary, but it is not
738 * guaranteed to use the same qName.
739 * @exception org.xml.sax.SAXException
740 * If there is an error writing the end tag, or if a handler
741 * further down the filter chain raises an exception.
742 * @see org.xml.sax.ContentHandler#endElement
743 */
744 @Override
745 public void endElement(final String uri, final String localName,
746 final String qName) throws SAXException {
747 this.write("</");
748 writeName(uri, localName, qName, true);
749 this.write('>');
750 if (elementLevel == 1)
751 this.write('\n');
752 super.endElement(uri, localName, qName);
753 nsSupport.popContext();
754 elementLevel--;
755 }
756
757 /**
758 * Flush the output.
759 *
760 * <p>
761 * This method flushes the output stream. It is especially useful when you
762 * need to make certain that the entire document has been written to output
763 * but do not want to close the output stream.
764 * </p>
765 *
766 * <p>
767 * This method is invoked automatically by the
768 * {@link #endDocument endDocument} method after writing a document.
769 * </p>
770 *
771 * @throws IOException
772 * @see #reset
773 */
774 public void flush() throws IOException {
775 output.flush();
776 }
777
778 /**
779 * Force a Namespace to be declared on the root element.
780 *
781 * <p>
782 * By default, the XMLWriter will declare only the Namespaces needed for an
783 * element; as a result, a Namespace may be declared many places in a
784 * document if it is not used on the root element.
785 * </p>
786 *
787 * <p>
788 * This method forces a Namespace to be declared on the root element even if
789 * it is not used there, and reduces the number of xmlns attributes in the
790 * document.
791 * </p>
792 *
793 * @param uri
794 * The Namespace URI to declare.
795 * @see #forceNSDecl(java.lang.String,java.lang.String)
796 * @see #setPrefix
797 */
798 public void forceNSDecl(final String uri) {
799 forcedDeclTable.put(uri, Boolean.TRUE);
800 }
801
802 /**
803 * Force a Namespace declaration with a preferred prefix.
804 *
805 * <p>
806 * This is a convenience method that invokes {@link #setPrefix setPrefix}
807 * then {@link #forceNSDecl(java.lang.String) forceNSDecl}.
808 * </p>
809 *
810 * @param uri
811 * The Namespace URI to declare on the root element.
812 * @param prefix
813 * The preferred prefix for the Namespace, or "" for the default
814 * Namespace.
815 * @see #setPrefix
816 * @see #forceNSDecl(java.lang.String)
817 */
818 public void forceNSDecl(final String uri, final String prefix) {
819 setPrefix(uri, prefix);
820 this.forceNSDecl(uri);
821 }
822
823 /**
824 * Force all Namespaces to be declared.
825 *
826 * This method is used on the root element to ensure that the predeclared
827 * Namespaces all appear.
828 */
829 private void forceNSDecls() {
830 final Enumeration prefixes = forcedDeclTable.keys();
831 while (prefixes.hasMoreElements()) {
832 final String prefix = (String) prefixes.nextElement();
833 doPrefix(prefix, null, true);
834 }
835 }
836
837 /**
838 * Get the current or preferred prefix for a Namespace URI.
839 *
840 * @param uri
841 * The Namespace URI.
842 * @return The preferred prefix, or "" for the default Namespace.
843 * @see #setPrefix
844 */
845 public String getPrefix(final String uri) {
846 return (String) prefixTable.get(uri);
847 }
848
849 // //////////////////////////////////////////////////////////////////
850 // Internal methods.
851 // //////////////////////////////////////////////////////////////////
852
853 /**
854 * Write ignorable whitespace.
855 *
856 * Pass the event on down the filter chain for further processing.
857 *
858 * @param ch
859 * The array of characters to write.
860 * @param start
861 * The starting position in the array.
862 * @param length
863 * The number of characters to write.
864 * @exception org.xml.sax.SAXException
865 * If there is an error writing the whitespace, or if a
866 * handler further down the filter chain raises an exception.
867 * @see org.xml.sax.ContentHandler#ignorableWhitespace
868 */
869 @Override
870 public void ignorableWhitespace(final char ch[], final int start,
871 final int length) throws SAXException {
872 writeEsc(ch, start, length, false);
873 super.ignorableWhitespace(ch, start, length);
874 }
875
876 /**
877 * Internal initialization method.
878 *
879 * <p>
880 * All of the public constructors invoke this method.
881 *
882 * @param writer
883 * The output destination, or null to use standard output.
884 */
885 private void init(final Writer writer) {
886 setOutput(writer);
887 nsSupport = new NamespaceSupport();
888 prefixTable = new Hashtable();
889 forcedDeclTable = new Hashtable();
890 doneDeclTable = new Hashtable();
891 }
892
893 /**
894 * Write a processing instruction.
895 *
896 * Pass the event on down the filter chain for further processing.
897 *
898 * @param target
899 * The PI target.
900 * @param data
901 * The PI data.
902 * @exception org.xml.sax.SAXException
903 * If there is an error writing the PI, or if a handler
904 * further down the filter chain raises an exception.
905 * @see org.xml.sax.ContentHandler#processingInstruction
906 */
907 @Override
908 public void processingInstruction(final String target, final String data)
909 throws SAXException {
910 this.write("<?");
911 this.write(target);
912 this.write(' ');
913 this.write(data);
914 this.write("?>");
915 if (elementLevel < 1)
916 this.write('\n');
917 super.processingInstruction(target, data);
918 }
919
920 /**
921 * Reset the writer.
922 *
923 * <p>
924 * This method is especially useful if the writer throws an exception before
925 * it is finished, and you want to reuse the writer for a new document. It
926 * is usually a good idea to invoke {@link #flush flush} before resetting
927 * the writer, to make sure that no output is lost.
928 * </p>
929 *
930 * <p>
931 * This method is invoked automatically by the
932 * {@link #startDocument startDocument} method before writing a new
933 * document.
934 * </p>
935 *
936 * <p>
937 * <strong>Note:</strong> this method will <em>not</em> clear the prefix
938 * or URI information in the writer or the selected output writer.
939 * </p>
940 *
941 * @see #flush
942 */
943 public void reset() {
944 elementLevel = 0;
945 prefixCounter = 0;
946 nsSupport.reset();
947 }
948
949 /**
950 * Set a new output destination for the document.
951 *
952 * @param writer
953 * The output destination, or null to use standard output.
954 * @see #flush
955 */
956 public void setOutput(final Writer writer) {
957 if (writer == null)
958 output = new OutputStreamWriter(System.out);
959 else
960 output = writer;
961 }
962
963 /**
964 * Specify a preferred prefix for a Namespace URI.
965 *
966 * <p>
967 * Note that this method does not actually force the Namespace to be
968 * declared; to do that, use the {@link #forceNSDecl(java.lang.String)
969 * forceNSDecl} method as well.
970 * </p>
971 *
972 * @param uri
973 * The Namespace URI.
974 * @param prefix
975 * The preferred prefix, or "" to select the default Namespace.
976 * @see #getPrefix
977 * @see #forceNSDecl(java.lang.String)
978 * @see #forceNSDecl(java.lang.String,java.lang.String)
979 */
980 public void setPrefix(final String uri, final String prefix) {
981 prefixTable.put(uri, prefix);
982 }
983
984 /**
985 * Write the XML declaration at the beginning of the document.
986 *
987 * Pass the event on down the filter chain for further processing.
988 *
989 * @exception org.xml.sax.SAXException
990 * If there is an error writing the XML declaration, or if a
991 * handler further down the filter chain raises an exception.
992 * @see org.xml.sax.ContentHandler#startDocument
993 */
994 @Override
995 public void startDocument() throws SAXException {
996 reset();
997 this.write("<?xml version=\"1.0\" standalone=\"yes\"?>\n\n");
998 super.startDocument();
999 }
1000
1001 /**
1002 * Start a new element without a qname, attributes or a Namespace URI.
1003 *
1004 * <p>
1005 * This method will provide an empty string for the Namespace URI, and empty
1006 * string for the qualified name, and a default empty attribute list. It
1007 * invokes #startElement(String, String, String, Attributes)} directly.
1008 * </p>
1009 *
1010 * @param localName
1011 * The element's local name.
1012 * @exception org.xml.sax.SAXException
1013 * If there is an error writing the start tag, or if a
1014 * handler further down the filter chain raises an exception.
1015 * @see #startElement(String, String, String, Attributes)
1016 */
1017 public void startElement(final String localName) throws SAXException {
1018 this.startElement("", localName, "", EMPTY_ATTS);
1019 }
1020
1021 // //////////////////////////////////////////////////////////////////
1022 // Constants.
1023 // //////////////////////////////////////////////////////////////////
1024
1025 /**
1026 * Start a new element without a qname or attributes.
1027 *
1028 * <p>
1029 * This method will provide a default empty attribute list and an empty
1030 * string for the qualified name. It invokes {@link #startElement(String,
1031 * String, String, Attributes)} directly.
1032 * </p>
1033 *
1034 * @param uri
1035 * The element's Namespace URI.
1036 * @param localName
1037 * The element's local name.
1038 * @exception org.xml.sax.SAXException
1039 * If there is an error writing the start tag, or if a
1040 * handler further down the filter chain raises an exception.
1041 * @see #startElement(String, String, String, Attributes)
1042 */
1043 public void startElement(final String uri, final String localName)
1044 throws SAXException {
1045 this.startElement(uri, localName, "", EMPTY_ATTS);
1046 }
1047
1048 // //////////////////////////////////////////////////////////////////
1049 // Internal state.
1050 // //////////////////////////////////////////////////////////////////
1051
1052 /**
1053 * Write a start tag.
1054 *
1055 * Pass the event on down the filter chain for further processing.
1056 *
1057 * @param uri
1058 * The Namespace URI, or the empty string if none is available.
1059 * @param localName
1060 * The element's local (unprefixed) name (required).
1061 * @param qName
1062 * The element's qualified (prefixed) name, or the empty string
1063 * is none is available. This method will use the qName as a
1064 * template for generating a prefix if necessary, but it is not
1065 * guaranteed to use the same qName.
1066 * @param atts
1067 * The element's attribute list (must not be null).
1068 * @exception org.xml.sax.SAXException
1069 * If there is an error writing the start tag, or if a
1070 * handler further down the filter chain raises an exception.
1071 * @see org.xml.sax.ContentHandler#startElement
1072 */
1073 @Override
1074 public void startElement(final String uri, final String localName,
1075 final String qName, final Attributes atts) throws SAXException {
1076 elementLevel++;
1077 nsSupport.pushContext();
1078 this.write('<');
1079 writeName(uri, localName, qName, true);
1080 writeAttributes(atts);
1081 if (elementLevel == 1)
1082 forceNSDecls();
1083 writeNSDecls();
1084 this.write('>');
1085 super.startElement(uri, localName, qName, atts);
1086 }
1087
1088 /**
1089 * Write a raw character.
1090 *
1091 * @param c
1092 * The character to write.
1093 * @exception org.xml.sax.SAXException
1094 * If there is an error writing the character, this method
1095 * will throw an IOException wrapped in a SAXException.
1096 */
1097 private void write(final char c) throws SAXException {
1098 try {
1099 output.write(c);
1100 } catch (final IOException e) {
1101 throw new SAXException(e);
1102 }
1103 }
1104
1105 /**
1106 * Write a raw string.
1107 *
1108 * @param s
1109 * @exception org.xml.sax.SAXException
1110 * If there is an error writing the string, this method will
1111 * throw an IOException wrapped in a SAXException
1112 */
1113 private void write(final String s) throws SAXException {
1114 try {
1115 output.write(s);
1116 } catch (final IOException e) {
1117 throw new SAXException(e);
1118 }
1119 }
1120
1121 /**
1122 * Write out an attribute list, escaping values.
1123 *
1124 * The names will have prefixes added to them.
1125 *
1126 * @param atts
1127 * The attribute list to write.
1128 * @exception SAXException
1129 * If there is an error writing the attribute list, this
1130 * method will throw an IOException wrapped in a
1131 * SAXException.
1132 */
1133 private void writeAttributes(final Attributes atts) throws SAXException {
1134 final int len = atts.getLength();
1135 for (int i = 0; i < len; i++) {
1136 final char ch[] = atts.getValue(i).toCharArray();
1137 this.write(' ');
1138 writeName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i),
1139 false);
1140 this.write("=\"");
1141 writeEsc(ch, 0, ch.length, true);
1142 this.write('"');
1143 }
1144 }
1145
1146 /**
1147 * Write an array of data characters with escaping.
1148 *
1149 * @param ch
1150 * The array of characters.
1151 * @param start
1152 * The starting position.
1153 * @param length
1154 * The number of characters to use.
1155 * @param isAttVal
1156 * true if this is an attribute value literal.
1157 * @exception SAXException
1158 * If there is an error writing the characters, this method
1159 * will throw an IOException wrapped in a SAXException.
1160 */
1161 private void writeEsc(final char ch[], final int start, final int length,
1162 final boolean isAttVal) throws SAXException {
1163 for (int i = start; i < start + length; i++)
1164 switch (ch[i]) {
1165 case '&':
1166 this.write("&");
1167 break;
1168 case '<':
1169 this.write("<");
1170 break;
1171 case '>':
1172 this.write(">");
1173 break;
1174 case '\"':
1175 if (isAttVal)
1176 this.write(""");
1177 else
1178 this.write('\"');
1179 break;
1180 default:
1181 if (ch[i] > '\u007f') {
1182 this.write("&#");
1183 this.write(Integer.toString(ch[i]));
1184 this.write(';');
1185 } else
1186 this.write(ch[i]);
1187 }
1188 }
1189
1190 /**
1191 * Write an element or attribute name.
1192 *
1193 * @param uri
1194 * The Namespace URI.
1195 * @param localName
1196 * The local name.
1197 * @param qName
1198 * The prefixed name, if available, or the empty string.
1199 * @param isElement
1200 * true if this is an element name, false if it is an attribute
1201 * name.
1202 * @exception org.xml.sax.SAXException
1203 * This method will throw an IOException wrapped in a
1204 * SAXException if there is an error writing the name.
1205 */
1206 private void writeName(final String uri, final String localName,
1207 final String qName, final boolean isElement) throws SAXException {
1208 final String prefix = doPrefix(uri, qName, isElement);
1209 if (prefix != null && !"".equals(prefix)) {
1210 this.write(prefix);
1211 this.write(':');
1212 }
1213 this.write(localName);
1214 }
1215
1216 /**
1217 * Write out the list of Namespace declarations.
1218 *
1219 * @exception org.xml.sax.SAXException
1220 * This method will throw an IOException wrapped in a
1221 * SAXException if there is an error writing the Namespace
1222 * declarations.
1223 */
1224 private void writeNSDecls() throws SAXException {
1225 final Enumeration prefixes = nsSupport.getDeclaredPrefixes();
1226 while (prefixes.hasMoreElements()) {
1227 final String prefix = (String) prefixes.nextElement();
1228 String uri = nsSupport.getURI(prefix);
1229 if (uri == null)
1230 uri = "";
1231 final char ch[] = uri.toCharArray();
1232 this.write(' ');
1233 if ("".equals(prefix))
1234 this.write("xmlns=\"");
1235 else {
1236 this.write("xmlns:");
1237 this.write(prefix);
1238 this.write("=\"");
1239 }
1240 writeEsc(ch, 0, ch.length, true);
1241 this.write('\"');
1242 }
1243 }
1244
1245 }
1246
1247 // end of XMLWriter.java