KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > tests > 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
6 package nu.xom.tests;
7
8 import java.io.IOException JavaDoc;
9 import java.io.OutputStreamWriter JavaDoc;
10 import java.io.Writer JavaDoc;
11 import java.util.Enumeration JavaDoc;
12 import java.util.Hashtable JavaDoc;
13
14 import org.xml.sax.Attributes JavaDoc;
15 import org.xml.sax.SAXException JavaDoc;
16 import org.xml.sax.ext.LexicalHandler JavaDoc;
17 import org.xml.sax.helpers.AttributesImpl JavaDoc;
18 import org.xml.sax.helpers.NamespaceSupport JavaDoc;
19 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
20
21
22 /**
23  * <p>
24  * The original version of this class was written and placed in the
25  * public domain by David Megginson. Elliotte Rusty Harold added
26  * <code>LexicalHandler</code> support. It is included here purely
27  * for help with testing the <code>SAXConverter</code> class. It is
28  * not part of the XOM API; nor is it used internally by XOM anywhere
29  * except in the <code>SAXConverter</code> tests.
30  * </p>
31  *
32  * <p>
33  * This class does not properly preserve additional namespace
34  * declarations in non-root elements. If you encounter that, the
35  * bug is here, not in <code>SAXConverter</code>.
36  * </p>
37  *
38  * @author David Megginson, Elliotte Rusty Harold
39  * @version 1.0
40  */

41 class XMLWriter extends XMLFilterImpl JavaDoc implements LexicalHandler JavaDoc {
42
43     ///////////////////////////////////////////////////////////////////
44
// Constructors.
45
///////////////////////////////////////////////////////////////////
46

47
48     /**
49      * <p>
50      * Create a new XML writer.
51      * </p>
52      *
53      * <p>Write to standard output.</p>
54      */

55     public XMLWriter() {
56         init(null);
57     }
58     
59
60     /**
61      * <p>
62      * Create a new XML writer.
63      * </p>
64      *
65      * <p>Write to the writer provided.</p>
66      *
67      * @param writer the output destination, or null to use standard
68      * output
69      */

70     public XMLWriter (Writer JavaDoc writer) {
71        init(writer);
72     }
73
74     /**
75      * <p>
76      * Internal initialization method.
77      * </p>
78      *
79      * <p>All of the public constructors invoke this method.
80      *
81      * @param writer the output destination, or null to use
82      * standard output.
83      */

84     private void init (Writer JavaDoc writer) {
85         setOutput(writer);
86         nsSupport = new NamespaceSupport JavaDoc();
87         prefixTable = new Hashtable JavaDoc();
88         forcedDeclTable = new Hashtable JavaDoc();
89         doneDeclTable = new Hashtable JavaDoc();
90     }
91
92
93     ///////////////////////////////////////////////////////////////////
94
// Public methods.
95
///////////////////////////////////////////////////////////////////
96

97
98     /**
99      * <p>
100      * Reset the writer.
101      * </p>
102      *
103      * <p>This method is especially useful if the writer throws an
104      * exception before it is finished, and you want to reuse the
105      * writer for a new document. It is usually a good idea to
106      * invoke {@link #flush flush} before resetting the writer,
107      * to make sure that no output is lost.</p>
108      *
109      * <p>This method is invoked automatically by the
110      * {@link #startDocument startDocument} method before writing
111      * a new document.</p>
112      *
113      * <p><strong>Note:</strong> this method will <em>not</em>
114      * clear the prefix or URI information in the writer or
115      * the selected output writer.</p>
116      *
117      * @see #flush
118      */

119     public void reset() {
120         elementLevel = 0;
121         prefixCounter = 0;
122         nsSupport.reset();
123     }
124     
125
126     /**
127      * <p>
128      * Flush the output.
129      * </p>
130      *
131      * <p>This method flushes the output stream. It is especially useful
132      * when you need to make certain that the entire document has
133      * been written to output but do not want to close the output
134      * stream.</p>
135      *
136      * <p>This method is invoked automatically by the
137      * {@link #endDocument endDocument} method after writing a
138      * document.</p>
139      *
140      * @throws IOException
141      *
142      * @see #reset
143      *
144      */

145     public void flush() throws IOException JavaDoc {
146        output.flush();
147     }
148     
149
150     /**
151      * <p>
152      * Set a new output destination for the document.
153      * </p>
154      *
155      * @param writer the output destination, or null to use
156      * standard output
157      *
158      * @return the current output writer
159      *
160      * @see #flush
161      */

162     public void setOutput (Writer JavaDoc writer) {
163         if (writer == null) {
164             output = new OutputStreamWriter JavaDoc(System.out);
165         }
166         else {
167             output = writer;
168         }
169     }
170
171
172     /**
173      * <p>
174      * Specify a preferred prefix for a namespace URI.
175      * </p>
176      *
177      * <p>Note that this method does not actually force the namespace
178      * to be declared; to do that, use the {@link
179      * #forceNSDecl(java.lang.String) forceNSDecl} method as well.</p>
180      *
181      * @param uri the namespace URI
182      * @param prefix the preferred prefix, or "" to select
183      * the default namespace
184      *
185      * @see #getPrefix
186      * @see #forceNSDecl(java.lang.String)
187      * @see #forceNSDecl(java.lang.String,java.lang.String)
188      */

189     public void setPrefix (String JavaDoc uri, String JavaDoc prefix) {
190        prefixTable.put(uri, prefix);
191     }
192     
193
194     /**
195      * <p>
196      * Get the current or preferred prefix for a namespace URI.
197      * </p>
198      *
199      * @param uri the namespace URI
200      *
201      * @return the preferred prefix, or "" for the default namespace
202      *
203      * @see #setPrefix
204      */

205     public String JavaDoc getPrefix (String JavaDoc uri) {
206        return (String JavaDoc)prefixTable.get(uri);
207     }
208     
209     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) {
210         this.forceNSDecl(uri, prefix);
211     }
212
213     /**
214      * <p>
215      * Force a namespace to be declared on the root element.
216      * </p>
217      *
218      * <p>By default, the XMLWriter will declare only the namespaces
219      * needed for an element; as a result, a namespace may be
220      * declared many places in a document if it is not used on the
221      * root element.</p>
222      *
223      * <p>This method forces a namespace to be declared on the root
224      * element even if it is not used there, and reduces the number
225      * of xmlns attributes in the document.</p>
226      *
227      * @param uri the namespace URI to declare
228      *
229      * @see #forceNSDecl(java.lang.String,java.lang.String)
230      * @see #setPrefix
231      */

