aoqi@0: /*
aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: // @@3RD PARTY CODE@@
aoqi@0:
aoqi@0: // XMLWriter.java - serialize an XML document.
aoqi@0: // Written by David Megginson, david@megginson.com
aoqi@0: // NO WARRANTY! This class is in the public domain.
aoqi@0:
aoqi@0: // Id: XMLWriter.java,v 1.5 2000/09/17 01:08:16 david Exp
aoqi@0:
aoqi@0: package com.sun.xml.internal.bind.marshaller;
aoqi@0:
aoqi@0: import java.io.IOException;
aoqi@0: import java.io.OutputStreamWriter;
aoqi@0: import java.io.Writer;
aoqi@0: import java.util.HashMap;
aoqi@0: import java.util.Map;
aoqi@0:
aoqi@0: import org.xml.sax.Attributes;
aoqi@0: import org.xml.sax.SAXException;
aoqi@0: import org.xml.sax.helpers.AttributesImpl;
aoqi@0: import org.xml.sax.helpers.XMLFilterImpl;
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Filter to write an XML document from a SAX event stream.
aoqi@0: *
aoqi@0: *
This class can be used by itself or as part of a SAX event
aoqi@0: * stream: it takes as input a series of SAX2 ContentHandler
aoqi@0: * events and uses the information in those events to write
aoqi@0: * an XML document. Since this class is a filter, it can also
aoqi@0: * pass the events on down a filter chain for further processing
aoqi@0: * (you can use the XMLWriter to take a snapshot of the current
aoqi@0: * state at any point in a filter chain), and it can be
aoqi@0: * used directly as a ContentHandler for a SAX2 XMLReader.
aoqi@0: *
aoqi@0: * The client creates a document by invoking the methods for
aoqi@0: * standard SAX2 events, always beginning with the
aoqi@0: * {@link #startDocument startDocument} method and ending with
aoqi@0: * the {@link #endDocument endDocument} method. There are convenience
aoqi@0: * methods provided so that clients to not have to create empty
aoqi@0: * attribute lists or provide empty strings as parameters; for
aoqi@0: * example, the method invocation
aoqi@0: *
aoqi@0: *
aoqi@0: * w.startElement("foo");
aoqi@0: *
aoqi@0: *
aoqi@0: * is equivalent to the regular SAX2 ContentHandler method
aoqi@0: *
aoqi@0: *
aoqi@0: * w.startElement("", "foo", "", new AttributesImpl());
aoqi@0: *
aoqi@0: *
aoqi@0: * Except that it is more efficient because it does not allocate
aoqi@0: * a new empty attribute list each time. The following code will send
aoqi@0: * a simple XML document to standard output:
aoqi@0: *
aoqi@0: *
aoqi@0: * XMLWriter w = new XMLWriter();
aoqi@0: *
aoqi@0: * w.startDocument();
aoqi@0: * w.startElement("greeting");
aoqi@0: * w.characters("Hello, world!");
aoqi@0: * w.endElement("greeting");
aoqi@0: * w.endDocument();
aoqi@0: *
aoqi@0: *
aoqi@0: * The resulting document will look like this:
aoqi@0: *
aoqi@0: *
aoqi@0: * <?xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <greeting>Hello, world!</greeting>
aoqi@0: *
aoqi@0: *
aoqi@0: * In fact, there is an even simpler convenience method,
aoqi@0: * dataElement, designed for writing elements that
aoqi@0: * contain only character data, so the code to generate the
aoqi@0: * document could be shortened to
aoqi@0: *
aoqi@0: *
aoqi@0: * XMLWriter w = new XMLWriter();
aoqi@0: *
aoqi@0: * w.startDocument();
aoqi@0: * w.dataElement("greeting", "Hello, world!");
aoqi@0: * w.endDocument();
aoqi@0: *
aoqi@0: *
aoqi@0: * Whitespace
aoqi@0: *
aoqi@0: * According to the XML Recommendation, all whitespace
aoqi@0: * in an XML document is potentially significant to an application,
aoqi@0: * so this class never adds newlines or indentation. If you
aoqi@0: * insert three elements in a row, as in
aoqi@0: *
aoqi@0: *
aoqi@0: * w.dataElement("item", "1");
aoqi@0: * w.dataElement("item", "2");
aoqi@0: * w.dataElement("item", "3");
aoqi@0: *
aoqi@0: *
aoqi@0: * you will end up with
aoqi@0: *
aoqi@0: *
aoqi@0: * <item>1</item><item>3</item><item>3</item>
aoqi@0: *
aoqi@0: *
aoqi@0: * You need to invoke one of the characters methods
aoqi@0: * explicitly to add newlines or indentation. Alternatively, you
aoqi@0: * can use {@link DataWriter}, which
aoqi@0: * is derived from this class -- it is optimized for writing
aoqi@0: * purely data-oriented (or field-oriented) XML, and does automatic
aoqi@0: * linebreaks and indentation (but does not support mixed content
aoqi@0: * properly).
aoqi@0: *
aoqi@0: *
aoqi@0: * Namespace Support
aoqi@0: *
aoqi@0: * The writer contains extensive support for XML Namespaces, so that
aoqi@0: * a client application does not have to keep track of prefixes and
aoqi@0: * supply xmlns attributes. By default, the XML writer will
aoqi@0: * generate Namespace declarations in the form _NS1, _NS2, etc., wherever
aoqi@0: * they are needed, as in the following example:
aoqi@0: *
aoqi@0: *
aoqi@0: * w.startDocument();
aoqi@0: * w.emptyElement("http://www.foo.com/ns/", "foo");
aoqi@0: * w.endDocument();
aoqi@0: *
aoqi@0: *
aoqi@0: * The resulting document will look like this:
aoqi@0: *
aoqi@0: *
aoqi@0: * <?xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
aoqi@0: *
aoqi@0: *
aoqi@0: * In many cases, document authors will prefer to choose their
aoqi@0: * own prefixes rather than using the (ugly) default names. The
aoqi@0: * XML writer allows two methods for selecting prefixes:
aoqi@0: *
aoqi@0: *
aoqi@0: * - the qualified name
aoqi@0: *
aoqi@0: *
aoqi@0: * Whenever the XML writer finds a new Namespace URI, it checks
aoqi@0: * to see if a qualified (prefixed) name is also available; if so
aoqi@0: * it attempts to use the name's prefix (as long as the prefix is
aoqi@0: * not already in use for another Namespace URI).
aoqi@0: *
aoqi@0: * The resulting document will look like this:
aoqi@0: *
aoqi@0: *
aoqi@0: * <?xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <foo:foo xmlns:foo="http://www.foo.com/ns/"/>
aoqi@0: *
aoqi@0: *
aoqi@0: * The default Namespace simply uses an empty string as the prefix:
aoqi@0: *
aoqi@0: *
aoqi@0: * w.setPrefix("http://www.foo.com/ns/", "");
aoqi@0: * w.startDocument();
aoqi@0: * w.emptyElement("http://www.foo.com/ns/", "foo");
aoqi@0: * w.endDocument();
aoqi@0: *
aoqi@0: *
aoqi@0: * The resulting document will look like this:
aoqi@0: *
aoqi@0: *
aoqi@0: * <?xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <foo xmlns="http://www.foo.com/ns/"/>
aoqi@0: *
aoqi@0: *
aoqi@0: * By default, the XML writer will not declare a Namespace until
aoqi@0: * it is actually used. Sometimes, this approach will create
aoqi@0: * a large number of Namespace declarations, as in the following
aoqi@0: * example:
aoqi@0: *
aoqi@0: *
aoqi@0: * <xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
aoqi@0: * <rdf:Description about="http://www.foo.com/ids/books/12345">
aoqi@0: * <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title>
aoqi@0: * <dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith</dc:title>
aoqi@0: * <dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09</dc:title>
aoqi@0: * </rdf:Description>
aoqi@0: * </rdf:RDF>
aoqi@0: *
aoqi@0: *
aoqi@0: * The "rdf" prefix is declared only once, because the RDF Namespace
aoqi@0: * is used by the root element and can be inherited by all of its
aoqi@0: * descendants; the "dc" prefix, on the other hand, is declared three
aoqi@0: * times, because no higher element uses the Namespace. To solve this
aoqi@0: * problem, you can instruct the XML writer to predeclare Namespaces
aoqi@0: * on the root element even if they are not used there:
aoqi@0: *
aoqi@0: *
aoqi@0: * w.forceNSDecl("http://www.purl.org/dc/");
aoqi@0: *
aoqi@0: *
aoqi@0: * Now, the "dc" prefix will be declared on the root element even
aoqi@0: * though it's not needed there, and can be inherited by its
aoqi@0: * descendants:
aoqi@0: *
aoqi@0: *
aoqi@0: * <xml version="1.0" standalone="yes"?>
aoqi@0: *
aoqi@0: * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
aoqi@0: * xmlns:dc="http://www.purl.org/dc/">
aoqi@0: * <rdf:Description about="http://www.foo.com/ids/books/12345">
aoqi@0: * <dc:title>A Dark Night</dc:title>
aoqi@0: * <dc:creator>Jane Smith</dc:title>
aoqi@0: * <dc:date>2000-09-09</dc:title>
aoqi@0: * </rdf:Description>
aoqi@0: * </rdf:RDF>
aoqi@0: *
aoqi@0: *
aoqi@0: * This approach is also useful for declaring Namespace prefixes
aoqi@0: * that be used by qualified names appearing in attribute values or
aoqi@0: * character data.
aoqi@0: *
aoqi@0: * @author David Megginson, david@megginson.com
aoqi@0: * @version 0.2
aoqi@0: * @since JAXB1.0
aoqi@0: * @see org.xml.sax.XMLFilter
aoqi@0: * @see org.xml.sax.ContentHandler
aoqi@0: */
aoqi@0: public class XMLWriter extends XMLFilterImpl
aoqi@0: {
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Constructors.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Create a new XML writer.
aoqi@0: *
aoqi@0: * Write to the writer provided.
aoqi@0: *
aoqi@0: * @param writer
aoqi@0: * The output destination, or null to use standard output.
aoqi@0: * @param encoding
aoqi@0: * If non-null string is specified, it is written as a part
aoqi@0: * of the XML declaration.
aoqi@0: */
aoqi@0: public XMLWriter (Writer writer, String encoding, CharacterEscapeHandler _escapeHandler )
aoqi@0: {
aoqi@0: init(writer,encoding);
aoqi@0: this.escapeHandler = _escapeHandler;
aoqi@0: }
aoqi@0:
aoqi@0: public XMLWriter (Writer writer, String encoding ) {
aoqi@0: this( writer, encoding, DumbEscapeHandler.theInstance );
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Internal initialization method.
aoqi@0: *
aoqi@0: * All of the public constructors invoke this method.
aoqi@0: *
aoqi@0: * @param writer The output destination, or null to use
aoqi@0: * standard output.
aoqi@0: */
aoqi@0: private void init (Writer writer,String encoding)
aoqi@0: {
aoqi@0: setOutput(writer,encoding);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Public methods.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Reset the writer.
aoqi@0: *
aoqi@0: *
This method is especially useful if the writer throws an
aoqi@0: * exception before it is finished, and you want to reuse the
aoqi@0: * writer for a new document. It is usually a good idea to
aoqi@0: * invoke {@link #flush flush} before resetting the writer,
aoqi@0: * to make sure that no output is lost.
aoqi@0: *
aoqi@0: * This method is invoked automatically by the
aoqi@0: * {@link #startDocument startDocument} method before writing
aoqi@0: * a new document.
aoqi@0: *
aoqi@0: * Note: this method will not
aoqi@0: * clear the prefix or URI information in the writer or
aoqi@0: * the selected output writer.
aoqi@0: *
aoqi@0: * @see #flush()
aoqi@0: */
aoqi@0: public void reset ()
aoqi@0: {
aoqi@0: elementLevel = 0;
aoqi@0: startTagIsClosed = true;
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Flush the output.
aoqi@0: *
aoqi@0: * This method flushes the output stream. It is especially useful
aoqi@0: * when you need to make certain that the entire document has
aoqi@0: * been written to output but do not want to close the output
aoqi@0: * stream.
aoqi@0: *
aoqi@0: * This method is invoked automatically by the
aoqi@0: * {@link #endDocument endDocument} method after writing a
aoqi@0: * document.
aoqi@0: *
aoqi@0: * @see #reset()
aoqi@0: */
aoqi@0: public void flush ()
aoqi@0: throws IOException
aoqi@0: {
aoqi@0: output.flush();
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Set a new output destination for the document.
aoqi@0: *
aoqi@0: * @param writer The output destination, or null to use
aoqi@0: * standard output.
aoqi@0: * @see #flush()
aoqi@0: */
aoqi@0: public void setOutput (Writer writer,String _encoding)
aoqi@0: {
aoqi@0: if (writer == null) {
aoqi@0: output = new OutputStreamWriter(System.out);
aoqi@0: } else {
aoqi@0: output = writer;
aoqi@0: }
aoqi@0: encoding = _encoding;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Set whether the writer should print out the XML declaration
aoqi@0: * (<?xml version='1.0' ... ?>).
aoqi@0: *
aoqi@0: * This option is set to true by default.
aoqi@0: */
aoqi@0: public void setXmlDecl( boolean _writeXmlDecl ) {
aoqi@0: this.writeXmlDecl = _writeXmlDecl;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Sets the header string.
aoqi@0: *
aoqi@0: * This string will be written right after the xml declaration
aoqi@0: * without any escaping. Useful for generating a boiler-plate
aoqi@0: * DOCTYPE decl, PIs, and comments.
aoqi@0: *
aoqi@0: * @param _header
aoqi@0: * passing null will work as if the empty string is passed.
aoqi@0: */
aoqi@0: public void setHeader( String _header ) {
aoqi@0: this.header = _header;
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: private final HashMap locallyDeclaredPrefix = new HashMap();
aoqi@0: public void startPrefixMapping( String prefix, String uri ) throws SAXException {
aoqi@0: locallyDeclaredPrefix.put(prefix,uri);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Methods from org.xml.sax.ContentHandler.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0: /**
aoqi@0: * Write the XML declaration at the beginning of the document.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the XML declaration, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#startDocument()
aoqi@0: */
aoqi@0: public void startDocument ()
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: reset();
aoqi@0:
aoqi@0: if(writeXmlDecl) {
aoqi@0: String e="";
aoqi@0: if(encoding!=null)
aoqi@0: e = " encoding=\""+encoding+'\"';
aoqi@0:
aoqi@0: writeXmlDecl("");
aoqi@0: }
aoqi@0:
aoqi@0: if(header!=null)
aoqi@0: write(header);
aoqi@0:
aoqi@0: super.startDocument();
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: protected void writeXmlDecl(String decl) throws IOException {
aoqi@0: write(decl);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a newline at the end of the document.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the newline, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#endDocument()
aoqi@0: */
aoqi@0: public void endDocument ()
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: super.endDocument();
aoqi@0: flush();
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a start tag.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @param uri The Namespace URI, or the empty string if none
aoqi@0: * is available.
aoqi@0: * @param localName The element's local (unprefixed) name (required).
aoqi@0: * @param qName The element's qualified (prefixed) name, or the
aoqi@0: * empty string is none is available. This method will
aoqi@0: * use the qName as a template for generating a prefix
aoqi@0: * if necessary, but it is not guaranteed to use the
aoqi@0: * same qName.
aoqi@0: * @param atts The element's attribute list (must not be null).
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the start tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
aoqi@0: */
aoqi@0: public void startElement (String uri, String localName,
aoqi@0: String qName, Attributes atts)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: if (!startTagIsClosed) {
aoqi@0: write(">");
aoqi@0: }
aoqi@0: elementLevel++;
aoqi@0: // nsSupport.pushContext();
aoqi@0:
aoqi@0: write('<');
aoqi@0: write(qName);
aoqi@0: writeAttributes(atts);
aoqi@0:
aoqi@0: // declare namespaces specified by the startPrefixMapping methods
aoqi@0: if(!locallyDeclaredPrefix.isEmpty()) {
aoqi@0: for (Map.Entry e : locallyDeclaredPrefix.entrySet()) {
aoqi@0: String p = e.getKey();
aoqi@0: String u = e.getValue();
aoqi@0: if (u == null) {
aoqi@0: u = "";
aoqi@0: }
aoqi@0: write(' ');
aoqi@0: if ("".equals(p)) {
aoqi@0: write("xmlns=\"");
aoqi@0: } else {
aoqi@0: write("xmlns:");
aoqi@0: write(p);
aoqi@0: write("=\"");
aoqi@0: }
aoqi@0: char ch[] = u.toCharArray();
aoqi@0: writeEsc(ch, 0, ch.length, true);
aoqi@0: write('\"');
aoqi@0: }
aoqi@0: locallyDeclaredPrefix.clear(); // clear the contents
aoqi@0: }
aoqi@0:
aoqi@0: // if (elementLevel == 1) {
aoqi@0: // forceNSDecls();
aoqi@0: // }
aoqi@0: // writeNSDecls();
aoqi@0: super.startElement(uri, localName, qName, atts);
aoqi@0: startTagIsClosed = false;
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write an end tag.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @param uri The Namespace URI, or the empty string if none
aoqi@0: * is available.
aoqi@0: * @param localName The element's local (unprefixed) name (required).
aoqi@0: * @param qName The element's qualified (prefixed) name, or the
aoqi@0: * empty string is none is available. This method will
aoqi@0: * use the qName as a template for generating a prefix
aoqi@0: * if necessary, but it is not guaranteed to use the
aoqi@0: * same qName.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the end tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
aoqi@0: */
aoqi@0: public void endElement (String uri, String localName, String qName)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: if (startTagIsClosed) {
aoqi@0: write("");
aoqi@0: write(qName);
aoqi@0: write('>');
aoqi@0: } else {
aoqi@0: write("/>");
aoqi@0: startTagIsClosed = true;
aoqi@0: }
aoqi@0: super.endElement(uri, localName, qName);
aoqi@0: // nsSupport.popContext();
aoqi@0: elementLevel--;
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write character data.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @param ch The array of characters to write.
aoqi@0: * @param start The starting position in the array.
aoqi@0: * @param len The number of characters to write.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the characters, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#characters(char[], int, int)
aoqi@0: */
aoqi@0: public void characters (char ch[], int start, int len)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: if (!startTagIsClosed) {
aoqi@0: write('>');
aoqi@0: startTagIsClosed = true;
aoqi@0: }
aoqi@0: writeEsc(ch, start, len, false);
aoqi@0: super.characters(ch, start, len);
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write ignorable whitespace.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @param ch The array of characters to write.
aoqi@0: * @param start The starting position in the array.
aoqi@0: * @param length The number of characters to write.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the whitespace, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
aoqi@0: */
aoqi@0: public void ignorableWhitespace (char ch[], int start, int length)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: writeEsc(ch, start, length, false);
aoqi@0: super.ignorableWhitespace(ch, start, length);
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a processing instruction.
aoqi@0: *
aoqi@0: * Pass the event on down the filter chain for further processing.
aoqi@0: *
aoqi@0: * @param target The PI target.
aoqi@0: * @param data The PI data.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the PI, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
aoqi@0: */
aoqi@0: public void processingInstruction (String target, String data)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: try {
aoqi@0: if (!startTagIsClosed) {
aoqi@0: write('>');
aoqi@0: startTagIsClosed = true;
aoqi@0: }
aoqi@0: write("");
aoqi@0: write(target);
aoqi@0: write(' ');
aoqi@0: write(data);
aoqi@0: write("?>");
aoqi@0: if (elementLevel < 1) {
aoqi@0: write('\n');
aoqi@0: }
aoqi@0: super.processingInstruction(target, data);
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Convenience methods.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Start a new element without a qname or attributes.
aoqi@0: *
aoqi@0: * This method will provide a default empty attribute
aoqi@0: * list and an empty string for the qualified name.
aoqi@0: * It invokes {@link
aoqi@0: * #startElement(String, String, String, Attributes)}
aoqi@0: * directly.
aoqi@0: *
aoqi@0: * @param uri The element's Namespace URI.
aoqi@0: * @param localName The element's local name.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the start tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #startElement(String, String, String, Attributes)
aoqi@0: */
aoqi@0: public void startElement (String uri, String localName)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: startElement(uri, localName, "", EMPTY_ATTS);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Start a new element without a qname, attributes or a Namespace URI.
aoqi@0: *
aoqi@0: * This method will provide an empty string for the
aoqi@0: * Namespace URI, and empty string for the qualified name,
aoqi@0: * and a default empty attribute list. It invokes
aoqi@0: * #startElement(String, String, String, Attributes)}
aoqi@0: * directly.
aoqi@0: *
aoqi@0: * @param localName The element's local name.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the start tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #startElement(String, String, String, Attributes)
aoqi@0: */
aoqi@0: public void startElement (String localName)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: startElement("", localName, "", EMPTY_ATTS);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * End an element without a qname.
aoqi@0: *
aoqi@0: * This method will supply an empty string for the qName.
aoqi@0: * It invokes {@link #endElement(String, String, String)}
aoqi@0: * directly.
aoqi@0: *
aoqi@0: * @param uri The element's Namespace URI.
aoqi@0: * @param localName The element's local name.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the end tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #endElement(String, String, String)
aoqi@0: */
aoqi@0: public void endElement (String uri, String localName)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: endElement(uri, localName, "");
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * End an element without a Namespace URI or qname.
aoqi@0: *
aoqi@0: * This method will supply an empty string for the qName
aoqi@0: * and an empty string for the Namespace URI.
aoqi@0: * It invokes {@link #endElement(String, String, String)}
aoqi@0: * directly.
aoqi@0: *
aoqi@0: * @param localName The element's local name.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the end tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #endElement(String, String, String)
aoqi@0: */
aoqi@0: public void endElement (String localName)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: endElement("", localName, "");
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write an element with character data content.
aoqi@0: *
aoqi@0: * This is a convenience method to write a complete element
aoqi@0: * with character data content, including the start tag
aoqi@0: * and end tag.
aoqi@0: *
aoqi@0: * This method invokes
aoqi@0: * {@link #startElement(String, String, String, Attributes)},
aoqi@0: * followed by
aoqi@0: * {@link #characters(String)}, followed by
aoqi@0: * {@link #endElement(String, String, String)}.
aoqi@0: *
aoqi@0: * @param uri The element's Namespace URI.
aoqi@0: * @param localName The element's local name.
aoqi@0: * @param qName The element's default qualified name.
aoqi@0: * @param atts The element's attributes.
aoqi@0: * @param content The character data content.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the empty tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #startElement(String, String, String, Attributes)
aoqi@0: * @see #characters(String)
aoqi@0: * @see #endElement(String, String, String)
aoqi@0: */
aoqi@0: public void dataElement (String uri, String localName,
aoqi@0: String qName, Attributes atts,
aoqi@0: String content)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: startElement(uri, localName, qName, atts);
aoqi@0: characters(content);
aoqi@0: endElement(uri, localName, qName);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write an element with character data content but no attributes.
aoqi@0: *
aoqi@0: * This is a convenience method to write a complete element
aoqi@0: * with character data content, including the start tag
aoqi@0: * and end tag. This method provides an empty string
aoqi@0: * for the qname and an empty attribute list.
aoqi@0: *
aoqi@0: * This method invokes
aoqi@0: * {@link #startElement(String, String, String, Attributes)},
aoqi@0: * followed by
aoqi@0: * {@link #characters(String)}, followed by
aoqi@0: * {@link #endElement(String, String, String)}.
aoqi@0: *
aoqi@0: * @param uri The element's Namespace URI.
aoqi@0: * @param localName The element's local name.
aoqi@0: * @param content The character data content.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the empty tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #startElement(String, String, String, Attributes)
aoqi@0: * @see #characters(String)
aoqi@0: * @see #endElement(String, String, String)
aoqi@0: */
aoqi@0: public void dataElement (String uri, String localName, String content)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: dataElement(uri, localName, "", EMPTY_ATTS, content);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write an element with character data content but no attributes or Namespace URI.
aoqi@0: *
aoqi@0: * This is a convenience method to write a complete element
aoqi@0: * with character data content, including the start tag
aoqi@0: * and end tag. The method provides an empty string for the
aoqi@0: * Namespace URI, and empty string for the qualified name,
aoqi@0: * and an empty attribute list.
aoqi@0: *
aoqi@0: * This method invokes
aoqi@0: * {@link #startElement(String, String, String, Attributes)},
aoqi@0: * followed by
aoqi@0: * {@link #characters(String)}, followed by
aoqi@0: * {@link #endElement(String, String, String)}.
aoqi@0: *
aoqi@0: * @param localName The element's local name.
aoqi@0: * @param content The character data content.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the empty tag, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #startElement(String, String, String, Attributes)
aoqi@0: * @see #characters(String)
aoqi@0: * @see #endElement(String, String, String)
aoqi@0: */
aoqi@0: public void dataElement (String localName, String content)
aoqi@0: throws SAXException
aoqi@0: {
aoqi@0: dataElement("", localName, "", EMPTY_ATTS, content);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a string of character data, with XML escaping.
aoqi@0: *
aoqi@0: * This is a convenience method that takes an XML
aoqi@0: * String, converts it to a character array, then invokes
aoqi@0: * {@link #characters(char[], int, int)}.
aoqi@0: *
aoqi@0: * @param data The character data.
aoqi@0: * @exception org.xml.sax.SAXException If there is an error
aoqi@0: * writing the string, or if a handler further down
aoqi@0: * the filter chain raises an exception.
aoqi@0: * @see #characters(char[], int, int)
aoqi@0: */
aoqi@0: public void characters (String data) throws SAXException {
aoqi@0: try {
aoqi@0: if (!startTagIsClosed) {
aoqi@0: write('>');
aoqi@0: startTagIsClosed = true;
aoqi@0: }
aoqi@0: char ch[] = data.toCharArray();
aoqi@0: characters(ch, 0, ch.length);
aoqi@0: } catch( IOException e ) {
aoqi@0: throw new SAXException(e);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Internal methods.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a raw character.
aoqi@0: *
aoqi@0: * @param c The character to write.
aoqi@0: */
aoqi@0: protected final void write (char c) throws IOException {
aoqi@0: output.write(c);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write a raw string.
aoqi@0: */
aoqi@0: protected final void write(String s) throws IOException {
aoqi@0: output.write(s);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write out an attribute list, escaping values.
aoqi@0: *
aoqi@0: * The names will have prefixes added to them.
aoqi@0: *
aoqi@0: * @param atts The attribute list to write.
aoqi@0: */
aoqi@0: private void writeAttributes (Attributes atts) throws IOException {
aoqi@0: int len = atts.getLength();
aoqi@0: for (int i = 0; i < len; i++) {
aoqi@0: char ch[] = atts.getValue(i).toCharArray();
aoqi@0: write(' ');
aoqi@0: write(atts.getQName(i));
aoqi@0: write("=\"");
aoqi@0: writeEsc(ch, 0, ch.length, true);
aoqi@0: write('"');
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Write an array of data characters with escaping.
aoqi@0: *
aoqi@0: * @param ch The array of characters.
aoqi@0: * @param start The starting position.
aoqi@0: * @param length The number of characters to use.
aoqi@0: * @param isAttVal true if this is an attribute value literal.
aoqi@0: */
aoqi@0: private void writeEsc (char ch[], int start,
aoqi@0: int length, boolean isAttVal)
aoqi@0: throws IOException
aoqi@0: {
aoqi@0: escapeHandler.escape(ch, start, length, isAttVal, output);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Constants.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0: private final Attributes EMPTY_ATTS = new AttributesImpl();
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0: // Internal state.
aoqi@0: ////////////////////////////////////////////////////////////////////
aoqi@0:
aoqi@0: private int elementLevel = 0;
aoqi@0: private Writer output;
aoqi@0: private String encoding;
aoqi@0: private boolean writeXmlDecl = true;
aoqi@0: /**
aoqi@0: * This string will be written right after the xml declaration
aoqi@0: * without any escaping. Useful for generating a boiler-plate DOCTYPE decl
aoqi@0: * , PIs, and comments.
aoqi@0: */
aoqi@0: private String header=null;
aoqi@0:
aoqi@0: private final CharacterEscapeHandler escapeHandler;
aoqi@0:
aoqi@0: private boolean startTagIsClosed = true;
aoqi@0: }
aoqi@0:
aoqi@0: // end of XMLWriter.java