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