~ubuntu-branches/ubuntu/saucy/restlet/saucy

« back to all changes in this revision

Viewing changes to org.restlet.ext.xml/src/org/restlet/ext/xml/XmlWriter.java

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-06-11 16:25:45 UTC
  • Revision ID: package-import@ubuntu.com-20120611162545-5w2o0resi5y3pybc
Tags: upstream-2.0.14
ImportĀ upstreamĀ versionĀ 2.0.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright 2005-2012 Restlet S.A.S.
 
3
 * 
 
4
 * The contents of this file are subject to the terms of one of the following
 
5
 * open source licenses: Apache 2.0 or LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL
 
6
 * 1.0 (the "Licenses"). You can select the license that you prefer but you may
 
7
 * not use this file except in compliance with one of these Licenses.
 
8
 * 
 
9
 * You can obtain a copy of the Apache 2.0 license at
 
10
 * http://www.opensource.org/licenses/apache-2.0
 
11
 * 
 
12
 * You can obtain a copy of the LGPL 3.0 license at
 
13
 * http://www.opensource.org/licenses/lgpl-3.0
 
14
 * 
 
15
 * You can obtain a copy of the LGPL 2.1 license at
 
16
 * http://www.opensource.org/licenses/lgpl-2.1
 
17
 * 
 
18
 * You can obtain a copy of the CDDL 1.0 license at
 
19
 * http://www.opensource.org/licenses/cddl1
 
20
 * 
 
21
 * You can obtain a copy of the EPL 1.0 license at
 
22
 * http://www.opensource.org/licenses/eclipse-1.0
 
23
 * 
 
24
 * See the Licenses for the specific language governing permissions and
 
25
 * limitations under the Licenses.
 
26
 * 
 
27
 * Alternatively, you can obtain a royalty free commercial license with less
 
28
 * limitations, transferable or non-transferable, directly at
 
29
 * http://www.restlet.com/products/restlet-framework
 
30
 * 
 
31
 * Restlet is a registered trademark of Restlet S.A.S.
 
32
 */
 
33
 
 
34
package org.restlet.ext.xml;
 
35
 
 
36
import java.io.IOException;
 
37
import java.io.OutputStream;
 
38
import java.io.OutputStreamWriter;
 
39
import java.io.UnsupportedEncodingException;
 
40
import java.io.Writer;
 
41
import java.nio.charset.Charset;
 
42
import java.nio.charset.CharsetEncoder;
 
43
import java.util.Enumeration;
 
44
import java.util.Map;
 
45
import java.util.Stack;
 
46
import java.util.TreeMap;
 
47
 
 
48
import org.xml.sax.Attributes;
 
49
import org.xml.sax.SAXException;
 
50
import org.xml.sax.XMLReader;
 
51
import org.xml.sax.helpers.AttributesImpl;
 
52
import org.xml.sax.helpers.NamespaceSupport;
 
53
import org.xml.sax.helpers.XMLFilterImpl;
 