232     public void forceNSDecl (String JavaDoc uri) {
233        forcedDeclTable.put(uri, Boolean.TRUE);
234     }
235     
236
237     /**
238      * <p>
239      * Force a namespace declaration with a preferred prefix.
240      * </p>
241      *
242      * <p>This is a convenience method that invokes {@link
243      * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
244      * forceNSDecl}.</p>
245      *
246      * @param uri the namespace URI to declare on the root element
247      * @param prefix the preferred prefix for the namespace, or ""
248      * for the default namespace
249      *
250      * @see #setPrefix
251      * @see #forceNSDecl(java.lang.String)
252      */

253     public void forceNSDecl (String JavaDoc uri, String JavaDoc prefix) {
254         setPrefix(uri, prefix);
255         forceNSDecl(uri);
256     }
257     
258
259     ///////////////////////////////////////////////////////////////////
260
// Methods from org.xml.sax.ContentHandler.
261
///////////////////////////////////////////////////////////////////
262

263
264     /**
265      * <p>
266      * Write the XML declaration at the beginning of the document.
267      * </p>
268      *
269      * <p>
270      * Pass the event on down the filter chain for further processing.
271      * </p>
272      *
273      * @throws org.xml.sax.SAXException if there is an error
274      * writing the XML declaration, or if a handler further
275      * down the filter chain raises an exception
276      *
277      * @see org.xml.sax.ContentHandler#startDocument
278      */

279     public void startDocument() throws SAXException JavaDoc {
280         reset();
281         write("<?xml version=\"1.0\" standalone=\"yes\"?>\n\n");
282         super.startDocument();
283     }
284
285
286     /**
287      * <p>
288      * Write a newline at the end of the document.
289      * </p>
290      *
291      * <p>
292      * Pass the event on down the filter chain for further processing.
293      * </p>
294      *
295      * @throws org.xml.sax.SAXException if there is an error
296      * writing the newline, or if a handler further down
297      * the filter chain raises an exception
298      *
299      * @see org.xml.sax.ContentHandler#endDocument
300      */

