summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:52 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:52 -0800
commita85355fd3e67e7a006e0f8b90c8142c0aefffce0 (patch)
treea5d3ea486b907c77abd820d7df12fb224ed0a417
downloadxmlwriter-a85355fd3e67e7a006e0f8b90c8142c0aefffce0.tar.gz
eclair snapshot
-rw-r--r--src/org/jheer/XMLWriter.java483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/org/jheer/XMLWriter.java b/src/org/jheer/XMLWriter.java
new file mode 100644
index 0000000..cd811d9
--- /dev/null
+++ b/src/org/jheer/XMLWriter.java
@@ -0,0 +1,483 @@
+/**
+ * Copyright (c) 2004-2006 Regents of the University of California.
+ 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 and this list of conditions.
+
+ 3. The name of the University may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+package org.jheer;
+
+//import java.io.PrintWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+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
+ * [Print]FileWriter to write the XML to. Call the {@link #begin()} or
+ * {@link #begin(String, int)} method when ready to start outputting
+ * XML. Then use the provided methods to generate the XML file.
+ * Finally, call either the {@link #finish()} or {@link #finish(String)}
+ * methods to signal the completion of the file.</p>
+ *
+ * @author <a href="http://jheer.org">jeffrey heer</a>
+ *
+ * Modified to take a FileWriter and now throws IOException.
+ */
+
+public class XMLWriter {
+
+// private PrintWriter m_out;
+ private FileWriter m_out;
+ private int m_bias = 0;
+ private int m_tab;
+ private ArrayList m_tagStack = new ArrayList();
+
+ /**
+ * Create a new XMLWriter.
+ * @param out the FileWriter to write the XML to
+ */
+// public XMLWriter(PrintWriter out) {
+ public XMLWriter(FileWriter out) {
+ this(out, 2);
+ }
+
+ /**
+ * Create a new XMLWriter.
+ * @param out the FileWriter 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) {
+ public XMLWriter(FileWriter out, int tabLength) {
+ m_out = out;
+ m_tab = 2;
+ }
+
+ /**
+ * Write <em>unescaped</em> text into the XML file. To write
+ * escaped text, use the {@link #content(String)} method instead.
+ * @param s the text to write. This String will not be escaped.
+ */
+ public void write(String s) throws IOException {
+ m_out.write(s);
+ }
+
+ /**
+ * Write <em>unescaped</em> text into the XML file, followed by
+ * a newline. To write escaped text, use the {@link #content(String)}
+ * method instead.
+ * @param s the text to write. This String will not be escaped.
+ */
+ public void writeln(String s) throws IOException {
+ m_out.write(s);
+ m_out.write("\n");
+ }
+
+ /**
+ * Write a newline into the XML file.
+ */
+ public void writeln() throws IOException {
+ m_out.write("\n");
+ }
+
+ /**
+ * Begin the XML document. This must be called before any other
+ * formatting methods. This method writes an XML header into
+ * the top of the output stream.
+ */
+ public void begin() throws IOException {
+ m_out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ writeln();
+ }
+
+ /**
+ * Begin the XML document. This must be called before any other
+ * formatting methods. This method writes 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) throws IOException {
+ begin();
+ m_out.write(header);
+ m_bias = bias;
+ }
+
+ /**
+ * Write a comment in the XML document. The comment will be written
+ * according to the current spacing and followed by a newline.
+ * @param comment the comment text
+ */
+ public void comment(String comment) throws IOException {
+ spacing();
+ m_out.write("<!-- ");
+ m_out.write(comment);
+ m_out.write(" -->");
+ writeln();
+ }
+
+ /**
+ * Internal method for writing 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) throws IOException
+ {
+ spacing();
+ m_out.write('<');
+ m_out.write(tag);
+ for ( int i=0; i<nattr; ++i ) {
+ m_out.write(' ');
+ m_out.write(names[i]);
+ m_out.write('=');
+ m_out.write('\"');
+ escapeString(values[i]);
+ m_out.write('\"');
+ }
+ if ( close ) m_out.write('/');
+ m_out.write('>');
+ writeln();
+
+ if ( !close ) {
+ m_tagStack.add(tag);
+ }
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, names, values, nattr, true);
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, names, values, nattr, false);
+ }
+
+ /**
+ * Write a new attribut to an existing tag. The attribute will be followed by a newline.
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ */
+ public void addAttribute(String name, String value) throws IOException {
+ spacing();
+ m_out.write(name);
+ m_out.write('=');
+ m_out.write('\"');
+ escapeString(value);
+ m_out.write('\"');
+ writeln();
+ }
+
+ /**
+ * Internal method for writing 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) throws IOException {
+ spacing();
+ m_out.write('<');
+ m_out.write(tag);
+ m_out.write(' ');
+ m_out.write(name);
+ m_out.write('=');
+ m_out.write('\"');
+ escapeString(value);
+ m_out.write('\"');
+ if ( close ) m_out.write('/');
+ m_out.write('>');
+ writeln();
+
+ if ( !close ) {
+ m_tagStack.add(tag);
+ }
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, name, value, true);
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, name, value, false);
+ }
+
+ /**
+ * Internal method for writing 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) throws IOException
+ {
+ spacing();
+ m_out.write('<');
+ m_out.write(tag);
+ for ( int i=0; i<nattr; ++i ) {
+ m_out.write(' ');
+ m_out.write((String)names.get(i));
+ m_out.write('=');
+ m_out.write('\"');
+ escapeString((String)values.get(i));
+ m_out.write('\"');
+ }
+ if ( close ) m_out.write('/');
+ m_out.write('>');
+ writeln();
+
+ if ( !close ) {
+ m_tagStack.add(tag);
+ }
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, names, values, nattr, true);
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ tag(tag, names, values, nattr, false);
+ }
+
+ /**
+ * Write 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) throws IOException {
+ 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() throws IOException {
+ String tag = (String)m_tagStack.remove(m_tagStack.size()-1);
+ spacing();
+ m_out.write('<');
+ m_out.write('/');
+ m_out.write(tag);
+ m_out.write('>');
+ writeln();
+ }
+
+ /**
+ * Write 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) throws IOException
+ {
+ spacing();
+ m_out.write('<'); m_out.write(tag); m_out.write(' ');
+ m_out.write(name); m_out.write('=');
+ m_out.write('\"'); escapeString(value); m_out.write('\"');
+ m_out.write('>');
+ escapeString(content);
+ m_out.write('<'); m_out.write('/'); m_out.write(tag); m_out.write('>');
+ writeln();
+ }
+
+ /**
+ * Write 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) throws IOException {
+ spacing();
+ m_out.write('<'); m_out.write(tag); m_out.write('>');
+ escapeString(content);
+ m_out.write('<'); m_out.write('/'); m_out.write(tag); m_out.write('>');
+ writeln();
+ }
+
+ /**
+ * Write content text.
+ * @param content the content text, this text will be escaped
+ */
+ public void content(String content) throws IOException {
+ escapeString(content);
+ }
+
+ /**
+ * Finish the XML document.
+ */
+ public void finish() throws IOException {
+ m_bias = 0;
+ m_out.flush();
+ }
+
+ /**
+ * Finish the XML document, writing 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) throws IOException {
+ m_bias = 0;
+ m_out.write(footer);
+ m_out.flush();
+ }
+
+ /**
+ * Write 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 writing outside the mechanisms
+ * provided by this class.
+ */
+ public void spacing() throws IOException {
+ int len = m_bias + m_tagStack.size() * m_tab;
+ for ( int i=0; i<len; ++i )
+ m_out.write(' ');
+ }
+
+ // ------------------------------------------------------------------------
+ // 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 =
+ { "&lt;", "&gt;", "&quot;", "&apos;", "&amp;" };
+
+ /**
+ * 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) throws IOException {
+ if ( str == null ) {
+ m_out.write("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.write("&#");
+ m_out.write(Integer.toString(c));
+ m_out.write(';');
+ } 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.write(VALID[j]);
+ break;
+ }
+ }
+ // if character is valid, don't escape
+ if (valid) {
+ m_out.write(c);
+ }
+ }
+ }
+ }
+
+} // end of class XMLWriter
+