Mega Code Archive

 
Categories / Java / XML
 

XML Writer

/**  * Copyright (c) 2004-2006 Regents of the University of California.  * See "license-prefuse.txt" for licensing terms.  */ import java.io.PrintWriter; import java.util.ArrayList; /**  * Utility class for writing XML files. This class provides convenience  * methods for creating XML documents, such as starting and ending  * tags, and adding content and comments. This class handles correct  * XML formatting and will properly escape text to ensure that the  * text remains valid XML.  *   * <p>To use this class, create a new instance with the desired  * PrintWriter to write the XML to. Call the {@link #begin()} or  * {@link #begin(String, int)} method when ready to start outputting  * XML. Then use the provided methods to generate the XML file.  * Finally, call either the {@link #finish()} or {@link #finish(String)}  * methods to signal the completion of the file.</p>  *   * @author <a href="http://jheer.org">jeffrey heer</a>  */ public class XMLWriter {          private PrintWriter m_out;     private int m_bias = 0;     private int m_tab;     private ArrayList m_tagStack = new ArrayList();          /**      * Create a new XMLWriter.      * @param out the print writer to write the XML to      */     public XMLWriter(PrintWriter out) {         this(out, 2);     }     /**      * Create a new XMLWriter.      * @param out the print writer to write the XML to      * @param tabLength the number of spaces to use for each      *  level of indentation in the XML file      */     public XMLWriter(PrintWriter out, int tabLength) {         m_out = out;         m_tab = 2;     }          /**      * Print <em>unescaped</em> text into the XML file. To print      * escaped text, use the {@link #content(String)} method instead.      * @param s the text to print. This String will not be escaped.      */     public void print(String s) {         m_out.print(s);     }     /**      * Print <em>unescaped</em> text into the XML file, followed by      * a newline. To print escaped text, use the {@link #content(String)}      * method instead.      * @param s the text to print. This String will not be escaped.      */     public void println(String s) {         m_out.print(s);         m_out.print("\n");     }          /**      * Print a newline into the XML file.      */     public void println() {         m_out.print("\n");     }          /**      * Begin the XML document. This must be called before any other      * formatting methods. This method prints an XML header into      * the top of the output stream.      */     public void begin() {         m_out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");         println();     }          /**      * Begin the XML document. This must be called before any other      * formatting methods. This method prints an XML header into      * the top of the output stream, plus additional header text      * provided by the client      * @param header header text to insert into the document      * @param bias the spacing bias to use for all subsequent indenting      */     public void begin(String header, int bias) {         begin();         m_out.print(header);         m_bias = bias;     }          /**      * Print a comment in the XML document. The comment will be printed      * according to the current spacing and followed by a newline.      * @param comment the comment text      */     public void comment(String comment) {         spacing();         m_out.print("<!-- ");         m_out.print(comment);         m_out.print(" -->");         println();     }          /**      * Internal method for printing a tag with attributes.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      * @param close true to close the tag, false to leave it      * open and adjust the spacing      */     protected void tag(String tag, String[] names, String[] values,             int nattr, boolean close)     {         spacing();         m_out.print('<');         m_out.print(tag);         for ( int i=0; i<nattr; ++i ) {             m_out.print(' ');             m_out.print(names[i]);             m_out.print('=');             m_out.print('\"');             escapeString(values[i]);             m_out.print('\"');         }         if ( close ) m_out.print('/');         m_out.print('>');         println();                  if ( !close ) {             m_tagStack.add(tag);         }     }          /**      * Print a closed tag with attributes. The tag will be followed by a      * newline.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      */     public void tag(String tag, String[] names, String[] values, int nattr)     {         tag(tag, names, values, nattr, true);     }          /**      * Print a start tag with attributes. The tag will be followed by a      * newline, and the indentation level will be increased.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      */     public void start(String tag, String[] names, String[] values, int nattr)     {         tag(tag, names, values, nattr, false);     }          /**      * Internal method for printing a tag with a single attribute.      * @param tag the tag name      * @param name the name of the attribute      * @param value the value of the attribute      * @param close true to close the tag, false to leave it      * open and adjust the spacing      */     protected void tag(String tag, String name, String value, boolean close) {         spacing();         m_out.print('<');         m_out.print(tag);         m_out.print(' ');         m_out.print(name);         m_out.print('=');         m_out.print('\"');         escapeString(value);         m_out.print('\"');         if ( close ) m_out.print('/');         m_out.print('>');         println();                  if ( !close ) {             m_tagStack.add(tag);         }     }          /**      * Print a closed tag with one attribute. The tag will be followed by a      * newline.      * @param tag the tag name      * @param name the name of the attribute      * @param value the value of the attribute      */     public void tag(String tag, String name, String value)     {         tag(tag, name, value, true);     }          /**      * Print a start tag with one attribute. The tag will be followed by a      * newline, and the indentation level will be increased.      * @param tag the tag name      * @param name the name of the attribute      * @param value the value of the attribute      */     public void start(String tag, String name, String value)     {         tag(tag, name, value, false);     }          /**      * Internal method for printing a tag with attributes.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      * @param close true to close the tag, false to leave it      * open and adjust the spacing      */     protected void tag(String tag, ArrayList names, ArrayList values,             int nattr, boolean close)     {         spacing();         m_out.print('<');         m_out.print(tag);         for ( int i=0; i<nattr; ++i ) {             m_out.print(' ');             m_out.print((String)names.get(i));             m_out.print('=');             m_out.print('\"');             escapeString((String)values.get(i));             m_out.print('\"');         }         if ( close ) m_out.print('/');         m_out.print('>');         println();                  if ( !close ) {             m_tagStack.add(tag);         }     }          /**      * Print a closed tag with attributes. The tag will be followed by a      * newline.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      */     public void tag(String tag, ArrayList names, ArrayList values, int nattr)     {         tag(tag, names, values, nattr, true);     }          /**      * Print a start tag with attributes. The tag will be followed by a      * newline, and the indentation level will be increased.      * @param tag the tag name      * @param names the names of the attributes      * @param values the values of the attributes      * @param nattr the number of attributes      */     public void start(String tag, ArrayList names, ArrayList values, int nattr)     {         tag(tag, names, values, nattr, false);     }          /**      * Print a start tag without attributes. The tag will be followed by a      * newline, and the indentation level will be increased.      * @param tag the tag name      */     public void start(String tag) {         tag(tag, (String[])null, null, 0, false);     }     /**      * Close the most recently opened tag. The tag will be followed by a      * newline, and the indentation level will be decreased.      */     public void end() {         String tag = (String)m_tagStack.remove(m_tagStack.size()-1);         spacing();         m_out.print('<');         m_out.print('/');         m_out.print(tag);         m_out.print('>');         println();     }          /**      * Print a new content tag with a single attribute, consisting of an      * open tag, content text, and a closing tag, all on one line.      * @param tag the tag name      * @param name the name of the attribute      * @param value the value of the attribute, this text will be escaped      * @param content the text content, this text will be escaped      */     public void contentTag(String tag, String name, String value,                             String content)     {         spacing();         m_out.print('<'); m_out.print(tag); m_out.print(' ');         m_out.print(name); m_out.print('=');         m_out.print('\"'); escapeString(value); m_out.print('\"');         m_out.print('>');             escapeString(content);         m_out.print('<'); m_out.print('/'); m_out.print(tag); m_out.print('>');         println();     }          /**      * Print a new content tag with no attributes, consisting of an      * open tag, content text, and a closing tag, all on one line.      * @param tag the tag name      * @param content the text content, this text will be escaped      */     public void contentTag(String tag, String content) {         spacing();         m_out.print('<'); m_out.print(tag); m_out.print('>');         escapeString(content);         m_out.print('<'); m_out.print('/'); m_out.print(tag); m_out.print('>');         println();     }          /**      * Print content text.      * @param content the content text, this text will be escaped      */     public void content(String content) {         escapeString(content);     }          /**      * Finish the XML document.      */     public void finish() {         m_bias = 0;         m_out.flush();     }          /**      * Finish the XML document, printing the given footer text at the      * end of the document.      * @param footer the footer text, this will not be escaped      */     public void finish(String footer) {         m_bias = 0;         m_out.print(footer);         m_out.flush();     }          /**      * Print the current spacing (determined by the indentation level)      * into the document. This method is used by many of the other      * formatting methods, and so should only need to be called in      * the case of custom text printing outside the mechanisms      * provided by this class.      */     public void spacing() {         int len = m_bias + m_tagStack.size() * m_tab;         for ( int i=0; i<len; ++i )             m_out.print(' ');     }          // ------------------------------------------------------------------------     // Escape Text          // unicode ranges and valid/invalid characters     private static final char   LOWER_RANGE = 0x20;     private static final char   UPPER_RANGE = 0x7f;     private static final char[] VALID_CHARS = { 0x9, 0xA, 0xD };          private static final char[] INVALID = { '<', '>', '"', '\'', '&' };     private static final String[] VALID =          { "&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) {         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