KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jahia > services > importexport > XMLWriter


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 10155 2005-08-11 16:16:05Z tdraier $
6

7 package org.jahia.services.importexport;
8
9 import java.io.IOException JavaDoc;
10 import java.io.OutputStreamWriter JavaDoc;
11 import java.io.Writer JavaDoc;
12 import java.util.Enumeration JavaDoc;
13 import java.util.Hashtable JavaDoc;
14
15 import org.xml.sax.Attributes JavaDoc;
16 import org.xml.sax.SAXException JavaDoc;
17 import org.xml.sax.XMLReader JavaDoc;
18 import org.xml.sax.helpers.AttributesImpl JavaDoc;
19 import org.xml.sax.helpers.NamespaceSupport JavaDoc;
20 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
21
22
23 /**
24  * Filter to write an XML document from a SAX event stream.
25  *
26  * <p>This class can be used by itself or as part of a SAX event
27  * stream: it takes as input a series of SAX2 ContentHandler
28  * events and uses the information in those events to write
29  * an XML document. Since this class is a filter, it can also
30  * pass the events on down a filter chain for further processing
31  * (you can use the XMLWriter to take a snapshot of the current
32  * state at any point in a filter chain), and it can be
33  * used directly as a ContentHandler for a SAX2 XMLReader.</p>
34  *
35  * <p>The client creates a document by invoking the methods for
36  * standard SAX2 events, always beginning with the
37  * {@link #startDocument startDocument} method and ending with
38  * the {@link #endDocument endDocument} method. There are convenience
39  * methods provided so that clients to not have to create empty
40  * attribute lists or provide empty strings as parameters; for
41  * example, the method invocation</p>
42  *
43  * <pre>
44  * w.startElement("foo");
45  * </pre>
46  *
47  * <p>is equivalent to the regular SAX2 ContentHandler method</p>
48  *
49  * <pre>
50  * w.startElement("", "foo", "", new AttributesImpl());
51  * </pre>
52  *
53  * <p>Except that it is more efficient because it does not allocate
54  * a new empty attribute list each time. The following code will send
55  * a simple XML document to standard output:</p>
56  *
57  * <pre>
58  * XMLWriter w = new XMLWriter();
59  *
60  * w.startDocument();
61  * w.startElement("greeting");
62  * w.characters("Hello, world!");
63  * w.endElement("greeting");
64  * w.endDocument();
65  * </pre>
66  *
67  * <p>The resulting document will look like this:</p>
68  *
69  * <pre>
70  * &lt;?xml version="1.0" standalone="yes"?>
71  *
72  * &lt;greeting>Hello, world!&lt;/greeting>
73  * </pre>
74  *
75  * <p>In fact, there is an even simpler convenience method,
76  * <var>dataElement</var>, designed for writing elements that
77  * contain only character data, so the code to generate the
78  * document could be shortened to</p>
79  *
80  * <pre>
81  * XMLWriter w = new XMLWriter();
82  *
83  * w.startDocument();
84  * w.dataElement("greeting", "Hello, world!");
85  * w.endDocument();
86  * </pre>
87  *
88  * <h2>Whitespace</h2>
89  *
90  * <p>According to the XML Recommendation, <em>all</em> whitespace
91  * in an XML document is potentially significant to an application,
92  * so this class never adds newlines or indentation. If you
93  * insert three elements in a row, as in</p>
94  *
95  * <pre>
96  * w.dataElement("item", "1");
97  * w.dataElement("item", "2");
98  * w.dataElement("item", "3");
99  * </pre>
100  *
101  * <p>you will end up with</p>
102  *
103  * <pre>
104  * &lt;item>1&lt;/item>&lt;item>3&lt;/item>&lt;item>3&lt;/item>
105  * </pre>
106  *
107  * <p>You need to invoke one of the <var>characters</var> methods
108  * explicitly to add newlines or indentation. Alternatively, you
109  * can use {@link com.megginson.sax.DataWriter DataWriter}, which
110  * is derived from this class -- it is optimized for writing
111  * purely data-oriented (or field-oriented) XML, and does automatic
112  * linebreaks and indentation (but does not support mixed content
113  * properly).</p>
114  *
115  *
116  * <h2>Namespace Support</h2>
117  *
118  * <p>The writer contains extensive support for XML Namespaces, so that
119  * a client application does not have to keep track of prefixes and
120  * supply <var>xmlns</var> attributes. By default, the XML writer will
121  * generate Namespace declarations in the form _NS1, _NS2, etc., wherever
122  * they are needed, as in the following example:</p>
123  *
124  * <pre>
125  * w.startDocument();
126  * w.emptyElement("http://www.foo.com/ns/", "foo");
127  * w.endDocument();
128  * </pre>
129  *
130  * <p>The resulting document will look like this:</p>
131  *
132  * <pre>
133  * &lt;?xml version="1.0" standalone="yes"?>
134  *
135  * &lt;_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
136  * </pre>
137  *
138  * <p>In many cases, document authors will prefer to choose their
139  * own prefixes rather than using the (ugly) default names. The
140  * XML writer allows two methods for selecting prefixes:</p>
141  *
142  * <ol>
143  * <li>the qualified name</li>
144  * <li>the {@link #setPrefix setPrefix} method.</li>
145  * </ol>
146  *
147  * <p>Whenever the XML writer finds a new Namespace URI, it checks
148  * to see if a qualified (prefixed) name is also available; if so
149  * it attempts to use the name's prefix (as long as the prefix is
150  * not already in use for another Namespace URI).</p>
151  *
152  * <p>Before writing a document, the client can also pre-map a prefix
153  * to a Namespace URI with the setPrefix method:</p>
154  *
155  * <pre>
156  * w.setPrefix("http://www.foo.com/ns/", "foo");
157  * w.startDocument();
158  * w.emptyElement("http://www.foo.com/ns/", "foo");
159  * w.endDocument();
160  * </pre>
161  *
162  * <p>The resulting document will look like this:</p>
163  *
164  * <pre>
165  * &lt;?xml version="1.0" standalone="yes"?>
166  *
167  * &lt;foo:foo xmlns:foo="http://www.foo.com/ns/"/>
168  * </pre>
169  *
170  * <p>The default Namespace simply uses an empty string as the prefix:</p>
171  *
172  * <pre>
173  * w.setPrefix("http://www.foo.com/ns/", "");
174  * w.startDocument();
175  * w.emptyElement("http://www.foo.com/ns/", "foo");
176  * w.endDocument();
177  * </pre>
178  *
179  * <p>The resulting document will look like this:</p>
180  *
181  * <pre>
182  * &lt;?xml version="1.0" standalone="yes"?>
183  *
184  * &lt;foo xmlns="http://www.foo.com/ns/"/>
185  * </pre>
186  *
187  * <p>By default, the XML writer will not declare a Namespace until
188  * it is actually used. Sometimes, this approach will create
189  * a large number of Namespace declarations, as in the following
190  * example:</p>
191  *
192  * <pre>
193  * &lt;xml version="1.0" standalone="yes"?>
194  *
195  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
196  * &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
197  * &lt;dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night&lt;/dc:title>
198  * &lt;dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith&lt;/dc:title>
199  * &lt;dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09&lt;/dc:title>
200  * &lt;/rdf:Description>
201  * &lt;/rdf:RDF>
202  * </pre>
203  *
204  * <p>The "rdf" prefix is declared only once, because the RDF Namespace
205  * is used by the root element and can be inherited by all of its
206  * descendants; the "dc" prefix, on the other hand, is declared three
207  * times, because no higher element uses the Namespace. To solve this
208  * problem, you can instruct the XML writer to predeclare Namespaces
209  * on the root element even if they are not used there:</p>
210  *
211  * <pre>
212  * w.forceNSDecl("http://www.purl.org/dc/");
213  * </pre>
214  *
215  * <p>Now, the "dc" prefix will be declared on the root element even
216  * though it's not needed there, and can be inherited by its
217  * descendants:</p>
218  *
219  * <pre>
220  * &lt;xml version="1.0" standalone="yes"?>
221  *
222  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
223  * xmlns:dc="http://www.purl.org/dc/">
224  * &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
225  * &lt;dc:title>A Dark Night&lt;/dc:title>
226  * &lt;dc:creator>Jane Smith&lt;/dc:title>
227  * &lt;dc:date>2000-09-09&lt;/dc:title>
228  * &lt;/rdf:Description>
229  * &lt;/rdf:RDF>
230  * </pre>
231  *
232  * <p>This approach is also useful for declaring Namespace prefixes
233  * that be used by qualified names appearing in attribute values or
234  * character data.</p>
235  *
236  * @author David Megginson, david@megginson.com
237  * @version 0.2
238  * @see org.xml.sax.XMLFilter
239  * @see org.xml.sax.ContentHandler
240  */