301     public void endDocument() throws SAXException JavaDoc {
302         write('\n');
303         super.endDocument();
304         try {
305             flush();
306         } catch (IOException JavaDoc ex) {
307             throw new SAXException JavaDoc(ex);
308         }
309     }
310     
311
312     /**
313      * <p>
314      * Write a start-tag.
315      * </p>
316      *
317      * <p>
318      * Pass the event on down the filter chain for further processing.
319      * </p>
320      *
321      * @param uri the namespace URI, or the empty string if none
322      * is available
323      * @param localName the element's local (unprefixed) name (required)
324      * @param qualifiedName the element's qualified (prefixed) name,
325      * or the empty string is none is available. This method
326      * will use the qualified name as a template for generating
327      * a prefix if necessary, but it is not guaranteed to use
328      * the same qualified name.
329      * @param atts the element's attribute list (must not be null)
330      *
331      * @throws org.xml.sax.SAXException if there is an error
332      * writing the start-tag, or if a handler further down
333      * the filter chain raises an exception
334      *
335      * @see org.xml.sax.ContentHandler#startElement
336      */

337     public void startElement (String JavaDoc uri, String JavaDoc localName,
338       String JavaDoc qualifiedName, Attributes JavaDoc atts) throws SAXException JavaDoc {
339         elementLevel++;
340         nsSupport.pushContext();
341         write('<');
342         writeName(uri, localName, qualifiedName, true);
343         writeAttributes(atts);
344         if (elementLevel == 1) {
345             forceNSDecls();
346         }
347         writeNSDecls();
348         write('>');
349         super.startElement(uri, localName, qualifiedName, atts);
350     }
351
352
353     /**
354      * <p>
355      * Write an end-tag.
356      * </p>
357      *
358      * <p>
359      * Pass the event on down the filter chain for further processing.
360      * </p>
361      *
362      * @param uri the namespace URI, or the empty string if none
363      * is available
364      * @param localName the element's local (unprefixed) name (required)
365      * @param qualifiedName the element's qualified (prefixed) name, or the
366      * empty string is none is available. This method will
367      * use the qName as a template for generating a prefix
368      * if necessary, but it is not guaranteed to use the
369      * same qualified name.
370      *
371      * @throws org.xml.sax.SAXException if there is an error
372      * writing the end-tag, or if a handler further down
373      * the filter chain raises an exception
374      *
375      * @see org.xml.sax.ContentHandler#endElement
376      */

377     public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qualifiedName)
378       throws SAXException JavaDoc {
379         write("</");
380         writeName(uri, localName, qualifiedName, true);
381         write('>');
382         if (elementLevel == 1) {
383             write('\n');
384         }
385         super.endElement(uri, localName, qualifiedName);
386         nsSupport.popContext();
387         elementLevel--;
388     }
389     
390
391     /**
392      * <p>
393      * Write character data.
394      * </p>
395      *
396      * <p>
397      * Pass the event on down the filter chain for further processing.
398      * </p>
399      *
400      * @param ch the array of characters to write
401      * @param start the starting position in the array
402      * @param length the number of characters to write
403      *
404      * @throws org.xml.sax.SAXException if there is an error
405      * writing the characters, or if a handler further down
406      * the filter chain raises an exception
407      *
408      * @see org.xml.sax.ContentHandler#characters
409      */

410     public void characters (char[] ch, int start, int length)
411       throws SAXException JavaDoc {
412         writeEsc(ch, start, length, false);
413         super.characters(ch, start, length);
414     }
415     
416
417     /**
418      * <p>
419      * Write ignorable whitespace.
420      * </p>
421      *
422      * <p>
423      * Pass the event on down the filter chain for further processing.
424      * </p>
425      *
426      * @param ch the array of characters to write
427      * @param start the starting position in the array
428      * @param length the number of characters to write
429      *
430      * @throws org.xml.sax.SAXException if there is an error
431      * writing the whitespace, or if a handler further down
432      * the filter chain raises an exception
433      *
434      * @see org.xml.sax.ContentHandler#ignorableWhitespace
435      */

436     public void ignorableWhitespace (char[] ch, int start, int length)
437       throws SAXException JavaDoc {
438         writeEsc(ch, start, length, false);
439         super.ignorableWhitespace(ch, start, length);
440     }
441     
442
443
444     /**
445      * <p>
446      * Write a processing instruction.
447      * </p>
448      *
449      * <p>
450      * Pass the event on down the filter chain for further processing.
451      * </p>
452      *
453      * @param target the processing instruction target
454      * @param data the processing instruction data
455      *
456      * @throws org.xml.sax.SAXException if there is an error
457      * writing the PI, or if a handler further down
458      * the filter chain raises an exception
459      *
460      * @see org.xml.sax.ContentHandler#processingInstruction
461      */