54
 
 
55
/**
 
56
 * XML writer doing the opposite work of a SAX-based XML reader. The
 
57
 * implementation is based on the work of David Megginson, the creator of SAX
 
58
 * who placed the original code in the public domain.
 
59
 * 
 
60
 * <p>
 
61
 * This class can be used by itself or as part of a SAX event stream: it takes
 
62
 * as input a series of SAX2 ContentHandler events and uses the information in
 
63
 * those events to write an XML document. Since this class is a filter, it can
 
64
 * also pass the events on down a filter chain for further processing (you can
 
65
 * use the XmlWriter to take a snapshot of the current state at any point in a
 
66
 * filter chain), and it can be used directly as a ContentHandler for a SAX2
 
67
 * XMLReader.
 
68
 * </p>
 
69
 * 
 
70
 * <p>
 
71
 * The client creates a document by invoking the methods for standard SAX2
 
72
 * events, always beginning with the {@link #startDocument startDocument} method
 
73
 * and ending with the {@link #endDocument endDocument} method. There are
 
74
 * convenience methods provided so that clients to not have to create empty
 
75
 * attribute lists or provide empty strings as parameters; for example, the
 
76
 * method invocation
 
77
 * </p>
 
78
 * 
 
79
 * <pre>
 
80
 * w.startElement(&quot;foo&quot;);
 
81
 * </pre>
 
82
 * 
 
83
 * <p>
 
84
 * is equivalent to the regular SAX2 ContentHandler method
 
85
 * </p>
 
86
 * 
 
87
 * <pre>
 
88
 * w.startElement(&quot;&quot;, &quot;foo&quot;, &quot;&quot;, new AttributesImpl());
 
89
 * </pre>
 
90
 * 
 
91
 * <p>
 
92
 * Except that it is more efficient because it does not allocate a new empty
 
93
 * attribute list each time. The following code will send a simple XML document
 
94
 * to standard output:
 
95
 * </p>
 
96
 * 
 
97
 * <pre>
 
98
 * XmlWriter w = new XmlWriter();
 
99
 * 
 
100
 * w.startDocument();
 
101
 * w.startElement(&quot;greeting&quot;);
 
102
 * w.characters(&quot;Hello, world!&quot;);
 
103
 * w.endElement(&quot;greeting&quot;);
 
104
 * w.endDocument();
 
105
 * </pre>
 
106
 * 
 
107
 * <p>
 
108
 * The resulting document will look like this:
 
109
 * </p>
 
110
 * 
 
111
 * <pre>
 
112
 *           &lt;?xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
113
 *          
 
114
 *           &lt;greeting&gt;Hello, world!&lt;/greeting&gt;
 
115
 * </pre>
 
116
 * 
 
117
 * <p>
 
118
 * In fact, there is an even simpler convenience method, <var>dataElement</var>,
 
119
 * designed for writing elements that contain only character data, so the code
 
120
 * to generate the document could be shortened to
 
121
 * </p>
 
122
 * 
 
123
 * <pre>
 
124
 * XmlWriter w = new XmlWriter();
 
125
 * 
 
126
 * w.startDocument();
 
127
 * w.dataElement(&quot;greeting&quot;, &quot;Hello, world!&quot;);
 
128
 * w.endDocument();
 
129
 * </pre>
 
130
 * 
 
131
 * <h2>Whitespace</h2>
 
132
 * 
 
133
 * <p>
 
134
 * According to the XML Recommendation, <em>all</em> whitespace in an XML
 
135
 * document is potentially significant to an application, so this class never
 
136
 * adds newlines or indentation. If you insert three elements in a row, as in
 
137
 * </p>
 
138
 * 
 
139
 * <pre>
 
140
 * w.dataElement(&quot;item&quot;, &quot;1&quot;);
 
141
 * w.dataElement(&quot;item&quot;, &quot;2&quot;);
 
142
 * w.dataElement(&quot;item&quot;, &quot;3&quot;);
 
143
 * </pre>
 
144
 * 
 
145
 * <p>
 
146
 * you will end up with
 
147
 * </p>
 
148
 * 
 
149
 * <pre>
 
150
 *           &lt;item&gt;1&lt;/item&gt;&lt;item&gt;3&lt;/item&gt;&lt;item&gt;3&lt;/item&gt;
 
151
 * </pre>
 
152
 * 
 
153
 * <p>
 
154
 * You need to invoke one of the <var>characters</var> methods explicitly to add
 
155
 * newlines or indentation. Alternatively, you can use the data format mode (set
 
156
 * the "dataFormat" property) which is optimized for writing purely
 
157
 * data-oriented (or field-oriented) XML, and does automatic linebreaks and
 
158
 * indentation (but does not support mixed content properly). See details below.
 
159
 * </p>
 
160
 * 
 
161
 * <h2>Namespace Support</h2>
 
162
 * 
 
163
 * <p>
 
164
 * The writer contains extensive support for XML Namespaces, so that a client
 
165
 * application does not have to keep track of prefixes and supply
 
166
 * <var>xmlns</var> attributes. By default, the XML writer will generate
 
167
 * Namespace declarations in the form _NS1, _NS2, etc., wherever they are
 
168
 * needed, as in the following example:
 
169
 * </p>
 
170
 * 
 
171
 * <pre>
 
172
 * w.startDocument();
 
173
 * w.emptyElement(&quot;http://www.foo.com/ns/&quot;, &quot;foo&quot;);
 
174
 * w.endDocument();
 
175
 * </pre>
 
176
 * 
 
177
 * <p>
 
178
 * The resulting document will look like this:
 
179
 * </p>
 
180
 * 
 
181
 * <pre>
 
182
 *           &lt;?xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
183
 *          
 
184
 *           &lt;_NS1:foo xmlns:_NS1=&quot;http://www.foo.com/ns/&quot;/&gt;
 
185
 * </pre>
 
186
 * 
 
187
 * <p>
 
188
 * In many cases, document authors will prefer to choose their own prefixes
 
189
 * rather than using the (ugly) default names. The XML writer allows two methods
 
190
 * for selecting prefixes:
 
191
 * </p>
 
192
 * 
 
193
 * <ol>
 
194
 * <li>the qualified name</li>
 
195
 * <li>the {@link #setPrefix setPrefix} method.</li>
 
196
 * </ol>
 
197
 * 
 
198
 * <p>
 
199
 * Whenever the XML writer finds a new Namespace URI, it checks to see if a
 
200
 * qualified (prefixed) name is also available; if so it attempts to use the
 
201
 * name's prefix (as long as the prefix is not already in use for another
 
202
 * Namespace URI).
 
203
 * </p>
 
204
 * 
 
205
 * <p>
 
206
 * Before writing a document, the client can also pre-map a prefix to a
 
207
 * Namespace URI with the setPrefix method:
 
208
 * </p>
 
209
 * 
 
210
 * <pre>
 
211
 * w.setPrefix(&quot;http://www.foo.com/ns/&quot;, &quot;foo&quot;);
 
212
 * w.startDocument();
 
213
 * w.emptyElement(&quot;http://www.foo.com/ns/&quot;, &quot;foo&quot;);
 
214
 * w.endDocument();
 
215
 * </pre>
 
216
 * 
 
217
 * <p>
 
218
 * The resulting document will look like this:
 
219
 * </p>
 
220
 * 
 
221
 * <pre>
 
222
 *           &lt;?xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
223
 *          
 
224
 *           &lt;foo:foo xmlns:foo=&quot;http://www.foo.com/ns/&quot;/&gt;
 
225
 * </pre>
 
226
 * 
 
227
 * <p>
 
228
 * The default Namespace simply uses an empty string as the prefix:
 
229
 * </p>
 
230
 * 
 
231
 * <pre>
 
232
 * w.setPrefix(&quot;http://www.foo.com/ns/&quot;, &quot;&quot;);
 
233
 * w.startDocument();
 
234
 * w.emptyElement(&quot;http://www.foo.com/ns/&quot;, &quot;foo&quot;);
 
235
 * w.endDocument();
 
236
 * </pre>
 
237
 * 
 
238
 * <p>
 
239
 * The resulting document will look like this:
 
240
 * </p>
 
241
 * 
 
242
 * <pre>
 
243
 *           &lt;?xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
244
 *          
 
245
 *           &lt;foo xmlns=&quot;http://www.foo.com/ns/&quot;/&gt;
 
246
 * </pre>
 
247
 * 
 
248
 * <p>
 
249
 * By default, the XML writer will not declare a Namespace until it is actually
 
250
 * used. Sometimes, this approach will create a large number of Namespace
 
251
 * declarations, as in the following example:
 
252
 * </p>
 
253
 * 
 
254
 * <pre>
 
255
 *           &lt;xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
256
 *          
 
257
 *           &lt;rdf:RDF xmlns:rdf=&quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#&quot;&gt;
 
258
 *            &lt;rdf:Description about=&quot;http://www.foo.com/ids/books/12345&quot;&gt;
 
259
 *             &lt;dc:title xmlns:dc=&quot;http://www.purl.org/dc/&quot;&gt;A Dark Night&lt;/dc:title&gt;
 
260
 *             &lt;dc:creator xmlns:dc=&quot;http://www.purl.org/dc/&quot;&gt;Jane Smith&lt;/dc:title&gt;
 
261
 *             &lt;dc:date xmlns:dc=&quot;http://www.purl.org/dc/&quot;&gt;2000-09-09&lt;/dc:title&gt;
 
262
 *            &lt;/rdf:Description&gt;
 
263
 *           &lt;/rdf:RDF&gt;
 
264
 * </pre>
 
265
 * 
 
266
 * <p>
 
267
 * The "rdf" prefix is declared only once, because the RDF Namespace is used by
 
268
 * the root element and can be inherited by all of its descendants; the "dc"
 
269
 * prefix, on the other hand, is declared three times, because no higher element
 
270
 * uses the Namespace. To solve this problem, you can instruct the XML writer to
 
271
 * predeclare Namespaces on the root element even if they are not used there:
 
272
 * </p>
 
273
 * 
 
274
 * <pre>
 
275
 * w.forceNSDecl(&quot;http://www.purl.org/dc/&quot;);
 
276
 * </pre>
 
277
 * 
 
278
 * <p>
 
279
 * Now, the "dc" prefix will be declared on the root element even though it's
 
280
 * not needed there, and can be inherited by its descendants:
 
281
 * </p>
 
282
 * 
 
283
 * <pre>
 
284
 *           &lt;xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
285
 *          
 
286
 *           &lt;rdf:RDF xmlns:rdf=&quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#&quot;
 
287
 *                       xmlns:dc=&quot;http://www.purl.org/dc/&quot;&gt;
 
288
 *            &lt;rdf:Description about=&quot;http://www.foo.com/ids/books/12345&quot;&gt;
 
289
 *             &lt;dc:title&gt;A Dark Night&lt;/dc:title&gt;
 
290
 *             &lt;dc:creator&gt;Jane Smith&lt;/dc:title&gt;
 
291
 *             &lt;dc:date&gt;2000-09-09&lt;/dc:title&gt;
 
292
 *            &lt;/rdf:Description&gt;
 
293
 *           &lt;/rdf:RDF&gt;
 
294
 * </pre>
 
295
 * 
 
296
 * <p>
 
297
 * This approach is also useful for declaring Namespace prefixes that be used by
 
298
 * qualified names appearing in attribute values or character data.
 
299
 * </p>
 
300
 * 
 
301
 * <h2>Data Format</h2>
 
302
 * 
 
303
 * <p>
 
304
 * This mode, enabled by the "dataFormat" property, pretty-prints field-oriented
 
305
 * XML without mixed content. All added indentation and newlines will be passed
 
306
 * on down the filter chain (if any).
 
307
 * </p>
 
308
 * 
 
309
 * <p>
 
310
 * In general, all whitespace in an XML document is potentially significant, so
 
311
 * a general-purpose XML writing tool cannot add newlines or indentation.
 
312
 * </p>
 
313
 * 
 
314
 * <p>
 
315
 * There is, however, a large class of XML documents where information is
 
316
 * strictly fielded: each element contains either character data or other
 
317
 * elements, but not both. For this special case, it is possible for a writing
 
318
 * tool to provide automatic indentation and newlines without requiring extra
 
319
 * work from the user. Note that this class will likely not yield appropriate
 
320
 * results for document-oriented XML like XHTML pages, which mix character data
 
321
 * and elements together.
 
322
 * </p>
 
323
 * 
 
324
 * <p>
 
325
 * This writer mode will automatically place each start tag on a new line,
 
326
 * optionally indented if an indent step is provided (by default, there is no
 
327
 * indentation). If an element contains other elements, the end tag will also
 
328
 * appear on a new line with leading indentation. Consider, for example, the
 
329
 * following code:
 
330
 * </p>
 
331
 * 
 
332
 * <pre>
 
333
 * XmlWriter w = new XmlWriter();
 
334
 * w.setDataFormat(true);
 
335
 * w.setIndentStep(2);
 
336
 * w.startDocument();
 
337
 * w.startElement(&quot;Person&quot;);
 
338
 * w.dataElement(&quot;name&quot;, &quot;Jane Smith&quot;);
 
339
 * w.dataElement(&quot;date-of-birth&quot;, &quot;1965-05-23&quot;);
 
340
 * w.dataElement(&quot;citizenship&quot;, &quot;US&quot;);
 
341
 * w.endElement(&quot;Person&quot;);
 
342
 * w.endDocument();
 
343
 * </pre>
 
344
 * 
 
345
 * <p>
 
346
 * This code will produce the following document:
 
347
 * </p>
 
348
 * 
 
349
 * <pre>
 
350
 *           &lt;?xml version=&quot;1.0&quot; standalone='yes'?&gt;
 
351
 *          
 
352
 *           &lt;Person&gt;
 
353
 *             &lt;name&gt;Jane Smith&lt;/name&gt;
 
354
 *             &lt;date-of-birth&gt;1965-05-23&lt;/date-of-birth&gt;
 
355
 *             &lt;citizenship&gt;US&lt;/citizenship&gt;
 
356
 *           &lt;/Person&gt;
 
357
 * </pre>
 
358
 * 
 
359
 * @see org.xml.sax.XMLFilter
 
360
 * @see org.xml.sax.ContentHandler
 
361
 * @author David Megginson, Jerome Louvel (contact@restlet.com)
 
362
 */
 
