KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sax > XMLWriter


1 /*--
2
3  Copyright (C) 2000 Brett McLaughlin & Jason Hunter.
4  All rights reserved.
5  
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions
8  are met:
9  
10  1. Redistributions of source code must retain the above copyright
11     notice, this list of conditions, and the following disclaimer.
12  
13  2. Redistributions in binary form must reproduce the above copyright
14     notice, this list of conditions, and the disclaimer that follows
15     these conditions in the documentation and/or other materials
16     provided with the distribution.
17
18  3. The name "JDOM" must not be used to endorse or promote products
19     derived from this software without prior written permission. For
20     written permission, please contact license@jdom.org.
21  
22  4. Products derived from this software may not be called "JDOM", nor
23     may "JDOM" appear in their name, without prior written permission
24     from the JDOM Project Management (pm@jdom.org).
25  
26  In addition, we request (but do not require) that you include in the
27  end-user documentation provided with the redistribution and/or in the
28  software itself an acknowledgement equivalent to the following:
29      "This product includes software developed by the
30       JDOM Project (http://www.jdom.org/)."
31  Alternatively, the acknowledgment may be graphical using the logos
32  available at http://www.jdom.org/images/logos.
33
34  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
38  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
41  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
43  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
44  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  SUCH DAMAGE.
46
47  This software consists of voluntary contributions made by many
48  individuals on behalf of the JDOM Project and was originally
49  created by Brett McLaughlin <brett@jdom.org> and
50  Jason Hunter <jhunter@jdom.org>. For more information on the
51  JDOM Project, please see <http://www.jdom.org/>.
52  
53  */

