001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: //@@3RD PARTY CODE@@
027: // DataWriter.java - XML writer for data-oriented files.
028: package com.sun.xml.internal.bind.marshaller;
029:
030: import java.io.IOException;
031: import java.io.Writer;
032: import java.util.Stack;
033:
034: import org.xml.sax.Attributes;
035: import org.xml.sax.SAXException;
036:
037: /**
038: * Write data- or field-oriented XML.
039: *
040: * <p>This filter pretty-prints field-oriented XML without mixed content.
041: * all added indentation and newlines will be passed on down
042: * the filter chain (if any).</p>
043: *
044: * <p>In general, all whitespace in an XML document is potentially
045: * significant, so a general-purpose XML writing tool like the
046: * {@link XMLWriter} class cannot
047: * add newlines or indentation.</p>
048: *
049: * <p>There is, however, a large class of XML documents where information
050: * is strictly fielded: each element contains either character data
051: * or other elements, but not both. For this special case, it is possible
052: * for a writing tool to provide automatic indentation and newlines
053: * without requiring extra work from the user. Note that this class
054: * will likely not yield appropriate results for document-oriented
055: * XML like XHTML pages, which mix character data and elements together.</p>
056: *
057: * <p>This writer will automatically place each start tag on a new line,
058: * optionally indented if an indent step is provided (by default, there
059: * is no indentation). If an element contains other elements, the end
060: * tag will also appear on a new line with leading indentation. Consider,
061: * for example, the following code:</p>
062: *
063: * <pre>
064: * DataWriter w = new DataWriter();
065: *
066: * w.setIndentStep(2);
067: * w.startDocument();
068: * w.startElement("Person");
069: * w.dataElement("name", "Jane Smith");
070: * w.dataElement("date-of-birth", "1965-05-23");
071: * w.dataElement("citizenship", "US");
072: * w.endElement("Person");
073: * w.endDocument();
074: * </pre>
075: *
076: * <p>This code will produce the following document:</p>
077: *
078: * <pre>
079: * <?xml version="1.0" standalone="yes"?>
080: *
081: * <Person>
082: * <name>Jane Smith</name>
083: * <date-of-birth>1965-05-23</date-of-birth>
084: * <citizenship>US</citizenship>
085: * </Person>
086: * </pre>
087: *
088: * <p>This class inherits from {@link XMLWriter},
089: * and provides all of the same support for Namespaces.</p>
090: *
091: * @since 1.0
092: * @author David Megginson, david@megginson.com
093: * @version 0.2
094: * @see XMLWriter
095: */
096: public class DataWriter extends XMLWriter {
097:
098: ////////////////////////////////////////////////////////////////////
099: // Constructors.
100: ////////////////////////////////////////////////////////////////////
101:
102: /**
103: * Create a new data writer for the specified output.
104: *
105: * @param writer The character stream where the XML document
106: * will be written.
107: * @param encoding
108: * If non-null string is specified, it is written as a part
109: * of the XML declaration.
110: */
111: public DataWriter(Writer writer, String encoding,
112: CharacterEscapeHandler _escapeHandler) {
113: super (writer, encoding, _escapeHandler);
114: }
115:
116: public DataWriter(Writer writer, String encoding) {
117: this (writer, encoding, DumbEscapeHandler.theInstance);
118: }
119:
120: ////////////////////////////////////////////////////////////////////
121: // Accessors and setters.
122: ////////////////////////////////////////////////////////////////////
123:
124: /**
125: * Return the current indent step.
126: *
127: * <p>Return the current indent step: each start tag will be
128: * indented by this number of spaces times the number of
129: * ancestors that the element has.</p>
130: *
131: * @return The number of spaces in each indentation step,
132: * or 0 or less for no indentation.
133: * @see #setIndentStep(int)
134: *
135: * @deprecated
136: * Only return the length of the indent string.
137: */
138: public int getIndentStep() {
139: return indentStep.length();
140: }
141:
142: /**
143: * Set the current indent step.
144: *
145: * @param indentStep The new indent step (0 or less for no
146: * indentation).
147: * @see #getIndentStep()
148: *
149: * @deprecated
150: * Should use the version that takes string.
151: */
152: public void setIndentStep(int indentStep) {
153: StringBuilder buf = new StringBuilder();
154: for (; indentStep > 0; indentStep--)
155: buf.append(' ');
156: setIndentStep(buf.toString());
157: }
158:
159: public void setIndentStep(String s) {
160: this .indentStep = s;
161: }
162:
163: ////////////////////////////////////////////////////////////////////
164: // Override methods from XMLWriter.
165: ////////////////////////////////////////////////////////////////////
166:
167: /**
168: * Reset the writer so that it can be reused.
169: *
170: * <p>This method is especially useful if the writer failed
171: * with an exception the last time through.</p>
172: *
173: * @see XMLWriter#reset()
174: */
175: public void reset() {
176: depth = 0;
177: state = SEEN_NOTHING;
178: stateStack = new Stack<Object>();
179: super .reset();
180: }
181:
182: protected void writeXmlDecl(String decl) throws IOException {
183: super .writeXmlDecl(decl);
184: write('\n');
185: }
186:
187: /**
188: * Write a start tag.
189: *
190: * <p>Each tag will begin on a new line, and will be
191: * indented by the current indent step times the number
192: * of ancestors that the element has.</p>
193: *
194: * <p>The newline and indentation will be passed on down
195: * the filter chain through regular characters events.</p>
196: *
197: * @param uri The element's Namespace URI.
198: * @param localName The element's local name.
199: * @param qName The element's qualified (prefixed) name.
200: * @param atts The element's attribute list.
201: * @exception org.xml.sax.SAXException If there is an error
202: * writing the start tag, or if a filter further
203: * down the chain raises an exception.
204: * @see XMLWriter#startElement(String, String, String, Attributes)
205: */
206: public void startElement(String uri, String localName,
207: String qName, Attributes atts) throws SAXException {
208: stateStack.push(SEEN_ELEMENT);
209: state = SEEN_NOTHING;
210: if (depth > 0) {
211: super .characters("\n");
212: }
213: doIndent();
214: super .startElement(uri, localName, qName, atts);
215: depth++;
216: }
217:
218: /**
219: * Write an end tag.
220: *
221: * <p>If the element has contained other elements, the tag
222: * will appear indented on a new line; otherwise, it will
223: * appear immediately following whatever came before.</p>
224: *
225: * <p>The newline and indentation will be passed on down
226: * the filter chain through regular characters events.</p>
227: *
228: * @param uri The element's Namespace URI.
229: * @param localName The element's local name.
230: * @param qName The element's qualified (prefixed) name.
231: * @exception org.xml.sax.SAXException If there is an error
232: * writing the end tag, or if a filter further
233: * down the chain raises an exception.
234: * @see XMLWriter#endElement(String, String, String)
235: */
236: public void endElement(String uri, String localName, String qName)
237: throws SAXException {
238: depth--;
239: if (state == SEEN_ELEMENT) {
240: super .characters("\n");
241: doIndent();
242: }
243: super .endElement(uri, localName, qName);
244: state = stateStack.pop();
245: }
246:
247: public void endDocument() throws SAXException {
248: try {
249: write('\n');
250: } catch (IOException e) {
251: throw new SAXException(e);
252: }
253: super .endDocument();
254: }
255:
256: // /**
257: // * Write a empty element tag.
258: // *
259: // * <p>Each tag will appear on a new line, and will be
260: // * indented by the current indent step times the number
261: // * of ancestors that the element has.</p>
262: // *
263: // * <p>The newline and indentation will be passed on down
264: // * the filter chain through regular characters events.</p>
265: // *
266: // * @param uri The element's Namespace URI.
267: // * @param localName The element's local name.
268: // * @param qName The element's qualified (prefixed) name.
269: // * @param atts The element's attribute list.
270: // * @exception org.xml.sax.SAXException If there is an error
271: // * writing the empty tag, or if a filter further
272: // * down the chain raises an exception.
273: // * @see XMLWriter#emptyElement(String, String, String, Attributes)
274: // */
275: // public void emptyElement (String uri, String localName,
276: // String qName, Attributes atts)
277: // throws SAXException
278: // {
279: // state = SEEN_ELEMENT;
280: // if (depth > 0) {
281: // super.characters("\n");
282: // }
283: // doIndent();
284: // super.emptyElement(uri, localName, qName, atts);
285: // }
286:
287: /**
288: * Write a sequence of characters.
289: *
290: * @param ch The characters to write.
291: * @param start The starting position in the array.
292: * @param length The number of characters to use.
293: * @exception org.xml.sax.SAXException If there is an error
294: * writing the characters, or if a filter further
295: * down the chain raises an exception.
296: * @see XMLWriter#characters(char[], int, int)
297: */
298: public void characters(char ch[], int start, int length)
299: throws SAXException {
300: state = SEEN_DATA;
301: super .characters(ch, start, length);
302: }
303:
304: ////////////////////////////////////////////////////////////////////
305: // Internal methods.
306: ////////////////////////////////////////////////////////////////////
307:
308: /**
309: * Print indentation for the current level.
310: *
311: * @exception org.xml.sax.SAXException If there is an error
312: * writing the indentation characters, or if a filter
313: * further down the chain raises an exception.
314: */
315: private void doIndent() throws SAXException {
316: if (depth > 0) {
317: char[] ch = indentStep.toCharArray();
318: for (int i = 0; i < depth; i++)
319: characters(ch, 0, ch.length);
320: }
321: }
322:
323: ////////////////////////////////////////////////////////////////////
324: // Constants.
325: ////////////////////////////////////////////////////////////////////
326:
327: private final static Object SEEN_NOTHING = new Object();
328: private final static Object SEEN_ELEMENT = new Object();
329: private final static Object SEEN_DATA = new Object();
330:
331: ////////////////////////////////////////////////////////////////////
332: // Internal state.
333: ////////////////////////////////////////////////////////////////////
334:
335: private Object state = SEEN_NOTHING;
336: private Stack<Object> stateStack = new Stack<Object>();
337:
338: private String indentStep = "";
339: private int depth = 0;
340:
341: }
342:
343: // end of DataWriter.java
|