241 public class XMLWriter extends XMLFilterImpl JavaDoc
242 {
243
244
245
246     ////////////////////////////////////////////////////////////////////
247
// Constructors.
248
////////////////////////////////////////////////////////////////////
249

250
251     /**
252      * Create a new XML writer.
253      *
254      * <p>Write to standard output.</p>
255      */

256     public XMLWriter ()
257     {
258         init(null);
259     }
260
261
262     /**
263      * Create a new XML writer.
264      *
265      * <p>Write to the writer provided.</p>
266      *
267      * @param writer The output destination, or null to use standard
268      * output.
269      */

270     public XMLWriter (Writer JavaDoc writer)
271     {
272         init(writer);
273     }
274
275
276     /**
277      * Create a new XML writer.
278      *
279      * <p>Use the specified XML reader as the parent.</p>
280      *
281      * @param xmlreader The parent in the filter chain, or null
282      * for no parent.
283      */

284     public XMLWriter (XMLReader JavaDoc xmlreader)
285     {
286         super(xmlreader);
287         init(null);
288     }
289
290
291     /**
292      * Create a new XML writer.
293      *
294      * <p>Use the specified XML reader as the parent, and write
295      * to the specified writer.</p>
296      *
297      * @param xmlreader The parent in the filter chain, or null
298      * for no parent.
299      * @param writer The output destination, or null to use standard
300      * output.
301      */

302     public XMLWriter (XMLReader JavaDoc xmlreader, Writer JavaDoc writer)
303     {
304         super(xmlreader);
305         init(writer);
306     }
307
308
309     /**
310      * Internal initialization method.
311      *
312      * <p>All of the public constructors invoke this method.
313      *
314      * @param writer The output destination, or null to use
315      * standard output.
316      */

317     private void init (Writer JavaDoc writer)
318     {
319         setOutput(writer);
320         nsSupport = new NamespaceSupport JavaDoc();
321         prefixTable = new Hashtable JavaDoc();
322         forcedDeclTable = new Hashtable JavaDoc();
323         doneDeclTable = new Hashtable JavaDoc();
324     }
325
326
327
328
329     ////////////////////////////////////////////////////////////////////
330
// Public methods.
331
////////////////////////////////////////////////////////////////////
332

333
334     /**
335      * Reset the writer.
336      *
337      * <p>This method is especially useful if the writer throws an
338      * exception before it is finished, and you want to reuse the
339      * writer for a new document. It is usually a good idea to
340      * invoke {@link #flush flush} before resetting the writer,
341      * to make sure that no output is lost.</p>
342      *
343      * <p>This method is invoked automatically by the
344      * {@link #startDocument startDocument} method before writing
345      * a new document.</p>
346      *
347      * <p><strong>Note:</strong> this method will <em>not</em>
348      * clear the prefix or URI information in the writer or
349      * the selected output writer.</p>
350      *
351      * @see #flush
352      */

353     public void reset ()
354     {
355         elementLevel = 0;
356         prefixCounter = 0;
357         nsSupport.reset();
358     }
359
360
361     /**
362      * Flush the output.
363      *
364      * <p>This method flushes the output stream. It is especially useful
365      * when you need to make certain that the entire document has
366      * been written to output but do not want to close the output
367      * stream.</p>
368      *
369      * <p>This method is invoked automatically by the
370      * {@link #endDocument endDocument} method after writing a
371      * document.</p>
372      *
373      * @see #reset
374      */

375     public void flush ()
376         throws IOException JavaDoc
377     {
378         output.flush();
379     }
380
381
382     /**
383      * Set a new output destination for the document.
384      *
385      * @param writer The output destination, or null to use
386      * standard output.
387      * @return The current output writer.
388      * @see #flush
389      */

390     public void setOutput (Writer JavaDoc writer)
391     {
392         if (writer == null) {
393             output = new OutputStreamWriter JavaDoc(System.out);
394         } else {
395             output = writer;
396         }
397     }
398
399
400     /**
401      * Specify a preferred prefix for a Namespace URI.
402      *
403      * <p>Note that this method does not actually force the Namespace
404      * to be declared; to do that, use the {@link
405      * #forceNSDecl(java.lang.String) forceNSDecl} method as well.</p>
406      *
407      * @param uri The Namespace URI.
408      * @param prefix The preferred prefix, or "" to select
409      * the default Namespace.
410      * @see #getPrefix
411      * @see #forceNSDecl(java.lang.String)
412      * @see #forceNSDecl(java.lang.String,java.lang.String)
413      */

414     public void setPrefix (String JavaDoc uri, String JavaDoc prefix)
415     {
416         prefixTable.put(uri, prefix);
417     }
418
419
420     /**
421      * Get the current or preferred prefix for a Namespace URI.
422      *
423      * @param uri The Namespace URI.
424      * @return The preferred prefix, or "" for the default Namespace.
425      * @see #setPrefix
426      */

427     public String JavaDoc getPrefix (String JavaDoc uri)
428     {
429         return (String JavaDoc)prefixTable.get(uri);
430     }
431
432
433     /**
434      * Force a Namespace to be declared on the root element.
435      *
436      * <p>By default, the XMLWriter will declare only the Namespaces
437      * needed for an element; as a result, a Namespace may be
438      * declared many places in a document if it is not used on the
439      * root element.</p>
440      *
441      * <p>This method forces a Namespace to be declared on the root
442      * element even if it is not used there, and reduces the number
443      * of xmlns attributes in the document.</p>
444      *
445      * @param uri The Namespace URI to declare.
446      * @see #forceNSDecl(java.lang.String,java.lang.String)
447      * @see #setPrefix
448      */

449     public void forceNSDecl (String JavaDoc uri)
450     {
451         forcedDeclTable.put(uri, Boolean.TRUE);
452     }
453
454
455     /**
456      * Force a Namespace declaration with a preferred prefix.
457      *
458      * <p>This is a convenience method that invokes {@link
459      * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
460      * forceNSDecl}.</p>
461      *
462      * @param uri The Namespace URI to declare on the root element.
463      * @param prefix The preferred prefix for the Namespace, or ""
464      * for the default Namespace.
465      * @see #setPrefix
466      * @see #forceNSDecl(java.lang.String)
467      */

468     public void forceNSDecl (String JavaDoc uri, String JavaDoc prefix)
469     {
470         setPrefix(uri, prefix);
471         forceNSDecl(uri);
472     }
473
474
475
476
477     ////////////////////////////////////////////////////////////////////
478
// Methods from org.xml.sax.ContentHandler.
479
////////////////////////////////////////////////////////////////////
480

481
482     /**
483      * Write the XML declaration at the beginning of the document.
484      *
485      * Pass the event on down the filter chain for further processing.
486      *
487      * @exception org.xml.sax.SAXException If there is an error
488      * writing the XML declaration, or if a handler further down
489      * the filter chain raises an exception.
490      * @see org.xml.sax.ContentHandler#startDocument
491      */

492     public void startDocument ()
493         throws SAXException JavaDoc
494     {
495         reset();
496         write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
497         super.startDocument();
498     }
499
500
501     /**
502      * Write a newline at the end of the document.
503      *
504      * Pass the event on down the filter chain for further processing.
505      *
506      * @exception org.xml.sax.SAXException If there is an error
507      * writing the newline, or if a handler further down
508      * the filter chain raises an exception.
509      * @see org.xml.sax.ContentHandler#endDocument
510      */

511     public void endDocument ()
512         throws SAXException JavaDoc
513     {
514         write('\n');
515         super.endDocument();
516         try {
517             flush();
518         } catch (IOException JavaDoc e) {
519             throw new SAXException JavaDoc(e);
520         }
521     }
522
523
524     /**
525      * Write a start tag.
526      *
527      * Pass the event on down the filter chain for further processing.
528      *
529      * @param uri The Namespace URI, or the empty string if none
530      * is available.
531      * @param localName The element's local (unprefixed) name (required).
532      * @param qName The element's qualified (prefixed) name, or the
533      * empty string is none is available. This method will
534      * use the qName as a template for generating a prefix
535      * if necessary, but it is not guaranteed to use the
536      * same qName.
537      * @param atts The element's attribute list (must not be null).
538      * @exception org.xml.sax.SAXException If there is an error
539      * writing the start tag, or if a handler further down
540      * the filter chain raises an exception.
541      * @see org.xml.sax.ContentHandler#startElement
542      */

543     public void startElement (String JavaDoc uri, String JavaDoc localName,
544                               String JavaDoc qName, Attributes JavaDoc atts)
545         throws SAXException JavaDoc
546     {
547         elementLevel++;
548         nsSupport.pushContext();
549         write('<');
550         writeName(uri, localName, qName, true);
551         writeAttributes(atts);
552         if (elementLevel == 1) {
553             forceNSDecls();
554         }
555 // writeNSDecls();
556
write('>');
557         super.startElement(uri, localName, qName, atts);
558     }
559
560
561     /**
562      * Write an end tag.
563      *
564      * Pass the event on down the filter chain for further processing.
565      *
566      * @param uri The Namespace URI, or the empty string if none
567      * is available.
568      * @param localName The element's local (unprefixed) name (required).
569      * @param qName The element's qualified (prefixed) name, or the
570      * empty string is none is available. This method will
571      * use the qName as a template for generating a prefix
572      * if necessary, but it is not guaranteed to use the
573      * same qName.
574      * @exception org.xml.sax.SAXException If there is an error
575      * writing the end tag, or if a handler further down
576      * the filter chain raises an exception.
577      * @see org.xml.sax.ContentHandler#endElement
578      */

579     public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
580         throws SAXException JavaDoc
581     {
582         write("</");
583         writeName(uri, localName, qName, true);
584         write('>');
585         if (elementLevel == 1) {
586             write('\n');
587         }
588         super.endElement(uri, localName, qName);
589         nsSupport.popContext();
590         elementLevel--;
591     }
592
593
594     /**
595      * Write character data.
596      *
597      * Pass the event on down the filter chain for further processing.
598      *
599      * @param ch The array of characters to write.
600      * @param start The starting position in the array.
601      * @param length The number of characters to write.
602      * @exception org.xml.sax.SAXException If there is an error
603      * writing the characters, or if a handler further down
604      * the filter chain raises an exception.
605      * @see org.xml.sax.ContentHandler#characters
606      */

607     public void characters (char ch[], int start, int len)
608         throws SAXException JavaDoc
609     {
610         writeEsc(ch, start, len, false);
611         super.characters(ch, start, len);
612     }
613
614
615     /**
616      * Write ignorable whitespace.
617      *
618      * Pass the event on down the filter chain for further processing.
619      *
620      * @param ch The array of characters to write.
621      * @param start The starting position in the array.
622      * @param length The number of characters to write.
623      * @exception org.xml.sax.SAXException If there is an error
624      * writing the whitespace, or if a handler further down
625      * the filter chain raises an exception.
626      * @see org.xml.sax.ContentHandler#ignorableWhitespace
627      */

628     public void ignorableWhitespace (char ch[], int start, int length)
629         throws SAXException JavaDoc
630     {
631         writeEsc(ch, start, length, false);
632         super.ignorableWhitespace(ch, start, length);
633     }
634
635
636
637     /**
638      * Write a processing instruction.
639      *
640      * Pass the event on down the filter chain for further processing.
641      *
642      * @param target The PI target.
643      * @param data The PI data.
644      * @exception org.xml.sax.SAXException If there is an error
645      * writing the PI, or if a handler further down
646      * the filter chain raises an exception.
647      * @see org.xml.sax.ContentHandler#processingInstruction
648      */

649     public void processingInstruction (String JavaDoc target, String JavaDoc data)
650         throws SAXException JavaDoc
651     {
652         write("<?");
653         write(target);
654         write(' ');
655         write(data);
656         write("?>");
657         if (elementLevel < 1) {
658             write('\n');
659         }
660         super.processingInstruction(target, data);
661     }
662
663
664
665
666     ////////////////////////////////////////////////////////////////////
667
// Additional markup.
668
////////////////////////////////////////////////////////////////////
669

670     /**
671      * Write an empty element.
672      *
673      * This method writes an empty element tag rather than a start tag
674      * followed by an end tag. Both a {@link #startElement
675      * startElement} and an {@link #endElement endElement} event will
676      * be passed on down the filter chain.
677      *
678      * @param uri The element's Namespace URI, or the empty string
679      * if the element has no Namespace or if Namespace
680      * processing is not being performed.
681      * @param localName The element's local name (without prefix). This
682      * parameter must be provided.
683      * @param qName The element's qualified name (with prefix), or
684      * the empty string if none is available. This parameter
685      * is strictly advisory: the writer may or may not use
686      * the prefix attached.
687      * @param atts The element's attribute list.
688      * @exception org.xml.sax.SAXException If there is an error
689      * writing the empty tag, or if a handler further down
690      * the filter chain raises an exception.
691      * @see #startElement
692      * @see #endElement
693      */

694     public void emptyElement (String JavaDoc uri, String JavaDoc localName,
695                               String JavaDoc qName, Attributes JavaDoc atts)
696         throws SAXException JavaDoc
697     {
698         nsSupport.pushContext();
699         write('<');
700         writeName(uri, localName, qName, true);
701         writeAttributes(atts);
702         if (elementLevel == 1) {
703             forceNSDecls();
704         }
705         writeNSDecls();
706         write("/>");
707         super.startElement(uri, localName, qName, atts);
708         super.endElement(uri, localName, qName);
709     }
710
711
712
713
714     ////////////////////////////////////////////////////////////////////
715
// Convenience methods.
716
////////////////////////////////////////////////////////////////////
717

718
719
720     /**
721      * Start a new element without a qname or attributes.
722      *
723      * <p>This method will provide a default empty attribute
724      * list and an empty string for the qualified name.
725      * It invokes {@link
726      * #startElement(String, String, String, Attributes)}
727      * directly.</p>
728      *
729      * @param uri The element's Namespace URI.
730      * @param localName The element's local name.
731      * @exception org.xml.sax.SAXException If there is an error
732      * writing the start tag, or if a handler further down
733      * the filter chain raises an exception.
734      * @see #startElement(String, String, String, Attributes)
735      */

736     public void startElement (String JavaDoc uri, String JavaDoc localName)
737         throws SAXException JavaDoc
738     {
739         startElement(uri, localName, "", EMPTY_ATTS);
740     }
741
742
743     /**
744      * Start a new element without a qname, attributes or a Namespace URI.
745      *
746      * <p>This method will provide an empty string for the
747      * Namespace URI, and empty string for the qualified name,
748      * and a default empty attribute list. It invokes
749      * #startElement(String, String, String, Attributes)}
750      * directly.</p>
751      *
752      * @param localName The element's local name.
753      * @exception org.xml.sax.SAXException If there is an error
754      * writing the start tag, or if a handler further down
755      * the filter chain raises an exception.
756      * @see #startElement(String, String, String, Attributes)
757      */

758     public void startElement (String JavaDoc localName)
759         throws SAXException JavaDoc
760     {
761         startElement("", localName, "", EMPTY_ATTS);
762     }
763
764
765     /**
766      * End an element without a qname.
767      *
768      * <p>This method will supply an empty string for the qName.
769      * It invokes {@link #endElement(String, String, String)}
770      * directly.</p>
771      *
772      * @param uri The element's Namespace URI.
773      * @param localName The element's local name.
774      * @exception org.xml.sax.SAXException If there is an error
775      * writing the end tag, or if a handler further down
776      * the filter chain raises an exception.
777      * @see #endElement(String, String, String)
778      */

779     public void endElement (String JavaDoc uri, String JavaDoc localName)
780         throws SAXException JavaDoc
781     {
782         endElement(uri, localName, "");
783     }
784
785
786     /**
787      * End an element without a Namespace URI or qname.
788      *
789      * <p>This method will supply an empty string for the qName
790      * and an empty string for the Namespace URI.
791      * It invokes {@link #endElement(String, String, String)}
792      * directly.</p>
793      *
794      * @param localName The element's local name.
795      * @exception org.xml.sax.SAXException If there is an error
796      * writing the end tag, or if a handler further down
797      * the filter chain raises an exception.
798      * @see #endElement(String, String, String)
799      */

800     public void endElement (String JavaDoc localName)
801         throws SAXException JavaDoc
802     {
803         endElement("", localName, "");
804     }
805
806
807     /**
808      * Add an empty element without a qname or attributes.
809      *
810      * <p>This method will supply an empty string for the qname
811      * and an empty attribute list. It invokes
812      * {@link #emptyElement(String, String, String, Attributes)}
813      * directly.</p>
814      *
815      * @param uri The element's Namespace URI.
816      * @param localName The element's local name.
817      * @exception org.xml.sax.SAXException If there is an error
818      * writing the empty tag, or if a handler further down
819      * the filter chain raises an exception.
820      * @see #emptyElement(String, String, String, Attributes)
821      */

822     public void emptyElement (String JavaDoc uri, String JavaDoc localName)
823         throws SAXException JavaDoc
824     {
825         emptyElement(uri, localName, "", EMPTY_ATTS);
826     }
827
828
829     /**
830      * Add an empty element without a Namespace URI, qname or attributes.
831      *
832      * <p>This method will supply an empty string for the qname,
833      * and empty string for the Namespace URI, and an empty
834      * attribute list. It invokes
835      * {@link #emptyElement(String, String, String, Attributes)}
836      * directly.</p>
837      *
838      * @param localName The element's local name.
839      * @exception org.xml.sax.SAXException If there is an error
840      * writing the empty tag, or if a handler further down
841      * the filter chain raises an exception.
842      * @see #emptyElement(String, String, String, Attributes)
843      */

844     public void emptyElement (String JavaDoc localName)
845         throws SAXException JavaDoc
846     {
847         emptyElement("", localName, "", EMPTY_ATTS);
848     }
849
850
851     /**
852      * Write an element with character data content.
853      *
854      * <p>This is a convenience method to write a complete element
855      * with character data content, including the start tag
856      * and end tag.</p>
857      *
858      * <p>This method invokes
859      * {@link #startElement(String, String, String, Attributes)},
860      * followed by
861      * {@link #characters(String)}, followed by
862      * {@link #endElement(String, String, String)}.</p>
863      *
864      * @param uri The element's Namespace URI.
865      * @param localName The element's local name.
866      * @param qName The element's default qualified name.
867      * @param atts The element's attributes.
868      * @param content The character data content.
869      * @exception org.xml.sax.SAXException If there is an error
870      * writing the empty tag, or if a handler further down
871      * the filter chain raises an exception.
872      * @see #startElement(String, String, String, Attributes)
873      * @see #characters(String)
874      * @see #endElement(String, String, String)
875      */

876     public void dataElement (String JavaDoc uri, String JavaDoc localName,
877                              String JavaDoc qName, Attributes JavaDoc atts,
878                              String JavaDoc content)
879         throws SAXException JavaDoc
880     {
881         startElement(uri, localName, qName, atts);
882         characters(content);
883         endElement(uri, localName, qName);
884     }
885
886
887     /**
888      * Write an element with character data content but no attributes.
889      *
890      * <p>This is a convenience method to write a complete element
891      * with character data content, including the start tag
892      * and end tag. This method provides an empty string
893      * for the qname and an empty attribute list.</p>
894      *
895      * <p>This method invokes
896      * {@link #startElement(String, String, String, Attributes)},
897      * followed by
898      * {@link #characters(String)}, followed by
899      * {@link #endElement(String, String, String)}.</p>
900      *
901      * @param uri The element's Namespace URI.
902      * @param localName The element's local name.
903      * @param content The character data content.
904      * @exception org.xml.sax.SAXException If there is an error
905      * writing the empty tag, or if a handler further down
906      * the filter chain raises an exception.
907      * @see #startElement(String, String, String, Attributes)
908      * @see #characters(String)
909      * @see #endElement(String, String, String)
910      */

911     public void dataElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc content)
912         throws SAXException JavaDoc
913     {
914         dataElement(uri, localName, "", EMPTY_ATTS, content);
915     }
916
917
918     /**
919      * Write an element with character data content but no attributes or Namespace URI.
920      *
921      * <p>This is a convenience method to write a complete element
922      * with character data content, including the start tag
923      * and end tag. The method provides an empty string for the
924      * Namespace URI, and empty string for the qualified name,
925      * and an empty attribute list.</p>
926      *
927      * <p>This method invokes
928      * {@link #startElement(String, String, String, Attributes)},
929      * followed by
930      * {@link #characters(String)}, followed by
931      * {@link #endElement(String, String, String)}.</p>
932      *
933      * @param localName The element's local name.
934      * @param content The character data content.
935      * @exception org.xml.sax.SAXException If there is an error
936      * writing the empty tag, or if a handler further down
937      * the filter chain raises an exception.
938      * @see #startElement(String, String, String, Attributes)
939      * @see #characters(String)
940      * @see #endElement(String, String, String)
941      */

