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