54 package sax;
55
56 import java.io.IOException JavaDoc;
57 import java.io.OutputStreamWriter JavaDoc;
58 import java.io.Writer JavaDoc;
59 import java.util.Enumeration JavaDoc;
60 import java.util.HashMap JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.Map JavaDoc;
63
64 import org.xml.sax.Attributes JavaDoc;
65 import org.xml.sax.InputSource JavaDoc;
66 import org.xml.sax.SAXException JavaDoc;
67 import org.xml.sax.XMLReader JavaDoc;
68 import org.xml.sax.helpers.NamespaceSupport JavaDoc;
69
70
71 /**
72  * Filter to write an XML document from a SAX event stream.
73  *
74  * <i>Code and comments adapted from XMLWriter-0.2, written
75  * by David Megginson and released into the public domain,
76  * without warranty.</i>
77  *
78  * <p>This class can be used by itself or as part of a SAX event
79  * stream: it takes as input a series of SAX2 ContentHandler
80  * events and uses the information in those events to write
81  * an XML document. Since this class is a filter, it can also
82  * pass the events on down a filter chain for further processing
83  * (you can use the XMLWriter to take a snapshot of the current
84  * state at any point in a filter chain), and it can be
85  * used directly as a ContentHandler for a SAX2 XMLReader.</p>
86  *
87  * <p>The client creates a document by invoking the methods for
88  * standard SAX2 events, always beginning with the
89  * {@link #startDocument startDocument} method and ending with
90  * the {@link #endDocument endDocument} method.</p>
91  *
92  * <p>The following code will send a simple XML document to
93  * standard output:</p>
94  *
95  * <pre>
96  * XMLWriter w = new XMLWriter();
97  *
98  * w.startDocument();
99  * w.dataElement("greeting", "Hello, world!");
100  * w.endDocument();
101  * </pre>
102  *
103  * <p>The resulting document will look like this:</p>
104  *
105  * <pre>
106  * &lt;?xml version="1.0"?>
107  *
108  * &lt;greeting>Hello, world!&lt;/greeting>
109  * </pre>
110  *
111  * <h2>Whitespace</h2>
112  *
113  * <p>According to the XML Recommendation, <em>all</em> whitespace
114  * in an XML document is potentially significant to an application,
115  * so this class never adds newlines or indentation. If you
116  * insert three elements in a row, as in</p>
117  *
118  * <pre>
119  * w.dataElement("item", "1");
120  * w.dataElement("item", "2");
121  * w.dataElement("item", "3");
122  * </pre>
123  *
124  * <p>you will end up with</p>
125  *
126  * <pre>
127  * &lt;item>1&lt;/item>&lt;item>3&lt;/item>&lt;item>3&lt;/item>
128  * </pre>
129  *
130  * <p>You need to invoke one of the <var>characters</var> methods
131  * explicitly to add newlines or indentation. Alternatively, you
132  * can use {@link samples.sax.DataFormatFilter DataFormatFilter}
133  * add linebreaks and indentation (but does not support mixed content
134  * properly).</p>
135  *
136  *
137  * <h2>Namespace Support</h2>
138  *
139  * <p>The writer contains extensive support for XML Namespaces, so that
140  * a client application does not have to keep track of prefixes and
141  * supply <var>xmlns</var> attributes. By default, the XML writer will
142  * generate Namespace declarations in the form _NS1, _NS2, etc., wherever
143  * they are needed, as in the following example:</p>
144  *
145  * <pre>
146  * w.startDocument();
147  * w.emptyElement("http://www.foo.com/ns/", "foo");
148  * w.endDocument();
149  * </pre>
150  *
151  * <p>The resulting document will look like this:</p>
152  *
153  * <pre>
154  * &lt;?xml version="1.0"?>
155  *
156  * &lt;_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
157  * </pre>
158  *
159  * <p>In many cases, document authors will prefer to choose their
160  * own prefixes rather than using the (ugly) default names. The
161  * XML writer allows two methods for selecting prefixes:</p>
162  *
163  * <ol>
164  * <li>the qualified name</li>
165  * <li>the {@link #setPrefix setPrefix} method.</li>
166  * </ol>
167  *
168  * <p>Whenever the XML writer finds a new Namespace URI, it checks
169  * to see if a qualified (prefixed) name is also available; if so
170  * it attempts to use the name's prefix (as long as the prefix is
171  * not already in use for another Namespace URI).</p>
172  *
173  * <p>Before writing a document, the client can also pre-map a prefix
174  * to a Namespace URI with the setPrefix method:</p>
175  *
176  * <pre>
177  * w.setPrefix("http://www.foo.com/ns/", "foo");
178  * w.startDocument();
179  * w.emptyElement("http://www.foo.com/ns/", "foo");
180  * w.endDocument();
181  * </pre>
182  *
183  * <p>The resulting document will look like this:</p>
184  *
185  * <pre>
186  * &lt;?xml version="1.0"?>
187  *
188  * &lt;foo:foo xmlns:foo="http://www.foo.com/ns/"/>
189  * </pre>
190  *
191  * <p>The default Namespace simply uses an empty string as the prefix:</p>
192  *
193  * <pre>
194  * w.setPrefix("http://www.foo.com/ns/", "");
195  * w.startDocument();
196  * w.emptyElement("http://www.foo.com/ns/", "foo");
197  * w.endDocument();
198  * </pre>
199  *
200  * <p>The resulting document will look like this:</p>
201  *
202  * <pre>
203  * &lt;?xml version="1.0"?>
204  *
205  * &lt;foo xmlns="http://www.foo.com/ns/"/>
206  * </pre>
207  *
208  * <p>By default, the XML writer will not declare a Namespace until
209  * it is actually used. Sometimes, this approach will create
210  * a large number of Namespace declarations, as in the following
211  * example:</p>
212  *
213  * <pre>
214  * &lt;xml version="1.0"?>
215  *
216  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
217  * &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
218  * &lt;dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night&lt;/dc:title>
219  * &lt;dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith&lt;/dc:title>
220  * &lt;dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09&lt;/dc:title>
221  * &lt;/rdf:Description>
222  * &lt;/rdf:RDF>
223  * </pre>
224  *
225  * <p>The "rdf" prefix is declared only once, because the RDF Namespace
226  * is used by the root element and can be inherited by all of its
227  * descendants; the "dc" prefix, on the other hand, is declared three
228  * times, because no higher element uses the Namespace. To solve this
229  * problem, you can instruct the XML writer to predeclare Namespaces
230  * on the root element even if they are not used there:</p>
231  *
232  * <pre>
233  * w.forceNSDecl("http://www.purl.org/dc/");
234  * </pre>
235  *
236  * <p>Now, the "dc" prefix will be declared on the root element even
237  * though it's not needed there, and can be inherited by its
238  * descendants:</p>
239  *
240  * <pre>
241  * &lt;xml version="1.0"?>
242  *
243  * &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
244  * xmlns:dc="http://www.purl.org/dc/">
245  * &lt;rdf:Description about="http://www.foo.com/ids/books/12345">
246  * &lt;dc:title>A Dark Night&lt;/dc:title>
247  * &lt;dc:creator>Jane Smith&lt;/dc:title>
248  * &lt;dc:date>2000-09-09&lt;/dc:title>
249  * &lt;/rdf:Description>
250  * &lt;/rdf:RDF>
251  * </pre>
252  *
253  * <p>This approach is also useful for declaring Namespace prefixes
254  * that be used by qualified names appearing in attribute values or
255  * character data.</p>
256  *
257  * @see XMLFilterBase
258  */