462     public void processingInstruction (String JavaDoc target, String JavaDoc data)
463       throws SAXException JavaDoc {
464         write("<?");
465         write(target);
466         write(' ');
467         write(data);
468         write("?>");
469         if (elementLevel < 1) {
470             write('\n');
471         }
472         super.processingInstruction(target, data);
473     }
474     
475
476
477     ///////////////////////////////////////////////////////////////////
478
// Additional markup.
479
///////////////////////////////////////////////////////////////////
480

481     /**
482      * <p>
483      * Write an empty element.
484      * </p>
485      *
486      * <p>
487      * This method writes an empty-element tag rather than a start-tag
488      * followed by an end-tag. Both a {@link #startElement
489      * startElement} and an {@link #endElement endElement} event will
490      * be passed on down the filter chain.
491      * </p>
492      *
493      * @param uri the element's namespace URI, or the empty string
494      * if the element has no namespace or if namespace
495      * processing is not being performed
496      * @param localName the element's local name (without prefix). This
497      * parameter must be provided.
498      * @param qualifiedName the element's qualified name (with prefix), or
499      * the empty string if none is available. This parameter
500      * is strictly advisory: the writer may or may not use
501      * the prefix attached.
502      * @param atts the element's attribute list
503      *
504      * @throws org.xml.sax.SAXException if there is an error
505      * writing the empty tag, or if a handler further down
506      * the filter chain raises an exception
507      *
508      * @see #startElement
509      * @see #endElement
510      */

511     public void emptyElement (String JavaDoc uri, String JavaDoc localName,
512       String JavaDoc qualifiedName, Attributes JavaDoc atts) throws SAXException JavaDoc {
513         nsSupport.pushContext();
514         write('<');
515         writeName(uri, localName, qualifiedName, true);
516         writeAttributes(atts);
517         if (elementLevel == 1) {
518             forceNSDecls();
519         }
520         writeNSDecls();
521         write("/>");
522         super.startElement(uri, localName, qualifiedName, atts);
523         super.endElement(uri, localName, qualifiedName);
524     }
525
526
527
528     ///////////////////////////////////////////////////////////////////
529
// Convenience methods.
530
///////////////////////////////////////////////////////////////////
531

532
533
534     /**
535      * <p>
536      * Start a new element without a qualified name or attributes.
537      * </p>
538      *
539      * <p>This method will provide a default empty attribute
540      * list and an empty string for the qualified name.
541      * It invokes {@link
542      * #startElement(String, String, String, Attributes)}
543      * directly.</p>
544      *
545      * @param uri the element's namespace URI
546      * @param localName the element's local name
547      *
548      * @throws org.xml.sax.SAXException if there is an error
549      * writing the start-tag, or if a handler further down
550      * the filter chain raises an exception
551      *
552      * @see #startElement(String, String, String, Attributes)
553      */

554     public void startElement (String JavaDoc uri, String JavaDoc localName)
555       throws SAXException JavaDoc {
556         startElement(uri, localName, "", EMPTY_ATTS);
557     }
558
559
560     /**
561      * <p>
562      * Start a new element without a qualified name,
563      * attributes or a namespace URI.</p>
564      *
565      * <p>This method will provide an empty string for the
566      * namespace URI, and empty string for the qualified name,
567      * and a default empty attribute list. It invokes
568      * #startElement(String, String, String, Attributes)}
569      * directly.</p>
570      *
571      * @param localName the element's local name
572      *
573      * @throws org.xml.sax.SAXException if there is an error
574      * writing the start-tag, or if a handler further down
575      * the filter chain raises an exception
576      *
577      * @see #startElement(String, String, String, Attributes)
578      */

579     public void startElement (String JavaDoc localName) throws SAXException JavaDoc {
580        startElement("", localName, "", EMPTY_ATTS);
581     }
582
583
584     /**
585      * <p>
586      * End an element without a qualfied name.
587      * </p>
588      *
589      * <p>This method will supply an empty string for the qName.
590      * It invokes {@link #endElement(String, String, String)}
591      * directly.</p>
592      *
593      * @param uri the element's namespace URI
594      * @param localName the element's local name
595      *
596      * @throws org.xml.sax.SAXException if there is an error
597      * writing the end-tag, or if a handler further down
598      * the filter chain raises an exception
599      *
600      * @see #endElement(String, String, String)
601      */

