aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: //@@3RD PARTY CODE@@ aoqi@0: aoqi@0: // DataWriter.java - XML writer for data-oriented files. aoqi@0: aoqi@0: package com.sun.xml.internal.bind.marshaller; aoqi@0: aoqi@0: import java.io.IOException; aoqi@0: import java.io.Writer; aoqi@0: import java.util.Stack; aoqi@0: aoqi@0: import org.xml.sax.Attributes; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Write data- or field-oriented XML. aoqi@0: * aoqi@0: *

This filter pretty-prints field-oriented XML without mixed content. aoqi@0: * all added indentation and newlines will be passed on down aoqi@0: * the filter chain (if any).

aoqi@0: * aoqi@0: *

In general, all whitespace in an XML document is potentially aoqi@0: * significant, so a general-purpose XML writing tool like the aoqi@0: * {@link XMLWriter} class cannot aoqi@0: * add newlines or indentation.

aoqi@0: * aoqi@0: *

There is, however, a large class of XML documents where information aoqi@0: * is strictly fielded: each element contains either character data aoqi@0: * or other elements, but not both. For this special case, it is possible aoqi@0: * for a writing tool to provide automatic indentation and newlines aoqi@0: * without requiring extra work from the user. Note that this class aoqi@0: * will likely not yield appropriate results for document-oriented aoqi@0: * XML like XHTML pages, which mix character data and elements together.

aoqi@0: * aoqi@0: *

This writer will automatically place each start tag on a new line, aoqi@0: * optionally indented if an indent step is provided (by default, there aoqi@0: * is no indentation). If an element contains other elements, the end aoqi@0: * tag will also appear on a new line with leading indentation. Consider, aoqi@0: * for example, the following code:

aoqi@0: * aoqi@0: *
aoqi@0:  * DataWriter w = new DataWriter();
aoqi@0:  *
aoqi@0:  * w.setIndentStep(2);
aoqi@0:  * w.startDocument();
aoqi@0:  * w.startElement("Person");
aoqi@0:  * w.dataElement("name", "Jane Smith");
aoqi@0:  * w.dataElement("date-of-birth", "1965-05-23");
aoqi@0:  * w.dataElement("citizenship", "US");
aoqi@0:  * w.endElement("Person");
aoqi@0:  * w.endDocument();
aoqi@0:  * 
aoqi@0: * aoqi@0: *

This code will produce the following document:

aoqi@0: * aoqi@0: *
aoqi@0:  * <?xml version="1.0" standalone="yes"?>
aoqi@0:  *
aoqi@0:  * <Person>
aoqi@0:  *   <name>Jane Smith</name>
aoqi@0:  *   <date-of-birth>1965-05-23</date-of-birth>
aoqi@0:  *   <citizenship>US</citizenship>
aoqi@0:  * </Person>
aoqi@0:  * 
aoqi@0: * aoqi@0: *

This class inherits from {@link XMLWriter}, aoqi@0: * and provides all of the same support for Namespaces.

