16934ac109bf80019d70ea684c203f12ca58c365emike ritter/**
26934ac109bf80019d70ea684c203f12ca58c365emike ritter * Copyright (c) 2004-2006 Regents of the University of California.
36934ac109bf80019d70ea684c203f12ca58c365emike ritter  All rights reserved.
46934ac109bf80019d70ea684c203f12ca58c365emike ritter
56934ac109bf80019d70ea684c203f12ca58c365emike ritter  Redistribution and use in source and binary forms, with or without
66934ac109bf80019d70ea684c203f12ca58c365emike ritter  modification, are permitted provided that the following conditions
76934ac109bf80019d70ea684c203f12ca58c365emike ritter  are met:
86934ac109bf80019d70ea684c203f12ca58c365emike ritter
96934ac109bf80019d70ea684c203f12ca58c365emike ritter  1. Redistributions of source code must retain the above copyright
106934ac109bf80019d70ea684c203f12ca58c365emike ritter  notice, this list of conditions and the following disclaimer.
116934ac109bf80019d70ea684c203f12ca58c365emike ritter
126934ac109bf80019d70ea684c203f12ca58c365emike ritter  2. Redistributions in binary form must reproduce the above copyright
136934ac109bf80019d70ea684c203f12ca58c365emike ritter  notice and this list of conditions.
146934ac109bf80019d70ea684c203f12ca58c365emike ritter
156934ac109bf80019d70ea684c203f12ca58c365emike ritter  3. The name of the University may not be used to endorse or promote products
166934ac109bf80019d70ea684c203f12ca58c365emike ritter  derived from this software without specific prior written permission.
176934ac109bf80019d70ea684c203f12ca58c365emike ritter
186934ac109bf80019d70ea684c203f12ca58c365emike ritter  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
196934ac109bf80019d70ea684c203f12ca58c365emike ritter  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
206934ac109bf80019d70ea684c203f12ca58c365emike ritter  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
216934ac109bf80019d70ea684c203f12ca58c365emike ritter  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
226934ac109bf80019d70ea684c203f12ca58c365emike ritter  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
236934ac109bf80019d70ea684c203f12ca58c365emike ritter  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
246934ac109bf80019d70ea684c203f12ca58c365emike ritter  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
256934ac109bf80019d70ea684c203f12ca58c365emike ritter  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
266934ac109bf80019d70ea684c203f12ca58c365emike ritter  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
276934ac109bf80019d70ea684c203f12ca58c365emike ritter  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
286934ac109bf80019d70ea684c203f12ca58c365emike ritter  SUCH DAMAGE.
296934ac109bf80019d70ea684c203f12ca58c365emike ritter */
306934ac109bf80019d70ea684c203f12ca58c365emike ritter
316934ac109bf80019d70ea684c203f12ca58c365emike ritterpackage org.jheer;
326934ac109bf80019d70ea684c203f12ca58c365emike ritter
336934ac109bf80019d70ea684c203f12ca58c365emike ritter//import java.io.PrintWriter;
346934ac109bf80019d70ea684c203f12ca58c365emike ritterimport java.io.FileWriter;
356934ac109bf80019d70ea684c203f12ca58c365emike ritterimport java.io.IOException;
366934ac109bf80019d70ea684c203f12ca58c365emike ritterimport java.util.ArrayList;
376934ac109bf80019d70ea684c203f12ca58c365emike ritter
386934ac109bf80019d70ea684c203f12ca58c365emike ritter/**
396934ac109bf80019d70ea684c203f12ca58c365emike ritter * Utility class for writing XML files. This class provides convenience
406934ac109bf80019d70ea684c203f12ca58c365emike ritter * methods for creating XML documents, such as starting and ending
416934ac109bf80019d70ea684c203f12ca58c365emike ritter * tags, and adding content and comments. This class handles correct
426934ac109bf80019d70ea684c203f12ca58c365emike ritter * XML formatting and will properly escape text to ensure that the
436934ac109bf80019d70ea684c203f12ca58c365emike ritter * text remains valid XML.
446934ac109bf80019d70ea684c203f12ca58c365emike ritter *
456934ac109bf80019d70ea684c203f12ca58c365emike ritter * <p>To use this class, create a new instance with the desired
466934ac109bf80019d70ea684c203f12ca58c365emike ritter * [Print]FileWriter to write the XML to. Call the {@link #begin()} or
476934ac109bf80019d70ea684c203f12ca58c365emike ritter * {@link #begin(String, int)} method when ready to start outputting
486934ac109bf80019d70ea684c203f12ca58c365emike ritter * XML. Then use the provided methods to generate the XML file.
496934ac109bf80019d70ea684c203f12ca58c365emike ritter * Finally, call either the {@link #finish()} or {@link #finish(String)}
506934ac109bf80019d70ea684c203f12ca58c365emike ritter * methods to signal the completion of the file.</p>
516934ac109bf80019d70ea684c203f12ca58c365emike ritter *
526934ac109bf80019d70ea684c203f12ca58c365emike ritter * @author <a href="http://jheer.org">jeffrey heer</a>
536934ac109bf80019d70ea684c203f12ca58c365emike ritter *
546934ac109bf80019d70ea684c203f12ca58c365emike ritter * Modified to take a FileWriter and now throws IOException.
556934ac109bf80019d70ea684c203f12ca58c365emike ritter */
566934ac109bf80019d70ea684c203f12ca58c365emike ritter
576934ac109bf80019d70ea684c203f12ca58c365emike ritterpublic class XMLWriter {
586934ac109bf80019d70ea684c203f12ca58c365emike ritter
596934ac109bf80019d70ea684c203f12ca58c365emike ritter//    private PrintWriter m_out;
606934ac109bf80019d70ea684c203f12ca58c365emike ritter    private FileWriter m_out;
616934ac109bf80019d70ea684c203f12ca58c365emike ritter    private int m_bias = 0;
626934ac109bf80019d70ea684c203f12ca58c365emike ritter    private int m_tab;
636934ac109bf80019d70ea684c203f12ca58c365emike ritter    private ArrayList m_tagStack = new ArrayList();
646934ac109bf80019d70ea684c203f12ca58c365emike ritter
656934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
666934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Create a new XMLWriter.
676934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param out the  FileWriter to write the XML to
686934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
696934ac109bf80019d70ea684c203f12ca58c365emike ritter//    public XMLWriter(PrintWriter out) {
706934ac109bf80019d70ea684c203f12ca58c365emike ritter    public XMLWriter(FileWriter out) {
716934ac109bf80019d70ea684c203f12ca58c365emike ritter        this(out, 2);
726934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
736934ac109bf80019d70ea684c203f12ca58c365emike ritter
746934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
756934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Create a new XMLWriter.
766934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param out the FileWriter to write the XML to
776934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tabLength the number of spaces to use for each
786934ac109bf80019d70ea684c203f12ca58c365emike ritter     *  level of indentation in the XML file
796934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
806934ac109bf80019d70ea684c203f12ca58c365emike ritter//    public XMLWriter(PrintWriter out, int tabLength) {
816934ac109bf80019d70ea684c203f12ca58c365emike ritter    public XMLWriter(FileWriter out, int tabLength) {
826934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out = out;
836934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_tab = 2;
846934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
856934ac109bf80019d70ea684c203f12ca58c365emike ritter
866934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
876934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write <em>unescaped</em> text into the XML file. To write
886934ac109bf80019d70ea684c203f12ca58c365emike ritter     * escaped text, use the {@link #content(String)} method instead.
896934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param s the text to write. This String will not be escaped.
906934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
916934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void write(String s) throws IOException {
926934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(s);
936934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
946934ac109bf80019d70ea684c203f12ca58c365emike ritter
956934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
966934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write <em>unescaped</em> text into the XML file, followed by
976934ac109bf80019d70ea684c203f12ca58c365emike ritter     * a newline. To write escaped text, use the {@link #content(String)}
986934ac109bf80019d70ea684c203f12ca58c365emike ritter     * method instead.
996934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param s the text to write. This String will not be escaped.
1006934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1016934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void writeln(String s) throws IOException {
1026934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(s);
1036934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write("\n");
1046934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1056934ac109bf80019d70ea684c203f12ca58c365emike ritter
1066934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1076934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a newline into the XML file.
1086934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1096934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void writeln() throws IOException {
1106934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write("\n");
1116934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1126934ac109bf80019d70ea684c203f12ca58c365emike ritter
1136934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1146934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Begin the XML document. This must be called before any other
1156934ac109bf80019d70ea684c203f12ca58c365emike ritter     * formatting methods. This method writes an XML header into
1166934ac109bf80019d70ea684c203f12ca58c365emike ritter     * the top of the output stream.
1176934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1186934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void begin() throws IOException {
1196934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1206934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
1216934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1226934ac109bf80019d70ea684c203f12ca58c365emike ritter
1236934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1246934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Begin the XML document. This must be called before any other
1256934ac109bf80019d70ea684c203f12ca58c365emike ritter     * formatting methods. This method writes an XML header into
1266934ac109bf80019d70ea684c203f12ca58c365emike ritter     * the top of the output stream, plus additional header text
1276934ac109bf80019d70ea684c203f12ca58c365emike ritter     * provided by the client
1286934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param header header text to insert into the document
1296934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param bias the spacing bias to use for all subsequent indenting
1306934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1316934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void begin(String header, int bias) throws IOException {
1326934ac109bf80019d70ea684c203f12ca58c365emike ritter        begin();
1336934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(header);
1346934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_bias = bias;
1356934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1366934ac109bf80019d70ea684c203f12ca58c365emike ritter
1376934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1386934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a comment in the XML document. The comment will be written
1396934ac109bf80019d70ea684c203f12ca58c365emike ritter     * according to the current spacing and followed by a newline.
1406934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param comment the comment text
1416934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1426934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void comment(String comment) throws IOException {
1436934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
1446934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write("<!-- ");
1456934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(comment);
1466934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(" -->");
1476934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
1486934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1496934ac109bf80019d70ea684c203f12ca58c365emike ritter
1506934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1516934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Internal method for writing a tag with attributes.
1526934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
1536934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
1546934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
1556934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
1566934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param close true to close the tag, false to leave it
1576934ac109bf80019d70ea684c203f12ca58c365emike ritter     * open and adjust the spacing
1586934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1596934ac109bf80019d70ea684c203f12ca58c365emike ritter    protected void tag(String tag, String[] names, String[] values,
1606934ac109bf80019d70ea684c203f12ca58c365emike ritter            int nattr, boolean close) throws IOException
1616934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
1626934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
1636934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<');
1646934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(tag);
1656934ac109bf80019d70ea684c203f12ca58c365emike ritter        for ( int i=0; i<nattr; ++i ) {
1666934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write(' ');
1676934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write(names[i]);
1686934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('=');
1696934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('\"');
1706934ac109bf80019d70ea684c203f12ca58c365emike ritter            escapeString(values[i]);
1716934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('\"');
1726934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
1736934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( close ) m_out.write('/');
1746934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('>');
1756934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
1766934ac109bf80019d70ea684c203f12ca58c365emike ritter
1776934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( !close ) {
1786934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_tagStack.add(tag);
1796934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
1806934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1816934ac109bf80019d70ea684c203f12ca58c365emike ritter
1826934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1836934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a closed tag with attributes. The tag will be followed by a
1846934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline.
1856934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
1866934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
1876934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
1886934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
1896934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
1906934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void tag(String tag, String[] names, String[] values, int nattr) throws IOException
1916934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
1926934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, names, values, nattr, true);
1936934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
1946934ac109bf80019d70ea684c203f12ca58c365emike ritter
1956934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
1966934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a start tag with attributes. The tag will be followed by a
1976934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline, and the indentation level will be increased.
1986934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
1996934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
2006934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
2016934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
2026934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2036934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void start(String tag, String[] names, String[] values, int nattr) throws IOException
2046934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
2056934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, names, values, nattr, false);
2066934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
2076934ac109bf80019d70ea684c203f12ca58c365emike ritter
2086934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
2096934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a new attribut to an existing tag.  The attribute will be followed by a newline.
2106934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param name the name of the attribute
2116934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param value the value of the attribute
2126934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2136934ac109bf80019d70ea684c203f12ca58c365emike ritter     public void addAttribute(String name, String value) throws IOException {
2146934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
2156934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(name);
2166934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('=');
2176934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('\"');
2186934ac109bf80019d70ea684c203f12ca58c365emike ritter        escapeString(value);
2196934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('\"');
2206934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
2216934ac109bf80019d70ea684c203f12ca58c365emike ritter     }
2226934ac109bf80019d70ea684c203f12ca58c365emike ritter
2236934ac109bf80019d70ea684c203f12ca58c365emike ritter     /**
2246934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Internal method for writing a tag with a single attribute.
2256934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
2266934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param name the name of the attribute
2276934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param value the value of the attribute
2286934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param close true to close the tag, false to leave it
2296934ac109bf80019d70ea684c203f12ca58c365emike ritter     * open and adjust the spacing
2306934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2316934ac109bf80019d70ea684c203f12ca58c365emike ritter    protected void tag(String tag, String name, String value, boolean close) throws IOException {
2326934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
2336934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<');
2346934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(tag);
2356934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(' ');
2366934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(name);
2376934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('=');
2386934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('\"');
2396934ac109bf80019d70ea684c203f12ca58c365emike ritter        escapeString(value);
2406934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('\"');
2416934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( close ) m_out.write('/');
2426934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('>');
2436934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
2446934ac109bf80019d70ea684c203f12ca58c365emike ritter
2456934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( !close ) {
2466934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_tagStack.add(tag);
2476934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
2486934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
2496934ac109bf80019d70ea684c203f12ca58c365emike ritter
2506934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
2516934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a closed tag with one attribute. The tag will be followed by a
2526934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline.
2536934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
2546934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param name the name of the attribute
2556934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param value the value of the attribute
2566934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2576934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void tag(String tag, String name, String value) throws IOException
2586934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
2596934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, name, value, true);
2606934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
2616934ac109bf80019d70ea684c203f12ca58c365emike ritter
2626934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
2636934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a start tag with one attribute. The tag will be followed by a
2646934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline, and the indentation level will be increased.
2656934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
2666934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param name the name of the attribute
2676934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param value the value of the attribute
2686934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2696934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void start(String tag, String name, String value) throws IOException
2706934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
2716934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, name, value, false);
2726934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
2736934ac109bf80019d70ea684c203f12ca58c365emike ritter
2746934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
2756934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Internal method for writing a tag with attributes.
2766934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
2776934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
2786934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
2796934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
2806934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param close true to close the tag, false to leave it
2816934ac109bf80019d70ea684c203f12ca58c365emike ritter     * open and adjust the spacing
2826934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
2836934ac109bf80019d70ea684c203f12ca58c365emike ritter    protected void tag(String tag, ArrayList names, ArrayList values,
2846934ac109bf80019d70ea684c203f12ca58c365emike ritter            int nattr, boolean close) throws IOException
2856934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
2866934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
2876934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<');
2886934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(tag);
2896934ac109bf80019d70ea684c203f12ca58c365emike ritter        for ( int i=0; i<nattr; ++i ) {
2906934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write(' ');
2916934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write((String)names.get(i));
2926934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('=');
2936934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('\"');
2946934ac109bf80019d70ea684c203f12ca58c365emike ritter            escapeString((String)values.get(i));
2956934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write('\"');
2966934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
2976934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( close ) m_out.write('/');
2986934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('>');
2996934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
3006934ac109bf80019d70ea684c203f12ca58c365emike ritter
3016934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( !close ) {
3026934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_tagStack.add(tag);
3036934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
3046934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3056934ac109bf80019d70ea684c203f12ca58c365emike ritter
3066934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3076934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a closed tag with attributes. The tag will be followed by a
3086934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline.
3096934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
3106934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
3116934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
3126934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
3136934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3146934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void tag(String tag, ArrayList names, ArrayList values, int nattr) throws IOException
3156934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
3166934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, names, values, nattr, true);
3176934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3186934ac109bf80019d70ea684c203f12ca58c365emike ritter
3196934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3206934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a start tag with attributes. The tag will be followed by a
3216934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline, and the indentation level will be increased.
3226934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
3236934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param names the names of the attributes
3246934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param values the values of the attributes
3256934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param nattr the number of attributes
3266934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3276934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void start(String tag, ArrayList names, ArrayList values, int nattr) throws IOException
3286934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
3296934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, names, values, nattr, false);
3306934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3316934ac109bf80019d70ea684c203f12ca58c365emike ritter
3326934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3336934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a start tag without attributes. The tag will be followed by a
3346934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline, and the indentation level will be increased.
3356934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
3366934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3376934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void start(String tag) throws IOException {
3386934ac109bf80019d70ea684c203f12ca58c365emike ritter        tag(tag, (String[])null, null, 0, false);
3396934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3406934ac109bf80019d70ea684c203f12ca58c365emike ritter
3416934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3426934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Close the most recently opened tag. The tag will be followed by a
3436934ac109bf80019d70ea684c203f12ca58c365emike ritter     * newline, and the indentation level will be decreased.
3446934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3456934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void end() throws IOException {
3466934ac109bf80019d70ea684c203f12ca58c365emike ritter        String tag = (String)m_tagStack.remove(m_tagStack.size()-1);
3476934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
3486934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<');
3496934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('/');
3506934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(tag);
3516934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('>');
3526934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
3536934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3546934ac109bf80019d70ea684c203f12ca58c365emike ritter
3556934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3566934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a new content tag with a single attribute, consisting of an
3576934ac109bf80019d70ea684c203f12ca58c365emike ritter     * open tag, content text, and a closing tag, all on one line.
3586934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
3596934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param name the name of the attribute
3606934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param value the value of the attribute, this text will be escaped
3616934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param content the text content, this text will be escaped
3626934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3636934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void contentTag(String tag, String name, String value, String content) throws IOException
3646934ac109bf80019d70ea684c203f12ca58c365emike ritter    {
3656934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
3666934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<'); m_out.write(tag); m_out.write(' ');
3676934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(name); m_out.write('=');
3686934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('\"'); escapeString(value); m_out.write('\"');
3696934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('>');
3706934ac109bf80019d70ea684c203f12ca58c365emike ritter        escapeString(content);
3716934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<'); m_out.write('/'); m_out.write(tag); m_out.write('>');
3726934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
3736934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3746934ac109bf80019d70ea684c203f12ca58c365emike ritter
3756934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3766934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write a new content tag with no attributes, consisting of an
3776934ac109bf80019d70ea684c203f12ca58c365emike ritter     * open tag, content text, and a closing tag, all on one line.
3786934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param tag the tag name
3796934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param content the text content, this text will be escaped
3806934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3816934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void contentTag(String tag, String content) throws IOException {
3826934ac109bf80019d70ea684c203f12ca58c365emike ritter        spacing();
3836934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<'); m_out.write(tag); m_out.write('>');
3846934ac109bf80019d70ea684c203f12ca58c365emike ritter        escapeString(content);
3856934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write('<'); m_out.write('/'); m_out.write(tag); m_out.write('>');
3866934ac109bf80019d70ea684c203f12ca58c365emike ritter        writeln();
3876934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3886934ac109bf80019d70ea684c203f12ca58c365emike ritter
3896934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3906934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write content text.
3916934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param content the content text, this text will be escaped
3926934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
3936934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void content(String content) throws IOException {
3946934ac109bf80019d70ea684c203f12ca58c365emike ritter        escapeString(content);
3956934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
3966934ac109bf80019d70ea684c203f12ca58c365emike ritter
3976934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
3986934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Finish the XML document.
3996934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
4006934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void finish() throws IOException {
4016934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_bias = 0;
4026934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.flush();
4036934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
4046934ac109bf80019d70ea684c203f12ca58c365emike ritter
4056934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
4066934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Finish the XML document, writing the given footer text at the
4076934ac109bf80019d70ea684c203f12ca58c365emike ritter     * end of the document.
4086934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param footer the footer text, this will not be escaped
4096934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
4106934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void finish(String footer) throws IOException {
4116934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_bias = 0;
4126934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.write(footer);
4136934ac109bf80019d70ea684c203f12ca58c365emike ritter        m_out.flush();
4146934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
4156934ac109bf80019d70ea684c203f12ca58c365emike ritter
4166934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
4176934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Write the current spacing (determined by the indentation level)
4186934ac109bf80019d70ea684c203f12ca58c365emike ritter     * into the document. This method is used by many of the other
4196934ac109bf80019d70ea684c203f12ca58c365emike ritter     * formatting methods, and so should only need to be called in
4206934ac109bf80019d70ea684c203f12ca58c365emike ritter     * the case of custom text writing outside the mechanisms
4216934ac109bf80019d70ea684c203f12ca58c365emike ritter     * provided by this class.
4226934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
4236934ac109bf80019d70ea684c203f12ca58c365emike ritter    public void spacing() throws IOException {
4246934ac109bf80019d70ea684c203f12ca58c365emike ritter        int len = m_bias + m_tagStack.size() * m_tab;
4256934ac109bf80019d70ea684c203f12ca58c365emike ritter        for ( int i=0; i<len; ++i )
4266934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write(' ');
4276934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
4286934ac109bf80019d70ea684c203f12ca58c365emike ritter
4296934ac109bf80019d70ea684c203f12ca58c365emike ritter    // ------------------------------------------------------------------------
4306934ac109bf80019d70ea684c203f12ca58c365emike ritter    // Escape Text
4316934ac109bf80019d70ea684c203f12ca58c365emike ritter
4326934ac109bf80019d70ea684c203f12ca58c365emike ritter    // unicode ranges and valid/invalid characters
4336934ac109bf80019d70ea684c203f12ca58c365emike ritter    private static final char   LOWER_RANGE = 0x20;
4346934ac109bf80019d70ea684c203f12ca58c365emike ritter    private static final char   UPPER_RANGE = 0x7f;
4356934ac109bf80019d70ea684c203f12ca58c365emike ritter    private static final char[] VALID_CHARS = { 0x9, 0xA, 0xD };
4366934ac109bf80019d70ea684c203f12ca58c365emike ritter
4376934ac109bf80019d70ea684c203f12ca58c365emike ritter    private static final char[] INVALID = { '<', '>', '"', '\'', '&' };
4386934ac109bf80019d70ea684c203f12ca58c365emike ritter    private static final String[] VALID =
4396934ac109bf80019d70ea684c203f12ca58c365emike ritter        { "&lt;", "&gt;", "&quot;", "&apos;", "&amp;" };
4406934ac109bf80019d70ea684c203f12ca58c365emike ritter
4416934ac109bf80019d70ea684c203f12ca58c365emike ritter    /**
4426934ac109bf80019d70ea684c203f12ca58c365emike ritter     * Escape a string such that it is safe to use in an XML document.
4436934ac109bf80019d70ea684c203f12ca58c365emike ritter     * @param str the string to escape
4446934ac109bf80019d70ea684c203f12ca58c365emike ritter     */
4456934ac109bf80019d70ea684c203f12ca58c365emike ritter    protected void escapeString(String str) throws IOException {
4466934ac109bf80019d70ea684c203f12ca58c365emike ritter        if ( str == null ) {
4476934ac109bf80019d70ea684c203f12ca58c365emike ritter            m_out.write("null");
4486934ac109bf80019d70ea684c203f12ca58c365emike ritter            return;
4496934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
4506934ac109bf80019d70ea684c203f12ca58c365emike ritter
4516934ac109bf80019d70ea684c203f12ca58c365emike ritter        int len = str.length();
4526934ac109bf80019d70ea684c203f12ca58c365emike ritter        for (int i = 0; i < len; ++i) {
4536934ac109bf80019d70ea684c203f12ca58c365emike ritter            char c = str.charAt(i);
4546934ac109bf80019d70ea684c203f12ca58c365emike ritter
4556934ac109bf80019d70ea684c203f12ca58c365emike ritter            if ( (c < LOWER_RANGE     && c != VALID_CHARS[0] &&
4566934ac109bf80019d70ea684c203f12ca58c365emike ritter                  c != VALID_CHARS[1] && c != VALID_CHARS[2])
4576934ac109bf80019d70ea684c203f12ca58c365emike ritter                 || (c > UPPER_RANGE) )
4586934ac109bf80019d70ea684c203f12ca58c365emike ritter            {
4596934ac109bf80019d70ea684c203f12ca58c365emike ritter                // character out of range, escape with character value
4606934ac109bf80019d70ea684c203f12ca58c365emike ritter                m_out.write("&#");
4616934ac109bf80019d70ea684c203f12ca58c365emike ritter                m_out.write(Integer.toString(c));
4626934ac109bf80019d70ea684c203f12ca58c365emike ritter                m_out.write(';');
4636934ac109bf80019d70ea684c203f12ca58c365emike ritter            } else {
4646934ac109bf80019d70ea684c203f12ca58c365emike ritter                boolean valid = true;
4656934ac109bf80019d70ea684c203f12ca58c365emike ritter                // check for invalid characters (e.g., "<", "&", etc)
4666934ac109bf80019d70ea684c203f12ca58c365emike ritter                for (int j=INVALID.length-1; j >= 0; --j )
4676934ac109bf80019d70ea684c203f12ca58c365emike ritter                {
4686934ac109bf80019d70ea684c203f12ca58c365emike ritter                    if ( INVALID[j] == c) {
4696934ac109bf80019d70ea684c203f12ca58c365emike ritter                        valid = false;
4706934ac109bf80019d70ea684c203f12ca58c365emike ritter                        m_out.write(VALID[j]);
4716934ac109bf80019d70ea684c203f12ca58c365emike ritter                        break;
4726934ac109bf80019d70ea684c203f12ca58c365emike ritter                    }
4736934ac109bf80019d70ea684c203f12ca58c365emike ritter                }
4746934ac109bf80019d70ea684c203f12ca58c365emike ritter                // if character is valid, don't escape
4756934ac109bf80019d70ea684c203f12ca58c365emike ritter                if (valid) {
4766934ac109bf80019d70ea684c203f12ca58c365emike ritter                    m_out.write(c);
4776934ac109bf80019d70ea684c203f12ca58c365emike ritter                }
4786934ac109bf80019d70ea684c203f12ca58c365emike ritter            }
4796934ac109bf80019d70ea684c203f12ca58c365emike ritter        }
4806934ac109bf80019d70ea684c203f12ca58c365emike ritter    }
4816934ac109bf80019d70ea684c203f12ca58c365emike ritter
4826934ac109bf80019d70ea684c203f12ca58c365emike ritter} // end of class XMLWriter
4836934ac109bf80019d70ea684c203f12ca58c365emike ritter
484