602     public void endElement(String JavaDoc uri, String JavaDoc localName)
603       throws SAXException JavaDoc {
604         endElement(uri, localName, "");
605     }
606
607
608     /**
609      * <p>
610      * End an element without a namespace URI or qualfiied name.
611      * </p>
612      *
613      * <p>This method will supply an empty string for the qName
614      * and an empty string for the namespace URI.
615      * It invokes {@link #endElement(String, String, String)}
616      * directly.</p>
617      *
618      * @param localName the element's local name`
619      *
620      * @throws org.xml.sax.SAXException if there is an error
621      * writing the end-tag, or if a handler further down
622      * the filter chain raises an exception
623      *
624      * @see #endElement(String, String, String)
625      */

626     public void endElement(String JavaDoc localName) throws SAXException JavaDoc {
627         endElement("", localName, "");
628     }
629
630
631     /**
632      * <p>
633      * Add an empty element without a qualified name or attributes.
634      * </p>
635      *
636      * <p>This method will supply an empty string for the qualified name
637      * and an empty attribute list. It invokes
638      * {@link #emptyElement(String, String, String, Attributes)}
639      * directly.</p>
640      *
641      * @param uri the element's namespace URI
642      * @param localName the element's local name
643      *
644      * @throws org.xml.sax.SAXException if there is an error
645      * writing the empty tag, or if a handler further down
646      * the filter chain raises an exception
647      *
648      * @see #emptyElement(String, String, String, Attributes)
649      */

650     public void emptyElement (String JavaDoc uri, String JavaDoc localName)
651       throws SAXException JavaDoc {
652         emptyElement(uri, localName, "", EMPTY_ATTS);
653     }
654
655
656     /**
657      * <p>
658      * Add an empty element without a namespace URI, qualified
659      * name or attributes.
660      * </p>
661      *
662      * <p>This method will supply an empty string for the qualified
663      * name, and empty string for the namespace URI, and an empty
664      * attribute list. It invokes
665      * {@link #emptyElement(String, String, String, Attributes)}
666      * directly.</p>
667      *
668      * @param localName the element's local name
669      *
670      * @throws org.xml.sax.SAXException if there is an error
671      * writing the empty tag, or if a handler further down
672      * the filter chain raises an exception
673      *
674      * @see #emptyElement(String, String, String, Attributes)
675      */

676     public void emptyElement (String JavaDoc localName) throws SAXException JavaDoc {
677        emptyElement("", localName, "", EMPTY_ATTS);
678     }
679
680
681     /**
682      * <p>
683      * Write an element with character data content.
684      * </p>
685      *
686      * <p>This is a convenience method to write a complete element
687      * with character data content, including the start-tag
688      * and end-tag.</p>
689      *
690      * <p>This method invokes
691      * {@link #startElement(String, String, String, Attributes)},
692      * followed by
693      * {@link #characters(String)}, followed by
694      * {@link #endElement(String, String, String)}.</p>
695      *
696      * @param uri the element's namespace URI
697      * @param localName the element's local name
698      * @param qualifiedName the element's default qualified name
699      * @param atts the element's attributes
700      * @param content the character data content
701      *
702      * @throws org.xml.sax.SAXException if there is an error
703      * writing the empty tag, or if a handler further down
704      * the filter chain raises an exception
705      *
706      * @see #startElement(String, String, String, Attributes)
707      * @see #characters(String)
708      * @see #endElement(String, String, String)
709      */

710     public void dataElement (String JavaDoc uri, String JavaDoc localName,
711       String JavaDoc qualifiedName, Attributes JavaDoc atts, String JavaDoc content)
712       throws SAXException JavaDoc {
713         startElement(uri, localName, qualifiedName, atts);
714         characters(content);
715         endElement(uri, localName, qualifiedName);
716     }
717
718
719     /**
720      * <p>
721      * Write an element with character data content but no attributes.
722      * </p>
723      *
724      * <p>This is a convenience method to write a complete element
725      * with character data content, including the start-tag
726      * and end-tag. This method provides an empty string
727      * for the qualified name and an empty attribute list.</p>
728      *
729      * <p>This method invokes
730      * {@link #startElement(String, String, String, Attributes)},
731      * followed by
732      * {@link #characters(String)}, followed by
733      * {@link #endElement(String, String, String)}.</p>
734      *
735      * @param uri the element's namespace URI
736      * @param localName the element's local name
737      * @param content the character data content
738      *
739      * @throws org.xml.sax.SAXException if there is an error
740      * writing the empty tag, or if a handler further down
741      * the filter chain raises an exception
742      *
743      * @see #startElement(String, String, String, Attributes)
744      * @see #characters(String)
745      * @see #endElement(String, String, String)
746      */

