/*-- Copyright (C) 2000 Brett McLaughlin & Jason Hunter. 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 disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution. 3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact license@jdom.org. 4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management (pm@jdom.org). In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following: "This product includes software developed by the JDOM Project (http://www.jdom.org/)." Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos. 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 JDOM Project and was originally created by Brett McLaughlin and Jason Hunter . For more information on the JDOM Project, please see . */ package org.jdom.output; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.io.StringWriter; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.jdom.Attribute; import org.jdom.CDATA; import org.jdom.Comment; import org.jdom.DocType; import org.jdom.Document; import org.jdom.Element; import org.jdom.Entity; import org.jdom.Namespace; import org.jdom.ProcessingInstruction; import org.jdom.UnescapedText; /** *

XMLOutputter takes a JDOM tree and formats it to a * stream as XML. This formatter performs typical document * formatting. The XML declaration and processing instructions are * always on their own lines. Empty elements are printed as * <empty/> and text-only contents are printed as * <tag>content</tag> on a single line. Constructor * parameters control the indent amount and whether new lines are * printed between elements. The other parameters are configurable * through the set* methods.

* *

For compact machine-readable output create a default * XMLOutputter and call setTrimText(true) to strip any whitespace * that was preserved from the source.

* *

There are output(...) methods to print any of the * standard JDOM classes, including Document and * Element, to either a Writer or an * OutputStream. Warning: using your own * Writer may cause the outputter's preferred character * encoding to be ignored. If you use encodings other than UTF8, we * recommend using the method that takes an OutputStream instead. *

* *

The methods outputString(...) are for convenience * only; for top performance you should call output(...) * and pass in your own Writer or * OutputStream to if possible.

* * @author Brett McLaughlin * @author Jason Hunter * @author Jason Reid * @author Wolfgang Werner * @author Elliotte Rusty Harold * @author David & Will (from Post Tool Design) * @author Dan Schaffer * @author Alex Chaffee (alex@jguru.com) * @version 1.0 */ public class XMLOutputter implements Cloneable { /** standard value to indent by, if we are indenting **/ protected static final String STANDARD_INDENT = " "; /** Whether or not to suppress the XML declaration * - default is false */ private boolean suppressDeclaration = false; /** The encoding format */ private String encoding = "UTF8"; /** Whether or not to output the encoding in the XML declaration * - default is false */ private boolean omitEncoding = false; /** The default indent is no spaces (as original document) */ private String indent = null; /** The initial number of indentations (so you can print a whole document indented, if you like) **/ // kind of dangerous having same name for instance and local // variable, but that's OK... private int indentLevel = 0; /** Whether or not to expand empty elements to * <tagName></tagName> - default is false */ private boolean expandEmptyElements = false; /** The default new line flag, set to do new lines only as in * original document */ private boolean newlines = false; /** New line separator */ private String lineSeparator = "\r\n"; /** should we preserve whitespace or not in text nodes? */ private boolean trimText = false; /** pad string-element boundaries with whitespace **/ private boolean padText = false; protected String padTextString = " "; /** *

* This will create an XMLOutputter with * no additional whitespace (indent or new lines) added; * the whitespace from the element text content is fully preserved. *

*/ public XMLOutputter() { } /** *

* This will create an XMLOutputter with * the given indent added but no new lines added; * all whitespace from the element text content is included as well. *

* * @param indent the indent string, usually some number of spaces */ public XMLOutputter(String indent) { this.indent = indent; } /** *

* This will create an XMLOutputter with * the given indent that prints newlines only if newlines is * true; * all whitespace from the element text content is included as well. *

* * @param indent the indent String, usually some number * of spaces * @param newlines true indicates new lines should be * printed, else new lines are ignored (compacted). */ public XMLOutputter(String indent, boolean newlines) { this.indent = indent; this.newlines = newlines; } /** *

* This will create an XMLOutputter with * the given indent and new lines printing only if newlines is * true, and encoding format encoding. *

* * @param indent the indent String, usually some number * of spaces * @param newlines true indicates new lines should be * printed, else new lines are ignored (compacted). * @param encoding set encoding format. */ public XMLOutputter(String indent, boolean newlines, String encoding) { this.indent = indent; this.newlines = newlines; this.encoding = encoding; } /** *

This will create an XMLOutputter with all the * options as set in the given XMLOutputter. Note * that XMLOutputter two = (XMLOutputter)one.clone(); * would work equally well.

* * @param that the XMLOutputter to clone **/ public XMLOutputter(XMLOutputter that) { this.suppressDeclaration = that.suppressDeclaration; this.omitEncoding = that.omitEncoding; this.indent = that.indent; this.indentLevel = that.indentLevel; this.expandEmptyElements = that.expandEmptyElements; this.newlines = that.newlines; this.encoding = that.encoding; this.lineSeparator = that.lineSeparator; this.trimText = that.trimText; this.padText = that.padText; } /** *

This will set the new-line separator. The default is * \r\n. Note that if the "newlines" property is * false, this value is irrelevant. To make it output the system * default line ending string, call * setLineSeparator(System.getProperty("line.separator")) *

* *
* We could change this to the System default, * but I prefer not to make output platform dependent. * A carriage return, linefeed pair is the most generally * acceptable linebreak. Another possibility is to use * only a line feed, which is XML's preferred (but not required) * solution. However, both carriage return and linefeed are * required for many network protocols, and the parser on the * other end should normalize this. --Rusty *
* * @see #setNewlines(boolean) * @param separator String line separator to use. **/ public void setLineSeparator(String separator) { lineSeparator = separator; } /** * @see #setLineSeparator(String) * @param newlines true indicates new lines should be * printed, else new lines are ignored (compacted). **/ public void setNewlines(boolean newlines) { this.newlines = newlines; } /** * @param encoding encoding format **/ public void setEncoding(String encoding) { this.encoding = encoding; } /** *

* This will set whether the XML declaration * (<?xml version="1.0" encoding="UTF-8"?>) * includes the encoding of the document. It is common to suppress * this in uses such as WML and other wireless device protocols. *

* * @param omitEncoding boolean indicating whether or not * the XML declaration should indicate the document encoding. */ public void setOmitEncoding(boolean omitEncoding) { this.omitEncoding = omitEncoding; } /** *

* This will set whether the XML declaration * (<?xml version="1.0"?>) * will be suppressed or not. It is common to suppress this in uses such * as SOAP and XML-RPC calls. *

* * @param suppressDeclaration boolean indicating whether or not * the XML declaration should be suppressed. */ public void setSuppressDeclaration(boolean suppressDeclaration) { this.suppressDeclaration = suppressDeclaration; } /** *

* This will set whether empty elements are expanded from * <tagName> to * <tagName></tagName>. *

* * @param expandEmptyElements boolean indicating whether or not * empty elements should be expanded. */ public void setExpandEmptyElements(boolean expandEmptyElements) { this.expandEmptyElements = expandEmptyElements; } /** *

This will set whether the text is output verbatim (false) * or with whitespace stripped as per {@link * org.jdom.Element#getTextTrim()}.

* *

Default: false

* * @param trimText boolean true=>trim the whitespace, * false=>use text verbatim **/ public void setTrimText(boolean trimText) { this.trimText = trimText; } /** *

Ensure that text immediately preceded by or followed by an * element will be "padded" with a single space. This is used to * allow make browser-friendly HTML, avoiding trimText's * transformation of, e.g., * The quick <b>brown</b> fox into * The quick<b>brown</b>fox (the latter * will run the three separate words together into a single word). * * This setting is not too useful if you haven't also called * {@link #setTrimText(boolean)}.

* *

Default: false

* * @param padText boolean if true, pad string-element * boundaries **/ public void setPadText(boolean padText) { this.padText = padText; } /** *

This will set the indent String to use; this * is usually a String of empty spaces. If you pass * null, or the empty string (""), then no indentation will * happen.

* Default: none (null) * * @param indent String to use for indentation. **/ public void setIndent(String indent) { // if passed the empty string, change it to null, for marginal // performance gains later (can compare to null first instead // of calling equals()) if ("".equals(indent)) indent = null; this.indent = indent; } /** * Set the indent on or off. If setting on, will use the value of * STANDARD_INDENT, which is usually two spaces. * * @param doIndent if true, set indenting on; if false, set indenting off **/ public void setIndent(boolean doIndent) { if (doIndent) { this.indent = STANDARD_INDENT; } else { this.indent = null; } } /** * Set the initial indentation level. This can be used to output * a document (or, more likely, an element) starting at a given * indent level, so it's not always flush against the left margin. * Default: 0 * * @param indentLevel the number of indents to start with **/ public void setIndentLevel(int indentLevel) { this.indentLevel = indentLevel; } /** *

* This will set the indent String's size; an indentSize * of 4 would result in the indention being equivalent to the * String "    " (four space chars). *

* * @param indentSize int number of spaces in indentation. */ public void setIndentSize(int indentSize) { StringBuffer indentBuffer = new StringBuffer(); for (int i=0; i * This will print the proper indent characters for the given indent level. *

* * @param out Writer to write to * @param level int indentation level */ protected void indent(Writer out, int level) throws IOException { if (indent != null && !indent.equals("")) { for (int i = 0; i < level; i++) { out.write(indent); } } } /** *

* This will print a new line only if the newlines flag was set to true *

* * @param out Writer to write to */ protected void maybePrintln(Writer out) throws IOException { if (newlines) { out.write(lineSeparator); } } /** * Get an OutputStreamWriter, use preferred encoding. */ protected Writer makeWriter(OutputStream out) throws java.io.UnsupportedEncodingException { Writer writer = new OutputStreamWriter (new BufferedOutputStream(out), this.encoding); return writer; } /** * Get an OutputStreamWriter, use specified encoding. */ protected Writer makeWriter(OutputStream out, String encoding) throws java.io.UnsupportedEncodingException { Writer writer = new OutputStreamWriter (new BufferedOutputStream(out), encoding); return writer; } /** *

* This will print the Document to the given output stream. * The characters are printed using the encoding specified in the * constructor, or a default of UTF-8. *

* * @param doc Document to format. * @param out OutputStream to write to. * @throws IOException - if there's any problem writing. */ public void output(Document doc, OutputStream out) throws IOException { Writer writer = makeWriter(out); output(doc, writer); writer.flush(); } /** *

This will print the Document to the given * Writer. *

* *

Warning: using your own Writer may cause the outputter's * preferred character encoding to be ignored. If you use * encodings other than UTF8, we recommend using the method that * takes an OutputStream instead.

* *

Note: as with all Writers, you may need to flush() yours * after this method returns.

* * @param doc Document to format. * @param out Writer to write to. * @throws IOException - if there's any problem writing. **/ public void output(Document doc, Writer writer) throws IOException { // Print out XML declaration if (indentLevel>0) indent(writer, indentLevel); printDeclaration(doc, writer, encoding); if (doc.getDocType() != null) { if (indentLevel>0) indent(writer, indentLevel); printDocType(doc.getDocType(), writer); } // Print out root element, as well as any root level // comments and processing instructions, // starting with no indentation Iterator i = doc.getMixedContent().iterator(); while (i.hasNext()) { Object obj = i.next(); if (obj instanceof Element) { output(doc.getRootElement(), writer); // at initial indentLevel } else if (obj instanceof Comment) { printComment((Comment) obj, writer, indentLevel); } else if (obj instanceof ProcessingInstruction) { printProcessingInstruction((ProcessingInstruction) obj, writer, indentLevel); } else if (obj instanceof CDATA) { printCDATASection((CDATA)obj, writer, indentLevel); } else if (obj instanceof UnescapedText) { printUnescapedText((UnescapedText)obj, writer, indentLevel); } } // Output final line separator writer.write(lineSeparator); } // output element /** *

* Print out an {@link Element}, including * its {@link Attribute}s, and its value, and all * contained (child) elements etc. *

* * @param element Element to output. * @param out Writer to write to. **/ public void output(Element element, Writer out) throws IOException { // If this is the root element we could pre-initialize the // namespace stack with the namespaces printElement(element, out, indentLevel, new NamespaceStack()); } /** *

* Print out an {@link Element}, including * its {@link Attribute}s, and its value, and all * contained (child) elements etc. *

* * @param element Element to output. * @param out Writer to write to. **/ public void output(Element element, OutputStream out) throws IOException { Writer writer = makeWriter(out); output(element, writer); writer.flush(); // Flush the output to the underlying stream } /** *

This will handle printing out an {@link * Element}'s content only, not including its tag, and * attributes. This can be useful for printing the content of an * element that contains HTML, like "<description>JDOM is * <b>fun>!</description>".

* * @param element Element to output. * @param out Writer to write to. * @param indent int level of indention. */ public void outputElementContent(Element element, Writer out) throws IOException { List mixedContent = element.getMixedContent(); printElementContent(element, out, indentLevel, new NamespaceStack(), mixedContent); } // output cdata /** *

* Print out a {@link CDATA} *

* * @param cdata CDATA to output. * @param out Writer to write to. **/ public void output(CDATA cdata, Writer out) throws IOException { printCDATASection(cdata, out, indentLevel); } /** *

* Print out a {@link CDATA} *

* * @param cdata CDATA to output. * @param out OutputStream to write to. **/ public void output(CDATA cdata, OutputStream out) throws IOException { Writer writer = makeWriter(out); output(cdata, writer); writer.flush(); // Flush the output to the underlying stream } // output comment /** *

* Print out a {@link Comment} *

* * @param comment Comment to output. * @param out Writer to write to. **/ public void output(Comment comment, Writer out) throws IOException { printComment(comment, out, indentLevel); } /** *

* Print out a {@link Comment} *

* * @param comment Comment to output. * @param out OutputStream to write to. **/ public void output(Comment comment, OutputStream out) throws IOException { Writer writer = makeWriter(out); output(comment, writer); writer.flush(); // Flush the output to the underlying stream } // output String /** *

Print out a {@link java.lang.String}. Perfoms * the necessary entity escaping and whitespace stripping.

* * @param string String to output. * @param out Writer to write to. **/ public void output(String string, Writer out) throws IOException { printString(string, out); } /** *

*

Print out a {@link java.lang.String}. Perfoms * the necessary entity escaping and whitespace stripping.

*

* * @param cdata CDATA to output. * @param out OutputStream to write to. **/ public void output(String string, OutputStream out) throws IOException { Writer writer = makeWriter(out); printString(string, writer); writer.flush(); // Flush the output to the underlying stream } // output Entity /** *

Print out an {@link Entity}. *

* * @param entity Entity to output. * @param out Writer to write to. **/ public void output(Entity entity, Writer out) throws IOException { printEntity(entity, out); } /** *

* Print out an {@link Entity}. *

* * @param cdata CDATA to output. * @param out OutputStream to write to. **/ public void output(Entity entity, OutputStream out) throws IOException { Writer writer = makeWriter(out); printEntity(entity, writer); writer.flush(); // Flush the output to the underlying stream } // output processingInstruction /** *

* Print out a {@link ProcessingInstruction} *

* * @param element ProcessingInstruction to output. * @param out Writer to write to. **/ public void output(ProcessingInstruction pi, Writer out) throws IOException { printProcessingInstruction(pi, out, indentLevel); } /** *

* Print out a {@link ProcessingInstruction} *

* * @param processingInstruction ProcessingInstruction * to output. * @param out OutputStream to write to. **/ public void output(ProcessingInstruction pi, OutputStream out) throws IOException { Writer writer = makeWriter(out); output(pi, writer); writer.flush(); // Flush the output to the underlying stream } // output as string /** * Return a string representing a document. Uses an internal * StringWriter. Warning: a String is Unicode, which may not match * the outputter's specified encoding. * * @param doc Document to format. **/ public String outputString(Document doc) throws IOException { StringWriter out = new StringWriter(); output(doc, out); out.flush(); return out.toString(); } /** * Return a string representing an element. Warning: a String is * Unicode, which may not match the outputter's specified * encoding. * * @param doc Element to format. **/ public String outputString(Element element) throws IOException { StringWriter out = new StringWriter(); output(element, out); out.flush(); return out.toString(); } // internal printing methods /** *

* This will write the declaration to the given Writer. * Assumes XML version 1.0 since we don't directly know. *

* * @param docType DocType whose declaration to write. * @param out Writer to write to. */ protected void printDeclaration(Document doc, Writer out, String encoding) throws IOException { // Only print of declaration is not suppressed if (!suppressDeclaration) { // Assume 1.0 version if (encoding.equals("UTF8")) { out.write(""); } else { out.write(""); } // Print new line after decl always, even if no other new lines // Helps the output look better and is semantically // inconsequential out.write(lineSeparator); } } /** *

* This will write the DOCTYPE declaration if one exists. *

* * @param doc Document whose declaration to write. * @param out Writer to write to. */ protected void printDocType(DocType docType, Writer out) throws IOException { if (docType == null) { return; } String publicID = docType.getPublicID(); String systemID = docType.getSystemID(); boolean hasPublic = false; out.write(""); maybePrintln(out); } /** *

* This will write the comment to the specified writer. *

* * @param comment Comment to write. * @param out Writer to write to. * @param indentLevel Current depth in hierarchy. */ protected void printComment(Comment comment, Writer out, int indentLevel) throws IOException { indent(out, indentLevel); out.write(comment.getSerializedForm()); //XXX maybePrintln(out); } /** *

* This will write the processing instruction to the specified writer. *

* * @param comment ProcessingInstruction to write. * @param out Writer to write to. * @param indentLevel Current depth in hierarchy. */ protected void printProcessingInstruction(ProcessingInstruction pi, Writer out, int indentLevel) throws IOException { indent(out, indentLevel); out.write(pi.getSerializedForm()); maybePrintln(out); } /** *

* This will write the unescaped text to the specified writer. *

* * @param comment UnescapedText to write. * @param out Writer to write to. * @param indentLevel Current depth in hierarchy. */ protected void printUnescapedText(UnescapedText ut, Writer out, int indentLevel) throws IOException { indent(out, indentLevel); out.write(ut.getSerializedForm()); maybePrintln(out); } /** *

* This will handle printing out an {@link CDATA}, * and its value. *

* * @param cdata CDATA to output. * @param out Writer to write to. * @param indent int level of indention. */ protected void printCDATASection(CDATA cdata, Writer out, int indentLevel) throws IOException { indent(out, indentLevel); out.write(cdata.getSerializedForm()); maybePrintln(out); } /** *

* This will handle printing out an {@link Element}, * its {@link Attribute}s, and its value. *

* * @param element Element to output. * @param out Writer to write to. * @param indent int level of indention. * @param namespaces List stack of Namespaces in scope. */ protected void printElement(Element element, Writer out, int indentLevel, NamespaceStack namespaces) throws IOException { List mixedContent = element.getMixedContent(); boolean empty = mixedContent.size() == 0; boolean stringOnly = !empty && mixedContent.size() == 1 && mixedContent.get(0) instanceof String; // Print beginning element tag /* maybe the doctype, xml declaration, and processing instructions should only break before and not after; then this check is unnecessary, or maybe the println should only come after and never before. Then the output always ends with a newline */ indent(out, indentLevel); // Print the beginning of the tag plus attributes and any // necessary namespace declarations out.write("<"); out.write(element.getQualifiedName()); int previouslyDeclaredNamespaces = namespaces.size(); Namespace ns = element.getNamespace(); // Add namespace decl only if it's not the XML namespace and it's // not the NO_NAMESPACE with the prefix "" not yet mapped // (we do output xmlns="" if the "" prefix was already used and we // need to reclaim it for the NO_NAMESPACE) if (ns != Namespace.XML_NAMESPACE && !(ns == Namespace.NO_NAMESPACE && namespaces.getURI("") == null)) { String prefix = ns.getPrefix(); String uri = namespaces.getURI(prefix); if (!ns.getURI().equals(uri)) { // output a new namespace decl namespaces.push(ns); printNamespace(ns, out); } } // Print out additional namespace declarations List additionalNamespaces = element.getAdditionalNamespaces(); if (additionalNamespaces != null) { for (int i=0; i"); } else { out.write(">"); } maybePrintln(out); } else { // we know it's not null or empty from above out.write(">"); if (stringOnly) { // if string only, print content on same line as tags printElementContent(element, out, indentLevel, namespaces, mixedContent); } else { maybePrintln(out); printElementContent(element, out, indentLevel, namespaces, mixedContent); indent(out, indentLevel); } out.write(""); maybePrintln(out); } // remove declared namespaces from stack while (namespaces.size() > previouslyDeclaredNamespaces) { namespaces.pop(); } } /** *

This will handle printing out an {@link * Element}'s content only, not including its tag, * attributes, and namespace info.

* * @param element Element to output. * @param out Writer to write to. * @param indent int level of indention. */ protected void printElementContent(Element element, Writer out, int indentLevel, NamespaceStack namespaces, List mixedContent) throws IOException { // get same local flags as printElement does // a little redundant code-wise, but not performance-wise boolean empty = mixedContent.size() == 0; boolean stringOnly = !empty && (mixedContent.size() == 1) && mixedContent.get(0) instanceof String; if (stringOnly) { // Print the tag with String on same line // Example: content String elementText = trimText ? element.getTextTrim() : element.getText(); out.write(escapeElementEntities(elementText)); } else { /** * Print with children on future lines * Rather than check for mixed content or not, just print * Example: * * */ // Iterate through children Object content = null; Class justOutput = null; for (int i=0, size=mixedContent.size(); i * This will handle printing out an {@link Entity}. * Only the entity reference such as &entity; * will be printed. However, subclasses are free to override * this method to print the contents of the entity instead. *

* * @param entity Entity to output. * @param out Writer to write to. */ protected void printEntity(Entity entity, Writer out) throws IOException { out.write(entity.getSerializedForm()); } /** *

* This will handle printing out any needed {@link Namespace} * declarations. *

* * @param ns Namespace to print definition of * @param out Writer to write to. */ protected void printNamespace(Namespace ns, Writer out) throws IOException { out.write(" xmlns"); String prefix = ns.getPrefix(); if (!prefix.equals("")) { out.write(":"); out.write(prefix); } out.write("=\""); out.write(ns.getURI()); out.write("\""); } /** *

* This will handle printing out an {@link Attribute} list. *

* * @param attributes List of Attribute objcts * @param out Writer to write to */ protected void printAttributes(List attributes, Element parent, Writer out, NamespaceStack namespaces) throws IOException { // I do not yet handle the case where the same prefix maps to // two different URIs. For attributes on the same element // this is illegal; but as yet we don't throw an exception // if someone tries to do this Set prefixes = new HashSet(); for (int i=0, size=attributes.size(); i < size; i++) { Attribute attribute = (Attribute)attributes.get(i); Namespace ns = attribute.getNamespace(); if (ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) { String prefix = ns.getPrefix(); String uri = namespaces.getURI(prefix); if (!ns.getURI().equals(uri)) { // output a new namespace decl printNamespace(ns, out); namespaces.push(ns); } } out.write(" "); out.write(attribute.getQualifiedName()); out.write("="); out.write("\""); out.write(escapeAttributeEntities(attribute.getValue())); out.write("\""); } } /** *

* This will take the five pre-defined entities in XML 1.0 and * convert their character representation to the appropriate * entity reference, suitable for XML attributes. *

* * @param st String input to escape. * @return String with escaped content. */ protected String escapeAttributeEntities(String st) { StringBuffer buff = new StringBuffer(); char[] block = st.toCharArray(); String stEntity = null; int i, last; for (i=0, last=0; i < block.length; i++) { switch(block[i]) { case '<' : stEntity = "<"; break; case '>' : stEntity = ">"; break; case '\'' : stEntity = "'"; break; case '\"' : stEntity = """; break; case '&' : stEntity = "&"; break; default : /* no-op */ ; } if (stEntity != null) { buff.append(block, last, i - last); buff.append(stEntity); stEntity = null; last = i + 1; } } if(last < block.length) { buff.append(block, last, i - last); } return buff.toString(); } /** *

* This will take the three pre-defined entities in XML 1.0 * (used specifically in XML elements) and * convert their character representation to the appropriate * entity reference, suitable for XML element. *

* * @param st String input to escape. * @return String with escaped content. */ protected String escapeElementEntities(String st) { StringBuffer buff = new StringBuffer(); char[] block = st.toCharArray(); String stEntity = null; int i, last; for (i=0, last=0; i < block.length; i++) { switch(block[i]) { case '<' : stEntity = "<"; break; case '>' : stEntity = ">"; break; case '&' : stEntity = "&"; break; default : /* no-op */ ; } if (stEntity != null) { buff.append(block, last, i - last); buff.append(stEntity); stEntity = null; last = i + 1; } } if(last < block.length) { buff.append(block, last, i - last); } return buff.toString(); } /** * parse command-line arguments of the form -omitEncoding * -indentSize 3 ... * @return int index of first parameter that we didn't understand **/ public int parseArgs(String[] args, int i) { for (; i