Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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   * &lt;?xml version="1.0" standalone="yes"?>
90   *
91   * &lt;greeting>Hello, world!&lt;/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  * &lt;item>1&lt;/item>&lt;item>3&lt;/item>&lt;item>3&lt;/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  * &lt;?xml version="1.0" standalone="yes"?>
153  *
154  * &lt;_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  * &lt;?xml version="1.0" standalone="yes"?>
185  *
186  * &lt;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  * &lt;?xml version="1.0" standalone="yes"?>
202  *
203  * &lt;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  * &lt;xml version="1.0" standalone="yes"?>
213  *
214  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
215  *  &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
216  *   &lt;dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night&lt;/dc:title>
217  *   &lt;dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith&lt;/dc:title>
218  *   &lt;dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09&lt;/dc:title>
219  *  &lt;/rdf:Description>
220  * &lt;/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  * &lt;xml version="1.0" standalone="yes"?>
240  *
241  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
242  *             xmlns:dc="http://www.purl.org/dc/">
243  *  &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
244  *   &lt;dc:title>A Dark Night&lt;/dc:title>
245  *   &lt;dc:creator>Jane Smith&lt;/dc:title>
246  *   &lt;dc:date>2000-09-09&lt;/dc:title>
247  *  &lt;/rdf:Description>
248  * &lt;/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("&amp;");
1099    break;
1100      case '<':
1101    write("&lt;");
1102    break;
1103      case '>':
1104    write("&gt;");
1105    break;
1106      case '\"':
1107    if (isAttVal) {
1108        write("&quot;");
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