Java/XML/Writer
Содержание
- 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
- 13 Whitespace
- 14 Namespace Support
Convenience methods for writing XML
<source lang="java">
/*
* 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. **
Example:
** To write the following XML: *
* <?xml version="1.0" encoding="UTF-8"?> * <xml-doc> * <foo a="1" b="2&3"/> * <bar>Hello World!</bar> * </xml-doc> *
* <p> * One can use the following code:*
* 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(); *
*/
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 true if pretty-printing is enabled, false * 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 String.valueOf(int). * @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 * String.valueOf(boolean). * @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. <foo/>. 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 String.valueOf(int). * @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 String.valueOf(boolean). * @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(""); } /** * 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); } } }
}
</source>
DOM writer
<source lang="java">
/*
* 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(""); 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
</source>
Makes writing XML much much easier
<source lang="java">
/*
* 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(""); 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; }
}
</source>
Use DOM L3 DOMBuilder, DOMBuilderFilter DOMWriter and other DOM L3 functionality to preparse, revalidate and safe document.
<source lang="java">
/*
* 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; }
}
</source>
Write DOM out
<source lang="java">
/*
* 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); } }
}
</source>
write Xml DOM Node
<source lang="java">
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)); }
}
</source>
Write Xml (Node n, OutputStream os)
<source lang="java">
/**
* 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));
} }
</source>
Writing a DOM Document to an XML File
<source lang="java">
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); }
}
</source>
XML Document Writer
<source lang="java">
/*
* 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 + ""); 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(); }
}
</source>
XML Writer
<source lang="java">
/**
* 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.
* * @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 unescaped 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 unescaped 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(""); 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
</source>
XMLWriter helper class
<source lang="java">
/*
* 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(); } }
}
</source>
XMLWriter.java - serialize an XML document
<source lang="java">
// 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. **
This class can be used by itself or as part of a SAX event * stream: it takes as input a series of SAX2 ContentHandler * events and uses the information in those events to write * an XML document. Since this class is a filter, it can also * pass the events on down a filter chain for further processing * (you can use the XMLWriter to take a snapshot of the current * state at any point in a filter chain), and it can be * used directly as a ContentHandler for a SAX2 XMLReader.
**
The client creates a document by invoking the methods for * standard SAX2 events, always beginning with the * {@link #startDocument startDocument} method and ending with * the {@link #endDocument endDocument} method. There are convenience * methods provided so that clients to not have to create empty * attribute lists or provide empty strings as parameters; for * example, the method invocation
**
* w.startElement("foo"); *
**
is equivalent to the regular SAX2 ContentHandler method
**
* w.startElement("", "foo", "", new AttributesImpl()); *
**
Except that it is more efficient because it does not allocate * a new empty attribute list each time. The following code will send * a simple XML document to standard output:
**
* XMLWriter w = new XMLWriter(); * * w.startDocument(); * w.startElement("greeting"); * w.characters("Hello, world!"); * w.endElement("greeting"); * w.endDocument(); *
**
The resulting document will look like this:
**
* <?xml version="1.0" standalone="yes"?> * * <greeting>Hello, world!</greeting> *
**
In fact, there is an even simpler convenience method, * dataElement, designed for writing elements that * contain only character data, so the code to generate the * document could be shortened to
**
* XMLWriter w = new XMLWriter(); * * w.startDocument(); * w.dataElement("greeting", "Hello, world!"); * w.endDocument(); *
**
Whitespace
**
According to the XML Recommendation, all whitespace * in an XML document is potentially significant to an application, * so this class never adds newlines or indentation. If you * insert three elements in a row, as in
**
* w.dataElement("item", "1"); * w.dataElement("item", "2"); * w.dataElement("item", "3"); *
**
you will end up with
**
* <item>1</item><item>3</item><item>3</item> *
**
You need to invoke one of the characters methods * explicitly to add newlines or indentation. Alternatively, you * can use {@link 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).
* **
Namespace Support
**
The writer contains extensive support for XML Namespaces, so that * a client application does not have to keep track of prefixes and * supply xmlns attributes. By default, the XML writer will * generate Namespace declarations in the form _NS1, _NS2, etc., wherever * they are needed, as in the following example:
**
* w.startDocument(); * w.emptyElement("http://www.foo.ru/ns/", "foo"); * w.endDocument(); *
**
The resulting document will look like this:
**
* <?xml version="1.0" standalone="yes"?> * * <_NS1:foo xmlns:_NS1="http://www.foo.ru/ns/"/> *
**
In many cases, document authors will prefer to choose their * own prefixes rather than using the (ugly) default names. The * XML writer allows two methods for selecting prefixes:
**
-
*
- the qualified name *
- the {@link #setPrefix setPrefix} method. *
**
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).
**
Before writing a document, the client can also pre-map a prefix * to a Namespace URI with the setPrefix method:
**
* w.setPrefix("http://www.foo.ru/ns/", "foo"); * w.startDocument(); * w.emptyElement("http://www.foo.ru/ns/", "foo"); * w.endDocument(); *
**
The resulting document will look like this:
**
* <?xml version="1.0" standalone="yes"?> * * <foo:foo xmlns:foo="http://www.foo.ru/ns/"/> *
**
The default Namespace simply uses an empty string as the prefix:
**
* w.setPrefix("http://www.foo.ru/ns/", ""); * w.startDocument(); * w.emptyElement("http://www.foo.ru/ns/", "foo"); * w.endDocument(); *
**
The resulting document will look like this:
**
* <?xml version="1.0" standalone="yes"?> * * <foo xmlns="http://www.foo.ru/ns/"/> *
**
By default, the XML writer will not declare a Namespace until * it is actually used. Sometimes, this approach will create * a large number of Namespace declarations, as in the following * example:
**
* <xml version="1.0" standalone="yes"?> * * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description about="http://www.foo.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> *
**
The "rdf" prefix is declared only once, because the RDF Namespace * is used by the root element and can be inherited by all of its * descendants; the "dc" prefix, on the other hand, is declared three * times, because no higher element uses the Namespace. To solve this * problem, you can instruct the XML writer to predeclare Namespaces * on the root element even if they are not used there:
**
* w.forceNSDecl("http://www.purl.org/dc/"); *
**
Now, the "dc" prefix will be declared on the root element even * though it"s not needed there, and can be inherited by its * descendants:
**
* <xml version="1.0" standalone="yes"?> * * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" * xmlns:dc="http://www.purl.org/dc/"> * <rdf:Description about="http://www.foo.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> *
**
This approach is also useful for declaring Namespace prefixes * that be used by qualified names appearing in attribute values or * character data.
* * @author David Megginson, david@megginson.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. **
Write to standard output.
*/ public XMLWriter () { init(null); } /** * Create a new XML writer. **
Write to the writer provided.
* * @param writer The output destination, or null to use standard * output. */ public XMLWriter (Writer writer) { init(writer); } /** * Create a new XML writer. **
Use the specified XML reader as the parent.
* * @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. **
Use the specified XML reader as the parent, and write * to the specified writer.
* * @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. **
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.
**
This method is invoked automatically by the * {@link #startDocument startDocument} method before writing * a new document.
**
Note: this method will not * clear the prefix or URI information in the writer or * the selected output writer.
* * @see #flush */ public void reset () { elementLevel = 0; prefixCounter = 0; nsSupport.reset(); } /** * Flush the output. **
This method flushes the output stream. It is especially useful * when you need to make certain that the entire document has * been written to output but do not want to close the output * stream.
**
This method is invoked automatically by the * {@link #endDocument endDocument} method after writing a * document.
* * @see #reset */ public void flush () throws IOException { output.flush(); } /** * Set a new output destination for the document. * * @param writer The output destination, or null to use * standard output. * @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. **
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.
* * @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. **
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.
**
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.
* * @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. **
This is a convenience method that invokes {@link * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String) * forceNSDecl}.
* * @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. **
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.
* * @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. **
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.
* * @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. **
This method will supply an empty string for the qName. * It invokes {@link #endElement(String, String, String)} * directly.
* * @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. **
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.
* * @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. **
This method will supply an empty string for the qname * and an empty attribute list. It invokes * {@link #emptyElement(String, String, String, Attributes)} * directly.
* * @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. **
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.
* * @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. **
This is a convenience method to write a complete element * with character data content, including the start tag * and end tag.
**
This method invokes * {@link #startElement(String, String, String, Attributes)}, * followed by * {@link #characters(String)}, followed by * {@link #endElement(String, String, String)}.
* * @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. **
This is a convenience method to write a complete element * with character data content, including the start tag * and end tag. This method provides an empty string * for the qname and an empty attribute list.
**
This method invokes * {@link #startElement(String, String, String, Attributes)}, * followed by * {@link #characters(String)}, followed by * {@link #endElement(String, String, String)}.
* * @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. **
This is a convenience method to write a complete element * with character data content, including the start tag * and end tag. The method provides an empty string for the * Namespace URI, and empty string for the qualified name, * and an empty attribute list.
**
This method invokes * {@link #startElement(String, String, String, Attributes)}, * followed by * {@link #characters(String)}, followed by * {@link #endElement(String, String, String)}.
* * @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. **
This is a convenience method that takes an XML * String, converts it to a character array, then invokes * {@link #characters(char[], int, int)}.
* * @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(""); } 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
</source>