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