aoqi@0: * aoqi@0: * @since 1.0 aoqi@0: * @author David Megginson, david@megginson.com aoqi@0: * @version 0.2 aoqi@0: * @see XMLWriter aoqi@0: */ aoqi@0: public class DataWriter extends XMLWriter aoqi@0: { aoqi@0: aoqi@0: aoqi@0: aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: // Constructors. aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Create a new data writer for the specified output. aoqi@0: * aoqi@0: * @param writer The character stream where the XML document aoqi@0: * will be written. aoqi@0: * @param encoding aoqi@0: * If non-null string is specified, it is written as a part aoqi@0: * of the XML declaration. aoqi@0: */ aoqi@0: public DataWriter ( Writer writer, String encoding, CharacterEscapeHandler _escapeHandler ) aoqi@0: { aoqi@0: super(writer,encoding,_escapeHandler); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public DataWriter (Writer writer, String encoding ) { aoqi@0: this( writer, encoding, DumbEscapeHandler.theInstance ); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: // Accessors and setters. aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Return the current indent step. aoqi@0: * aoqi@0: *

Return the current indent step: each start tag will be aoqi@0: * indented by this number of spaces times the number of aoqi@0: * ancestors that the element has.

aoqi@0: * aoqi@0: * @return The number of spaces in each indentation step, aoqi@0: * or 0 or less for no indentation. aoqi@0: * @see #setIndentStep(int) aoqi@0: * aoqi@0: * @deprecated aoqi@0: * Only return the length of the indent string. aoqi@0: */ aoqi@0: public int getIndentStep () aoqi@0: { aoqi@0: return indentStep.length(); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Set the current indent step. aoqi@0: * aoqi@0: * @param indentStep The new indent step (0 or less for no aoqi@0: * indentation). aoqi@0: * @see #getIndentStep() aoqi@0: * aoqi@0: * @deprecated aoqi@0: * Should use the version that takes string. aoqi@0: */ aoqi@0: public void setIndentStep (int indentStep) aoqi@0: { aoqi@0: StringBuilder buf = new StringBuilder(); aoqi@0: for( ; indentStep>0; indentStep-- ) aoqi@0: buf.append(' '); aoqi@0: setIndentStep(buf.toString()); aoqi@0: } aoqi@0: aoqi@0: public void setIndentStep(String s) { aoqi@0: this.indentStep = s; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: // Override methods from XMLWriter. aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Reset the writer so that it can be reused. aoqi@0: * aoqi@0: *

This method is especially useful if the writer failed aoqi@0: * with an exception the last time through.

aoqi@0: * aoqi@0: * @see XMLWriter#reset() aoqi@0: */ aoqi@0: public void reset () aoqi@0: { aoqi@0: depth = 0; aoqi@0: state = SEEN_NOTHING; aoqi@0: stateStack = new Stack(); aoqi@0: super.reset(); aoqi@0: } aoqi@0: aoqi@0: protected void writeXmlDecl(String decl) throws IOException { aoqi@0: super.writeXmlDecl(decl); aoqi@0: write('\n'); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Write a start tag. aoqi@0: * aoqi@0: *

Each tag will begin on a new line, and will be aoqi@0: * indented by the current indent step times the number aoqi@0: * of ancestors that the element has.

aoqi@0: * aoqi@0: *

The newline and indentation will be passed on down aoqi@0: * the filter chain through regular characters events.

aoqi@0: * aoqi@0: * @param uri The element's Namespace URI. aoqi@0: * @param localName The element's local name. aoqi@0: * @param qName The element's qualified (prefixed) name. aoqi@0: * @param atts The element's attribute list. aoqi@0: * @exception org.xml.sax.SAXException If there is an error aoqi@0: * writing the start tag, or if a filter further aoqi@0: * down the chain raises an exception. aoqi@0: * @see XMLWriter#startElement(String, String, String, Attributes) aoqi@0: */ aoqi@0: public void startElement (String uri, String localName, aoqi@0: String qName, Attributes atts) aoqi@0: throws SAXException aoqi@0: { aoqi@0: stateStack.push(SEEN_ELEMENT); aoqi@0: state = SEEN_NOTHING; aoqi@0: if (depth > 0) { aoqi@0: super.characters("\n"); aoqi@0: } aoqi@0: doIndent(); aoqi@0: super.startElement(uri, localName, qName, atts); aoqi@0: depth++; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Write an end tag. aoqi@0: * aoqi@0: *

If the element has contained other elements, the tag aoqi@0: * will appear indented on a new line; otherwise, it will aoqi@0: * appear immediately following whatever came before.

aoqi@0: * aoqi@0: *

The newline and indentation will be passed on down aoqi@0: * the filter chain through regular characters events.

aoqi@0: * aoqi@0: * @param uri The element's Namespace URI. aoqi@0: * @param localName The element's local name. aoqi@0: * @param qName The element's qualified (prefixed) name. aoqi@0: * @exception org.xml.sax.SAXException If there is an error aoqi@0: * writing the end tag, or if a filter further aoqi@0: * down the chain raises an exception. aoqi@0: * @see XMLWriter#endElement(String, String, String) aoqi@0: */ aoqi@0: public void endElement (String uri, String localName, String qName) aoqi@0: throws SAXException aoqi@0: { aoqi@0: depth--; aoqi@0: if (state == SEEN_ELEMENT) { aoqi@0: super.characters("\n"); aoqi@0: doIndent(); aoqi@0: } aoqi@0: super.endElement(uri, localName, qName); aoqi@0: state = stateStack.pop(); aoqi@0: } aoqi@0: aoqi@0: public void endDocument() throws SAXException { aoqi@0: try { aoqi@0: write('\n'); aoqi@0: } catch( IOException e ) { aoqi@0: throw new SAXException(e); aoqi@0: } aoqi@0: super.endDocument(); aoqi@0: } aoqi@0: aoqi@0: // /** aoqi@0: // * Write a empty element tag. aoqi@0: // * aoqi@0: // *

Each tag will appear on a new line, and will be aoqi@0: // * indented by the current indent step times the number aoqi@0: // * of ancestors that the element has.

aoqi@0: // * aoqi@0: // *

The newline and indentation will be passed on down aoqi@0: // * the filter chain through regular characters events.

aoqi@0: // * aoqi@0: // * @param uri The element's Namespace URI. aoqi@0: // * @param localName The element's local name. aoqi@0: // * @param qName The element's qualified (prefixed) name. aoqi@0: // * @param atts The element's attribute list. aoqi@0: // * @exception org.xml.sax.SAXException If there is an error aoqi@0: // * writing the empty tag, or if a filter further aoqi@0: // * down the chain raises an exception. aoqi@0: // * @see XMLWriter#emptyElement(String, String, String, Attributes) aoqi@0: // */ aoqi@0: // public void emptyElement (String uri, String localName, aoqi@0: // String qName, Attributes atts) aoqi@0: // throws SAXException aoqi@0: // { aoqi@0: // state = SEEN_ELEMENT; aoqi@0: // if (depth > 0) { aoqi@0: // super.characters("\n"); aoqi@0: // } aoqi@0: // doIndent(); aoqi@0: // super.emptyElement(uri, localName, qName, atts); aoqi@0: // } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Write a sequence of characters. aoqi@0: * aoqi@0: * @param ch The characters to write. aoqi@0: * @param start The starting position in the array. aoqi@0: * @param length The number of characters to use. aoqi@0: * @exception org.xml.sax.SAXException If there is an error aoqi@0: * writing the characters, or if a filter further aoqi@0: * down the chain raises an exception. aoqi@0: * @see XMLWriter#characters(char[], int, int) aoqi@0: */ aoqi@0: public void characters (char ch[], int start, int length) aoqi@0: throws SAXException aoqi@0: { aoqi@0: state = SEEN_DATA; aoqi@0: super.characters(ch, start, length); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: // Internal methods. aoqi@0: //////////////////////////////////////////////////////////////////// aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Print indentation for the current level. aoqi@0: * aoqi@0: * @exception org.xml.sax.SAXException If there is an error aoqi@0: * writing the indentation characters, or if a filter aoqi@0: * further down the chain raises an exception. aoqi@0: */ aoqi@0: private void doIndent () aoqi@0: throws SAXException aoqi@0: { aoqi@0: if (depth > 0) { aoqi@0: char[] ch = indentStep.toCharArray(); aoqi@0: for( int i=0; i stateStack = new Stack(); aoqi@0: aoqi@0: private String indentStep = ""; aoqi@0: private int depth = 0; aoqi@0: aoqi@0: } aoqi@0: aoqi@0: // end of DataWriter.java