747     public void dataElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc content)
748       throws SAXException JavaDoc {
749        dataElement(uri, localName, "", EMPTY_ATTS, content);
750     }
751
752
753     /**
754      * <p>
755      * Write an element with character data content but no attributes
756      * or namespace URI.
757      * </p>
758      *
759      * <p>This is a convenience method to write a complete element
760      * with character data content, including the start-tag
761      * and end-tag. The method provides an empty string for the
762      * namespace URI, and empty string for the qualified name,
763      * and an empty attribute list.</p>
764      *
765      * <p>This method invokes
766      * {@link #startElement(String, String, String, Attributes)},
767      * followed by
768      * {@link #characters(String)}, followed by
769      * {@link #endElement(String, String, String)}.</p>
770      *
771      * @param localName the element's local name
772      * @param content the character data content
773      *
774      * @throws org.xml.sax.SAXException if there is an error
775      * writing the empty tag, or if a handler further down
776      * the filter chain raises an exception
777      *
778      * @see #startElement(String, String, String, Attributes)
779      * @see #characters(String)
780      * @see #endElement(String, String, String)
781      */

782     public void dataElement (String JavaDoc localName, String JavaDoc content)
783       throws SAXException JavaDoc {
784         dataElement("", localName, "", EMPTY_ATTS, content);
785     }
786
787     /**
788      * <p>
789      * Write a string of character data, with XML escaping.
790      * </p>
791      *
792      * <p>This is a convenience method that takes an XML
793      * String, converts it to a character array, then invokes
794      * {@link #characters(char[], int, int)}.</p>
795      *
796      * @param data the character data
797      * @throws org.xml.sax.SAXException if there is an error
798      * writing the string, or if a handler further down
799      * the filter chain raises an exception
800      * @see #characters(char[], int, int)
801      */

802     public void characters(String JavaDoc data) throws SAXException JavaDoc {
803         char[] ch = data.toCharArray();
804         characters(ch, 0, ch.length);
805     }
806
807
808
809     ///////////////////////////////////////////////////////////////////
810
// Internal methods.
811
///////////////////////////////////////////////////////////////////
812

813
814     /**
815      * <p>
816      * Force all namespaces to be declared.
817      * </p>
818      *
819      * <p>
820      * This method is used on the root element to ensure that
821      * the predeclared namespaces all appear.
822      * </p>
823      */

824     private void forceNSDecls() {
825         Enumeration JavaDoc prefixes = forcedDeclTable.keys();
826         while (prefixes.hasMoreElements()) {
827             String JavaDoc prefix = (String JavaDoc)prefixes.nextElement();
828             doPrefix(prefix, null, true);
829         }
830     }
831
832
833     /**
834      * <p>
835      * Determine the prefix for an element or attribute name.
836      * </p>
837      *
838      * TODO: this method probably needs some cleanup.
839      *
840      * @param uri the namespace URI
841      * @param qName the qualified name (optional); this will be used
842      * to indicate the preferred prefix if none is currently
843      * bound.
844      * @param isElement true if this is an element name, false
845      * if it is an attribute name (which cannot use the
846      * default namespace).
847      */

