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