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