848     private String JavaDoc doPrefix (String JavaDoc uri, String JavaDoc qName, boolean isElement) {
849         String JavaDoc defaultNS = nsSupport.getURI("");
850         if ("".equals(uri)) {
851             if (isElement && defaultNS != null)
852             nsSupport.declarePrefix("", "");
853             return null;
854         }
855         String JavaDoc prefix;
856         if (isElement && defaultNS != null && uri.equals(defaultNS)) {
857             prefix = "";
858         } else {
859             prefix = nsSupport.getPrefix(uri);
860         }
861         if (prefix != null) {
862             return prefix;
863         }
864         prefix = (String JavaDoc) doneDeclTable.get(uri);
865         if (prefix != null &&
866             ((!isElement || defaultNS != null) &&
867              "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
868             prefix = null;
869         }
870         if (prefix == null) {
871             prefix = (String JavaDoc) prefixTable.get(uri);
872             if (prefix != null &&
873             ((!isElement || defaultNS != null) &&
874              "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
875             prefix = null;
876             }
877         }
878         if (prefix == null && qName != null && !"".equals(qName)) {
879             int i = qName.indexOf(':');
880             if (i == -1) {
881             if (isElement && defaultNS == null) {
882                 prefix = "";
883             }
884             } else {
885             prefix = qName.substring(0, i);
886             }
887         }
888         for (;
889              prefix == null || nsSupport.getURI(prefix) != null;
890              prefix = "__NS" + ++prefixCounter)
891             ;
892         nsSupport.declarePrefix(prefix, uri);
893         doneDeclTable.put(uri, prefix);
894         return prefix;
895     }
896     
897
898     /**
899      * <p>
900      * Write a raw character.
901      * </p>
902      *
903      * @param c the character to write
904      *
905      * @throws org.xml.sax.SAXException if there is an error writing
906      * the character, this method will throw an IOException
907      * wrapped in a SAXException
908      */

909     private void write(char c) throws SAXException JavaDoc {
910         try {
911             output.write(c);
912         }
913         catch (IOException JavaDoc ex) {
914             throw new SAXException JavaDoc(ex);
915         }
916     }
917     
918
919     /**
920      * <p>
921      * Write a raw string.
922      * </p>
923      *
924      * @param s
925      *
926      * @throws org.xml.sax.SAXException if there is an error writing
927      * the string, this method will throw an IOException
928      * wrapped in a SAXException
929      */

930     private void write (String JavaDoc s) throws SAXException JavaDoc {
931         try {
932             output.write(s);
933         }
934         catch (IOException JavaDoc e) {
935             throw new SAXException JavaDoc(e);
936         }
937     }
938
939
940     /**
941      * <p>
942      * Write out an attribute list, escaping values.
943      *</p>
944      *
945      * <p>
946      * The names will have prefixes added to them.
947      * </p>
948      *
949      * @param atts the attribute list to write
950      *
951      * @throws org.xml.SAXException if there is an error writing
952      * the attribute list, this method will throw an
953      * IOException wrapped in a SAXException
954      */

955     private void writeAttributes (Attributes JavaDoc atts)
956       throws SAXException JavaDoc {
957         int len = atts.getLength();
958         for (int i = 0; i < len; i++) {
959             char[] ch = atts.getValue(i).toCharArray();
960             write(' ');
961             writeName(atts.getURI(i), atts.getLocalName(i),
962                   atts.getQName(i), false);
963             write("=\"");
964             writeEsc(ch, 0, ch.length, true);
965             write('"');
966         }
967     }
968
969
970     /**
971      * <p>
972      * Write an array of data characters with escaping.
973      * </p>
974      *
975      * @param ch the array of characters
976      * @param start the starting position
977      * @param length the number of characters to use
978      * @param isAttVal true if this is an attribute value literal
979      *
980      * @throws org.xml.SAXException if there is an error writing
981      * the characters, this method will throw an
982      * IOException wrapped in a SAXException
983      */

984     private void writeEsc (char[] ch, int start, int length, boolean isAttVal)
985       throws SAXException JavaDoc {
986         for (int i = start; i < start + length; i++) {
987             switch (ch[i]) {
988             case '&':
989                 write("&amp;");
990                 break;
991             case '<':
992                 write("&lt;");
993                 break;
994             case '>':
995                 write("&gt;");
996                 break;
997             case '\"':
998                 if (isAttVal) {
999                     write("&quot;");
1000                }
1001                else {
1002                    write('\"');
1003                }
1004                break;
1005            default:
1006                if (ch[i] > '\u007f') {
1007                    write("&#");
1008                    write(Integer.toString(ch[i]));
1009                    write(';');
1010                }
1011                else {
1012                    write(ch[i]);
1013                }
1014            }
1015       }
1016    }
1017
1018    /**
1019     * <p>
1020     * Write an array of data characters without escaping.
1021     * </p>
1022     *
1023     * @param ch the array of characters
1024     * @param start the starting position
1025     * @param length the number of characters to use
1026     *
1027     * @throws org.xml.SAXException if there is an error writing
1028     * the characters, this method will throw an
1029     * IOException wrapped in a SAXException.
1030     */

1031    private void write(char[] ch, int start, int length)
1032      throws SAXException JavaDoc {
1033          
1034        try {
1035            output.write(ch, start, length);
1036        }
1037        catch (IOException JavaDoc e) {
1038            throw new SAXException JavaDoc(e);
1039        }
1040        
1041    }
1042
1043
1044    /**
1045     * <p>
1046     * Write out the list of namespace declarations.
1047     * </p>
1048     *
1049     * @throws org.xml.sax.SAXException This method will throw
1050     * an IOException wrapped in a SAXException if
1051     * there is an error writing the namespace
1052     * declarations
1053     */

1054    private void writeNSDecls() throws SAXException JavaDoc {
1055        Enumeration JavaDoc prefixes = nsSupport.getDeclaredPrefixes();
1056        while (prefixes.hasMoreElements()) {
1057            String JavaDoc prefix = (String JavaDoc) prefixes.nextElement();
1058            String JavaDoc uri = nsSupport.getURI(prefix);
1059            if (uri == null) {
1060              uri = "";
1061            }
1062            char[] ch = uri.toCharArray();
1063            write(' ');
1064            if ("".equals(prefix)) {
1065              write("xmlns=\"");
1066            }
1067            else {
1068                write("xmlns:");
1069                write(prefix);
1070                write("=\"");
1071            }
1072            writeEsc(ch, 0, ch.length, true);
1073            write('\"');
1074        }
1075    }
1076    
1077
1078    /**
1079     * <p>
1080     * Write an element or attribute name.
1081     * </p>
1082     *
1083     * @param uri the namespace URI
1084     * @param localName the local name
1085     * @param qualifiedName the prefixed name, if available,
1086     * or the empty string.
1087     * @param isElement true if this is an element name, false if it
1088     * is an attribute name
1089     *
1090     * @throws org.xml.sax.SAXException this method will throw an
1091     * IOException wrapped in a SAXException if there is
1092     * an error writing the name
1093     */

1094    private void writeName (String JavaDoc uri, String JavaDoc localName,
1095      String JavaDoc qualifiedName, boolean isElement)
1096      throws SAXException JavaDoc {
1097        String JavaDoc prefix = doPrefix(uri, qualifiedName, isElement);
1098        if (prefix != null && !"".equals(prefix)) {
1099            write(prefix);
1100            write(':');
1101        }
1102        write(localName);
1103    }
1104
1105
1106
1107    ////////////////////////////////////////////////////////////////////
1108
// Constants.
1109
////////////////////////////////////////////////////////////////////
1110

1111    private final Attributes JavaDoc EMPTY_ATTS = new AttributesImpl JavaDoc();
1112
1113
1114    ////////////////////////////////////////////////////////////////////
1115
// Internal state.
1116
////////////////////////////////////////////////////////////////////
1117

1118    private Hashtable JavaDoc prefixTable;
1119    private Hashtable JavaDoc forcedDeclTable;
1120    private Hashtable JavaDoc doneDeclTable;
1121    private int elementLevel = 0;
1122    private Writer JavaDoc output;
1123    private NamespaceSupport JavaDoc nsSupport;
1124    private int prefixCounter = 0;
1125
1126
1127    ///////////////////////////////////////////////////////////////////
1128
// LexicalHandler methods.
1129
///////////////////////////////////////////////////////////////////
1130

1131
1132    public void endCDATA() {}
1133    
1134    public void endDTD() throws SAXException JavaDoc {
1135        write(">");
1136    }
1137    
1138    public void startCDATA() {}
1139    
1140    public void comment(char[] ch, int start, int length)
1141      throws SAXException JavaDoc {
1142        write("<!--");
1143        write(ch, start, length);
1144        write("-->");
1145        if (elementLevel < 1) {
1146            write('\n');
1147        }
1148    }
1149
1150    public void endEntity(String JavaDoc name) {}
1151    public void startEntity(String JavaDoc name) {}
1152    
1153    public void startDTD(String JavaDoc name, String JavaDoc publicID, String JavaDoc systemID)
1154      throws SAXException JavaDoc {
1155        write("<!DOCTYPE ");
1156        write(name);
1157        if (systemID != null) {
1158            if (publicID != null) {
1159                write(" PUBLIC \"");
1160                write(publicID);
1161                write("\" \"");
1162                write(systemID);
1163                write("\"");
1164            }
1165            else {
1166                write(" SYSTEM \"");
1167                write(systemID);
1168                write("\"");
1169            }
1170        }
1171
1172    }
1173    
1174}
1175
1176// end of XMLWriter.java
1177

1178
Popular Tags