1 /*
2 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 //@@3RD PARTY CODE@@
26
27 // DataWriter.java - XML writer for data-oriented files.
28
29 package com.sun.xml.internal.txw2.output;
30
31 import org.xml.sax.Attributes;
32 import org.xml.sax.SAXException;
33
34 import java.io.Writer;
35 import java.util.Stack;
36
37
38 /**
39 * Write data- or field-oriented XML.
40 *
41 * <p>This filter pretty-prints field-oriented XML without mixed content.
42 * all added indentation and newlines will be passed on down
43 * the filter chain (if any).</p>
44 *
45 * <p>In general, all whitespace in an XML document is potentially
46 * significant, so a general-purpose XML writing tool like the
47 * {@link XMLWriter} class cannot
48 * add newlines or indentation.</p>
49 *
50 * <p>There is, however, a large class of XML documents where information
51 * is strictly fielded: each element contains either character data
52 * or other elements, but not both. For this special case, it is possible
53 * for a writing tool to provide automatic indentation and newlines
54 * without requiring extra work from the user. Note that this class
55 * will likely not yield appropriate results for document-oriented
56 * XML like XHTML pages, which mix character data and elements together.</p>
57 *
58 * <p>This writer will automatically place each start tag on a new line,
59 * optionally indented if an indent step is provided (by default, there
60 * is no indentation). If an element contains other elements, the end
61 * tag will also appear on a new line with leading indentation. Consider,
62 * for example, the following code:</p>
63 *
64 * <pre>
65 * DataWriter w = new DataWriter();
66 *
67 * w.setIndentStep(2);
68 * w.startDocument();
69 * w.startElement("Person");
70 * w.dataElement("name", "Jane Smith");
71 * w.dataElement("date-of-birth", "1965-05-23");
72 * w.dataElement("citizenship", "US");
73 * w.endElement("Person");
74 * w.endDocument();
75 * </pre>
76 *
77 * <p>This code will produce the following document:</p>
78 *
79 * <pre>
80 * <?xml version="1.0" standalone="yes"?>
81 *
82 * <Person>
83 * <name>Jane Smith</name>
84 * <date-of-birth>1965-05-23</date-of-birth>
85 * <citizenship>US</citizenship>
86 * </Person>
87 * </pre>
88 *
89 * <p>This class inherits from {@link XMLWriter},
90 * and provides all of the same support for Namespaces.</p>
91 *
92 * @since 1.0
93 * @author David Megginson, david@megginson.com
94 * @see XMLWriter
95 */
96 public class DataWriter extends XMLWriter
97 {
98
99
100
101 ////////////////////////////////////////////////////////////////////
102 // Constructors.
103 ////////////////////////////////////////////////////////////////////
104
105
106 /**
107 * Create a new data writer for the specified output.
108 *
109 * @param writer The character stream where the XML document
110 * will be written.
111 * @param encoding
112 * If non-null string is specified, it is written as a part
113 * of the XML declaration.
114 */
115 public DataWriter ( Writer writer, String encoding, CharacterEscapeHandler _escapeHandler )
116 {
117 super(writer,encoding,_escapeHandler);
118 }
119
120
121 public DataWriter (Writer writer, String encoding ) {
122 this( writer, encoding, DumbEscapeHandler.theInstance );
123 }
124
125 public DataWriter (Writer writer) {
126 this( writer, null, DumbEscapeHandler.theInstance );
127 }
128
129
130
131 ////////////////////////////////////////////////////////////////////
132 // Accessors and setters.
133 ////////////////////////////////////////////////////////////////////
134
135
136 /**
137 * Return the current indent step.
138 *
139 * <p>Return the current indent step: each start tag will be
140 * indented by this number of spaces times the number of
141 * ancestors that the element has.</p>
142 *
143 * @return The number of spaces in each indentation step,
144 * or 0 or less for no indentation.
145 * @see #setIndentStep(int)
146 *
147 * @deprecated
148 * Only return the length of the indent string.
149 */
150 public int getIndentStep ()
151 {
152 return indentStep.length();
153 }
154
155
156 /**
157 * Set the current indent step.
158 *
159 * @param indentStep The new indent step (0 or less for no
160 * indentation).
161 * @see #getIndentStep()
162 *
163 * @deprecated
164 * Should use the version that takes string.
165 */
166 public void setIndentStep (int indentStep)
167 {
168 StringBuilder s = new StringBuilder();
169 for( ; indentStep>0; indentStep-- ) s.append(' ');
170 setIndentStep(s.toString());
171 }
172
173 public void setIndentStep(String s) {
174 this.indentStep = s;
175 }
176
177
178
179 ////////////////////////////////////////////////////////////////////
180 // Override methods from XMLWriter.
181 ////////////////////////////////////////////////////////////////////
182
183
184 /**
185 * Reset the writer so that it can be reused.
186 *
187 * <p>This method is especially useful if the writer failed
188 * with an exception the last time through.</p>
189 *
190 * @see XMLWriter#reset()
191 */
192 public void reset ()
193 {
194 depth = 0;
195 state = SEEN_NOTHING;
196 stateStack = new Stack();
197 super.reset();
198 }
199
200
201 /**
202 * Write a start tag.
203 *
204 * <p>Each tag will begin on a new line, and will be
205 * indented by the current indent step times the number
206 * of ancestors that the element has.</p>
207 *
208 * <p>The newline and indentation will be passed on down
209 * the filter chain through regular characters events.</p>
210 *
211 * @param uri The element's Namespace URI.
212 * @param localName The element's local name.
213 * @param qName The element's qualified (prefixed) name.
214 * @param atts The element's attribute list.
215 * @exception org.xml.sax.SAXException If there is an error
216 * writing the start tag, or if a filter further
217 * down the chain raises an exception.
218 * @see XMLWriter#startElement(String, String, String, Attributes)
219 */
220 public void startElement (String uri, String localName,
221 String qName, Attributes atts)
222 throws SAXException
223 {
224 stateStack.push(SEEN_ELEMENT);
225 state = SEEN_NOTHING;
226 if (depth > 0) {
227 super.characters("\n");
228 }
229 doIndent();
230 super.startElement(uri, localName, qName, atts);
231 depth++;
232 }
233
234
235 /**
236 * Write an end tag.
237 *
238 * <p>If the element has contained other elements, the tag
239 * will appear indented on a new line; otherwise, it will
240 * appear immediately following whatever came before.</p>
241 *
242 * <p>The newline and indentation will be passed on down
243 * the filter chain through regular characters events.</p>
244 *
245 * @param uri The element's Namespace URI.
246 * @param localName The element's local name.
247 * @param qName The element's qualified (prefixed) name.
248 * @exception org.xml.sax.SAXException If there is an error
249 * writing the end tag, or if a filter further
250 * down the chain raises an exception.
251 * @see XMLWriter#endElement(String, String, String)
252 */
253 public void endElement (String uri, String localName, String qName)
254 throws SAXException
255 {
256 depth--;
257 if (state == SEEN_ELEMENT) {
258 super.characters("\n");
259 doIndent();
260 }
261 super.endElement(uri, localName, qName);
262 state = stateStack.pop();
263 }
264
265
266 // /**
267 // * Write a empty element tag.
268 // *
269 // * <p>Each tag will appear on a new line, and will be
270 // * indented by the current indent step times the number
271 // * of ancestors that the element has.</p>
272 // *
273 // * <p>The newline and indentation will be passed on down
274 // * the filter chain through regular characters events.</p>
275 // *
276 // * @param uri The element's Namespace URI.
277 // * @param localName The element's local name.
278 // * @param qName The element's qualified (prefixed) name.
279 // * @param atts The element's attribute list.
280 // * @exception org.xml.sax.SAXException If there is an error
281 // * writing the empty tag, or if a filter further
282 // * down the chain raises an exception.
283 // * @see XMLWriter#emptyElement(String, String, String, Attributes)
284 // */
285 // public void emptyElement (String uri, String localName,
286 // String qName, Attributes atts)
287 // throws SAXException
288 // {
289 // state = SEEN_ELEMENT;
290 // if (depth > 0) {
291 // super.characters("\n");
292 // }
293 // doIndent();
294 // super.emptyElement(uri, localName, qName, atts);
295 // }
296
297
298 /**
299 * Write a sequence of characters.
300 *
301 * @param ch The characters to write.
302 * @param start The starting position in the array.
303 * @param length The number of characters to use.
304 * @exception org.xml.sax.SAXException If there is an error
305 * writing the characters, or if a filter further
306 * down the chain raises an exception.
307 * @see XMLWriter#characters(char[], int, int)
308 */
309 public void characters (char ch[], int start, int length)
310 throws SAXException
311 {
312 state = SEEN_DATA;
313 super.characters(ch, start, length);
314 }
315
316 public void comment(char ch[], int start, int length) throws SAXException {
317 if (depth > 0) {
318 super.characters("\n");
319 }
320 doIndent();
321 super.comment(ch,start,length);
322 }
323
324
325
326 ////////////////////////////////////////////////////////////////////
327 // Internal methods.
328 ////////////////////////////////////////////////////////////////////
329
330
331 /**
332 * Print indentation for the current level.
333 *
334 * @exception org.xml.sax.SAXException If there is an error
335 * writing the indentation characters, or if a filter
336 * further down the chain raises an exception.
337 */
338 private void doIndent ()
339 throws SAXException
340 {
341 if (depth > 0) {
342 char[] ch = indentStep.toCharArray();
343 for( int i=0; i<depth; i++ )
344 characters(ch, 0, ch.length);
345 }
346 }
347
348
349
350 ////////////////////////////////////////////////////////////////////
351 // Constants.
352 ////////////////////////////////////////////////////////////////////
353
354 private final static Object SEEN_NOTHING = new Object();
355 private final static Object SEEN_ELEMENT = new Object();
356 private final static Object SEEN_DATA = new Object();
357
358
359
360 ////////////////////////////////////////////////////////////////////
361 // Internal state.
362 ////////////////////////////////////////////////////////////////////
363
364 private Object state = SEEN_NOTHING;
365 private Stack stateStack = new Stack();
366
367 private String indentStep = "";
368 private int depth = 0;
369
370 }
371
372 // end of DataWriter.java