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