942     public void dataElement (String JavaDoc localName, String JavaDoc content)
943         throws SAXException JavaDoc
944     {
945         dataElement("", localName, "", EMPTY_ATTS, content);
946     }
947
948
949     /**
950      * Write a string of character data, with XML escaping.
951      *
952      * <p>This is a convenience method that takes an XML
953      * String, converts it to a character array, then invokes
954      * {@link #characters(char[], int, int)}.</p>
955      *
956      * @param data The character data.
957      * @exception org.xml.sax.SAXException If there is an error
958      * writing the string, or if a handler further down
959      * the filter chain raises an exception.
960      * @see #characters(char[], int, int)
961      */

962     public void characters (String JavaDoc data)
963         throws SAXException JavaDoc
964     {
965         char ch[] = data.toCharArray();
966         characters(ch, 0, ch.length);
967     }
968
969
970
971
972     ////////////////////////////////////////////////////////////////////
973
// Internal methods.
974
////////////////////////////////////////////////////////////////////
975

976
977     /**
978      * Force all Namespaces to be declared.
979      *
980      * This method is used on the root element to ensure that
981      * the predeclared Namespaces all appear.
982      */

983     private void forceNSDecls ()
984     {
985         Enumeration JavaDoc prefixes = forcedDeclTable.keys();
986         while (prefixes.hasMoreElements()) {
987             String JavaDoc prefix = (String JavaDoc)prefixes.nextElement();
988             doPrefix(prefix, null, true);
989         }
990     }
991
992
993     /**
994      * Determine the prefix for an element or attribute name.
995      *
996      * TODO: this method probably needs some cleanup.
997      *
998      * @param uri The Namespace URI.
999      * @param qName The qualified name (optional); this will be used
1000     * to indicate the preferred prefix if none is currently
1001     * bound.
1002     * @param isElement true if this is an element name, false
1003     * if it is an attribute name (which cannot use the
1004     * default Namespace).
1005     */

