Java/XML/Writer — различия между версиями
Admin (обсуждение | вклад) м (1 версия) |
|
(нет различий)
|
Текущая версия на 07:19, 1 июня 2010
Содержание
- 1 Convenience methods for writing XML
- 2 DOM writer
- 3 Makes writing XML much much easier
- 4 Use DOM L3 DOMBuilder, DOMBuilderFilter DOMWriter and other DOM L3 functionality to preparse, revalidate and safe document.
- 5 Write DOM out
- 6 write Xml DOM Node
- 7 Write Xml (Node n, OutputStream os)
- 8 Writing a DOM Document to an XML File
- 9 XML Document Writer
- 10 XML Writer
- 11 XMLWriter helper class
- 12 XMLWriter.java - serialize an XML document
Convenience methods for writing XML
/*
* Copyright Aduna (http://www.aduna-software.ru/) (c) 1997-2006.
*
* Licensed under the Aduna BSD-style license.
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
/**
* A utility class offering convenience methods for writing XML. This class
* takes care of character escaping, identation, etc. This class does not verify
* that the written data is legal XML. It is the callers responsibility to make
* sure that elements are properly nested, etc.
*
* <h3>Example:</h3>
* <p>
* To write the following XML:
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <xml-doc>
* <foo a="1" b="2&amp;3"/>
* <bar>Hello World!</bar>
* </xml-doc>
*</pre>
* <p>
* One can use the following code:
* <pre>
* XMLWriter xmlWriter = new XMLWriter(myWriter);
* xmlWriter.setPrettyPrint(true);
*
* xmlWriter.startDocument();
* xmlWriter.startTag("xml-doc");
*
* xmlWriter.setAttribute("a", 1);
* xmlWriter.setAttribute("b", "2&3");
* xmlWriter.simpleTag("foo");
*
* xmlWriter.textTag("bar", "Hello World!");
*
* xmlWriter.endTag("xml-doc");
* xmlWriter.endDocument();
* </pre>
*/
public class XMLWriter {
/*-----------*
* Constants *
*-----------*/
/**
* The (platform-dependent) line separator.
*/
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/*-----------*
* Variables *
*-----------*/
/**
* The writer to write the XML to.
*/
private Writer _writer;
/**
* The required character encoding of the written data.
*/
private String _charEncoding;
/**
* Flag indicating whether the output should be printed pretty, i.e. adding
* newlines and indentation.
*/
private boolean _prettyPrint = false;
/**
* The current indentation level, i.e. the number of tabs to indent a start
* or end tag.
*/
protected int _indentLevel = 0;
/**
* The string to use for indentation, e.g. a tab or a number of spaces.
*/
private String _indentString = "\t";
/**
* A mapping from attribute names to values for the next start tag.
*/
private HashMap<String, String> _attributes = new LinkedHashMap<String, String>();
/*--------------*
* Constructors *
*--------------*/
/**
* Creates a new XMLWriter that will write its data to the supplied Writer.
* Character encoding issues are left to the supplier of the Writer.
*
* @param writer The Writer to write the XML to.
*/
public XMLWriter(Writer writer) {
_writer = writer;
}
/**
* Creates a new XMLWriter that will write its data to the supplied
* OutputStream in the default UTF-8 character encoding.
*
* @param outputStream The OutputStream to write the XML to.
*/
public XMLWriter(OutputStream outputStream) {
try {
_charEncoding = "UTF-8";
_writer = new OutputStreamWriter(outputStream, _charEncoding);
}
catch (UnsupportedEncodingException e) {
// UTF-8 must be supported by all compliant JVM"s,
// this exception should never be thrown.
throw new RuntimeException(
"UTF-8 character encoding not supported on this platform");
}
}
/**
* Creates a new XMLWriter that will write its data to the supplied
* OutputStream in specified character encoding.
*
* @param outputStream The OutputStream to write the XML to.
*/
public XMLWriter(OutputStream outputStream, String charEncoding)
throws UnsupportedEncodingException
{
_charEncoding = charEncoding;
_writer = new OutputStreamWriter(outputStream, _charEncoding);
}
/*---------*
* Methods *
*---------*/
/**
* Enables or disables pretty-printing. If pretty-printing is enabled, the
* XMLWriter will add newlines and indentation to the written data.
* Pretty-printing is disabled by default.
*
* @param prettyPrint Flag indicating whether pretty-printing should be
* enabled.
*/
public void setPrettyPrint(boolean prettyPrint) {
_prettyPrint = prettyPrint;
}
/**
* Checks whether pretty-printing is enabled.
*
* @return <tt>true</tt> if pretty-printing is enabled, <tt>false</tt>
* otherwise.
*/
public boolean prettyPrintEnabled() {
return _prettyPrint;
}
/**
* Sets the string that should be used for indentation when pretty-printing
* is enabled. The default indentation string is a tab character.
*
* @param indentString The indentation string, e.g. a tab or a number of
* spaces.
*/
public void setIndentString(String indentString) {
_indentString = indentString;
}
/**
* Gets the string used for indentation.
*
* @return the indentation string.
*/
public String getIndentString() {
return _indentString;
}
/**
* Writes the XML header for the XML file.
*
* @throws IOException If an I/O error occurs.
*/
public void startDocument()
throws IOException
{
_write("<?xml version="1.0"");
if (_charEncoding != null) {
_write(" encoding="" + _charEncoding + """);
}
_writeLn("?>");
}
/**
* Finishes writing and flushes the OutputStream or Writer that this
* XMLWriter is writing to.
*/
public void endDocument()
throws IOException
{
_writer.flush();
_writer = null;
}
/**
* Sets an attribute for the next start tag.
*
* @param name The name of the attribute.
* @param value The value of the attribute.
*/
public void setAttribute(String name, String value) {
_attributes.put(name, value);
}
/**
* Sets an attribute for the next start element.
*
* @param name The name of the attribute.
* @param value The value of the attribute. The integer value will be
* transformed to a string using the method <tt>String.valueOf(int)</tt>.
* @see java.lang.String#valueOf(int)
*/
public void setAttribute(String name, int value) {
setAttribute(name, String.valueOf(value));
}
/**
* Sets an attribute for the next start element.
*
* @param name The name of the attribute.
* @param value The value of the attribute. The boolean value will be
* transformed to a string using the method
* <tt>String.valueOf(boolean)</tt>.
* @see java.lang.String#valueOf(boolean)
*/
public void setAttribute(String name, boolean value) {
setAttribute(name, String.valueOf(value));
}
/**
* Writes a start tag containing the previously set attributes.
*
* @param elName The element name.
* @see #setAttribute(java.lang.String,java.lang.String)
*/
public void startTag(String elName)
throws IOException
{
_writeIndent();
_write("<" + elName);
_writeAtts();
_writeLn(">");
_indentLevel++;
}
/**
* Writes an end tag.
*
* @param elName The element name.
*/
public void endTag(String elName)
throws IOException
{
_indentLevel--;
_writeIndent();
_writeLn("</" + elName + ">");
}
/**
* Writes an "empty" element, e.g. <tt><foo/></tt>. The tag will
* contain any previously set attributes.
*
* @param elName The element name.
* @see #setAttribute(java.lang.String,java.lang.String)
*/
public void emptyElement(String elName)
throws IOException
{
_writeIndent();
_write("<" + elName);
_writeAtts();
_writeLn("/>");
}
/**
* Writes a start and end tag with the supplied text between them. The start
* tag will contain any previously set attributes.
*
* @param elName The element name.
* @param text The text.
* @see #setAttribute(java.lang.String,java.lang.String)
*/
public void textElement(String elName, String text)
throws IOException
{
_writeIndent();
_write("<" + elName);
_writeAtts();
_write(">");
text(text);
_writeLn("</" + elName + ">");
}
/**
* Writes a start and end tag with the supplied text between them, without
* the usual escape rules. The start tag will contain any previously set
* attributes.
*
* @param elName The element name.
* @param text The text.
* @see #setAttribute(java.lang.String,java.lang.String)
*/
public void unescapedTextElement(String elName, String text)
throws IOException
{
_writeIndent();
_write("<" + elName);
_writeAtts();
_write(">");
_write(text);
_writeLn("</" + elName + ">");
}
/**
* Writes a start and end tag with the supplied value between them. The
* start tag will contain any previously set attributes.
*
* @param elName The element name.
* @param value The value. The integer value will be transformed to a string
* using the method <tt>String.valueOf(int)</tt>.
* @see java.lang.String#valueOf(int)
*/
public void textElement(String elName, int value)
throws IOException
{
textElement(elName, String.valueOf(value));
}
/**
* Writes a start and end tag with the supplied boolean value between them.
* The start tag will contain any previously set attributes.
*
* @param elName The element name.
* @param value The boolean value. The integer value will be transformed to
* a string using the method <tt>String.valueOf(boolean)</tt>.
* @see java.lang.String#valueOf(boolean)
*/
public void textElement(String elName, boolean value)
throws IOException
{
textElement(elName, String.valueOf(value));
}
/**
* Writes a piece of text.
*
* @param text The text.
*/
public void text(String text)
throws IOException
{
_write(text);
}
/**
* Writes a comment.
*
* @param comment The comment.
*/
public void comment(String comment)
throws IOException
{
_writeIndent();
_writeLn("<!-- " + comment + " -->");
}
/**
* Writes an empty line. A call to this method will be ignored when
* pretty-printing is disabled.
*
* @see #setPrettyPrint
*/
public void emptyLine()
throws IOException
{
_writeLn("");
}
/**
* Writes any set attributes and clears them afterwards.
*/
private void _writeAtts()
throws IOException
{
for (Entry<String,String> entry : _attributes.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
_write(" " + name + "="");
if (value != null) {
_write(value);
}
_write(""");
}
_attributes.clear();
}
/**
* Writes a string.
*/
protected void _write(String s)
throws IOException
{
_writer.write(s);
}
/**
* Writes a string followed by a line-separator. The line-separator is not
* written when pretty-printing is disabled.
*/
protected void _writeLn(String s)
throws IOException
{
_write(s);
if (_prettyPrint) {
_write(LINE_SEPARATOR);
}
}
/**
* Writes as much indentation strings as appropriate for the current
* indentation level. A call to this method is ignored when pretty-printing
* is disabled.
*/
protected void _writeIndent()
throws IOException
{
if (_prettyPrint) {
for (int i = 0; i < _indentLevel; i++) {
_write(_indentString);
}
}
}
}
DOM writer
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don"t indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS"" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* A sample DOM writer. This sample program illustrates how to
* traverse a DOM tree in order to print a document that is parsed.
*
* @author Andy Clark, IBM
*
* @version $Id: DOMWriter.java,v 1.2 2007/07/19 04:35:46 ofung Exp $
*/
public class DOMWriter {
//
// Constants
//
// feature ids
/** Namespaces feature id (http://xml.org/sax/features/namespaces). */
protected static final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces";
/** Validation feature id (http://xml.org/sax/features/validation). */
protected static final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation";
/** Schema validation feature id (http://apache.org/xml/features/validation/schema). */
protected static final String SCHEMA_VALIDATION_FEATURE_ID = "http://apache.org/xml/features/validation/schema";
/** Schema full checking feature id (http://apache.org/xml/features/validation/schema-full-checking). */
protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID = "http://apache.org/xml/features/validation/schema-full-checking";
// property ids
/** Lexical handler property id (http://xml.org/sax/properties/lexical-handler). */
protected static final String LEXICAL_HANDLER_PROPERTY_ID = "http://xml.org/sax/properties/lexical-handler";
// default settings
/** Default parser name. */
protected static final String DEFAULT_PARSER_NAME = "dom.wrappers.Xerces";
/** Default namespaces support (true). */
protected static final boolean DEFAULT_NAMESPACES = true;
/** Default validation support (false). */
protected static final boolean DEFAULT_VALIDATION = false;
/** Default Schema validation support (false). */
protected static final boolean DEFAULT_SCHEMA_VALIDATION = false;
/** Default Schema full checking support (false). */
protected static final boolean DEFAULT_SCHEMA_FULL_CHECKING = false;
/** Default canonical output (false). */
protected static final boolean DEFAULT_CANONICAL = false;
//
// Data
//
/** Print writer. */
protected PrintWriter fOut;
/** Canonical output. */
protected boolean fCanonical;
/** Processing XML 1.1 document. */
protected boolean fXML11;
//
// Constructors
//
/** Default constructor. */
public DOMWriter() {
} // <init>()
public DOMWriter(boolean canonical) {
fCanonical = canonical;
} // <init>(boolean)
public DOMWriter( OutputStream out, String encoding ) throws UnsupportedEncodingException {
this();
setOutput(out,encoding);
}
//
// Public methods
//
/** Sets whether output is canonical. */
public void setCanonical(boolean canonical) {
fCanonical = canonical;
} // setCanonical(boolean)
/** Sets the output stream for printing. */
public void setOutput(OutputStream stream, String encoding)
throws UnsupportedEncodingException {
if (encoding == null) {
encoding = "UTF8";
}
java.io.Writer writer = new OutputStreamWriter(stream, encoding);
fOut = new PrintWriter(writer);
} // setOutput(OutputStream,String)
/** Sets the output writer. */
public void setOutput(java.io.Writer writer) {
fOut = writer instanceof PrintWriter
? (PrintWriter)writer : new PrintWriter(writer);
} // setOutput(java.io.Writer)
/** Writes the specified node, recursively. */
public void write(Node node) {
// is there anything to do?
if (node == null) {
return;
}
short type = node.getNodeType();
switch (type) {
case Node.DOCUMENT_NODE: {
Document document = (Document)node;
fXML11 = "1.1".equals(getVersion(document));
if (!fCanonical) {
if (fXML11) {
fOut.println("<?xml version=\"1.1\" encoding=\"UTF-8\"?>");
}
else {
fOut.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
fOut.flush();
write(document.getDoctype());
}
write(document.getDocumentElement());
break;
}
case Node.DOCUMENT_TYPE_NODE: {
DocumentType doctype = (DocumentType)node;
fOut.print("<!DOCTYPE ");
fOut.print(doctype.getName());
String publicId = doctype.getPublicId();
String systemId = doctype.getSystemId();
if (publicId != null) {
fOut.print(" PUBLIC "");
fOut.print(publicId);
fOut.print("" "");
fOut.print(systemId);
fOut.print("\"");
}
else if (systemId != null) {
fOut.print(" SYSTEM "");
fOut.print(systemId);
fOut.print("\"");
}
String internalSubset = doctype.getInternalSubset();
if (internalSubset != null) {
fOut.println(" [");
fOut.print(internalSubset);
fOut.print("]");
}
fOut.println(">");
break;
}
case Node.ELEMENT_NODE: {
fOut.print("<");
fOut.print(node.getNodeName());
Attr attrs[] = sortAttributes(node.getAttributes());
for (int i = 0; i < attrs.length; i++) {
Attr attr = attrs[i];
fOut.print(" ");
fOut.print(attr.getNodeName());
fOut.print("=\"");
normalizeAndPrint(attr.getNodeValue(), true);
fOut.print(""");
}
fOut.print(">");
fOut.flush();
Node child = node.getFirstChild();
while (child != null) {
write(child);
child = child.getNextSibling();
}
break;
}
case Node.ENTITY_REFERENCE_NODE: {
if (fCanonical) {
Node child = node.getFirstChild();
while (child != null) {
write(child);
child = child.getNextSibling();
}
}
else {
fOut.print("&");
fOut.print(node.getNodeName());
fOut.print(";");
fOut.flush();
}
break;
}
case Node.CDATA_SECTION_NODE: {
if (fCanonical) {
normalizeAndPrint(node.getNodeValue(), false);
}
else {
fOut.print("<![CDATA[");
fOut.print(node.getNodeValue());
fOut.print("]]>");
}
fOut.flush();
break;
}
case Node.TEXT_NODE: {
normalizeAndPrint(node.getNodeValue(), false);
fOut.flush();
break;
}
case Node.PROCESSING_INSTRUCTION_NODE: {
fOut.print("<?");
fOut.print(node.getNodeName());
String data = node.getNodeValue();
if (data != null && data.length() > 0) {
fOut.print(" ");
fOut.print(data);
}
fOut.print("?>");
fOut.flush();
break;
}
case Node.ruMENT_NODE: {
if (!fCanonical) {
fOut.print("<!--");
String comment = node.getNodeValue();
if (comment != null && comment.length() > 0) {
fOut.print(comment);
}
fOut.print("-->");
fOut.flush();
}
}
}
if (type == Node.ELEMENT_NODE) {
fOut.print("</");
fOut.print(node.getNodeName());
fOut.print(">");
fOut.flush();
}
} // write(Node)
/** Returns a sorted list of attributes. */
protected Attr[] sortAttributes(NamedNodeMap attrs) {
int len = (attrs != null) ? attrs.getLength() : 0;
Attr array[] = new Attr[len];
for (int i = 0; i < len; i++) {
array[i] = (Attr)attrs.item(i);
}
for (int i = 0; i < len - 1; i++) {
String name = array[i].getNodeName();
int index = i;
for (int j = i + 1; j < len; j++) {
String curName = array[j].getNodeName();
if (curName.rupareTo(name) < 0) {
name = curName;
index = j;
}
}
if (index != i) {
Attr temp = array[i];
array[i] = array[index];
array[index] = temp;
}
}
return array;
} // sortAttributes(NamedNodeMap):Attr[]
//
// Protected methods
//
/** Normalizes and prints the given string. */
protected void normalizeAndPrint(String s, boolean isAttValue) {
int len = (s != null) ? s.length() : 0;
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
normalizeAndPrint(c, isAttValue);
}
} // normalizeAndPrint(String,boolean)
/** Normalizes and print the given character. */
protected void normalizeAndPrint(char c, boolean isAttValue) {
switch (c) {
case "<": {
fOut.print("<");
break;
}
case ">": {
fOut.print(">");
break;
}
case "&": {
fOut.print("&");
break;
}
case """: {
// A """ that appears in character data
// does not need to be escaped.
if (isAttValue) {
fOut.print(""");
}
else {
fOut.print("\"");
}
break;
}
case "\r": {
// If CR is part of the document"s content, it
// must not be printed as a literal otherwise
// it would be normalized to LF when the document
// is reparsed.
fOut.print("
");
break;
}
case "\n": {
if (fCanonical) {
fOut.print("
");
break;
}
// else, default print char
}
default: {
// In XML 1.1, control chars in the ranges [#x1-#x1F, #x7F-#x9F] must be escaped.
//
// Escape space characters that would be normalized to #x20 in attribute values
// when the document is reparsed.
//
// Escape NEL (0x85) and LSEP (0x2028) that appear in content
// if the document is XML 1.1, since they would be normalized to LF
// when the document is reparsed.
if (fXML11 && ((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A)
|| (c >= 0x7F && c <= 0x9F) || c == 0x2028)
|| isAttValue && (c == 0x09 || c == 0x0A)) {
fOut.print("&#x");
fOut.print(Integer.toHexString(c).toUpperCase());
fOut.print(";");
}
else {
fOut.print(c);
}
}
}
} // normalizeAndPrint(char,boolean)
/** Extracts the XML version from the Document. */
protected String getVersion(Document document) {
if (document == null) {
return null;
}
String version = null;
Method getXMLVersion = null;
try {
getXMLVersion = document.getClass().getMethod("getXmlVersion", new Class[]{});
// If Document class implements DOM L3, this method will exist.
if (getXMLVersion != null) {
version = (String) getXMLVersion.invoke(document, null);
}
}
catch (Exception e) {
// Either this locator object doesn"t have
// this method, or we"re on an old JDK.
}
return version;
} // getVersion(Document)
//
// Private static methods
//
/** Prints the usage. */
private static void printUsage() {
System.err.println("usage: java dom.Writer (options) uri ...");
System.err.println();
System.err.println("options:");
System.err.println(" -p name Select parser by name.");
System.err.println(" -n | -N Turn on/off namespace processing.");
System.err.println(" -v | -V Turn on/off validation.");
System.err.println(" -s | -S Turn on/off Schema validation support.");
System.err.println(" NOTE: Not supported by all parsers.");
System.err.println(" -f | -F Turn on/off Schema full checking.");
System.err.println(" NOTE: Requires use of -s and not supported by all parsers.");
System.err.println(" -c | -C Turn on/off Canonical XML output.");
System.err.println(" NOTE: This is not W3C canonical output.");
System.err.println(" -h This help screen.");
System.err.println();
System.err.println("defaults:");
System.err.println(" Parser: "+DEFAULT_PARSER_NAME);
System.err.print(" Namespaces: ");
System.err.println(DEFAULT_NAMESPACES ? "on" : "off");
System.err.print(" Validation: ");
System.err.println(DEFAULT_VALIDATION ? "on" : "off");
System.err.print(" Schema: ");
System.err.println(DEFAULT_SCHEMA_VALIDATION ? "on" : "off");
System.err.print(" Schema full checking: ");
System.err.println(DEFAULT_SCHEMA_FULL_CHECKING ? "on" : "off");
System.err.print(" Canonical: ");
System.err.println(DEFAULT_CANONICAL ? "on" : "off");
} // printUsage()
} // class Writer
Makes writing XML much much easier
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org /)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Commons" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS"" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org />.
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Stack;
/**
* Makes writing XML much much easier.
* Improved from
*
* @author Last changed by: $Author: gommma $
* @version $Revision: 859 $ $Date: 2008-11-02 12:50:23 +0100 (dom, 02 nov 2008) $
* @since 1.0
*/
public class XmlWriter
{
/**
* CDATA start tag: {@value}
*/
public static final String CDATA_START = "<![CDATA[";
/**
* CDATA end tag: {@value}
*/
public static final String CDATA_END = "]]>";
/**
* Default encoding value which is {@value}
*/
public static final String DEFAULT_ENCODING = "UTF-8";
/**
* Logger for this class
*/
private Writer out; // underlying writer
private String encoding; // the encoding to be written into the XML header/metatag
private Stack stack = new Stack(); // of xml element names
private StringBuffer attrs; // current attribute string
private boolean empty; // is the current node empty
private boolean closed = true; // is the current node closed...
private boolean pretty = true; // is pretty printing enabled?
/**
* was text the last thing output?
*/
private boolean wroteText = false;
/**
* output this to indent one level when pretty printing
*/
private String indent = " ";
/**
* output this to end a line when pretty printing
*/
private String newline = "\n";
/**
* Create an XmlWriter on top of an existing java.io.Writer.
*/
public XmlWriter(Writer writer)
{
this(writer, null);
}
/**
* Create an XmlWriter on top of an existing java.io.Writer.
*/
public XmlWriter(Writer writer, String encoding)
{
setWriter(writer, encoding);
}
/**
* Create an XmlWriter on top of an existing {@link java.io.OutputStream}.
* @param outputStream
* @param encoding The encoding to be used for writing to the given output
* stream. Can be null. If it is null the
* {@link #DEFAULT_ENCODING} is used.
* @throws UnsupportedEncodingException
* @since 2.4
*/
public XmlWriter(OutputStream outputStream, String encoding)
throws UnsupportedEncodingException
{
if(encoding==null)
{
encoding = DEFAULT_ENCODING;
}
OutputStreamWriter writer = new OutputStreamWriter(outputStream, encoding);
setWriter(writer, encoding);
}
/**
* Turn pretty printing on or off.
* Pretty printing is enabled by default, but it can be turned off
* to generate more compact XML.
*
* @param enable true to enable, false to disable pretty printing.
*/
public void enablePrettyPrint(boolean enable)
{
this.pretty = enable;
}
/**
* Specify the string to prepend to a line for each level of indent.
* It is 2 spaces (" ") by default. Some may prefer a single tab ("\t")
* or a different number of spaces. Specifying an empty string will turn
* off indentation when pretty printing.
*
* @param indent representing one level of indentation while pretty printing.
*/
public void setIndent(String indent)
{
this.indent = indent;
}
/**
* Specify the string used to terminate each line when pretty printing.
* It is a single newline ("\n") by default. Users who need to read
* generated XML documents in Windows editors like Notepad may wish to
* set this to a carriage return/newline sequence ("\r\n"). Specifying
* an empty string will turn off generation of line breaks when pretty
* printing.
*
* @param newline representing the newline sequence when pretty printing.
*/
public void setNewline(String newline)
{
this.newline = newline;
}
/**
* A helper method. It writes out an element which contains only text.
*
* @param name String name of tag
* @param text String of text to go inside the tag
*/
public XmlWriter writeElementWithText(String name, String text) throws IOException
{
writeElement(name);
writeText(text);
return endElement();
}
/**
* A helper method. It writes out empty entities.
*
* @param name String name of tag
*/
public XmlWriter writeEmptyElement(String name) throws IOException
{
writeElement(name);
return endElement();
}
/**
* Begin to write out an element. Unlike the helper tags, this tag
* will need to be ended with the endElement method.
*
* @param name String name of tag
*/
public XmlWriter writeElement(String name) throws IOException
{
return openElement(name);
}
/**
* Begin to output an element.
*
* @param name name of element.
*/
private XmlWriter openElement(String name) throws IOException
{
boolean wasClosed = this.closed;
closeOpeningTag();
this.closed = false;
if (this.pretty)
{
// ! wasClosed separates adjacent opening tags by a newline.
// this.wroteText makes sure an element embedded within the text of
// its parent element begins on a new line, indented to the proper
// level. This solves only part of the problem of pretty printing
// entities which contain both text and child entities.
if (!wasClosed || this.wroteText)
{
this.out.write(newline);
}
for (int i = 0; i < this.stack.size(); i++)
{
this.out.write(indent); // Indent opening tag to proper level
}
}
this.out.write("<");
this.out.write(name);
stack.add(name);
this.empty = true;
this.wroteText = false;
return this;
}
// close off the opening tag
private void closeOpeningTag() throws IOException
{
if (!this.closed)
{
writeAttributes();
this.closed = true;
this.out.write(">");
}
}
// write out all current attributes
private void writeAttributes() throws IOException
{
if (this.attrs != null)
{
this.out.write(this.attrs.toString());
this.attrs.setLength(0);
this.empty = false;
}
}
/**
* Write an attribute out for the current element.
* Any XML characters in the value are escaped.
* Currently it does not actually throw the exception, but
* the API is set that way for future changes.
*
* @param attr name of attribute.
* @param value value of attribute.
* @see #writeAttribute(String, String, boolean)
*/
public XmlWriter writeAttribute(String attr, String value) throws IOException
{
return this.writeAttribute(attr, value, false);
}
/**
* Write an attribute out for the current element.
* Any XML characters in the value are escaped.
* Currently it does not actually throw the exception, but
* the API is set that way for future changes.
*
* @param attr name of attribute.
* @param value value of attribute.
* @param literally If the writer should be literally on the given value
* which means that meta characters will also be preserved by escaping them.
* Mainly preserves newlines and tabs.
*/
public XmlWriter writeAttribute(String attr, String value, boolean literally) throws IOException
{
if(this.wroteText==true) {
throw new IllegalStateException("The text for the current element has already been written. Cannot add attributes afterwards.");
}
// maintain API
if (false) throw new IOException();
if (this.attrs == null)
{
this.attrs = new StringBuffer();
}
this.attrs.append(" ");
this.attrs.append(attr);
this.attrs.append("=\"");
String val = escapeXml(value);
if(literally){
val = escapeMetaCharacters(val);
}
this.attrs.append(val);
this.attrs.append("\"");
return this;
}
/**
* End the current element. This will throw an exception
* if it is called when there is not a currently open
* element.
*/
public XmlWriter endElement() throws IOException
{
if (this.stack.empty())
{
throw new IOException("Called endElement too many times. ");
}
String name = (String)this.stack.pop();
if (name != null)
{
if (this.empty)
{
writeAttributes();
this.out.write("/>");
}
else
{
if (this.pretty && !this.wroteText)
{
for (int i = 0; i < this.stack.size(); i++)
{
this.out.write(indent); // Indent closing tag to proper level
}
}
this.out.write("</");
this.out.write(name);
this.out.write(">");
}
if (this.pretty)
this.out.write(newline); // Add a newline after the closing tag
this.empty = false;
this.closed = true;
this.wroteText = false;
}
return this;
}
/**
* Close this writer. It does not close the underlying
* writer, but does throw an exception if there are
* as yet unclosed tags.
*/
public void close() throws IOException
{
this.out.flush();
if (!this.stack.empty())
{
throw new IOException("Tags are not all closed. " +
"Possibly, " + this.stack.pop() + " is unclosed. ");
}
}
/**
* Output body text. Any XML characters are escaped.
* @param text The text to be written
* @return This writer
* @throws IOException
* @see #writeText(String, boolean)
*/
public XmlWriter writeText(String text) throws IOException
{
return this.writeText(text, false);
}
/**
* Output body text. Any XML characters are escaped.
* @param text The text to be written
* @param literally If the writer should be literally on the given value
* which means that meta characters will also be preserved by escaping them.
* Mainly preserves newlines and tabs.
* @return This writer
* @throws IOException
*/
public XmlWriter writeText(String text, boolean literally) throws IOException
{
closeOpeningTag();
this.empty = false;
this.wroteText = true;
String val = escapeXml(text);
if(literally){
val = escapeMetaCharacters(val);
}
this.out.write(val);
return this;
}
/**
* Write out a chunk of CDATA. This helper method surrounds the
* passed in data with the CDATA tag.
*
* @param cdata of CDATA text.
*/
public XmlWriter writeCData(String cdata) throws IOException
{
closeOpeningTag();
boolean hasAlreadyEnclosingCdata = cdata.startsWith(CDATA_START) && cdata.endsWith(CDATA_END);
// There may already be CDATA sections inside the data.
// But CDATA sections can"t be nested - can"t have ]]> inside a CDATA section.
// (See http://www.w3.org/TR/REC-xml/#NT-CDStart in the W3C specs)
// The solutions is to replace any occurrence of "]]>" by "]]]]><![CDATA[>",
// so that the top CDATA section is split into many valid CDATA sections (you
// can look at the "]]]]>" as if it was an escape sequence for "]]>").
if(!hasAlreadyEnclosingCdata) {
cdata = cdata.replaceAll(CDATA_END, "]]]]><![CDATA[>");
}
this.empty = false;
this.wroteText = true;
if(!hasAlreadyEnclosingCdata)
this.out.write(CDATA_START);
this.out.write(cdata);
if(!hasAlreadyEnclosingCdata)
this.out.write(CDATA_END);
return this;
}
/**
* Write out a chunk of comment. This helper method surrounds the
* passed in data with the XML comment tag.
*
* @param comment of text to comment.
*/
public XmlWriter writeComment(String comment) throws IOException
{
writeChunk("<!-- " + comment + " -->");
return this;
}
private void writeChunk(String data) throws IOException
{
closeOpeningTag();
this.empty = false;
if (this.pretty && !this.wroteText)
{
for (int i = 0; i < this.stack.size(); i++)
{
this.out.write(indent);
}
}
this.out.write(data);
if (this.pretty)
{
this.out.write(newline);
}
}
// Two example methods. They should output the same XML:
// <person name="fred" age="12"><phone>425343</phone><bob/></person>
static public void main(String[] args) throws IOException
{
test1();
test2();
}
static public void test1() throws IOException
{
Writer writer = new java.io.StringWriter();
XmlWriter xmlwriter = new XmlWriter(writer);
xmlwriter.writeElement("person").writeAttribute("name", "fred").writeAttribute("age", "12").writeElement("phone").writeText("4254343").endElement().writeElement("friends").writeElement("bob").endElement().writeElement("jim").endElement().endElement().endElement();
xmlwriter.close();
System.err.println(writer.toString());
}
static public void test2() throws IOException
{
Writer writer = new java.io.StringWriter();
XmlWriter xmlwriter = new XmlWriter(writer);
xmlwriter.writeComment("Example of XmlWriter running");
xmlwriter.writeElement("person");
xmlwriter.writeAttribute("name", "fred");
xmlwriter.writeAttribute("age", "12");
xmlwriter.writeElement("phone");
xmlwriter.writeText("4254343");
xmlwriter.endElement();
xmlwriter.writeComment("Examples of empty tags");
// xmlwriter.setDefaultNamespace("test");
xmlwriter.writeElement("friends");
xmlwriter.writeEmptyElement("bob");
xmlwriter.writeEmptyElement("jim");
xmlwriter.endElement();
xmlwriter.writeElementWithText("foo", "This is an example.");
xmlwriter.endElement();
xmlwriter.close();
System.err.println(writer.toString());
}
////////////////////////////////////////////////////////////////////////////
// Added for DbUnit
/**
* Escapes some meta characters like \n, \r that should be preserved in the XML
* so that a reader will not filter out those symbols.
* @param str The string to be escaped
* @return The escaped string
* @since 2.3.0
*/
private String escapeMetaCharacters(String str)
{
// 2. Do additional escapes. See http://www.w3.org/TR/2004/REC-xml-20040204/#AVNormalize
str = replace(str, "\n", "
"); // linefeed (LF)
str = replace(str, "\r", "
"); // carriage return (CR)
return str;
}
private String escapeXml(String str)
{
str = replace(str, "&", "&");
str = replace(str, "<", "<");
str = replace(str, ">", ">");
str = replace(str, "\"", """);
str = replace(str, """, "'");
str = replace(str, "\t", "	"); // tab
return str;
}
private String replace(String value, String original, String replacement)
{
StringBuffer buffer = null;
int startIndex = 0;
int lastEndIndex = 0;
for (; ;)
{
startIndex = value.indexOf(original, lastEndIndex);
if (startIndex == -1)
{
if (buffer != null)
{
buffer.append(value.substring(lastEndIndex));
}
break;
}
if (buffer == null)
{
buffer = new StringBuffer((int)(original.length() * 1.5));
}
buffer.append(value.substring(lastEndIndex, startIndex));
buffer.append(replacement);
lastEndIndex = startIndex + original.length();
}
return buffer == null ? value : buffer.toString();
}
private void setEncoding(String encoding)
{
if (encoding == null && out instanceof OutputStreamWriter)
encoding = ((OutputStreamWriter)out).getEncoding();
if (encoding != null)
{
encoding = encoding.toUpperCase();
// Use official encoding names where we know them,
// avoiding the Java-only names. When using common
// encodings where we can easily tell if characters
// are out of range, we"ll escape out-of-range
// characters using character refs for safety.
// I _think_ these are all the main synonyms for these!
if ("UTF8".equals(encoding))
{
encoding = "UTF-8";
}
else if ("US-ASCII".equals(encoding)
|| "ASCII".equals(encoding))
{
// dangerMask = (short)0xff80;
encoding = "US-ASCII";
}
else if ("ISO-8859-1".equals(encoding)
|| "8859_1".equals(encoding)
|| "ISO8859_1".equals(encoding))
{
// dangerMask = (short)0xff00;
encoding = "ISO-8859-1";
}
else if ("UNICODE".equals(encoding)
|| "UNICODE-BIG".equals(encoding)
|| "UNICODE-LITTLE".equals(encoding))
{
encoding = "UTF-16";
// TODO: UTF-16BE, UTF-16LE ... no BOM; what
// release of JDK supports those Unicode names?
}
// if (dangerMask != 0)
// stringBuf = new StringBuffer();
}
this.encoding = encoding;
}
/**
* Resets the handler to write a new text document.
*
* @param writer XML text is written to this writer.
* @param encoding if non-null, and an XML declaration is written,
* this is the name that will be used for the character encoding.
*
* @exception IllegalStateException if the current
* document hasn"t yet ended (i.e. the output stream {@link #out} is not null)
*/
final public void setWriter(Writer writer, String encoding)
{
if (this.out != null)
throw new IllegalStateException(
"can"t change stream in mid course");
this.out = writer;
if (this.out != null)
setEncoding(encoding);
// if (!(this.out instanceof BufferedWriter))
// this.out = new BufferedWriter(this.out);
}
public XmlWriter writeDeclaration() throws IOException
{
if (this.encoding != null)
{
this.out.write("<?xml version="1.0"");
this.out.write(" encoding="" + this.encoding + """);
this.out.write("?>");
this.out.write(this.newline);
}
return this;
}
public XmlWriter writeDoctype(String systemId, String publicId) throws IOException
{
if (systemId != null || publicId != null)
{
this.out.write("<!DOCTYPE dataset");
if (systemId != null)
{
this.out.write(" SYSTEM \"");
this.out.write(systemId);
this.out.write("\"");
}
if (publicId != null)
{
this.out.write(" PUBLIC \"");
this.out.write(publicId);
this.out.write("\"");
}
this.out.write(">");
this.out.write(this.newline);
}
return this;
}
}
Use DOM L3 DOMBuilder, DOMBuilderFilter DOMWriter and other DOM L3 functionality to preparse, revalidate and safe document.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMError;
import org.w3c.dom.DOMErrorHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSParser;
import org.w3c.dom.ls.LSParserFilter;
import org.w3c.dom.ls.LSSerializer;
import org.w3c.dom.traversal.NodeFilter;
/**
* This sample program illustrates how to use DOM L3 DOMBuilder,
* DOMBuilderFilter DOMWriter and other DOM L3 functionality to preparse,
* revalidate and safe document.
*/
public class DOM3 implements DOMErrorHandler, LSParserFilter {
/** Default namespaces support (true). */
protected static final boolean DEFAULT_NAMESPACES = true;
/** Default validation support (false). */
protected static final boolean DEFAULT_VALIDATION = false;
/** Default Schema validation support (false). */
protected static final boolean DEFAULT_SCHEMA_VALIDATION = false;
static LSParser builder;
public static void main(String[] argv) {
if (argv.length == 0) {
printUsage();
System.exit(1);
}
try {
// get DOM Implementation using DOM Registry
System.setProperty(DOMImplementationRegistry.PROPERTY,
"org.apache.xerces.dom.DOMXSImplementationSourceImpl");
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
// create DOMBuilder
builder = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
DOMConfiguration config = builder.getDomConfig();
// create Error Handler
DOMErrorHandler errorHandler = new DOM3();
// create filter
LSParserFilter filter = new DOM3();
builder.setFilter(filter);
// set error handler
config.setParameter("error-handler", errorHandler);
// set validation feature
// config.setParameter("validate", Boolean.FALSE);
config.setParameter("validate", Boolean.TRUE);
// set schema language
config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema");
// config.setParameter("psvi",Boolean.TRUE);
// config.setParameter("schema-type","http://www.w3.org/TR/REC-xml");
// set schema location
config.setParameter("schema-location", "personal.xsd");
// parse document
System.out.println("Parsing " + argv[0] + "...");
Document doc = builder.parseURI(argv[0]);
// set error handler on the Document
config = doc.getDomConfig();
config.setParameter("error-handler", errorHandler);
// set validation feature
config.setParameter("validate", Boolean.TRUE);
config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema");
// config.setParameter("schema-type","http://www.w3.org/TR/REC-xml");
config.setParameter("schema-location", "data/personal.xsd");
// remove comments from the document
config.setParameter("comments", Boolean.FALSE);
System.out.println("Normalizing document... ");
doc.normalizeDocument();
// create DOMWriter
LSSerializer domWriter = impl.createLSSerializer();
System.out.println("Serializing document... ");
config = domWriter.getDomConfig();
config.setParameter("xml-declaration", Boolean.FALSE);
// config.setParameter("validate",errorHandler);
// serialize document to standard output
// domWriter.writeNode(System.out, doc);
LSOutput dOut = impl.createLSOutput();
dOut.setByteStream(System.out);
domWriter.write(doc, dOut);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static void printUsage() {
System.err.println("usage: java dom.DOM3 uri ...");
System.err.println();
System.err.println("NOTE: You can only validate DOM tree against XML Schemas.");
} // printUsage()
public boolean handleError(DOMError error) {
short severity = error.getSeverity();
if (severity == DOMError.SEVERITY_ERROR) {
System.out.println("[dom3-error]: " + error.getMessage());
}
if (severity == DOMError.SEVERITY_WARNING) {
System.out.println("[dom3-warning]: " + error.getMessage());
}
return true;
}
/**
* @see org.w3c.dom.ls.LSParserFilter#acceptNode(Node)
*/
public short acceptNode(Node enode) {
return NodeFilter.FILTER_ACCEPT;
}
/**
* @see org.w3c.dom.ls.LSParserFilter#getWhatToShow()
*/
public int getWhatToShow() {
return NodeFilter.SHOW_ELEMENT;
}
/**
* @see org.w3c.dom.ls.LSParserFilter#startElement(Element)
*/
public short startElement(Element elt) {
return LSParserFilter.FILTER_ACCEPT;
}
}
Write DOM out
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
/**
* Utility class collecting library methods related to XML processing. Stolen
* from nbbuild/antsrc and openide/.../xml.
*
* @author Petr Kuzel, Jesse Glick
*/
public final class XMLUtil {
public static void write(Document doc, OutputStream out) throws IOException {
// XXX note that this may fail to write out namespaces correctly if the
// document
// is created with namespaces and no explicit prefixes; however no code in
// this package is likely to be doing so
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
DocumentType dt = doc.getDoctype();
if (dt != null) {
String pub = dt.getPublicId();
if (pub != null) {
t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, pub);
}
t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dt.getSystemId());
}
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // NOI18N
t.setOutputProperty(OutputKeys.INDENT, "yes"); // NOI18N
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); // NOI18N
Source source = new DOMSource(doc);
Result result = new StreamResult(out);
t.transform(source, result);
} catch (Exception e) {
throw (IOException) new IOException(e.toString()).initCause(e);
} catch (TransformerFactoryConfigurationError e) {
throw (IOException) new IOException(e.toString()).initCause(e);
}
}
}
write Xml DOM Node
import java.io.OutputStream;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Node;
/**
*
*
* @author Costin Manolache
*/
public class Main {
public static void writeXml(Node n, OutputStream os) throws TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
// identity
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(n), new StreamResult(os));
}
}
Write Xml (Node n, OutputStream os)
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.OutputStream;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Node;
/**
* Few simple utils to read DOM. This is originally from the Jakarta Commons
* Modeler.
*
* @author Costin Manolache
*/
public class Utils {
public static void writeXml(Node n, OutputStream os) throws TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
// identity
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(n), new StreamResult(os));
}
}
Writing a DOM Document to an XML File
import java.io.File;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
public class Main {
public static void main(String[] argv) throws Exception {
Document doc = null;
String filename = "name.xml";
Source source = new DOMSource(doc);
File file = new File(filename);
Result result = new StreamResult(file);
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);
}
}
XML Document Writer
/*
* Copyright (c) 2000 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 2nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book (recommended),
* visit http://www.davidflanagan.ru/javaexamples2.
*/
import java.io.PrintWriter;
import org.w3c.dom.CDATASection;
import org.w3c.dom.rument;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
/**
* Output a DOM Level 1 Document object to a java.io.PrintWriter as a simple XML
* document. This class does not handle every type of DOM node, and it doesn"t
* deal with all the details of XML like DTDs, character encodings and preserved
* and ignored whitespace. However, it does output basic well-formed XML that
* can be parsed by a non-validating parser.
*/
public class XMLDocumentWriter {
PrintWriter out; // the stream to send output to
/** Initialize the output stream */
public XMLDocumentWriter(PrintWriter out) {
this.out = out;
}
/** Close the output stream. */
public void close() {
out.close();
}
/** Output a DOM Node (such as a Document) to the output stream */
public void write(Node node) {
write(node, "");
}
/**
* Output the specified DOM Node object, printing it using the specified
* indentation string
*/
public void write(Node node, String indent) {
// The output depends on the type of the node
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE: { // If its a Document node
Document doc = (Document) node;
out.println(indent + "<?xml version="1.0"?>"); // Output header
Node child = doc.getFirstChild(); // Get the first node
while (child != null) { // Loop "till no more nodes
write(child, indent); // Output node
child = child.getNextSibling(); // Get next node
}
break;
}
case Node.DOCUMENT_TYPE_NODE: { // It is a <!DOCTYPE> tag
DocumentType doctype = (DocumentType) node;
// Note that the DOM Level 1 does not give us information about
// the the public or system ids of the doctype, so we can"t output
// a complete <!DOCTYPE> tag here. We can do better with Level 2.
out.println("<!DOCTYPE " + doctype.getName() + ">");
break;
}
case Node.ELEMENT_NODE: { // Most nodes are Elements
Element elt = (Element) node;
out.print(indent + "<" + elt.getTagName()); // Begin start tag
NamedNodeMap attrs = elt.getAttributes(); // Get attributes
for (int i = 0; i < attrs.getLength(); i++) { // Loop through them
Node a = attrs.item(i);
out.print(" " + a.getNodeName() + "="" + // Print attr. name
fixup(a.getNodeValue()) + """); // Print attr. value
}
out.println(">"); // Finish start tag
String newindent = indent + " "; // Increase indent
Node child = elt.getFirstChild(); // Get child
while (child != null) { // Loop
write(child, newindent); // Output child
child = child.getNextSibling(); // Get next child
}
out.println(indent + "</" + // Output end tag
elt.getTagName() + ">");
break;
}
case Node.TEXT_NODE: { // Plain text node
Text textNode = (Text) node;
String text = textNode.getData().trim(); // Strip off space
if ((text != null) && text.length() > 0) // If non-empty
out.println(indent + fixup(text)); // print text
break;
}
case Node.PROCESSING_INSTRUCTION_NODE: { // Handle PI nodes
ProcessingInstruction pi = (ProcessingInstruction) node;
out.println(indent + "<?" + pi.getTarget() + " " + pi.getData()
+ "?>");
break;
}
case Node.ENTITY_REFERENCE_NODE: { // Handle entities
out.println(indent + "&" + node.getNodeName() + ";");
break;
}
case Node.CDATA_SECTION_NODE: { // Output CDATA sections
CDATASection cdata = (CDATASection) node;
// Careful! Don"t put a CDATA section in the program itself!
out.println(indent + "<" + "![CDATA[" + cdata.getData() + "]]"
+ ">");
break;
}
case Node.ruMENT_NODE: { // Comments
Comment c = (Comment) node;
out.println(indent + "<!--" + c.getData() + "-->");
break;
}
default: // Hopefully, this won"t happen too much!
System.err.println("Ignoring node: " + node.getClass().getName());
break;
}
}
// This method replaces reserved characters with entities.
String fixup(String s) {
StringBuffer sb = new StringBuffer();
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
switch (c) {
default:
sb.append(c);
break;
case "<":
sb.append("<");
break;
case ">":
sb.append(">");
break;
case "&":
sb.append("&");
break;
case """:
sb.append(""");
break;
case "\"":
sb.append("'");
break;
}
}
return sb.toString();
}
}
XML Writer
/**
* Copyright (c) 2004-2006 Regents of the University of California.
* See "license-prefuse.txt" for licensing terms.
*/
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Utility class for writing XML files. This class provides convenience
* methods for creating XML documents, such as starting and ending
* tags, and adding content and comments. This class handles correct
* XML formatting and will properly escape text to ensure that the
* text remains valid XML.
*
* <p>To use this class, create a new instance with the desired
* PrintWriter to write the XML to. Call the {@link #begin()} or
* {@link #begin(String, int)} method when ready to start outputting
* XML. Then use the provided methods to generate the XML file.
* Finally, call either the {@link #finish()} or {@link #finish(String)}
* methods to signal the completion of the file.</p>
*
* @author
*/
public class XMLWriter {
private PrintWriter m_out;
private int m_bias = 0;
private int m_tab;
private ArrayList m_tagStack = new ArrayList();
/**
* Create a new XMLWriter.
* @param out the print writer to write the XML to
*/
public XMLWriter(PrintWriter out) {
this(out, 2);
}
/**
* Create a new XMLWriter.
* @param out the print writer to write the XML to
* @param tabLength the number of spaces to use for each
* level of indentation in the XML file
*/
public XMLWriter(PrintWriter out, int tabLength) {
m_out = out;
m_tab = 2;
}
/**
* Print <em>unescaped</em> text into the XML file. To print
* escaped text, use the {@link #content(String)} method instead.
* @param s the text to print. This String will not be escaped.
*/
public void print(String s) {
m_out.print(s);
}
/**
* Print <em>unescaped</em> text into the XML file, followed by
* a newline. To print escaped text, use the {@link #content(String)}
* method instead.
* @param s the text to print. This String will not be escaped.
*/
public void println(String s) {
m_out.print(s);
m_out.print("\n");
}
/**
* Print a newline into the XML file.
*/
public void println() {
m_out.print("\n");
}
/**
* Begin the XML document. This must be called before any other
* formatting methods. This method prints an XML header into
* the top of the output stream.
*/
public void begin() {
m_out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
println();
}
/**
* Begin the XML document. This must be called before any other
* formatting methods. This method prints an XML header into
* the top of the output stream, plus additional header text
* provided by the client
* @param header header text to insert into the document
* @param bias the spacing bias to use for all subsequent indenting
*/
public void begin(String header, int bias) {
begin();
m_out.print(header);
m_bias = bias;
}
/**
* Print a comment in the XML document. The comment will be printed
* according to the current spacing and followed by a newline.
* @param comment the comment text
*/
public void comment(String comment) {
spacing();
m_out.print("<!-- ");
m_out.print(comment);
m_out.print(" -->");
println();
}
/**
* Internal method for printing a tag with attributes.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
* @param close true to close the tag, false to leave it
* open and adjust the spacing
*/
protected void tag(String tag, String[] names, String[] values,
int nattr, boolean close)
{
spacing();
m_out.print("<");
m_out.print(tag);
for ( int i=0; i<nattr; ++i ) {
m_out.print(" ");
m_out.print(names[i]);
m_out.print("=");
m_out.print("\"");
escapeString(values[i]);
m_out.print("\"");
}
if ( close ) m_out.print("/");
m_out.print(">");
println();
if ( !close ) {
m_tagStack.add(tag);
}
}
/**
* Print a closed tag with attributes. The tag will be followed by a
* newline.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
*/
public void tag(String tag, String[] names, String[] values, int nattr)
{
tag(tag, names, values, nattr, true);
}
/**
* Print a start tag with attributes. The tag will be followed by a
* newline, and the indentation level will be increased.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
*/
public void start(String tag, String[] names, String[] values, int nattr)
{
tag(tag, names, values, nattr, false);
}
/**
* Internal method for printing a tag with a single attribute.
* @param tag the tag name
* @param name the name of the attribute
* @param value the value of the attribute
* @param close true to close the tag, false to leave it
* open and adjust the spacing
*/
protected void tag(String tag, String name, String value, boolean close) {
spacing();
m_out.print("<");
m_out.print(tag);
m_out.print(" ");
m_out.print(name);
m_out.print("=");
m_out.print("\"");
escapeString(value);
m_out.print("\"");
if ( close ) m_out.print("/");
m_out.print(">");
println();
if ( !close ) {
m_tagStack.add(tag);
}
}
/**
* Print a closed tag with one attribute. The tag will be followed by a
* newline.
* @param tag the tag name
* @param name the name of the attribute
* @param value the value of the attribute
*/
public void tag(String tag, String name, String value)
{
tag(tag, name, value, true);
}
/**
* Print a start tag with one attribute. The tag will be followed by a
* newline, and the indentation level will be increased.
* @param tag the tag name
* @param name the name of the attribute
* @param value the value of the attribute
*/
public void start(String tag, String name, String value)
{
tag(tag, name, value, false);
}
/**
* Internal method for printing a tag with attributes.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
* @param close true to close the tag, false to leave it
* open and adjust the spacing
*/
protected void tag(String tag, ArrayList names, ArrayList values,
int nattr, boolean close)
{
spacing();
m_out.print("<");
m_out.print(tag);
for ( int i=0; i<nattr; ++i ) {
m_out.print(" ");
m_out.print((String)names.get(i));
m_out.print("=");
m_out.print("\"");
escapeString((String)values.get(i));
m_out.print("\"");
}
if ( close ) m_out.print("/");
m_out.print(">");
println();
if ( !close ) {
m_tagStack.add(tag);
}
}
/**
* Print a closed tag with attributes. The tag will be followed by a
* newline.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
*/
public void tag(String tag, ArrayList names, ArrayList values, int nattr)
{
tag(tag, names, values, nattr, true);
}
/**
* Print a start tag with attributes. The tag will be followed by a
* newline, and the indentation level will be increased.
* @param tag the tag name
* @param names the names of the attributes
* @param values the values of the attributes
* @param nattr the number of attributes
*/
public void start(String tag, ArrayList names, ArrayList values, int nattr)
{
tag(tag, names, values, nattr, false);
}
/**
* Print a start tag without attributes. The tag will be followed by a
* newline, and the indentation level will be increased.
* @param tag the tag name
*/
public void start(String tag) {
tag(tag, (String[])null, null, 0, false);
}
/**
* Close the most recently opened tag. The tag will be followed by a
* newline, and the indentation level will be decreased.
*/
public void end() {
String tag = (String)m_tagStack.remove(m_tagStack.size()-1);
spacing();
m_out.print("<");
m_out.print("/");
m_out.print(tag);
m_out.print(">");
println();
}
/**
* Print a new content tag with a single attribute, consisting of an
* open tag, content text, and a closing tag, all on one line.
* @param tag the tag name
* @param name the name of the attribute
* @param value the value of the attribute, this text will be escaped
* @param content the text content, this text will be escaped
*/
public void contentTag(String tag, String name, String value,
String content)
{
spacing();
m_out.print("<"); m_out.print(tag); m_out.print(" ");
m_out.print(name); m_out.print("=");
m_out.print("\""); escapeString(value); m_out.print("\"");
m_out.print(">");
escapeString(content);
m_out.print("<"); m_out.print("/"); m_out.print(tag); m_out.print(">");
println();
}
/**
* Print a new content tag with no attributes, consisting of an
* open tag, content text, and a closing tag, all on one line.
* @param tag the tag name
* @param content the text content, this text will be escaped
*/
public void contentTag(String tag, String content) {
spacing();
m_out.print("<"); m_out.print(tag); m_out.print(">");
escapeString(content);
m_out.print("<"); m_out.print("/"); m_out.print(tag); m_out.print(">");
println();
}
/**
* Print content text.
* @param content the content text, this text will be escaped
*/
public void content(String content) {
escapeString(content);
}
/**
* Finish the XML document.
*/
public void finish() {
m_bias = 0;
m_out.flush();
}
/**
* Finish the XML document, printing the given footer text at the
* end of the document.
* @param footer the footer text, this will not be escaped
*/
public void finish(String footer) {
m_bias = 0;
m_out.print(footer);
m_out.flush();
}
/**
* Print the current spacing (determined by the indentation level)
* into the document. This method is used by many of the other
* formatting methods, and so should only need to be called in
* the case of custom text printing outside the mechanisms
* provided by this class.
*/
public void spacing() {
int len = m_bias + m_tagStack.size() * m_tab;
for ( int i=0; i<len; ++i )
m_out.print(" ");
}
// ------------------------------------------------------------------------
// Escape Text
// unicode ranges and valid/invalid characters
private static final char LOWER_RANGE = 0x20;
private static final char UPPER_RANGE = 0x7f;
private static final char[] VALID_CHARS = { 0x9, 0xA, 0xD };
private static final char[] INVALID = { "<", ">", """, "\"", "&" };
private static final String[] VALID =
{ "<", ">", """, "'", "&" };
/**
* Escape a string such that it is safe to use in an XML document.
* @param str the string to escape
*/
protected void escapeString(String str) {
if ( str == null ) {
m_out.print("null");
return;
}
int len = str.length();
for (int i = 0; i < len; ++i) {
char c = str.charAt(i);
if ( (c < LOWER_RANGE && c != VALID_CHARS[0] &&
c != VALID_CHARS[1] && c != VALID_CHARS[2])
|| (c > UPPER_RANGE) )
{
// character out of range, escape with character value
m_out.print("&#");
m_out.print(Integer.toString(c));
m_out.print(";");
} else {
boolean valid = true;
// check for invalid characters (e.g., "<", "&", etc)
for (int j=INVALID.length-1; j >= 0; --j )
{
if ( INVALID[j] == c) {
valid = false;
m_out.print(VALID[j]);
break;
}
}
// if character is valid, don"t escape
if (valid) {
m_out.print(c);
}
}
}
}
} // end of class XMLWriter
XMLWriter helper class
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.Writer;
/**
* XMLWriter helper class.
*
* @author
*/
public class XMLWriter {
// -------------------------------------------------------------- Constants
/**
* Opening tag.
*/
public static final int OPENING = 0;
/**
* Closing tag.
*/
public static final int CLOSING = 1;
/**
* Element with no content.
*/
public static final int NO_CONTENT = 2;
// ----------------------------------------------------- Instance Variables
/**
* Buffer.
*/
protected StringBuffer buffer = new StringBuffer();
/**
* Writer.
*/
protected Writer writer = null;
// ----------------------------------------------------------- Constructors
/**
* Constructor.
*/
public XMLWriter() {
}
/**
* Constructor.
*/
public XMLWriter(Writer writer) {
this.writer = writer;
}
// --------------------------------------------------------- Public Methods
/**
* Retrieve generated XML.
*
* @return String containing the generated XML
*/
public String toString() {
return buffer.toString();
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param namespaceInfo Namespace info
* @param name Property name
* @param value Property value
*/
public void writeProperty(String namespace, String namespaceInfo,
String name, String value) {
writeElement(namespace, namespaceInfo, name, OPENING);
buffer.append(value);
writeElement(namespace, namespaceInfo, name, CLOSING);
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param name Property name
* @param value Property value
*/
public void writeProperty(String namespace, String name, String value) {
writeElement(namespace, name, OPENING);
buffer.append(value);
writeElement(namespace, name, CLOSING);
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param name Property name
*/
public void writeProperty(String namespace, String name) {
writeElement(namespace, name, NO_CONTENT);
}
/**
* Write an element.
*
* @param name Element name
* @param namespace Namespace abbreviation
* @param type Element type
*/
public void writeElement(String namespace, String name, int type) {
writeElement(namespace, null, name, type);
}
/**
* Write an element.
*
* @param namespace Namespace abbreviation
* @param namespaceInfo Namespace info
* @param name Element name
* @param type Element type
*/
public void writeElement(String namespace, String namespaceInfo,
String name, int type) {
if ((namespace != null) && (namespace.length() > 0)) {
switch (type) {
case OPENING:
if (namespaceInfo != null) {
buffer.append("<" + namespace + ":" + name + " xmlns:"
+ namespace + "=\""
+ namespaceInfo + "\">");
} else {
buffer.append("<" + namespace + ":" + name + ">");
}
break;
case CLOSING:
buffer.append("</" + namespace + ":" + name + ">\n");
break;
case NO_CONTENT:
default:
if (namespaceInfo != null) {
buffer.append("<" + namespace + ":" + name + " xmlns:"
+ namespace + "=\""
+ namespaceInfo + "\"/>");
} else {
buffer.append("<" + namespace + ":" + name + "/>");
}
break;
}
} else {
switch (type) {
case OPENING:
buffer.append("<" + name + ">");
break;
case CLOSING:
buffer.append("</" + name + ">\n");
break;
case NO_CONTENT:
default:
buffer.append("<" + name + "/>");
break;
}
}
}
/**
* Write text.
*
* @param text Text to append
*/
public void writeText(String text) {
buffer.append(text);
}
/**
* Write data.
*
* @param data Data to append
*/
public void writeData(String data) {
buffer.append(data);
}
/**
* Write XML Header.
*/
public void writeXMLHeader() {
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
}
/**
* Send data and reinitializes buffer.
*/
public void sendData()
throws IOException {
if (writer != null) {
writer.write(buffer.toString());
buffer = new StringBuffer();
}
}
}
XMLWriter.java - serialize an XML document
// XMLWriter.java - serialize an XML document.
// Written by David Megginson, david@megginson.ru
// and placed by him into the public domain.
// Extensively modified by John Cowan for TagSoup.
// TagSoup is licensed under the Apache License,
// Version 2.0. You may obtain a copy of this license at
// http://www.apache.org/licenses/LICENSE-2.0 . You may also have
// additional legal rights not granted by this license.
//
// TagSoup is distributed in the hope that it will be useful, but
// unless required by applicable law or agreed to in writing, TagSoup
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied; not even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.NamespaceSupport;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.ext.LexicalHandler;
/**
* Filter to write an XML document from a SAX event stream.
*
* <p>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.</p>
*
* <p>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</p>
*
* <pre>
* w.startElement("foo");
* </pre>
*
* <p>is equivalent to the regular SAX2 ContentHandler method</p>
*
* <pre>
* w.startElement("", "foo", "", new AttributesImpl());
* </pre>
*
* <p>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:</p>
*
* <pre>
* XMLWriter w = new XMLWriter();
*
* w.startDocument();
* w.startElement("greeting");
* w.characters("Hello, world!");
* w.endElement("greeting");
* w.endDocument();
* </pre>
*
* <p>The resulting document will look like this:</p>
*
* <pre>
* <?xml version="1.0" standalone="yes"?>
*
* <greeting>Hello, world!</greeting>
* </pre>
*
* <p>In fact, there is an even simpler convenience method,
* <var>dataElement</var>, designed for writing elements that
* contain only character data, so the code to generate the
* document could be shortened to</p>
*
* <pre>
* XMLWriter w = new XMLWriter();
*
* w.startDocument();
* w.dataElement("greeting", "Hello, world!");
* w.endDocument();
* </pre>
*
* <h2>Whitespace</h2>
*
* <p>According to the XML Recommendation, <em>all</em> 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</p>
*
* <pre>
* w.dataElement("item", "1");
* w.dataElement("item", "2");
* w.dataElement("item", "3");
* </pre>
*
* <p>you will end up with</p>
*
* <pre>
* <item>1</item><item>3</item><item>3</item>
* </pre>
*
* <p>You need to invoke one of the <var>characters</var> methods
* explicitly to add newlines or indentation. Alternatively, you
* can use {@link com.megginson.sax.DataWriter 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).</p>
*
*
* <h2>Namespace Support</h2>
*
* <p>The writer contains extensive support for XML Namespaces, so that
* a client application does not have to keep track of prefixes and
* supply <var>xmlns</var> 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:</p>
*
* <pre>
* w.startDocument();
* w.emptyElement("http://www.foo.ru/ns/", "foo");
* w.endDocument();
* </pre>
*
* <p>The resulting document will look like this:</p>
*
* <pre>
* <?xml version="1.0" standalone="yes"?>
*
* <_NS1:foo xmlns:_NS1="http://www.foo.ru/ns/"/>
* </pre>
*
* <p>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:</p>
*
* <ol>
* <li>the qualified name</li>
* <li>the {@link #setPrefix setPrefix} method.</li>
* </ol>
*
* <p>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).</p>
*
* <p>Before writing a document, the client can also pre-map a prefix
* to a Namespace URI with the setPrefix method:</p>
*
* <pre>
* w.setPrefix("http://www.foo.ru/ns/", "foo");
* w.startDocument();
* w.emptyElement("http://www.foo.ru/ns/", "foo");
* w.endDocument();
* </pre>
*
* <p>The resulting document will look like this:</p>
*
* <pre>
* <?xml version="1.0" standalone="yes"?>
*
* <foo:foo xmlns:foo="http://www.foo.ru/ns/"/>
* </pre>
*
* <p>The default Namespace simply uses an empty string as the prefix:</p>
*
* <pre>
* w.setPrefix("http://www.foo.ru/ns/", "");
* w.startDocument();
* w.emptyElement("http://www.foo.ru/ns/", "foo");
* w.endDocument();
* </pre>
*
* <p>The resulting document will look like this:</p>
*
* <pre>
* <?xml version="1.0" standalone="yes"?>
*
* <foo xmlns="http://www.foo.ru/ns/"/>
* </pre>
*
* <p>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:</p>
*
* <pre>
* <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.ru/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>
* </pre>
*
* <p>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:</p>
*
* <pre>
* w.forceNSDecl("http://www.purl.org/dc/");
* </pre>
*
* <p>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:</p>
*
* <pre>
* <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.ru/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>
* </pre>
*
* <p>This approach is also useful for declaring Namespace prefixes
* that be used by qualified names appearing in attribute values or
* character data.</p>
*
* @author David Megginson, david@megginson.ru
* @version 0.2
* @see org.xml.sax.XMLFilter
* @see org.xml.sax.ContentHandler
*/
public class XMLWriter extends XMLFilterImpl implements LexicalHandler
{
////////////////////////////////////////////////////////////////////
// Constructors.
////////////////////////////////////////////////////////////////////
/**
* Create a new XML writer.
*
* <p>Write to standard output.</p>
*/
public XMLWriter ()
{
init(null);
}
/**
* Create a new XML writer.
*
* <p>Write to the writer provided.</p>
*
* @param writer The output destination, or null to use standard
* output.
*/
public XMLWriter (Writer writer)
{
init(writer);
}
/**
* Create a new XML writer.
*
* <p>Use the specified XML reader as the parent.</p>
*
* @param xmlreader The parent in the filter chain, or null
* for no parent.
*/
public XMLWriter (XMLReader xmlreader)
{
super(xmlreader);
init(null);
}
/**
* Create a new XML writer.
*
* <p>Use the specified XML reader as the parent, and write
* to the specified writer.</p>
*
* @param xmlreader The parent in the filter chain, or null
* for no parent.
* @param writer The output destination, or null to use standard
* output.
*/
public XMLWriter (XMLReader xmlreader, Writer writer)
{
super(xmlreader);
init(writer);
}
/**
* Internal initialization method.
*
* <p>All of the public constructors invoke this method.
*
* @param writer The output destination, or null to use
* standard output.
*/
private void init (Writer writer)
{
setOutput(writer);
nsSupport = new NamespaceSupport();
prefixTable = new Hashtable();
forcedDeclTable = new Hashtable();
doneDeclTable = new Hashtable();
outputProperties = new Properties();
}
////////////////////////////////////////////////////////////////////
// Public methods.
////////////////////////////////////////////////////////////////////
/**
* Reset the writer.
*
* <p>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.</p>
*
* <p>This method is invoked automatically by the
* {@link #startDocument startDocument} method before writing
* a new document.</p>
*
* <p><strong>Note:</strong> this method will <em>not</em>
* clear the prefix or URI information in the writer or
* the selected output writer.</p>
*
* @see #flush
*/
public void reset ()
{
elementLevel = 0;
prefixCounter = 0;
nsSupport.reset();
}
/**
* Flush the output.
*
* <p>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.</p>
*
* <p>This method is invoked automatically by the
* {@link #endDocument endDocument} method after writing a
* document.</p>
*
* @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.
* @return The current output writer.
* @see #flush
*/
public void setOutput (Writer writer)
{
if (writer == null) {
output = new OutputStreamWriter(System.out);
} else {
output = writer;
}
}
/**
* Specify a preferred prefix for a Namespace URI.
*
* <p>Note that this method does not actually force the Namespace
* to be declared; to do that, use the {@link
* #forceNSDecl(java.lang.String) forceNSDecl} method as well.</p>
*
* @param uri The Namespace URI.
* @param prefix The preferred prefix, or "" to select
* the default Namespace.
* @see #getPrefix
* @see #forceNSDecl(java.lang.String)
* @see #forceNSDecl(java.lang.String,java.lang.String)
*/
public void setPrefix (String uri, String prefix)
{
prefixTable.put(uri, prefix);
}
/**
* Get the current or preferred prefix for a Namespace URI.
*
* @param uri The Namespace URI.
* @return The preferred prefix, or "" for the default Namespace.
* @see #setPrefix
*/
public String getPrefix (String uri)
{
return (String)prefixTable.get(uri);
}
/**
* Force a Namespace to be declared on the root element.
*
* <p>By default, the XMLWriter will declare only the Namespaces
* needed for an element; as a result, a Namespace may be
* declared many places in a document if it is not used on the
* root element.</p>
*
* <p>This method forces a Namespace to be declared on the root
* element even if it is not used there, and reduces the number
* of xmlns attributes in the document.</p>
*
* @param uri The Namespace URI to declare.
* @see #forceNSDecl(java.lang.String,java.lang.String)
* @see #setPrefix
*/
public void forceNSDecl (String uri)
{
forcedDeclTable.put(uri, Boolean.TRUE);
}
/**
* Force a Namespace declaration with a preferred prefix.
*
* <p>This is a convenience method that invokes {@link
* #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
* forceNSDecl}.</p>
*
* @param uri The Namespace URI to declare on the root element.
* @param prefix The preferred prefix for the Namespace, or ""
* for the default Namespace.
* @see #setPrefix
* @see #forceNSDecl(java.lang.String)
*/
public void forceNSDecl (String uri, String prefix)
{
setPrefix(uri, prefix);
forceNSDecl(uri);
}
////////////////////////////////////////////////////////////////////
// Methods from org.xml.sax.ContentHandler.
////////////////////////////////////////////////////////////////////
/**
* Write the XML declaration at the beginning of the document.
*
* Pass the event on down the filter chain for further processing.
*
* @exception org.xml.sax.SAXException If there is an error
* writing the XML declaration, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#startDocument
*/
public void startDocument ()
throws SAXException
{
reset();
if (!("yes".equals(outputProperties.getProperty(OMIT_XML_DECLARATION, "no")))) {
write("<?xml");
if (version == null) {
write(" version=\"1.0\"");
} else {
write(" version=\"");
write(version);
write("\"");
}
if (outputEncoding != null && outputEncoding != "") {
write(" encoding=\"");
write(outputEncoding);
write("\"");
}
if (standalone == null) {
write(" standalone=\"yes\"?>\n");
} else {
write(" standalone=\"");
write(standalone);
write("\"");
}
}
super.startDocument();
}
/**
* Write a newline at the end of the document.
*
* Pass the event on down the filter chain for further processing.
*
* @exception org.xml.sax.SAXException If there is an error
* writing the newline, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#endDocument
*/
public void endDocument ()
throws SAXException
{
write("\n");
super.endDocument();
try {
flush();
} catch (IOException e) {
throw new SAXException(e);
}
}
/**
* Write a start tag.
*
* Pass the event on down the filter chain for further processing.
*
* @param uri The Namespace URI, or the empty string if none
* is available.
* @param localName The element"s local (unprefixed) name (required).
* @param qName The element"s qualified (prefixed) name, or the
* empty string is none is available. This method will
* use the qName as a template for generating a prefix
* if necessary, but it is not guaranteed to use the
* same qName.
* @param atts The element"s attribute list (must not be null).
* @exception org.xml.sax.SAXException If there is an error
* writing the start tag, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#startElement
*/
public void startElement (String uri, String localName,
String qName, Attributes atts)
throws SAXException
{
elementLevel++;
nsSupport.pushContext();
if (forceDTD && !hasOutputDTD) startDTD(localName == null ? qName : localName, "", "");
write("<");
writeName(uri, localName, qName, true);
writeAttributes(atts);
if (elementLevel == 1) {
forceNSDecls();
}
writeNSDecls();
write(">");
// System.out.println("%%%% startElement [" + qName + "] htmlMode = " + htmlMode);
if (htmlMode && (qName.equals("script") || qName.equals("style"))) {
cdataElement = true;
// System.out.println("%%%% CDATA element");
}
super.startElement(uri, localName, qName, atts);
}
/**
* Write an end tag.
*
* Pass the event on down the filter chain for further processing.
*
* @param uri The Namespace URI, or the empty string if none
* is available.
* @param localName The element"s local (unprefixed) name (required).
* @param qName The element"s qualified (prefixed) name, or the
* empty string is none is available. This method will
* use the qName as a template for generating a prefix
* if necessary, but it is not guaranteed to use the
* same qName.
* @exception org.xml.sax.SAXException If there is an error
* writing the end tag, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#endElement
*/
public void endElement (String uri, String localName, String qName)
throws SAXException
{
if (!(htmlMode &&
(uri.equals("http://www.w3.org/1999/xhtml") ||
uri.equals("")) &&
(qName.equals("area") || qName.equals("base") ||
qName.equals("basefont") || qName.equals("br") ||
qName.equals("col") || qName.equals("frame") ||
qName.equals("hr") || qName.equals("img") ||
qName.equals("input") || qName.equals("isindex") ||
qName.equals("link") || qName.equals("meta") ||
qName.equals("param")))) {
write("</");
writeName(uri, localName, qName, true);
write(">");
}
if (elementLevel == 1) {
write("\n");
}
cdataElement = false;
super.endElement(uri, localName, qName);
nsSupport.popContext();
elementLevel--;
}
/**
* Write character data.
*
* Pass the event on down the filter chain for further processing.
*
* @param ch The array of characters to write.
* @param start The starting position in the array.
* @param length The number of characters to write.
* @exception org.xml.sax.SAXException If there is an error
* writing the characters, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#characters
*/
public void characters (char ch[], int start, int len)
throws SAXException
{
if (!cdataElement) {
writeEsc(ch, start, len, false);
}
else {
for (int i = start; i < start + len; i++) {
write(ch[i]);
}
}
super.characters(ch, start, len);
}
/**
* Write ignorable whitespace.
*
* Pass the event on down the filter chain for further processing.
*
* @param ch The array of characters to write.
* @param start The starting position in the array.
* @param length The number of characters to write.
* @exception org.xml.sax.SAXException If there is an error
* writing the whitespace, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#ignorableWhitespace
*/
public void ignorableWhitespace (char ch[], int start, int length)
throws SAXException
{
writeEsc(ch, start, length, false);
super.ignorableWhitespace(ch, start, length);
}
/**
* Write a processing instruction.
*
* Pass the event on down the filter chain for further processing.
*
* @param target The PI target.
* @param data The PI data.
* @exception org.xml.sax.SAXException If there is an error
* writing the PI, or if a handler further down
* the filter chain raises an exception.
* @see org.xml.sax.ContentHandler#processingInstruction
*/
public void processingInstruction (String target, String data)
throws SAXException
{
write("<?");
write(target);
write(" ");
write(data);
write("?>");
if (elementLevel < 1) {
write("\n");
}
super.processingInstruction(target, data);
}
////////////////////////////////////////////////////////////////////
// Additional markup.
////////////////////////////////////////////////////////////////////
/**
* Write an empty element.
*
* This method writes an empty element tag rather than a start tag
* followed by an end tag. Both a {@link #startElement
* startElement} and an {@link #endElement endElement} event will
* be passed on down the filter chain.
*
* @param uri The element"s Namespace URI, or the empty string
* if the element has no Namespace or if Namespace
* processing is not being performed.
* @param localName The element"s local name (without prefix). This
* parameter must be provided.
* @param qName The element"s qualified name (with prefix), or
* the empty string if none is available. This parameter
* is strictly advisory: the writer may or may not use
* the prefix attached.
* @param atts The element"s attribute list.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement
* @see #endElement
*/
public void emptyElement (String uri, String localName,
String qName, Attributes atts)
throws SAXException
{
nsSupport.pushContext();
write("<");
writeName(uri, localName, qName, true);
writeAttributes(atts);
if (elementLevel == 1) {
forceNSDecls();
}
writeNSDecls();
write("/>");
super.startElement(uri, localName, qName, atts);
super.endElement(uri, localName, qName);
}
////////////////////////////////////////////////////////////////////
// Convenience methods.
////////////////////////////////////////////////////////////////////
/**
* Start a new element without a qname or attributes.
*
* <p>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.</p>
*
* @param uri The element"s Namespace URI.
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the start tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement(String, String, String, Attributes)
*/
public void startElement (String uri, String localName)
throws SAXException
{
startElement(uri, localName, "", EMPTY_ATTS);
}
/**
* Start a new element without a qname, attributes or a Namespace URI.
*
* <p>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.</p>
*
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the start tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement(String, String, String, Attributes)
*/
public void startElement (String localName)
throws SAXException
{
startElement("", localName, "", EMPTY_ATTS);
}
/**
* End an element without a qname.
*
* <p>This method will supply an empty string for the qName.
* It invokes {@link #endElement(String, String, String)}
* directly.</p>
*
* @param uri The element"s Namespace URI.
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the end tag, or if a handler further down
* the filter chain raises an exception.
* @see #endElement(String, String, String)
*/
public void endElement (String uri, String localName)
throws SAXException
{
endElement(uri, localName, "");
}
/**
* End an element without a Namespace URI or qname.
*
* <p>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.</p>
*
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the end tag, or if a handler further down
* the filter chain raises an exception.
* @see #endElement(String, String, String)
*/
public void endElement (String localName)
throws SAXException
{
endElement("", localName, "");
}
/**
* Add an empty element without a qname or attributes.
*
* <p>This method will supply an empty string for the qname
* and an empty attribute list. It invokes
* {@link #emptyElement(String, String, String, Attributes)}
* directly.</p>
*
* @param uri The element"s Namespace URI.
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #emptyElement(String, String, String, Attributes)
*/
public void emptyElement (String uri, String localName)
throws SAXException
{
emptyElement(uri, localName, "", EMPTY_ATTS);
}
/**
* Add an empty element without a Namespace URI, qname or attributes.
*
* <p>This method will supply an empty string for the qname,
* and empty string for the Namespace URI, and an empty
* attribute list. It invokes
* {@link #emptyElement(String, String, String, Attributes)}
* directly.</p>
*
* @param localName The element"s local name.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #emptyElement(String, String, String, Attributes)
*/
public void emptyElement (String localName)
throws SAXException
{
emptyElement("", localName, "", EMPTY_ATTS);
}
/**
* Write an element with character data content.
*
* <p>This is a convenience method to write a complete element
* with character data content, including the start tag
* and end tag.</p>
*
* <p>This method invokes
* {@link #startElement(String, String, String, Attributes)},
* followed by
* {@link #characters(String)}, followed by
* {@link #endElement(String, String, String)}.</p>
*
* @param uri The element"s Namespace URI.
* @param localName The element"s local name.
* @param qName The element"s default qualified name.
* @param atts The element"s attributes.
* @param content The character data content.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement(String, String, String, Attributes)
* @see #characters(String)
* @see #endElement(String, String, String)
*/
public void dataElement (String uri, String localName,
String qName, Attributes atts,
String content)
throws SAXException
{
startElement(uri, localName, qName, atts);
characters(content);
endElement(uri, localName, qName);
}
/**
* Write an element with character data content but no attributes.
*
* <p>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.</p>
*
* <p>This method invokes
* {@link #startElement(String, String, String, Attributes)},
* followed by
* {@link #characters(String)}, followed by
* {@link #endElement(String, String, String)}.</p>
*
* @param uri The element"s Namespace URI.
* @param localName The element"s local name.
* @param content The character data content.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement(String, String, String, Attributes)
* @see #characters(String)
* @see #endElement(String, String, String)
*/
public void dataElement (String uri, String localName, String content)
throws SAXException
{
dataElement(uri, localName, "", EMPTY_ATTS, content);
}
/**
* Write an element with character data content but no attributes or Namespace URI.
*
* <p>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.</p>
*
* <p>This method invokes
* {@link #startElement(String, String, String, Attributes)},
* followed by
* {@link #characters(String)}, followed by
* {@link #endElement(String, String, String)}.</p>
*
* @param localName The element"s local name.
* @param content The character data content.
* @exception org.xml.sax.SAXException If there is an error
* writing the empty tag, or if a handler further down
* the filter chain raises an exception.
* @see #startElement(String, String, String, Attributes)
* @see #characters(String)
* @see #endElement(String, String, String)
*/
public void dataElement (String localName, String content)
throws SAXException
{
dataElement("", localName, "", EMPTY_ATTS, content);
}
/**
* Write a string of character data, with XML escaping.
*
* <p>This is a convenience method that takes an XML
* String, converts it to a character array, then invokes
* {@link #characters(char[], int, int)}.</p>
*
* @param data The character data.
* @exception org.xml.sax.SAXException If there is an error
* writing the string, or if a handler further down
* the filter chain raises an exception.
* @see #characters(char[], int, int)
*/
public void characters (String data)
throws SAXException
{
char ch[] = data.toCharArray();
characters(ch, 0, ch.length);
}
////////////////////////////////////////////////////////////////////
// Internal methods.
////////////////////////////////////////////////////////////////////
/**
* Force all Namespaces to be declared.
*
* This method is used on the root element to ensure that
* the predeclared Namespaces all appear.
*/
private void forceNSDecls ()
{
Enumeration prefixes = forcedDeclTable.keys();
while (prefixes.hasMoreElements()) {
String prefix = (String)prefixes.nextElement();
doPrefix(prefix, null, true);
}
}
/**
* Determine the prefix for an element or attribute name.
*
* TODO: this method probably needs some cleanup.
*
* @param uri The Namespace URI.
* @param qName The qualified name (optional); this will be used
* to indicate the preferred prefix if none is currently
* bound.
* @param isElement true if this is an element name, false
* if it is an attribute name (which cannot use the
* default Namespace).
*/
private String doPrefix (String uri, String qName, boolean isElement)
{
String defaultNS = nsSupport.getURI("");
if ("".equals(uri)) {
if (isElement && defaultNS != null)
nsSupport.declarePrefix("", "");
return null;
}
String prefix;
if (isElement && defaultNS != null && uri.equals(defaultNS)) {
prefix = "";
} else {
prefix = nsSupport.getPrefix(uri);
}
if (prefix != null) {
return prefix;
}
prefix = (String) doneDeclTable.get(uri);
if (prefix != null &&
((!isElement || defaultNS != null) &&
"".equals(prefix) || nsSupport.getURI(prefix) != null)) {
prefix = null;
}
if (prefix == null) {
prefix = (String) prefixTable.get(uri);
if (prefix != null &&
((!isElement || defaultNS != null) &&
"".equals(prefix) || nsSupport.getURI(prefix) != null)) {
prefix = null;
}
}
if (prefix == null && qName != null && !"".equals(qName)) {
int i = qName.indexOf(":");
if (i == -1) {
if (isElement && defaultNS == null) {
prefix = "";
}
} else {
prefix = qName.substring(0, i);
}
}
for (;
prefix == null || nsSupport.getURI(prefix) != null;
prefix = "__NS" + ++prefixCounter)
;
nsSupport.declarePrefix(prefix, uri);
doneDeclTable.put(uri, prefix);
return prefix;
}
/**
* Write a raw character.
*
* @param c The character to write.
* @exception org.xml.sax.SAXException If there is an error writing
* the character, this method will throw an IOException
* wrapped in a SAXException.
*/
private void write (char c)
throws SAXException
{
try {
output.write(c);
} catch (IOException e) {
throw new SAXException(e);
}
}
/**
* Write a raw string.
*
* @param s
* @exception org.xml.sax.SAXException If there is an error writing
* the string, this method will throw an IOException
* wrapped in a SAXException
*/
private void write (String s)
throws SAXException
{
try {
output.write(s);
} catch (IOException e) {
throw new SAXException(e);
}
}
/**
* Write out an attribute list, escaping values.
*
* The names will have prefixes added to them.
*
* @param atts The attribute list to write.
* @exception org.xml.SAXException If there is an error writing
* the attribute list, this method will throw an
* IOException wrapped in a SAXException.
*/
private void writeAttributes (Attributes atts)
throws SAXException
{
int len = atts.getLength();
for (int i = 0; i < len; i++) {
char ch[] = atts.getValue(i).toCharArray();
write(" ");
writeName(atts.getURI(i), atts.getLocalName(i),
atts.getQName(i), false);
if (htmlMode &&
booleanAttribute(atts.getLocalName(i), atts.getQName(i), atts.getValue(i))) break;
write("=\"");
writeEsc(ch, 0, ch.length, true);
write(""");
}
}
private String[] booleans = {"checked", "compact", "declare", "defer",
"disabled", "ismap", "multiple",
"nohref", "noresize", "noshade",
"nowrap", "readonly", "selected"};
// Return true if the attribute is an HTML boolean from the above list.
private boolean booleanAttribute (String localName, String qName, String value)
{
String name = localName;
if (name == null) {
int i = qName.indexOf(":");
if (i != -1) name = qName.substring(i + 1, qName.length());
}
if (!name.equals(value)) return false;
for (int j = 0; j < booleans.length; j++) {
if (name.equals(booleans[j])) return true;
}
return false;
}
/**
* Write an array of data characters with escaping.
*
* @param ch The array of characters.
* @param start The starting position.
* @param length The number of characters to use.
* @param isAttVal true if this is an attribute value literal.
* @exception org.xml.SAXException If there is an error writing
* the characters, this method will throw an
* IOException wrapped in a SAXException.
*/
private void writeEsc (char ch[], int start,
int length, boolean isAttVal)
throws SAXException
{
for (int i = start; i < start + length; i++) {
switch (ch[i]) {
case "&":
write("&");
break;
case "<":
write("<");
break;
case ">":
write(">");
break;
case "\"":
if (isAttVal) {
write(""");
} else {
write("\"");
}
break;
default:
if (!unicodeMode && ch[i] > "\u007f") {
write("&#");
write(Integer.toString(ch[i]));
write(";");
} else {
write(ch[i]);
}
}
}
}
/**
* Write out the list of Namespace declarations.
*
* @exception org.xml.sax.SAXException This method will throw
* an IOException wrapped in a SAXException if
* there is an error writing the Namespace
* declarations.
*/
private void writeNSDecls ()
throws SAXException
{
Enumeration prefixes = nsSupport.getDeclaredPrefixes();
while (prefixes.hasMoreElements()) {
String prefix = (String) prefixes.nextElement();
String uri = nsSupport.getURI(prefix);
if (uri == null) {
uri = "";
}
char ch[] = uri.toCharArray();
write(" ");
if ("".equals(prefix)) {
write("xmlns=\"");
} else {
write("xmlns:");
write(prefix);
write("=\"");
}
writeEsc(ch, 0, ch.length, true);
write("\"");
}
}
/**
* Write an element or attribute name.
*
* @param uri The Namespace URI.
* @param localName The local name.
* @param qName The prefixed name, if available, or the empty string.
* @param isElement true if this is an element name, false if it
* is an attribute name.
* @exception org.xml.sax.SAXException This method will throw an
* IOException wrapped in a SAXException if there is
* an error writing the name.
*/
private void writeName (String uri, String localName,
String qName, boolean isElement)
throws SAXException
{
String prefix = doPrefix(uri, qName, isElement);
if (prefix != null && !"".equals(prefix)) {
write(prefix);
write(":");
}
if (localName != null && !"".equals(localName)) {
write(localName);
} else {
int i = qName.indexOf(":");
write(qName.substring(i + 1, qName.length()));
}
}
////////////////////////////////////////////////////////////////////
// Default LexicalHandler implementation
////////////////////////////////////////////////////////////////////
public void comment(char[] ch, int start, int length) throws SAXException
{
write("<!--");
for (int i = start; i < start + length; i++) {
write(ch[i]);
if (ch[i] == "-" && i + 1 <= start + length && ch[i+1] == "-")
write(" ");
}
write("-->");
}
public void endCDATA() throws SAXException { }
public void endDTD() throws SAXException { }
public void endEntity(String name) throws SAXException { }
public void startCDATA() throws SAXException { }
public void startDTD(String name, String publicid, String systemid) throws SAXException {
if (name == null) return; // can"t cope
if (hasOutputDTD) return; // only one DTD
hasOutputDTD = true;
write("<!DOCTYPE ");
write(name);
if (systemid == null) systemid = "";
if (overrideSystem != null) systemid = overrideSystem;
char sysquote = (systemid.indexOf(""") != -1) ? "\"": """;
if (overridePublic != null) publicid = overridePublic;
if (!(publicid == null || "".equals(publicid))) {
char pubquote = (publicid.indexOf(""") != -1) ? "\"": """;
write(" PUBLIC ");
write(pubquote);
write(publicid);
write(pubquote);
write(" ");
}
else {
write(" SYSTEM ");
}
write(sysquote);
write(systemid);
write(sysquote);
write(">\n");
}
public void startEntity(String name) throws SAXException { }
////////////////////////////////////////////////////////////////////
// Output properties
////////////////////////////////////////////////////////////////////
public String getOutputProperty(String key) {
return outputProperties.getProperty(key);
}
public void setOutputProperty(String key, String value) {
outputProperties.setProperty(key, value);
// System.out.println("%%%% key = [" + key + "] value = [" + value +"]");
if (key.equals(ENCODING)) {
outputEncoding = value;
unicodeMode = value.substring(0, 3).equalsIgnoreCase("utf");
// System.out.println("%%%% unicodeMode = " + unicodeMode);
}
else if (key.equals(METHOD)) {
htmlMode = value.equals("html");
}
else if (key.equals(DOCTYPE_PUBLIC)) {
overridePublic = value;
forceDTD = true;
}
else if (key.equals(DOCTYPE_SYSTEM)) {
overrideSystem = value;
forceDTD = true;
}
else if (key.equals(VERSION)) {
version = value;
}
else if (key.equals(STANDALONE)) {
standalone = value;
}
// System.out.println("%%%% htmlMode = " + htmlMode);
}
////////////////////////////////////////////////////////////////////
// Constants.
////////////////////////////////////////////////////////////////////
private final Attributes EMPTY_ATTS = new AttributesImpl();
public static final String CDATA_SECTION_ELEMENTS =
"cdata-section-elements";
public static final String DOCTYPE_PUBLIC = "doctype-public";
public static final String DOCTYPE_SYSTEM = "doctype-system";
public static final String ENCODING = "encoding";
public static final String INDENT = "indent"; // currently ignored
public static final String MEDIA_TYPE = "media-type"; // currently ignored
public static final String METHOD = "method"; // currently html or xml
public static final String OMIT_XML_DECLARATION = "omit-xml-declaration";
public static final String STANDALONE = "standalone"; // currently ignored
public static final String VERSION = "version";
////////////////////////////////////////////////////////////////////
// Internal state.
////////////////////////////////////////////////////////////////////
private Hashtable prefixTable;
private Hashtable forcedDeclTable;
private Hashtable doneDeclTable;
private int elementLevel = 0;
private Writer output;
private NamespaceSupport nsSupport;
private int prefixCounter = 0;
private Properties outputProperties;
private boolean unicodeMode = false;
private String outputEncoding = "";
private boolean htmlMode = false;
private boolean forceDTD = false;
private boolean hasOutputDTD = false;
private String overridePublic = null;
private String overrideSystem = null;
private String version = null;
private String standalone = null;
private boolean cdataElement = false;
}
// end of XMLWriter.java