363
public final class XmlWriter extends XMLFilterImpl {
 
364
    private static final Object SEEN_NOTHING = new Object();
 
365
 
 
366
    private static final Object SEEN_ELEMENT = new Object();
 
367
 
 
368
    private static final Object SEEN_DATA = new Object();
 
369
 
 
370
    /**
 
371
     * Constant representing empty attributes.
 
372
     */
 
373
    private final Attributes EMPTY_ATTS = new AttributesImpl();
 
374
 
 
375
    /**
 
376
     * The prefixes table.
 
377
     */
 
378
    private volatile Map<String, String> prefixTable;
 
379
 
 
380
    /**
 
381
     * The forced declarations table.
 
382
     */
 
383
    private volatile Map<String, Boolean> forcedDeclTable;
 
384
 
 
385
    /**
 
386
     * The document declarations table.
 
387
     */
 
388
    private volatile Map<String, String> doneDeclTable;
 
389
 
 
390
    /**
 
391
     * The element level.
 
392
     */
 
393
    private volatile int elementLevel = 0;
 
394
 
 
395
    /**
 
396
     * The namespace support.
 
397
     */
 
398
    private volatile NamespaceSupport nsSupport;
 
399
 
 
400
    /**
 
401
     * The prefix counter.
 
402
     */
 
403
    private volatile int prefixCounter = 0;
 
404
 
 
405
    /**
 
406
     * The underlying writer.
 
407
     */
 
408
    private volatile Writer output;
 
409
 
 
410
    private volatile Object state = SEEN_NOTHING;
 
411
 
 
412
    private volatile Stack<Object> stateStack = new Stack<Object>();
 
413
 
 
414
    private volatile boolean dataFormat = false;
 
415
 
 
416
    private volatile int indentStep = 0;
 
417
 
 
418
    private volatile int depth = 0;
 
419
 
 
420
    /**
 
421
     * Create a new XML writer.
 
422
     * <p>
 
423
     * Write to standard output.
 
424
     * </p>
 
425
     */
 
426
    public XmlWriter() {
 
427
        init(null);
 
428
    }
 
429
 
 
430
    /**
 
431
     * Constructor.
 
432
     * 
 
433
     * @param out
 
434
     *            The underlying output stream.
 
435
     */
 
436
    public XmlWriter(OutputStream out) {
 
437
        this(new OutputStreamWriter(out));
 
438
    }
 
439
 
 
440
    /**
 
441
     * Constructor.
 
442
     * 
 
443
     * @param out
 
444
     *            The underlying output stream.
 
445
     */
 
446
    public XmlWriter(OutputStream out, Charset cs) {
 
447
        this(new OutputStreamWriter(out, cs));
 
448
    }
 
449
 
 
450
    /**
 
451
     * Constructor.
 
452
     * 
 
453
     * @param out
 
454
     *            The underlying output stream.
 
455
     */
 
456
    public XmlWriter(OutputStream out, CharsetEncoder enc) {
 
457
        this(new OutputStreamWriter(out, enc));
 
458
    }
 
459
 
 
460
    /**
 
461
     * Constructor.
 
462
     * 
 
463
     * @param out
 
464
     *            The underlying output stream.
 
465
     */
 
466
    public XmlWriter(OutputStream out, String charsetName)
 
467
            throws UnsupportedEncodingException {
 
468
        this(new OutputStreamWriter(out, charsetName));
 
469
    }
 
470
 
 
471
    /**
 
472
     * Create a new XML writer.
 
473
     * <p>
 
474
     * Write to the writer provided.
 
475
     * </p>
 
476
     * 
 
477
     * @param writer
 
478
     *            The output destination, or null to use standard output.
 
479
     */
 
480
    public XmlWriter(Writer writer) {
 
481
        init(writer);
 
482
    }
 
483
 
 
484
    /**
 
485
     * Create a new XML writer.
 
486
     * <p>
 
487
     * Use the specified XML reader as the parent.
 
488
     * </p>
 
489
     * 
 
490
     * @param xmlreader
 
491
     *            The parent in the filter chain, or null for no parent.
 
492
     */
 
493
    public XmlWriter(XMLReader xmlreader) {
 
494
        super(xmlreader);
 
495
        init(null);
 
496
    }
 
497
 
 
498
    /**
 
499
     * Create a new XML writer.
 
500
     * <p>
 
501
     * Use the specified XML reader as the parent, and write to the specified
 
502
     * writer.
 
503
     * </p>
 
504
     * 
 
505
     * @param xmlreader
 
506
     *            The parent in the filter chain, or null for no parent.
 
507
     * @param writer
 
508
     *            The output destination, or null to use standard output.
 
509
     */
 
510
    public XmlWriter(XMLReader xmlreader, Writer writer) {
 
511
        super(xmlreader);
 
512
        init(writer);
 
513
    }
 
514
 
 
515
    /**
 
516
     * Write character data. Pass the event on down the filter chain for further
 
517
     * processing.
 
518
     * 
 
519
     * @param ch
 
520
     *            The array of characters to write.
 
521
     * @param start
 
522
     *            The starting position in the array.
 
523
     * @param len
 
524
     *            The number of characters to write.
 
525
     * @exception org.xml.sax.SAXException
 
526
     *                If there is an error writing the characters, or if a
 
527
     *                restlet further down the filter chain raises an exception.
 
528
     * @see org.xml.sax.ContentHandler#characters
 
529
     */
 
530
    private void characters(boolean dataFormat, char ch[], int start, int len)
 
531
            throws SAXException {
 
532
        if (dataFormat) {
 
533
            this.state = SEEN_DATA;
 
534
        }
 
535
 
 
536
        writeEsc(ch, start, len, false);
 
537
        super.characters(ch, start, len);
 
538
    }
 
539
 
 
540
    // //////////////////////////////////////////////////////////////////
 
541
    // Public methods.
 
542
    // //////////////////////////////////////////////////////////////////
 
543
 
 
544
    /**
 
545
     * Write a string of character data, with XML escaping.
 
546
     * <p>
 
547
     * This is a convenience method that takes an XML String, converts it to a
 
548
     * character array, then invokes {@link #characters(char[], int, int)}.
 
549
     * </p>
 
550
     * 
 
551
     * @param data
 
552
     *            The character data.
 
553
     * @exception org.xml.sax.SAXException
 
554
     *                If there is an error writing the string, or if a restlet
 
555
     *                further down the filter chain raises an exception.
 
556
     * @see #characters(char[], int, int)
 
557
     */
 
558
    private void characters(boolean dataFormat, String data)
 
559
            throws SAXException {
 
560
        final char ch[] = data.toCharArray();
 
561
        characters(dataFormat, ch, 0, ch.length);
 
562
    }
 
563
 
 
564
    /**
 
565
     * Write character data. Pass the event on down the filter chain for further
 
566
     * processing.
 
567
     * 
 
568
     * @param ch
 
569
     *            The array of characters to write.
 
570
     * @param start
 
571
     *            The starting position in the array.
 
572
     * @param len
 
573
     *            The number of characters to write.
 
574
     * @exception org.xml.sax.SAXException
 
575
     *                If there is an error writing the characters, or if a
 
576
     *                restlet further down the filter chain raises an exception.
 
577
     * @see org.xml.sax.ContentHandler#characters
 
578
     */
 
579
    @Override
 
580
    public void characters(char ch[], int start, int len) throws SAXException {
 
581
        characters(isDataFormat(), ch, start, len);
 
582
    }
 
583
 
 
584
    /**
 
585
     * Write a string of character data, with XML escaping.
 
586
     * <p>
 
587
     * This is a convenience method that takes an XML String, converts it to a
 
588
     * character array, then invokes {@link #characters(char[], int, int)}.
 
589
     * </p>
 
590
     * 
 
591
     * @param data
 
592
     *            The character data.
 
593
     * @exception org.xml.sax.SAXException
 
594
     *                If there is an error writing the string, or if a restlet
 
595
     *                further down the filter chain raises an exception.
 
596
     * @see #characters(char[], int, int)
 
597
     */
 
598
    public void characters(String data) throws SAXException {
 
599
        characters(false, data);
 
600
    }
 
601
 
 
602
    /**
 
603
     * Write an element with character data content but no attributes or
 
604
     * Namespace URI.
 
605
     * 
 
606
     * <p>
 
607
     * This is a convenience method to write a complete element with character
 
608
     * data content, including the start tag and end tag. The method provides an
 
609
     * empty string for the Namespace URI, and empty string for the qualified
 
610
     * name, and an empty attribute list.
 
611
     * </p>
 
612
     * 
 
613
     * <p>
 
614
     * This method invokes
 
615
     * {@link #startElement(String, String, String, Attributes)}, followed by
 
616
     * {@link #characters(String)}, followed by
 
617
     * {@link #endElement(String, String, String)}.
 
618
     * </p>
 
619
     * 
 
620
     * @param localName
 
621
     *            The element's local name.
 
622
     * @param content
 
623
     *            The character data content.
 
624
     * @exception org.xml.sax.SAXException
 
625
     *                If there is an error writing the empty tag, or if a
 
626
     *                restlet further down the filter chain raises an exception.
 
627
     * @see #startElement(String, String, String, Attributes)
 
628
     * @see #characters(String)
 
629
     * @see #endElement(String, String, String)
 
630
     */
 
631
    public void dataElement(String localName, String content)
 
632
            throws SAXException {
 
633
        dataElement("", localName, "", this.EMPTY_ATTS, content);
 
634
    }
 
635
 
 
636
    /**
 
637
     * Write an element with character data content but no attributes.
 
638
     * 
 
639
     * <p>
 
640
     * This is a convenience method to write a complete element with character
 
641
     * data content, including the start tag and end tag. This method provides
 
642
     * an empty string for the qname and an empty attribute list.
 
643
     * </p>
 
644
     * 
 
645
     * <p>
 
646
     * This method invokes
 
647
     * {@link #startElement(String, String, String, Attributes)}, followed by
 
648
     * {@link #characters(String)}, followed by
 
649
     * {@link #endElement(String, String, String)}.
 
650
     * </p>
 
651
     * 
 
652
     * @param uri
 
653
     *            The element's Namespace URI.
 
654
     * @param localName
 
655
     *            The element's local name.
 
656
     * @param content
 
657
     *            The character data content.
 
658
     * @exception org.xml.sax.SAXException
 
659
     *                If there is an error writing the empty tag, or if a
 
660
     *                restlet further down the filter chain raises an exception.
 
661
     * @see #startElement(String, String, String, Attributes)
 
662
     * @see #characters(String)
 
663
     * @see #endElement(String, String, String)
 
664
     */
 
665
    public void dataElement(String uri, String localName, String content)
 
666
            throws SAXException {
 
667
        dataElement(uri, localName, "", this.EMPTY_ATTS, content);
 
668
    }
 
669
 
 
670
    /**
 
671
     * Write an element with character data content.
 
672
     * 
 
673
     * <p>
 
674
     * This is a convenience method to write a complete element with character
 
675
     * data content, including the start tag and end tag.
 
676
     * </p>
 
677
     * 
 
678
     * <p>
 
679
     * This method invokes
 
680
     * {@link #startElement(String, String, String, Attributes)}, followed by
 
681
     * {@link #characters(String)}, followed by
 
682
     * {@link #endElement(String, String, String)}.
 
683
     * </p>
 
684
     * 
 
685
     * @param uri
 
686
     *            The element's Namespace URI.
 
687
     * @param localName
 
688
     *            The element's local name.
 
689
     * @param qName
 
690
     *            The element's default qualified name.
 
691
     * @param atts
 
692
     *            The element's attributes.
 
693
     * @param content
 
694
     *            The character data content.
 
695
     * @exception org.xml.sax.SAXException
 
696
     *                If there is an error writing the empty tag, or if a
 
697
     *                restlet further down the filter chain raises an exception.
 
698
     * @see #startElement(String, String, String, Attributes)
 
699
     * @see #characters(String)
 
700
     * @see #endElement(String, String, String)
 
701
     */
 
702
    public void dataElement(String uri, String localName, String qName,
 
703
            Attributes atts, String content) throws SAXException {
 
704
        startElement(uri, localName, qName, atts);
 
705
        characters(content);
 
706
        endElement(uri, localName, qName);
 
707
    }
 
708
 
 
709
    /**
 
710
     * Print indentation for the current level.
 
711
     * 
 
712
     * @exception org.xml.sax.SAXException
 
713
     *                If there is an error writing the indentation characters,
 
714
     *                or if a filter further down the chain raises an exception.
 
715
     */
 
716
    private void doIndent() throws SAXException {
 
717
        if ((this.indentStep > 0) && (this.depth > 0)) {
 
718
            final int n = this.indentStep * this.depth;
 
719
            final char ch[] = new char[n];
 
720
            for (int i = 0; i < n; i++) {
 
721
                ch[i] = ' ';
 
722
            }
 
723
            characters(ch, 0, n);
 
724
        }
 
725
    }
 
726
 
 
727
    /**
 
728
     * Determine the prefix for an element or attribute name. TODO: this method
 
729
     * probably needs some cleanup.
 
730
     * 
 
731
     * @param uri
 
732
     *            The Namespace URI.
 
733
     * @param qName
 
734
     *            The qualified name (optional); this will be used to indicate
 
735
     *            the preferred prefix if none is currently bound.
 
736
     * @param isElement
 
737
     *            true if this is an element name, false if it is an attribute
 
738
     *            name (which cannot use the default Namespace).
 
739
     */
 
740
    private String doPrefix(String uri, String qName, boolean isElement) {
 
741
        final String defaultNS = this.nsSupport.getURI("");
 
742
        if ("".equals(uri) || uri == null) {
 
743
            if (isElement && (defaultNS != null)) {
 
744
                this.nsSupport.declarePrefix("", "");
 
745
            }
 
746
            return null;
 
747
        }
 
748
        String prefix;
 
749
        if (isElement && (defaultNS != null) && uri.equals(defaultNS)) {
 
750
            prefix = "";
 
751
        } else {
 
752
            prefix = this.nsSupport.getPrefix(uri);
 
753
        }
 
754
        if (prefix != null) {
 
755
            return prefix;
 
756
        }
 
757
        prefix = this.doneDeclTable.get(uri);
 
758
        if ((prefix != null)
 
759
                && (((!isElement || (defaultNS != null)) && "".equals(prefix)) || (this.nsSupport
 
760
                        .getURI(prefix) != null))) {
 
761
            prefix = null;
 
762
        }
 
763
        if (prefix == null) {
 
764
            prefix = this.prefixTable.get(uri);
 
765
            if ((prefix != null)
 
766
                    && (((!isElement || (defaultNS != null)) && ""
 
767
                            .equals(prefix)) || (this.nsSupport.getURI(prefix) != null))) {
 
768
                prefix = null;
 
769
            }
 
770
        }
 
771
        if ((prefix == null) && (qName != null) && !"".equals(qName)) {
 
772
            final int i = qName.indexOf(':');
 
773
            if (i == -1) {
 
774
                if (isElement && (defaultNS == null)) {
 
775
                    prefix = "";
 
776
                }
 
777
            } else {
 
778
                prefix = qName.substring(0, i);
 
779
            }
 
780
        }
 
781
        for (; (prefix == null) || (this.nsSupport.getURI(prefix) != null); prefix = "__NS"
 
782
                + ++this.prefixCounter) {
 
783
            // Do nothing
 
784
        }
 
785
 
 
786
        this.nsSupport.declarePrefix(prefix, uri);
 
787
        this.doneDeclTable.put(uri, prefix);
 
788
        return prefix;
 
789
    }
 
790
 
 
791
    // //////////////////////////////////////////////////////////////////
 
792
    // Methods from org.xml.sax.ContentHandler.
 
793
    // //////////////////////////////////////////////////////////////////
 
794
 
 
795
    /**
 
796
     * Add an empty element without a Namespace URI, qname or attributes.
 
797
     * 
 
798
     * <p>
 
799
     * This method will supply an empty string for the qname, and empty string
 
800
     * for the Namespace URI, and an empty attribute list. It invokes
 
801
     * {@link #emptyElement(String, String, String, Attributes)} directly.
 
802
     * </p>
 
803
     * 
 
804
     * @param localName
 
805
     *            The element's local name.
 
806
     * @exception org.xml.sax.SAXException
 
807
     *                If there is an error writing the empty tag, or if a
 
808
     *                restlet further down the filter chain raises an exception.
 
809
     * @see #emptyElement(String, String, String, Attributes)
 
810
     */
 
811
    public void emptyElement(String localName) throws SAXException {
 
812
        emptyElement("", localName, "", this.EMPTY_ATTS);
 
813
    }
 
814
 
 
815
    /**
 
816
     * Add an empty element without a qname or attributes.
 
817
     * 
 
818
     * <p>
 
819
     * This method will supply an empty string for the qname and an empty
 
820
     * attribute list. It invokes
 
821
     * {@link #emptyElement(String, String, String, Attributes)} directly.
 
822
     * </p>
 
823
     * 
 
824
     * @param uri
 
825
     *            The element's Namespace URI.
 
826
     * @param localName
 
827
     *            The element's local name.
 
828
     * @exception org.xml.sax.SAXException
 
829
     *                If there is an error writing the empty tag, or if a
 
830
     *                restlet further down the filter chain raises an exception.
 
831
     * @see #emptyElement(String, String, String, Attributes)
 
832
     */
 
833
    public void emptyElement(String uri, String localName) throws SAXException {
 
834
        emptyElement(uri, localName, "", this.EMPTY_ATTS);
 
835
    }
 
836
 
 
837
    /**
 
838
     * Write an empty element. This method writes an empty element tag rather
 
839
     * than a start tag followed by an end tag. Both a {@link #startElement
 
840
     * startElement} and an {@link #endElement endElement} event will be passed
 
841
     * on down the filter chain.
 
842
     * 
 
843
     * @param uri
 
844
     *            The element's Namespace URI, or the empty string if the
 
845
     *            element has no Namespace or if Namespace processing is not
 
846
     *            being performed.
 
847
     * @param localName
 
848
     *            The element's local name (without prefix). This parameter must
 
849
     *            be provided.
 
850
     * @param qName
 
851
     *            The element's qualified name (with prefix), or the empty
 
852
     *            string if none is available. This parameter is strictly
 
853
     *            advisory: the writer may or may not use the prefix attached.
 
854
     * @param atts
 
855
     *            The element's attribute list.
 
856
     * @exception org.xml.sax.SAXException
 
857
     *                If there is an error writing the empty tag, or if a
 
858
     *                restlet further down the filter chain raises an exception.
 
859
     * @see #startElement
 
860
     * @see #endElement
 
861
     */
 
862
    public void emptyElement(String uri, String localName, String qName,
 
863
            Attributes atts) throws SAXException {
 
864
        if (isDataFormat()) {
 
865
            this.state = SEEN_ELEMENT;
 
866
            if (this.depth > 0) {
 
867
                characters(false, "\n");
 
868
            }
 
869
            doIndent();
 
870
        }
 
871
 
 
872
        this.nsSupport.pushContext();
 
873
        write('<');
 
874
        writeName(uri, localName, qName, true);
 
875
        writeAttributes(atts);
 
876
        if (this.elementLevel == 1) {
 
877
            forceNSDecls();
 
878
        }
 
879
        writeNSDecls();
 
880
        write("/>");
 
881
        super.startElement(uri, localName, qName, atts);
 
882
        super.endElement(uri, localName, qName);
 
883
    }
 
884
 
 
885
    /**
 
886
     * Write a newline at the end of the document. Pass the event on down the
 
887
     * filter chain for further processing.
 
888
     * 
 
889
     * @exception org.xml.sax.SAXException
 
890
     *                If there is an error writing the newline, or if a restlet
 
891
     *                further down the filter chain raises an exception.
 
892
     * @see org.xml.sax.ContentHandler#endDocument
 
893
     */
 
894
    @Override
 
895
    public void endDocument() throws SAXException {
 
896
        write('\n');
 
897
        super.endDocument();
 
898
        try {
 
899
            flush();
 
900
        } catch (IOException e) {
 
901
            throw new SAXException(e);
 
902
        }
 
903
    }
 
904
 
 
905
    /**
 
906
     * End an element without a Namespace URI or qname.
 
907
     * 
 
908
     * <p>
 
909
     * This method will supply an empty string for the qName and an empty string
 
910
     * for the Namespace URI. It invokes
 
911
     * {@link #endElement(String, String, String)} directly.
 
912
     * </p>
 
913
     * 
 
914
     * @param localName
 
915
     *            The element's local name.
 
916
     * @exception org.xml.sax.SAXException
 
917
     *                If there is an error writing the end tag, or if a restlet
 
918
     *                further down the filter chain raises an exception.
 
919
     * @see #endElement(String, String, String)
 
920
     */
 
921
    public void endElement(String localName) throws SAXException {
 
922
        endElement("", localName, "");
 
923
    }
 
924
 
 
925
    /**
 
926
     * End an element without a qname.
 
927
     * 
 
928
     * <p>
 
929
     * This method will supply an empty string for the qName. It invokes
 
930
     * {@link #endElement(String, String, String)} directly.
 
931
     * </p>
 
932
     * 
 
933
     * @param uri
 
934
     *            The element's Namespace URI.
 
935
     * @param localName
 
936
     *            The element's local name.
 
937
     * @exception org.xml.sax.SAXException
 
938
     *                If there is an error writing the end tag, or if a restlet
 
939
     *                further down the filter chain raises an exception.
 
940
     * @see #endElement(String, String, String)
 
941
     */
 
942
    public void endElement(String uri, String localName) throws SAXException {
 
943
        endElement(uri, localName, "");
 
944
    }
 
945
 
 
946
    /**
 
947
     * Write an end tag. Pass the event on down the filter chain for further
 
948
     * processing.
 
949
     * 
 
950
     * @param uri
 
951
     *            The Namespace URI, or the empty string if none is available.
 
952
     * @param localName
 
953
     *            The element's local (unprefixed) name (required).
 
954
     * @param qName
 
955
     *            The element's qualified (prefixed) name, or the empty string
 
956
     *            is none is available. This method will use the qName as a
 
957
     *            template for generating a prefix if necessary, but it is not
 
958
     *            guaranteed to use the same qName.
 
959
     * @exception org.xml.sax.SAXException
 
960
     *                If there is an error writing the end tag, or if a restlet
 
961
     *                further down the filter chain raises an exception.
 
962
     * @see org.xml.sax.ContentHandler#endElement
 
963
     */
 
964
    @Override
 
965
    public void endElement(String uri, String localName, String qName)
 
966
            throws SAXException {
 
967
        if (isDataFormat()) {
 
968
            this.depth--;
 
969
            if (this.state == SEEN_ELEMENT) {
 
970
                characters(false, "\n");
 
971
                doIndent();
 
972
            }
 
973
        }
 
974
 
 
975
        write("</");
 
976
        writeName(uri, localName, qName, true);
 
977
        write('>');
 
978
        if (this.elementLevel == 1) {
 
979
            write('\n');
 
980
        }
 
981
        super.endElement(uri, localName, qName);
 
982
        this.nsSupport.popContext();
 
983
        this.elementLevel--;
 
984
 
 
985
        if (isDataFormat()) {
 
986
            this.state = this.stateStack.pop();
 
987
        }
 
988
    }
 
989
 
 
990
    /**
 
991
     * Flush the output.
 
992
     * <p>
 
993
     * This method flushes the output stream. It is especially useful when you
 
994
     * need to make certain that the entire document has been written to output
 
995
     * but do not want to close the output stream.
 
996
     * </p>
 
997
     * <p>
 
998
     * This method is invoked automatically by the {@link #endDocument
 
999
     * endDocument} method after writing a document.
 
1000
     * </p>
 
1001
     * 
 
1002
     * @see #reset
 
1003
     */
 
1004
    public void flush() throws IOException {
 
1005
        this.output.flush();
 
1006
    }
 
1007
 
 
1008
    // //////////////////////////////////////////////////////////////////
 
1009
    // Additional markup.
 
1010
    // //////////////////////////////////////////////////////////////////
 
1011
 
 
1012
    /**
 
1013
     * Force a Namespace to be declared on the root element.
 
1014
     * <p>
 
1015
     * By default, the XMLWriter will declare only the Namespaces needed for an
 
1016
     * element; as a result, a Namespace may be declared many places in a
 
1017
     * document if it is not used on the root element.
 
1018
     * </p>
 
1019
     * <p>
 
1020
     * This method forces a Namespace to be declared on the root element even if
 
1021
     * it is not used there, and reduces the number of xmlns attributes in the
 
1022
     * document.
 
1023
     * </p>
 
1024
     * 
 
1025
     * @param uri
 
1026
     *            The Namespace URI to declare.
 
1027
     * @see #forceNSDecl(java.lang.String,java.lang.String)
 
1028
     * @see #setPrefix
 
1029
     */
 
1030
    public void forceNSDecl(String uri) {
 
1031
        this.forcedDeclTable.put(uri, Boolean.TRUE);
 
1032
    }
 
1033
 
 
1034
    // //////////////////////////////////////////////////////////////////
 
1035
    // Convenience methods.
 
1036
    // //////////////////////////////////////////////////////////////////
 
1037
 
 
1038
    /**
 
1039
     * Force a Namespace declaration with a preferred prefix.
 
1040
     * <p>
 
1041
     * This is a convenience method that invokes {@link #setPrefix setPrefix}
 
1042
     * then {@link #forceNSDecl(java.lang.String) forceNSDecl}.
 
1043
     * </p>
 
1044
     * 
 
1045
     * @param uri
 
1046
     *            The Namespace URI to declare on the root element.
 
1047
     * @param prefix
 
1048
     *            The preferred prefix for the Namespace, or "" for the default
 
1049
     *            Namespace.
 
1050
     * @see #setPrefix
 
1051
     * @see #forceNSDecl(java.lang.String)
 
1052
     */
 
1053
    public void forceNSDecl(String uri, String prefix) {
 
1054
        setPrefix(uri, prefix);
 
1055
        forceNSDecl(uri);
 
1056
    }
 
1057
 
 
1058
    /**
 
1059
     * Force all Namespaces to be declared. This method is used on the root
 
1060
     * element to ensure that the predeclared Namespaces all appear.
 
1061
     */
 
1062
    private void forceNSDecls() {
 
1063
        for (final String prefix : this.forcedDeclTable.keySet()) {
 
1064
            doPrefix(prefix, null, true);
 
1065
        }
 
1066
    }
 
1067
 
 
1068
    /**
 
1069
     * Return the current indent step.
 
1070
     * <p>
 
1071
     * Return the current indent step: each start tag will be indented by this
 
1072
     * number of spaces times the number of ancestors that the element has.
 
1073
     * </p>
 
1074
     * 
 
1075
     * @return The number of spaces in each indentation step, or 0 or less for
 
1076
     *         no indentation.
 
1077
     */
 
1078
    public int getIndentStep() {
 
1079
        return this.indentStep;
 
1080
    }
 
1081
 
 
1082
    /**
 
1083
     * Get the current or preferred prefix for a Namespace URI.
 
1084
     * 
 
1085
     * @param uri
 
1086
     *            The Namespace URI.
 
1087
     * @return The preferred prefix, or "" for the default Namespace.
 
1088
     * @see #setPrefix
 
1089
     */
 
1090
    public String getPrefix(String uri) {
 
1091
        return this.prefixTable.get(uri);
 
1092
    }
 
1093
 
 
1094
    /**
 
1095
     * Returns the underlying writer.
 
1096
     * 
 
1097
     * @return The underlying writer.
 
1098
     */
 
1099
    public Writer getWriter() {
 
1100
        return this.output;
 
1101
    }
 
1102
 
 
1103
    /**
 
1104
     * Write ignorable whitespace. Pass the event on down the filter chain for
 
1105
     * further processing.
 
1106
     * 
 
1107
     * @param ch
 
1108
     *            The array of characters to write.
 
1109
     * @param start
 
1110
     *            The starting position in the array.
 
1111
     * @param length
 
1112
     *            The number of characters to write.
 
1113
     * @exception org.xml.sax.SAXException
 
1114
     *                If there is an error writing the whitespace, or if a
 
1115
     *                restlet further down the filter chain raises an exception.
 
1116
     * @see org.xml.sax.ContentHandler#ignorableWhitespace
 
1117
     */
 
1118
    @Override
 
1119
    public void ignorableWhitespace(char ch[], int start, int length)
 
1120
            throws SAXException {
 
1121
        writeEsc(ch, start, length, false);
 
1122
        super.ignorableWhitespace(ch, start, length);
 
1123
    }
 
1124
 
 
1125
    /**
 
1126
     * Internal initialization method.
 
1127
     * 
 
1128
     * <p>
 
1129
     * All of the public constructors invoke this method.
 
1130
     * 
 
1131
     * @param writer
 
1132
     *            The output destination, or null to use standard output.
 
1133
     */
 
1134
    private void init(Writer writer) {
 
1135
        setOutput(writer);
 
1136
        this.nsSupport = new NamespaceSupport();
 
1137
        this.prefixTable = new TreeMap<String, String>();
 
1138
        this.forcedDeclTable = new TreeMap<String, Boolean>();
 
1139
        this.doneDeclTable = new TreeMap<String, String>();
 
1140
    }
 
1141
 
 
1142
    public boolean isDataFormat() {
 
1143
        return this.dataFormat;
 
1144
    }
 
1145
 
 
1146
    /**
 
1147
     * Write a processing instruction. Pass the event on down the filter chain
 
1148
     * for further processing.
 
1149
     * 
 
1150
     * @param target
 
1151
     *            The PI target.
 
1152
     * @param data
 
1153
     *            The PI data.
 
1154
     * @exception org.xml.sax.SAXException
 
1155
     *                If there is an error writing the PI, or if a restlet
 
1156
     *                further down the filter chain raises an exception.
 
1157
     * @see org.xml.sax.ContentHandler#processingInstruction
 
1158
     */
 
1159
    @Override
 
1160
    public void processingInstruction(String target, String data)
 
1161
            throws SAXException {
 
1162
        write("<?");
 
1163
        write(target);
 
1164
        write(' ');
 
1165
        write(data);
 
1166
        write("?>");
 
1167
        if (this.elementLevel < 1) {
 
1168
            write('\n');
 
1169
        }
 
1170
        super.processingInstruction(target, data);
 
1171
    }
 
1172
 
 
1173
    /**
 
1174
     * Reset the writer.
 
1175
     * 
 
1176
     * <p>
 
1177
     * This method is especially useful if the writer throws an exception before
 
1178
     * it is finished, and you want to reuse the writer for a new document. It
 
1179
     * is usually a good idea to invoke {@link #flush flush} before resetting
 
1180
     * the writer, to make sure that no output is lost.
 
1181
     * </p>
 
1182
     * 
 
1183
     * <p>
 
1184
     * This method is invoked automatically by the {@link #startDocument
 
1185
     * startDocument} method before writing a new document.
 
1186
     * </p>
 
1187
     * 
 
1188
     * <p>
 
1189
     * <strong>Note:</strong> this method will <em>not</em> clear the prefix or
 
1190
     * URI information in the writer or the selected output writer.
 
1191
     * </p>
 
1192
     * 
 
1193
     * @see #flush
 
1194
     */
 
1195
    public void reset() {
 
1196
        if (isDataFormat()) {
 
1197
            this.depth = 0;
 
1198
            this.state = SEEN_NOTHING;
 
1199
            this.stateStack = new Stack<Object>();
 
1200
        }
 
1201
 
 
1202
        this.elementLevel = 0;
 
1203
        this.prefixCounter = 0;
 
1204
        this.nsSupport.reset();
 
1205
    }
 
1206
 
 
1207
    public void setDataFormat(boolean dataFormat) {
 
1208
        this.dataFormat = dataFormat;
 
1209
    }
 
1210
 
 
1211
    // //////////////////////////////////////////////////////////////////
 
1212
    // Internal methods.
 
1213
    // //////////////////////////////////////////////////////////////////
 
1214
 
 
1215
    /**
 
1216
     * Set the current indent step.
 
1217
     * 
 
1218
     * @param indentStep
 
1219
     *            The new indent step (0 or less for no indentation).
 
1220
     */
 
1221
    public void setIndentStep(int indentStep) {
 
1222
        this.indentStep = indentStep;
 
1223
    }
 
1224
 
 
1225
    /**
 
1226
     * Set a new output destination for the document.
 
1227
     * 
 
1228
     * @param writer
 
1229
     *            The output destination, or null to use standard output.
 
1230
     * @see #flush
 
1231
     */
 
1232
    public void setOutput(Writer writer) {
 
1233
        if (writer == null) {
 
1234
            this.output = new OutputStreamWriter(System.out);
 
1235
        } else {
 
1236
            this.output = writer;
 
1237
        }
 
1238
    }
 
1239
 
 
1240
    /**
 
1241
     * Specify a preferred prefix for a Namespace URI.
 
1242
     * <p>
 
1243
     * Note that this method does not actually force the Namespace to be
 
1244
     * declared; to do that, use the {@link #forceNSDecl(java.lang.String)
 
1245
     * forceNSDecl} method as well.
 
1246
     * </p>
 
1247
     * 
 
1248
     * @param uri
 
1249
     *            The Namespace URI.
 
1250
     * @param prefix
 
1251
     *            The preferred prefix, or "" to select the default Namespace.
 
1252
     * @see #getPrefix
 
1253
     * @see #forceNSDecl(java.lang.String)
 
1254
     * @see #forceNSDecl(java.lang.String,java.lang.String)
 
1255
     */
 
1256
    public void setPrefix(String uri, String prefix) {
 
1257
        this.prefixTable.put(uri, prefix);
 
1258
    }
 
1259
 
 
1260
    /**
 
1261
     * Write the XML declaration at the beginning of the document. Pass the
 
1262
     * event on down the filter chain for further processing.
 
1263
     * 
 
1264
     * @exception org.xml.sax.SAXException
 
1265
     *                If there is an error writing the XML declaration, or if a
 
1266
     *                restlet further down the filter chain raises an exception.
 
1267
     * @see org.xml.sax.ContentHandler#startDocument
 
1268
     */
 
1269
    @Override
 
1270
    public void startDocument() throws SAXException {
 
1271
        reset();
 
1272
        write("<?xml version=\"1.0\" standalone='yes'?>\n");
 
1273
        super.startDocument();
 
1274
    }
 
1275
 
 
1276
    /**
 
1277
     * Start a new element without a qname, attributes or a Namespace URI.
 
1278
     * 
 
1279
     * <p>
 
1280
     * This method will provide an empty string for the Namespace URI, and empty
 
1281
     * string for the qualified name, and a default empty attribute list. It
 
1282
     * invokes #startElement(String, String, String, Attributes)} directly.
 
1283
     * </p>
 
1284
     * 
 
1285
     * @param localName
 
1286
     *            The element's local name.
 
1287
     * @exception org.xml.sax.SAXException
 
1288
     *                If there is an error writing the start tag, or if a
 
1289
     *                restlet further down the filter chain raises an exception.
 
1290
     * @see #startElement(String, String, String, Attributes)
 
1291
     */
 
1292
    public void startElement(String localName) throws SAXException {
 
1293
        startElement("", localName, "", this.EMPTY_ATTS);
 
1294
    }
 
1295
 
 
1296
    /**
 
1297
     * Start a new element without a qname or attributes.
 
1298
     * 
 
1299
     * <p>
 
1300
     * This method will provide a default empty attribute list and an empty
 
1301
     * string for the qualified name. It invokes
 
1302
     * {@link #startElement(String, String, String, Attributes)} directly.
 
1303
     * </p>
 
1304
     * 
 
1305
     * @param uri
 
1306
     *            The element's Namespace URI.
 
1307
     * @param localName
 
1308
     *            The element's local name.
 
1309
     * @exception org.xml.sax.SAXException
 
1310
     *                If there is an error writing the start tag, or if a
 
1311
     *                restlet further down the filter chain raises an exception.
 
1312
     * @see #startElement(String, String, String, Attributes)
 
1313
     */
 
1314
    public void startElement(String uri, String localName) throws SAXException {
 
1315
        startElement(uri, localName, "", this.EMPTY_ATTS);
 
1316
    }
 
1317
 
 
1318
    /**
 
1319
     * Write a start tag. Pass the event on down the filter chain for further
 
1320
     * processing.
 
1321
     * 
 
1322
     * @param uri
 
1323
     *            The Namespace URI, or the empty string if none is available.
 
1324
     * @param localName
 
1325
     *            The element's local (unprefixed) name (required).
 
1326
     * @param qName
 
1327
     *            The element's qualified (prefixed) name, or the empty string
 
1328
     *            is none is available. This method will use the qName as a
 
1329
     *            template for generating a prefix if necessary, but it is not
 
1330
     *            guaranteed to use the same qName.
 
1331
     * @param atts
 
1332
     *            The element's attribute list (must not be null).
 
1333
     * @exception org.xml.sax.SAXException
 
1334
     *                If there is an error writing the start tag, or if a
 
1335
     *                restlet further down the filter chain raises an exception.
 
1336
     * @see org.xml.sax.ContentHandler#startElement
 
1337
     */
 
1338
    @Override
 
1339
    public void startElement(String uri, String localName, String qName,
 
1340
            Attributes atts) throws SAXException {
 
1341
        if (isDataFormat()) {
 
1342
            this.stateStack.push(SEEN_ELEMENT);
 
1343
            this.state = SEEN_NOTHING;
 
1344
            if (this.depth > 0) {
 
1345
                characters("\n");
 
1346
            }
 
1347
            doIndent();
 
1348
        }
 
1349
 
 
1350
        this.elementLevel++;
 
1351
        this.nsSupport.pushContext();
 
1352
        write('<');
 
1353
        writeName(uri, localName, qName, true);
 
1354
        writeAttributes(atts);
 
1355
        if (this.elementLevel == 1) {
 
1356
            forceNSDecls();
 
1357
        }
 
1358
        writeNSDecls();
 
1359
        write('>');
 
1360
        super.startElement(uri, localName, qName, atts);
 
1361
 
 
1362
        if (isDataFormat()) {
 
1363
            this.depth++;
 
1364
        }
 
1365
    }
 
1366
 
 
1367
    /**
 
1368
     * Write a raw character.
 
1369
     * 
 
1370
     * @param c
 
1371
     *            The character to write.
 
1372
     * @exception org.xml.sax.SAXException
 
1373
     *                If there is an error writing the character, this method
 
1374
     *                will throw an IOException wrapped in a SAXException.
 
1375
     */
 
1376
    private void write(char c) throws SAXException {
 
1377
        try {
 
1378
            this.output.write(c);
 
1379
        } catch (IOException e) {
 
1380
            throw new SAXException(e);
 
1381
        }
 
1382
    }
 
1383
 
 
1384
    /**
 
1385
     * Write a raw string.
 
1386
     * 
 
1387
     * @param s
 
1388
     * @exception org.xml.sax.SAXException
 
1389
     *                If there is an error writing the string, this method will
 
1390
     *                throw an IOException wrapped in a SAXException
 
1391
     */
 
1392
    private void write(String s) throws SAXException {
 
1393
        try {
 
1394
            this.output.write(s);
 
1395
        } catch (IOException e) {
 
1396
            throw new SAXException(e);
 
1397
        }
 
1398
    }
 
1399
 
 
1400
    /**
 
1401
     * Write out an attribute list, escaping values. The names will have
 
1402
     * prefixes added to them.
 
1403
     * 
 
1404
     * @param atts
 
1405
     *            The attribute list to write.
 
1406
     * @exception org.xml.SAXException
 
1407
     *                If there is an error writing the attribute list, this
 
1408
     *                method will throw an IOException wrapped in a
 
1409
     *                SAXException.
 
1410
     */
 
1411
    private void writeAttributes(Attributes atts) throws SAXException {
 
1412
        final int len = atts.getLength();
 
1413
        for (int i = 0; i < len; i++) {
 
1414
            if ("xmlns".equals(atts.getQName(i))) {
 
1415
                // Redefines the default namespace.
 
1416
                forceNSDecl(atts.getValue(i));
 
1417
            } else if (atts.getQName(i) != null
 
1418
                    && atts.getQName(i).startsWith("xmlns")) {
 
1419
                // Defines the namespace using its prefix.
 
1420
                forceNSDecl(atts.getValue(i), atts.getLocalName(i));
 
1421
            } else {
 
1422
                final char ch[] = atts.getValue(i).toCharArray();
 
1423
                write(' ');
 
1424
                writeName(atts.getURI(i), atts.getLocalName(i),
 
1425
                        atts.getQName(i), false);
 
1426
                write("=\"");
 
1427
                writeEsc(ch, 0, ch.length, true);
 
1428
                write('"');
 
1429
            }
 
1430
        }
 
1431
    }
 
1432
 
 
1433
    /**
 
1434
     * Write an array of data characters with escaping.
 
1435
     * 
 
1436
     * @param ch
 
1437
     *            The array of characters.
 
1438
     * @param start
 
1439
     *            The starting position.
 
1440
     * @param length
 
1441
     *            The number of characters to use.
 
1442
     * @param isAttVal
 
1443
     *            true if this is an attribute value literal.
 
1444
     * @exception org.xml.SAXException
 
1445
     *                If there is an error writing the characters, this method
 
1446
     *                will throw an IOException wrapped in a SAXException.
 
1447
     */
 
1448
    private void writeEsc(char ch[], int start, int length, boolean isAttVal)
 
1449
            throws SAXException {
 
1450
        for (int i = start; i < start + length; i++) {
 
1451
            switch (ch[i]) {
 
1452
            case '&':
 
1453
                write("&amp;");
 
1454
                break;
 
1455
            case '<':
 
1456
                write("&lt;");
 
1457
                break;
 
1458
            case '>':
 
1459
                write("&gt;");
 
1460
                break;
 
1461
            case '\"':
 
1462
                if (isAttVal) {
 
1463
                    write("&quot;");
 
1464
                } else {
 
1465
                    write('\"');
 
1466
                }
 
1467
                break;
 
1468
            default:
 
1469
                if (ch[i] > '\u007f') {
 
1470
                    write("&#");
 
1471
                    write(Integer.toString(ch[i]));
 
1472
                    write(';');
 
1473
                } else {
 
1474
                    write(ch[i]);
 
1475
                }
 
1476
            }
 
1477
        }
 
1478
    }
 
1479
 
 
1480
    /**
 
1481
     * Write an element or attribute name.
 
1482
     * 
 
1483
     * @param uri
 
1484
     *            The Namespace URI.
 
1485
     * @param localName
 
1486
     *            The local name.
 
1487
     * @param qName
 
1488
     *            The prefixed name, if available, or the empty string.
 
1489
     * @param isElement
 
1490
     *            true if this is an element name, false if it is an attribute
 
1491
     *            name.
 
1492
     * @exception org.xml.sax.SAXException
 
1493
     *                This method will throw an IOException wrapped in a
 
1494
     *                SAXException if there is an error writing the name.
 
1495
     */
 
1496
    private void writeName(String uri, String localName, String qName,
 
1497
            boolean isElement) throws SAXException {
 
1498
 
 
1499
        final String prefix = doPrefix(uri, qName, isElement);
 
1500
        if ((prefix != null) && !"".equals(prefix)) {
 
1501
            write(prefix);
 
1502
            write(':');
 
1503
        }
 
1504
        write(localName);
 
1505
    }
 
1506
 
 
1507
    /**
 
1508
     * Write out the list of Namespace declarations.
 
1509
     * 
 
1510
     * @exception org.xml.sax.SAXException
 
1511
     *                This method will throw an IOException wrapped in a
 
1512
     *                SAXException if there is an error writing the Namespace
 
1513
     *                declarations.
 
1514
     */
 
1515
    @SuppressWarnings("unchecked")
 
1516
    private void writeNSDecls() throws SAXException {
 
1517
        final Enumeration<String> prefixes = this.nsSupport
 
1518
                .getDeclaredPrefixes();
 
1519
        while (prefixes.hasMoreElements()) {
 
1520
            final String prefix = prefixes.nextElement();
 
1521
            String uri = this.nsSupport.getURI(prefix);
 
1522
            if (uri == null) {
 
1523
                uri = "";
 
1524
            }
 
1525
            final char ch[] = uri.toCharArray();
 
1526
            write(' ');
 
1527
            if ("".equals(prefix)) {
 
1528
                write("xmlns=\"");
 
1529
            } else {
 
1530
                write("xmlns:");
 
1531
                write(prefix);
 
1532
                write("=\"");
 
1533
            }
 
1534
            writeEsc(ch, 0, ch.length, true);
 
1535
            write('\"');
 
1536
        }
 
1537
    }
 
1538
 
 
1539
}