259 public class XMLWriter extends XMLFilterBase
260 {
261
262
263     ////////////////////////////////////////////////////////////////////
264
// Constructors.
265
////////////////////////////////////////////////////////////////////
266

267
268     /**
269      * Create a new XML writer.
270      *
271      * <p>Write to standard output.</p>
272      */

273     public XMLWriter()
274     {
275         init(null);
276     }
277
278
279     /**
280      * Create a new XML writer.
281      *
282      * <p>Write to the writer provided.</p>
283      *
284      * @param writer The output destination, or null to use standard
285      * output.
286      */

287     public XMLWriter(Writer writer)
288     {
289         init(writer);
290     }
291
292
293     /**
294      * Create a new XML writer.
295      *
296      * <p>Use the specified XML reader as the parent.</p>
297      *
298      * @param xmlreader The parent in the filter chain, or null
299      * for no parent.
300      */

301     public XMLWriter(XMLReader JavaDoc xmlreader)
302     {
303         super(xmlreader);
304         init(null);
305     }
306
307
308     /**
309      * Create a new XML writer.
310      *
311      * <p>Use the specified XML reader as the parent, and write
312      * to the specified writer.</p>
313      *
314      * @param xmlreader The parent in the filter chain, or null
315      * for no parent.
316      * @param writer The output destination, or null to use standard
317      * output.
318      */

319     public XMLWriter(XMLReader JavaDoc xmlreader, Writer writer)
320     {
321         super(xmlreader);
322         init(writer);
323     }
324
325
326     /**
327      * Internal initialization method.
328      *
329      * <p>All of the public constructors invoke this method.
330      *
331      * @param writer The output destination, or null to use
332      * standard output.
333      */

334     private void init (Writer writer)
335     {
336         setOutput(writer);
337         nsSupport = new NamespaceSupport JavaDoc();
338         prefixTable = new HashMap JavaDoc();
339         forcedDeclTable = new HashMap JavaDoc();
340         doneDeclTable = new HashMap JavaDoc();
341     }
342
343
344
345     ////////////////////////////////////////////////////////////////////
346
// Public methods.
347
////////////////////////////////////////////////////////////////////
348

349
350     /**
351      * Reset the writer.
352      *
353      * <p>This method is especially useful if the writer throws an
354      * exception before it is finished, and you want to reuse the
355      * writer for a new document. It is usually a good idea to
356      * invoke {@link #flush flush} before resetting the writer,
357      * to make sure that no output is lost.</p>
358      *
359      * <p>This method is invoked automatically by the
360      * {@link #startDocument startDocument} method before writing
361      * a new document.</p>
362      *
363      * <p><strong>Note:</strong> this method will <em>not</em>
364      * clear the prefix or URI information in the writer or
365      * the selected output writer.</p>
366      *
367      * @see #flush
368      */

369     public void reset ()
370     {
371         openElement = false;
372         elementLevel = 0;
373         prefixCounter = 0;
374         nsSupport.reset();
375         inDTD = false;
376     }
377
378
379     /**
380      * Flush the output.
381      *
382      * <p>This method flushes the output stream. It is especially useful
383      * when you need to make certain that the entire document has
384      * been written to output but do not want to close the output
385      * stream.</p>
386      *
387      * <p>This method is invoked automatically by the
388      * {@link #endDocument endDocument} method after writing a
389      * document.</p>
390      *
391      * @see #reset
392      */

393     public void flush ()
394     throws IOException JavaDoc
395     {
396         output.flush();
397     }
398
399
400     /**
401      * Set a new output destination for the document.
402      *
403      * @param writer The output destination, or null to use
404      * standard output.
405      * @return The current output writer.
406      * @see #flush
407      */

408     public void setOutput (Writer writer)
409     {
410         if (writer == null) {
411             output = new OutputStreamWriter JavaDoc(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 JavaDoc uri, String JavaDoc prefix)
433     {
434         prefixTable.put(uri, prefix);
435     }
436
437
438     /**
439      * Get the current or preferred prefix for a Namespace URI.
440      *
441      * @param uri The Namespace URI.
442      * @return The preferred prefix, or "" for the default Namespace.
443      * @see #setPrefix
444      */

445     public String JavaDoc getPrefix (String JavaDoc uri)
446     {
447         return (String JavaDoc)prefixTable.get(uri);
448     }
449
450
451     /**
452      * Force a Namespace to be declared on the root element.
453      *
454      * <p>By default, the XMLWriter will declare only the Namespaces
455      * needed for an element; as a result, a Namespace may be
456      * declared many places in a document if it is not used on the
457      * root element.</p>
458      *
459      * <p>This method forces a Namespace to be declared on the root
460      * element even if it is not used there, and reduces the number
461      * of xmlns attributes in the document.</p>
462      *
463      * @param uri The Namespace URI to declare.
464      * @see #forceNSDecl(java.lang.String,java.lang.String)
465      * @see #setPrefix
466      */

467     public void forceNSDecl (String JavaDoc uri)
468     {
469         forcedDeclTable.put(uri, Boolean.TRUE);
470     }
471
472
473     /**
474      * Force a Namespace declaration with a preferred prefix.
475      *
476      * <p>This is a convenience method that invokes {@link
477      * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
478      * forceNSDecl}.</p>
479      *
480      * @param uri The Namespace URI to declare on the root element.
481      * @param prefix The preferred prefix for the Namespace, or ""
482      * for the default Namespace.
483      * @see #setPrefix
484      * @see #forceNSDecl(java.lang.String)
485      */

486     public void forceNSDecl (String JavaDoc uri, String JavaDoc prefix)
487     {
488         setPrefix(uri, prefix);
489         forceNSDecl(uri);
490     }
491
492
493
494     ////////////////////////////////////////////////////////////////////
495
// Methods from org.xml.sax.ContentHandler.
496
////////////////////////////////////////////////////////////////////
497

498
499     /**
500      * Write the XML declaration at the beginning of the document.
501      *
502      * Pass the event on down the filter chain for further processing.
503      *
504      * @exception org.xml.sax.SAXException If there is an error
505      * writing the XML declaration, or if a handler further down
506      * the filter chain raises an exception.
507      * @see org.xml.sax.ContentHandler#startDocument
508      */

509     public void startDocument ()
510     throws SAXException JavaDoc
511     {
512         reset();
513       //write("<?xml version=\"1.0\" standalone=\"yes\"?>\n\n");
514
write("<?xml version=\"1.0\"?>\n\n");
515         super.startDocument();
516     }
517
518
519     /**
520      * Write a newline at the end of the document.
521      *
522      * Pass the event on down the filter chain for further processing.
523      *
524      * @exception org.xml.sax.SAXException If there is an error
525      * writing the newline, or if a handler further down
526      * the filter chain raises an exception.
527      * @see org.xml.sax.ContentHandler#endDocument
528      */

529     public void endDocument ()
530     throws SAXException JavaDoc
531     {
532         closeElement();
533         write('\n');
534         super.endDocument();
535         try {
536             flush();
537         } catch (IOException JavaDoc e) {
538             throw new SAXException JavaDoc(e);
539         }
540     }
541
542
543     /**
544      * Write a start tag.
545      *
546      * Pass the event on down the filter chain for further processing.
547      *
548      * @param uri The Namespace URI, or the empty string if none
549      * is available.
550      * @param localName The element's local (unprefixed) name (required).
551      * @param qName The element's qualified (prefixed) name, or the
552      * empty string is none is available. This method will
553      * use the qName as a template for generating a prefix
554      * if necessary, but it is not guaranteed to use the
555      * same qName.
556      * @param atts The element's attribute list (must not be null).
557      * @exception org.xml.sax.SAXException If there is an error
558      * writing the start tag, or if a handler further down
559      * the filter chain raises an exception.
560      * @see org.xml.sax.ContentHandler#startElement
561      */

562     public void startElement (String JavaDoc uri, String JavaDoc localName,
563                               String JavaDoc qName, Attributes JavaDoc atts)
564     throws SAXException JavaDoc
565     {
566         closeElement();
567         elementLevel++;
568         nsSupport.pushContext();
569         write('<');
570         writeName(uri, localName, qName, true);
571         writeAttributes(atts);
572         if (elementLevel == 1) {
573             forceNSDecls();
574         }
575         writeNSDecls();
576         openElement = true;
577         super.startElement(uri, localName, qName, atts);
578     }
579
580
581     /**
582      * Write an end tag.
583      *
584      * Pass the event on down the filter chain for further processing.
585      *
586      * @param uri The Namespace URI, or the empty string if none
587      * is available.
588      * @param localName The element's local (unprefixed) name (required).
589      * @param qName The element's qualified (prefixed) name, or the
590      * empty string is none is available. This method will
591      * use the qName as a template for generating a prefix
592      * if necessary, but it is not guaranteed to use the
593      * same qName.
594      * @exception org.xml.sax.SAXException If there is an error
595      * writing the end tag, or if a handler further down
596      * the filter chain raises an exception.
597      * @see org.xml.sax.ContentHandler#endElement
598      */

599     public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
600     throws SAXException JavaDoc
601     {
602         if (openElement) {
603             write("/>");
604             openElement = false;
605         } else {
606             write("</");
607             writeName(uri, localName, qName, true);
608             write('>');
609         }
610         if (elementLevel == 1) {
611             write('\n');
612         }
613         super.endElement(uri, localName, qName);
614         nsSupport.popContext();
615         elementLevel--;
616     }
617
618
619     /**
620      * Write character data.
621      *
622      * Pass the event on down the filter chain for further processing.
623      *
624      * @param ch The array of characters to write.
625      * @param start The starting position in the array.
626      * @param length The number of characters to write.
627      * @exception org.xml.sax.SAXException If there is an error
628      * writing the characters, or if a handler further down
629      * the filter chain raises an exception.
630      * @see org.xml.sax.ContentHandler#characters
631      */

632     public void characters (char ch[], int start, int len)
633     throws SAXException JavaDoc
634     {
635         closeElement();
636         writeEsc(ch, start, len, false);
637         super.characters(ch, start, len);
638     }
639
640
641     /**
642      * Write ignorable whitespace.
643      *
644      * Pass the event on down the filter chain for further processing.
645      *
646      * @param ch The array of characters to write.
647      * @param start The starting position in the array.
648      * @param length The number of characters to write.
649      * @exception org.xml.sax.SAXException If there is an error
650      * writing the whitespace, or if a handler further down
651      * the filter chain raises an exception.
652      * @see org.xml.sax.ContentHandler#ignorableWhitespace
653      */

654     public void ignorableWhitespace (char ch[], int start, int length)
655     throws SAXException JavaDoc
656     {
657         closeElement();
658         writeEsc(ch, start, length, false);
659         super.ignorableWhitespace(ch, start, length);
660     }
661
662
663
664     /**
665      * Write a processing instruction.
666      *
667      * Pass the event on down the filter chain for further processing.
668      *
669      * @param target The PI target.
670      * @param data The PI data.
671      * @exception org.xml.sax.SAXException If there is an error
672      * writing the PI, or if a handler further down
673      * the filter chain raises an exception.
674      * @see org.xml.sax.ContentHandler#processingInstruction
675      */

676     public void processingInstruction (String JavaDoc target, String JavaDoc data)
677     throws SAXException JavaDoc
678     {
679         closeElement();
680         write("<?");
681         write(target);
682         write(' ');
683         write(data);
684         write("?>");
685         if (elementLevel < 1) {
686             write('\n');
687         }
688         super.processingInstruction(target, data);
689     }
690
691
692
693     ////////////////////////////////////////////////////////////////////
694
// Methods from org.xml.sax.ext.LexicalHandler.
695
////////////////////////////////////////////////////////////////////
696

697
698     /**
699      * Write start of DOCTYPE declaration.
700      *
701      * Pass the event on down the filter chain for further processing.
702      *
703      * @param name The document type name.
704      * @param publicId The declared public identifier for the
705      * external DTD subset, or null if none was declared.
706      * @param systemId The declared system identifier for the
707      * external DTD subset, or null if none was declared.
708      * @exception org.xml.sax.SAXException If a filter
709      * further down the chain raises an exception.
710      * @see org.xml.sax.ext.LexicalHandler#startDTD
711      */

712     public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
713     throws SAXException JavaDoc {
714       //closeElement();
715
inDTD = true;
716         write("<!DOCTYPE ");
717         write(name);
718         boolean hasPublic = publicId != null && !publicId.equals("");
719         if (hasPublic) {
720             write(" PUBLIC \"");
721             write(publicId);
722             write('\"');
723         }
724         if (systemId != null && !systemId.equals("")) {
725             if (!hasPublic) {
726                 write(" SYSTEM");
727             }
728             write(" \"");
729             write(systemId);
730             write('\"');
731         }
732         write(">\n\n");
733         super.startDTD(name, publicId, systemId);
734     }
735
736
737     /**
738      * Write end of DOCTYPE declaration.
739      *
740      * Pass the event on down the filter chain for further processing.
741      *
742      * @exception org.xml.sax.SAXException If a filter
743      * further down the chain raises an exception.
744      * @see org.xml.sax.ext.LexicalHandler#endDTD
745      */

746     public void endDTD()
747     throws SAXException JavaDoc {
748         inDTD = false;
749         super.endDTD();
750     }
751
752
753     /*
754      * Write entity.
755      *
756      * Pass the event on down the filter chain for further processing.
757      *
758      * @param name The name of the entity. If it is a parameter
759      * entity, the name will begin with '%', and if it is the
760      * external DTD subset, it will be "[dtd]".
761      * @exception org.xml.sax.SAXException If a filter
762      * further down the chain raises an exception.
763      * @see org.xml.sax.ext.LexicalHandler#startEntity
764      */

765     public void startEntity(String JavaDoc name)
766     throws SAXException JavaDoc {
767         closeElement();
768         write('&');
769         write(name);
770         write(';');
771         super.startEntity(name);
772     }
773
774
775     /*
776      * Filter a end entity event.
777      *
778      * Pass the event on down the filter chain for further processing.
779      *
780      * @param name The name of the entity that is ending.
781      * @exception org.xml.sax.SAXException If a filter
782      * further down the chain raises an exception.
783      * @see org.xml.sax.ext.LexicalHandler#endEntity
784      */

785     public void endEntity(String JavaDoc name)
786     throws SAXException JavaDoc {
787         super.endEntity(name);
788     }
789
790
791     /*
792      * Write start of CDATA.
793      *
794      * Pass the event on down the filter chain for further processing.
795      *
796      * @exception org.xml.sax.SAXException If a filter
797      * further down the chain raises an exception.
798      * @see org.xml.sax.ext.LexicalHandler#startCDATA
799      */

800     public void startCDATA()
801     throws SAXException JavaDoc {
802         closeElement();
803         write("<![CDATA[");
804         super.startCDATA();
805     }
806
807
808     /*
809      * Write end of CDATA.
810      *
811      * Pass the event on down the filter chain for further processing.
812      *
813      * @exception org.xml.sax.SAXException If a filter
814      * further down the chain raises an exception.
815      * @see org.xml.sax.ext.LexicalHandler#endCDATA
816      */

817     public void endCDATA()
818     throws SAXException JavaDoc {
819         write("]]>");
820         super.endCDATA();
821     }
822
823
824     /*
825      * Write a comment.
826      *
827      * Pass the event on down the filter chain for further processing.
828      *
829      * @param ch An array holding the characters in the comment.
830      * @param start The starting position in the array.
831      * @param length The number of characters to use from the array.
832      * @exception org.xml.sax.SAXException If a filter
833      * further down the chain raises an exception.
834      * @see org.xml.sax.ext.LexicalHandler#comment
835      */

836     public void comment(char[] ch, int start, int length)
837     throws SAXException JavaDoc {
838         if (!inDTD) {
839             closeElement();
840             write("<!--");
841             write(ch, start, length);
842             write("-->");
843             if (elementLevel < 1) {
844                 write('\n');
845             }
846         }
847         super.comment(ch, start, length);
848     }
849
850
851
852     ////////////////////////////////////////////////////////////////////
853
// Internal methods.
854
////////////////////////////////////////////////////////////////////
855

856
857     /**
858      * Force all Namespaces to be declared.
859      *
860      * This method is used on the root element to ensure that
861      * the predeclared Namespaces all appear.
862      */

863     private void forceNSDecls ()
864     {
865         Iterator JavaDoc prefixes = forcedDeclTable.keySet().iterator();
866         while (prefixes.hasNext()) {
867             String JavaDoc prefix = (String JavaDoc)prefixes.next();
868             doPrefix(prefix, null, true);
869         }
870     }
871
872
873     /**
874      * Determine the prefix for an element or attribute name.
875      *
876      * TODO: this method probably needs some cleanup.
877      *
878      * @param uri The Namespace URI.
879      * @param qName The qualified name (optional); this will be used
880      * to indicate the preferred prefix if none is currently
881      * bound.
882      * @param isElement true if this is an element name, false
883      * if it is an attribute name (which cannot use the
884      * default Namespace).
885      */

886     private String JavaDoc doPrefix (String JavaDoc uri, String JavaDoc qName, boolean isElement)
887     {
888         String JavaDoc defaultNS = nsSupport.getURI("");
889         if ("".equals(uri)) {
890             if (isElement && defaultNS != null)
891             nsSupport.declarePrefix("", "");
892             return null;
893         }
894         String JavaDoc prefix;
895         if (isElement && defaultNS != null && uri.equals(defaultNS)) {
896             prefix = "";
897         } else {
898             prefix = nsSupport.getPrefix(uri);
899         }
900         if (prefix != null) {
901             return prefix;
902         }
903         prefix = (String JavaDoc) doneDeclTable.get(uri);
904         if (prefix != null &&
905         ((!isElement || defaultNS != null) &&
906         "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
907             prefix = null;
908         }
909         if (prefix == null) {
910             prefix = (String JavaDoc) prefixTable.get(uri);
911             if (prefix != null &&
912             ((!isElement || defaultNS != null) &&
913             "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
914                 prefix = null;
915             }
916         }
917         if (prefix == null && qName != null && !"".equals(qName)) {
918             int i = qName.indexOf(':');
919             if (i == -1) {
920                 if (isElement && defaultNS == null) {
921                     prefix = "";
922                 }
923             } else {
924                 prefix = qName.substring(0, i);
925             }
926         }
927         for (;
928         prefix == null || nsSupport.getURI(prefix) != null;
929         prefix = "__NS" + ++prefixCounter)
930         ;
931         nsSupport.declarePrefix(prefix, uri);
932         doneDeclTable.put(uri, prefix);
933         return prefix;
934     }
935
936
937     /**
938      * Write a raw character.
939      *
940      * @param c The character to write.
941      * @exception org.xml.sax.SAXException If there is an error writing
942      * the character, this method will throw an IOException
943      * wrapped in a SAXException.
944      */

945     private void write (char c)
946     throws SAXException JavaDoc
947     {
948         try {
949             output.write(c);
950         } catch (IOException JavaDoc e) {
951             throw new SAXException JavaDoc(e);
952         }
953     }
954
955
956     /**
957      * Write a portion of an array of characters.
958      *
959      * @param cbuf Array of characters.
960      * @param off Offset from which to start writing characters.
961      * @param len Number of characters to write.
962      * @exception org.xml.sax.SAXException If there is an error writing
963      * the character, this method will throw an IOException
964      * wrapped in a SAXException.
965      */

966     private void write (char[] cbuf, int off, int len)
967     throws SAXException JavaDoc
968     {
969         try {
970             output.write(cbuf, off, len);
971         } catch (IOException JavaDoc e) {
972             throw new SAXException JavaDoc(e);
973         }
974     }
975
976
977     /**
978      * Write a raw string.
979      *
980      * @param s
981      * @exception org.xml.sax.SAXException If there is an error writing
982      * the string, this method will throw an IOException
983      * wrapped in a SAXException
984      */

985     private void write (String JavaDoc s)
986     throws SAXException JavaDoc
987     {
988         try {
989             output.write(s);
990         } catch (IOException JavaDoc e) {
991             throw new SAXException JavaDoc(e);
992         }
993     }
994
995
996     /**
997      * Write out an attribute list, escaping values.
998      *
999      * The names will have prefixes added to them.
1000     *
1001     * @param atts The attribute list to write.
1002     * @exception org.xml.SAXException If there is an error writing
1003     * the attribute list, this method will throw an
1004     * IOException wrapped in a SAXException.
1005     */

1006    private void writeAttributes (Attributes JavaDoc atts)
1007    throws SAXException JavaDoc
1008    {
1009        int len = atts.getLength();
1010        for (int i = 0; i < len; i++) {
1011            char ch[] = atts.getValue(i).toCharArray();
1012            write(' ');
1013            writeName(atts.getURI(i), atts.getLocalName(i),
1014            atts.getQName(i), false);
1015            write("=\"");
1016            writeEsc(ch, 0, ch.length, true);
1017            write('"');
1018        }
1019    }
1020
1021
1022    /**
1023     * Write an array of data characters with escaping.
1024     *
1025     * @param ch The array of characters.
1026     * @param start The starting position.
1027     * @param length The number of characters to use.
1028     * @param isAttVal true if this is an attribute value literal.
1029     * @exception org.xml.SAXException If there is an error writing
1030     * the characters, this method will throw an
1031     * IOException wrapped in a SAXException.
1032     */

1033    private void writeEsc (char ch[], int start,
1034                           int length, boolean isAttVal)
1035    throws SAXException JavaDoc
1036    {
1037        for (int i = start; i < start + length; i++) {
1038            switch (ch[i]) {
1039                case '&':
1040                write("&amp;");
1041                break;
1042                case '<':
1043                write("&lt;");
1044                break;
1045                case '>':
1046                write("&gt;");
1047                break;
1048                case '\"':
1049                if (isAttVal) {
1050                    write("&quot;");
1051                } else {
1052                    write('\"');
1053                }
1054                break;
1055                default:
1056                if (ch[i] > '\u007f') {
1057                    write("&#");
1058                    write(Integer.toString(ch[i]));
1059                    write(';');
1060                } else {
1061                    write(ch[i]);
1062                }
1063            }
1064        }
1065    }
1066
1067
1068    /**
1069     * Write out the list of Namespace declarations.
1070     *
1071     * @exception org.xml.sax.SAXException This method will throw
1072     * an IOException wrapped in a SAXException if
1073     * there is an error writing the Namespace
1074     * declarations.
1075     */

1076    private void writeNSDecls ()
1077    throws SAXException JavaDoc
1078    {
1079        Enumeration JavaDoc prefixes = nsSupport.getDeclaredPrefixes();
1080        while (prefixes.hasMoreElements()) {
1081            String JavaDoc prefix = (String JavaDoc) prefixes.nextElement();
1082            String JavaDoc uri = nsSupport.getURI(prefix);
1083            if (uri == null) {
1084                uri = "";
1085            }
1086            char ch[] = uri.toCharArray();
1087            write(' ');
1088            if ("".equals(prefix)) {
1089                write("xmlns=\"");
1090            } else {
1091                write("xmlns:");
1092                write(prefix);
1093                write("=\"");
1094            }
1095            writeEsc(ch, 0, ch.length, true);
1096            write('\"');
1097        }
1098    }
1099
1100
1101    /**
1102     * Write an element or attribute name.
1103     *
1104     * @param uri The Namespace URI.
1105     * @param localName The local name.
1106     * @param qName The prefixed name, if available, or the empty string.
1107     * @param isElement true if this is an element name, false if it
1108     * is an attribute name.
1109     * @exception org.xml.sax.SAXException This method will throw an
1110     * IOException wrapped in a SAXException if there is
1111     * an error writing the name.
1112     */

1113    private void writeName (String JavaDoc uri, String JavaDoc localName,
1114                            String JavaDoc qName, boolean isElement)
1115    throws SAXException JavaDoc
1116    {
1117        String JavaDoc prefix = doPrefix(uri, qName, isElement);
1118        if (prefix != null && !"".equals(prefix)) {
1119            write(prefix);
1120            write(':');
1121        }
1122        write(localName);
1123    }
1124
1125
1126    /**
1127     * If start element tag is still open, write closing bracket.
1128     */

1129    private void closeElement()
1130    throws SAXException JavaDoc
1131    {
1132        if (openElement) {
1133            write('>');
1134            openElement = false;
1135        }
1136    }
1137
1138
1139
1140    ////////////////////////////////////////////////////////////////////
1141
// Internal state.
1142
////////////////////////////////////////////////////////////////////
1143

1144    private Map JavaDoc prefixTable;
1145    private Map JavaDoc forcedDeclTable;
1146    private Map JavaDoc doneDeclTable;
1147    private boolean openElement = false;
1148    private int elementLevel = 0;
1149    private Writer output;
1150    private NamespaceSupport JavaDoc nsSupport;
1151    private int prefixCounter = 0;
1152    private boolean inDTD = false;
1153
1154}
1155
1156// end of XMLWriter.java
1157
Popular Tags