1006    private String JavaDoc doPrefix (String JavaDoc uri, String JavaDoc qName, boolean isElement)
1007    {
1008        String JavaDoc defaultNS = nsSupport.getURI("");
1009        if ("".equals(uri)) {
1010            if (isElement && defaultNS != null)
1011                nsSupport.declarePrefix("", "");
1012            return null;
1013        }
1014        String JavaDoc prefix;
1015        if (isElement && defaultNS != null && uri.equals(defaultNS)) {
1016            prefix = "";
1017        } else {
1018            prefix = nsSupport.getPrefix(uri);
1019        }
1020        if (prefix != null) {
1021            return prefix;
1022        }
1023        prefix = (String JavaDoc) doneDeclTable.get(uri);
1024        if (prefix != null &&
1025            ((!isElement || defaultNS != null) &&
1026             "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
1027            prefix = null;
1028        }
1029        if (prefix == null) {
1030            prefix = (String JavaDoc) prefixTable.get(uri);
1031            if (prefix != null &&
1032                ((!isElement || defaultNS != null) &&
1033                 "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
1034                prefix = null;
1035            }
1036        }
1037        if (prefix == null && qName != null && !"".equals(qName)) {
1038            int i = qName.indexOf(':');
1039            if (i == -1) {
1040                if (isElement && defaultNS == null) {
1041                    prefix = "";
1042                }
1043            } else {
1044                prefix = qName.substring(0, i);
1045            }
1046        }
1047        for (;
1048             prefix == null || nsSupport.getURI(prefix) != null;
1049             prefix = "__NS" + ++prefixCounter)
1050            ;
1051        nsSupport.declarePrefix(prefix, uri);
1052        doneDeclTable.put(uri, prefix);
1053        return prefix;
1054    }
1055
1056
1057    /**
1058     * Write a raw character.
1059     *
1060     * @param c The character to write.
1061     * @exception org.xml.sax.SAXException If there is an error writing
1062     * the character, this method will throw an IOException
1063     * wrapped in a SAXException.
1064     */

1065    private void write (char c)
1066        throws SAXException JavaDoc
1067    {
1068        try {
1069            output.write(c);
1070        } catch (IOException JavaDoc e) {
1071            throw new SAXException JavaDoc(e);
1072        }
1073    }
1074
1075
1076    /**
1077     * Write a raw string.
1078     *
1079     * @param s
1080     * @exception org.xml.sax.SAXException If there is an error writing
1081     * the string, this method will throw an IOException
1082     * wrapped in a SAXException
1083     */

1084    private void write (String JavaDoc s)
1085    throws SAXException JavaDoc
1086    {
1087        try {
1088            output.write(s);
1089        } catch (IOException JavaDoc e) {
1090            throw new SAXException JavaDoc(e);
1091        }
1092    }
1093
1094
1095    /**
1096     * Write out an attribute list, escaping values.
1097     *
1098     * The names will have prefixes added to them.
1099     *
1100     * @param atts The attribute list to write.
1101     * @exception org.xml.SAXException If there is an error writing
1102     * the attribute list, this method will throw an
1103     * IOException wrapped in a SAXException.
1104     */

1105    private void writeAttributes (Attributes JavaDoc atts)
1106        throws SAXException JavaDoc
1107    {
1108        int len = atts.getLength();
1109        for (int i = 0; i < len; i++) {
1110            char ch[] = atts.getValue(i).toCharArray();
1111            write(' ');
1112            writeName(atts.getURI(i), atts.getLocalName(i),
1113                      atts.getQName(i), false);
1114            write("=\"");
1115            writeEsc(ch, 0, ch.length, true);
1116            write('"');
1117        }
1118    }
1119
1120
1121    /**
1122     * Write an array of data characters with escaping.
1123     *
1124     * @param ch The array of characters.
1125     * @param start The starting position.
1126     * @param length The number of characters to use.
1127     * @param isAttVal true if this is an attribute value literal.
1128     * @exception org.xml.SAXException If there is an error writing
1129     * the characters, this method will throw an
1130     * IOException wrapped in a SAXException.
1131     */

1132    private void writeEsc (char ch[], int start,
1133                             int length, boolean isAttVal)
1134        throws SAXException JavaDoc
1135    {
1136        for (int i = start; i < start + length; i++) {
1137            switch (ch[i]) {
1138            case '&':
1139                write("&amp;");
1140                break;
1141            case '<':
1142                write("&lt;");
1143                break;
1144            case '>':
1145                write("&gt;");
1146                break;
1147            case '\"':
1148                if (isAttVal) {
1149                    write("&quot;");
1150                } else {
1151                    write('\"');
1152                }
1153                break;
1154            default:
1155// if (ch[i] > '\u007f') {
1156
// write("&#");
1157
// write(Integer.toString(ch[i]));
1158
// write(';');
1159
// } else {
1160
write(ch[i]);
1161// }
1162
}
1163        }
1164    }
1165
1166
1167    /**
1168     * Write out the list of Namespace declarations.
1169     *
1170     * @exception org.xml.sax.SAXException This method will throw
1171     * an IOException wrapped in a SAXException if
1172     * there is an error writing the Namespace
1173     * declarations.
1174     */

1175    private void writeNSDecls ()
1176        throws SAXException JavaDoc
1177    {
1178        Enumeration JavaDoc prefixes = nsSupport.getDeclaredPrefixes();
1179        while (prefixes.hasMoreElements()) {
1180            String JavaDoc prefix = (String JavaDoc) prefixes.nextElement();
1181            String JavaDoc uri = nsSupport.getURI(prefix);
1182            if (uri == null) {
1183                uri = "";
1184            }
1185            char ch[] = uri.toCharArray();
1186            write(' ');
1187            if ("".equals(prefix)) {
1188                write("xmlns=\"");
1189            } else {
1190                write("xmlns:");
1191                write(prefix);
1192                write("=\"");
1193            }
1194            writeEsc(ch, 0, ch.length, true);
1195            write('\"');
1196        }
1197    }
1198
1199
1200    /**
1201     * Write an element or attribute name.
1202     *
1203     * @param uri The Namespace URI.
1204     * @param localName The local name.
1205     * @param qName The prefixed name, if available, or the empty string.
1206     * @param isElement true if this is an element name, false if it
1207     * is an attribute name.
1208     * @exception org.xml.sax.SAXException This method will throw an
1209     * IOException wrapped in a SAXException if there is
1210     * an error writing the name.
1211     */

1212    private void writeName (String JavaDoc uri, String JavaDoc localName,
1213                              String JavaDoc qName, boolean isElement)
1214        throws SAXException JavaDoc
1215    {
1216        String JavaDoc prefix = doPrefix(uri, qName, isElement);
1217        if (prefix != null && !"".equals(prefix)) {
1218            write(prefix);
1219            write(':');
1220        }
1221        write(localName);
1222    }
1223
1224
1225
1226
1227    ////////////////////////////////////////////////////////////////////
1228
// Constants.
1229
////////////////////////////////////////////////////////////////////
1230

1231    private final Attributes JavaDoc EMPTY_ATTS = new AttributesImpl JavaDoc();
1232
1233
1234
1235
1236    ////////////////////////////////////////////////////////////////////
1237
// Internal state.
1238
////////////////////////////////////////////////////////////////////
1239

1240    private Hashtable JavaDoc prefixTable;
1241    private Hashtable JavaDoc forcedDeclTable;
1242    private Hashtable JavaDoc doneDeclTable;
1243    private int elementLevel = 0;
1244    private Writer JavaDoc output;
1245    private NamespaceSupport JavaDoc nsSupport;
1246    private int prefixCounter = 0;
1247}
Popular Tags