2
* Copyright 2005-2012 Restlet S.A.S.
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.
9
* You can obtain a copy of the Apache 2.0 license at
10
* http://www.opensource.org/licenses/apache-2.0
12
* You can obtain a copy of the LGPL 3.0 license at
13
* http://www.opensource.org/licenses/lgpl-3.0
15
* You can obtain a copy of the LGPL 2.1 license at
16
* http://www.opensource.org/licenses/lgpl-2.1
18
* You can obtain a copy of the CDDL 1.0 license at
19
* http://www.opensource.org/licenses/cddl1
21
* You can obtain a copy of the EPL 1.0 license at
22
* http://www.opensource.org/licenses/eclipse-1.0
24
* See the Licenses for the specific language governing permissions and
25
* limitations under the Licenses.
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
31
* Restlet is a registered trademark of Restlet S.A.S.
34
package org.restlet.ext.xml;
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;
45
import java.util.Stack;
46
import java.util.TreeMap;
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;
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.
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
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
80
* w.startElement("foo");
84
* is equivalent to the regular SAX2 ContentHandler method
88
* w.startElement("", "foo", "", new AttributesImpl());
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
98
* XmlWriter w = new XmlWriter();
101
* w.startElement("greeting");
102
* w.characters("Hello, world!");
103
* w.endElement("greeting");
108
* The resulting document will look like this:
112
* <?xml version="1.0" standalone='yes'?>
114
* <greeting>Hello, world!</greeting>
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
124
* XmlWriter w = new XmlWriter();
127
* w.dataElement("greeting", "Hello, world!");
131
* <h2>Whitespace</h2>
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
140
* w.dataElement("item", "1");
141
* w.dataElement("item", "2");
142
* w.dataElement("item", "3");
146
* you will end up with
150
* <item>1</item><item>3</item><item>3</item>
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.
161
* <h2>Namespace Support</h2>
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:
173
* w.emptyElement("http://www.foo.com/ns/", "foo");
178
* The resulting document will look like this:
182
* <?xml version="1.0" standalone='yes'?>
184
* <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
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:
194
* <li>the qualified name</li>
195
* <li>the {@link #setPrefix setPrefix} method.</li>
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
206
* Before writing a document, the client can also pre-map a prefix to a
207
* Namespace URI with the setPrefix method:
211
* w.setPrefix("http://www.foo.com/ns/", "foo");
213
* w.emptyElement("http://www.foo.com/ns/", "foo");
218
* The resulting document will look like this:
222
* <?xml version="1.0" standalone='yes'?>
224
* <foo:foo xmlns:foo="http://www.foo.com/ns/"/>
228
* The default Namespace simply uses an empty string as the prefix:
232
* w.setPrefix("http://www.foo.com/ns/", "");
234
* w.emptyElement("http://www.foo.com/ns/", "foo");
239
* The resulting document will look like this:
243
* <?xml version="1.0" standalone='yes'?>
245
* <foo xmlns="http://www.foo.com/ns/"/>
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:
255
* <xml version="1.0" standalone='yes'?>
257
* <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
258
* <rdf:Description about="http://www.foo.com/ids/books/12345">
259
* <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title>
260
* <dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith</dc:title>
261
* <dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09</dc:title>
262
* </rdf:Description>
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:
275
* w.forceNSDecl("http://www.purl.org/dc/");
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:
284
* <xml version="1.0" standalone='yes'?>
286
* <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
287
* xmlns:dc="http://www.purl.org/dc/">
288
* <rdf:Description about="http://www.foo.com/ids/books/12345">
289
* <dc:title>A Dark Night</dc:title>
290
* <dc:creator>Jane Smith</dc:title>
291
* <dc:date>2000-09-09</dc:title>
292
* </rdf:Description>
297
* This approach is also useful for declaring Namespace prefixes that be used by
298
* qualified names appearing in attribute values or character data.
301
* <h2>Data Format</h2>
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).
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.
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.
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
333
* XmlWriter w = new XmlWriter();
334
* w.setDataFormat(true);
335
* w.setIndentStep(2);
337
* w.startElement("Person");
338
* w.dataElement("name", "Jane Smith");
339
* w.dataElement("date-of-birth", "1965-05-23");
340
* w.dataElement("citizenship", "US");
341
* w.endElement("Person");
346
* This code will produce the following document:
350
* <?xml version="1.0" standalone='yes'?>
353
* <name>Jane Smith</name>
354
* <date-of-birth>1965-05-23</date-of-birth>
355
* <citizenship>US</citizenship>
359
* @see org.xml.sax.XMLFilter
360
* @see org.xml.sax.ContentHandler
361
* @author David Megginson, Jerome Louvel (contact@restlet.com)
363
public final class XmlWriter extends XMLFilterImpl {
364
private static final Object SEEN_NOTHING = new Object();
366
private static final Object SEEN_ELEMENT = new Object();
368
private static final Object SEEN_DATA = new Object();
371
* Constant representing empty attributes.
373
private final Attributes EMPTY_ATTS = new AttributesImpl();
376
* The prefixes table.
378
private volatile Map<String, String> prefixTable;
381
* The forced declarations table.
383
private volatile Map<String, Boolean> forcedDeclTable;
386
* The document declarations table.
388
private volatile Map<String, String> doneDeclTable;
393
private volatile int elementLevel = 0;
396
* The namespace support.
398
private volatile NamespaceSupport nsSupport;
401
* The prefix counter.
403
private volatile int prefixCounter = 0;
406
* The underlying writer.
408
private volatile Writer output;
410
private volatile Object state = SEEN_NOTHING;
412
private volatile Stack<Object> stateStack = new Stack<Object>();
414
private volatile boolean dataFormat = false;
416
private volatile int indentStep = 0;
418
private volatile int depth = 0;
421
* Create a new XML writer.
423
* Write to standard output.
434
* The underlying output stream.
436
public XmlWriter(OutputStream out) {
437
this(new OutputStreamWriter(out));
444
* The underlying output stream.
446
public XmlWriter(OutputStream out, Charset cs) {
447
this(new OutputStreamWriter(out, cs));
454
* The underlying output stream.
456
public XmlWriter(OutputStream out, CharsetEncoder enc) {
457
this(new OutputStreamWriter(out, enc));
464
* The underlying output stream.
466
public XmlWriter(OutputStream out, String charsetName)
467
throws UnsupportedEncodingException {
468
this(new OutputStreamWriter(out, charsetName));
472
* Create a new XML writer.
474
* Write to the writer provided.
478
* The output destination, or null to use standard output.
480
public XmlWriter(Writer writer) {
485
* Create a new XML writer.
487
* Use the specified XML reader as the parent.
491
* The parent in the filter chain, or null for no parent.
493
public XmlWriter(XMLReader xmlreader) {
499
* Create a new XML writer.
501
* Use the specified XML reader as the parent, and write to the specified
506
* The parent in the filter chain, or null for no parent.
508
* The output destination, or null to use standard output.
510
public XmlWriter(XMLReader xmlreader, Writer writer) {
516
* Write character data. Pass the event on down the filter chain for further
520
* The array of characters to write.
522
* The starting position in the array.
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
530
private void characters(boolean dataFormat, char ch[], int start, int len)
531
throws SAXException {
533
this.state = SEEN_DATA;
536
writeEsc(ch, start, len, false);
537
super.characters(ch, start, len);
540
// //////////////////////////////////////////////////////////////////
542
// //////////////////////////////////////////////////////////////////
545
* Write a string of character data, with XML escaping.
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)}.
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)
558
private void characters(boolean dataFormat, String data)
559
throws SAXException {
560
final char ch[] = data.toCharArray();
561
characters(dataFormat, ch, 0, ch.length);
565
* Write character data. Pass the event on down the filter chain for further
569
* The array of characters to write.
571
* The starting position in the array.
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
580
public void characters(char ch[], int start, int len) throws SAXException {
581
characters(isDataFormat(), ch, start, len);
585
* Write a string of character data, with XML escaping.
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)}.
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)
598
public void characters(String data) throws SAXException {
599
characters(false, data);
603
* Write an element with character data content but no attributes or
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.
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)}.
621
* The element's local name.
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)
631
public void dataElement(String localName, String content)
632
throws SAXException {
633
dataElement("", localName, "", this.EMPTY_ATTS, content);
637
* Write an element with character data content but no attributes.
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.
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)}.
653
* The element's Namespace URI.
655
* The element's local name.
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)
665
public void dataElement(String uri, String localName, String content)
666
throws SAXException {
667
dataElement(uri, localName, "", this.EMPTY_ATTS, content);
671
* Write an element with character data content.
674
* This is a convenience method to write a complete element with character
675
* data content, including the start tag and end tag.
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)}.
686
* The element's Namespace URI.
688
* The element's local name.
690
* The element's default qualified name.
692
* The element's attributes.
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)
702
public void dataElement(String uri, String localName, String qName,
703
Attributes atts, String content) throws SAXException {
704
startElement(uri, localName, qName, atts);
706
endElement(uri, localName, qName);
710
* Print indentation for the current level.
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.
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++) {
723
characters(ch, 0, n);
728
* Determine the prefix for an element or attribute name. TODO: this method
729
* probably needs some cleanup.
734
* The qualified name (optional); this will be used to indicate
735
* the preferred prefix if none is currently bound.
737
* true if this is an element name, false if it is an attribute
738
* name (which cannot use the default Namespace).
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("", "");
749
if (isElement && (defaultNS != null) && uri.equals(defaultNS)) {
752
prefix = this.nsSupport.getPrefix(uri);
754
if (prefix != null) {
757
prefix = this.doneDeclTable.get(uri);
759
&& (((!isElement || (defaultNS != null)) && "".equals(prefix)) || (this.nsSupport
760
.getURI(prefix) != null))) {
763
if (prefix == null) {
764
prefix = this.prefixTable.get(uri);
766
&& (((!isElement || (defaultNS != null)) && ""
767
.equals(prefix)) || (this.nsSupport.getURI(prefix) != null))) {
771
if ((prefix == null) && (qName != null) && !"".equals(qName)) {
772
final int i = qName.indexOf(':');
774
if (isElement && (defaultNS == null)) {
778
prefix = qName.substring(0, i);
781
for (; (prefix == null) || (this.nsSupport.getURI(prefix) != null); prefix = "__NS"
782
+ ++this.prefixCounter) {
786
this.nsSupport.declarePrefix(prefix, uri);
787
this.doneDeclTable.put(uri, prefix);
791
// //////////////////////////////////////////////////////////////////
792
// Methods from org.xml.sax.ContentHandler.
793
// //////////////////////////////////////////////////////////////////
796
* Add an empty element without a Namespace URI, qname or attributes.
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.
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)
811
public void emptyElement(String localName) throws SAXException {
812
emptyElement("", localName, "", this.EMPTY_ATTS);
816
* Add an empty element without a qname or attributes.
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.
825
* The element's Namespace URI.
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)
833
public void emptyElement(String uri, String localName) throws SAXException {
834
emptyElement(uri, localName, "", this.EMPTY_ATTS);
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.
844
* The element's Namespace URI, or the empty string if the
845
* element has no Namespace or if Namespace processing is not
848
* The element's local name (without prefix). This parameter must
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.
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.
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");
872
this.nsSupport.pushContext();
874
writeName(uri, localName, qName, true);
875
writeAttributes(atts);
876
if (this.elementLevel == 1) {
881
super.startElement(uri, localName, qName, atts);
882
super.endElement(uri, localName, qName);
886
* Write a newline at the end of the document. Pass the event on down the
887
* filter chain for further processing.
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
895
public void endDocument() throws SAXException {
900
} catch (IOException e) {
901
throw new SAXException(e);
906
* End an element without a Namespace URI or qname.
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.
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)
921
public void endElement(String localName) throws SAXException {
922
endElement("", localName, "");
926
* End an element without a qname.
929
* This method will supply an empty string for the qName. It invokes
930
* {@link #endElement(String, String, String)} directly.
934
* The element's Namespace URI.
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)
942
public void endElement(String uri, String localName) throws SAXException {
943
endElement(uri, localName, "");
947
* Write an end tag. Pass the event on down the filter chain for further
951
* The Namespace URI, or the empty string if none is available.
953
* The element's local (unprefixed) name (required).
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
965
public void endElement(String uri, String localName, String qName)
966
throws SAXException {
967
if (isDataFormat()) {
969
if (this.state == SEEN_ELEMENT) {
970
characters(false, "\n");
976
writeName(uri, localName, qName, true);
978
if (this.elementLevel == 1) {
981
super.endElement(uri, localName, qName);
982
this.nsSupport.popContext();
985
if (isDataFormat()) {
986
this.state = this.stateStack.pop();
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.
998
* This method is invoked automatically by the {@link #endDocument
999
* endDocument} method after writing a document.
1004
public void flush() throws IOException {
1005
this.output.flush();
1008
// //////////////////////////////////////////////////////////////////
1009
// Additional markup.
1010
// //////////////////////////////////////////////////////////////////
1013
* Force a Namespace to be declared on the root element.
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.
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
1026
* The Namespace URI to declare.
1027
* @see #forceNSDecl(java.lang.String,java.lang.String)
1030
public void forceNSDecl(String uri) {
1031
this.forcedDeclTable.put(uri, Boolean.TRUE);
1034
// //////////////////////////////////////////////////////////////////
1035
// Convenience methods.
1036
// //////////////////////////////////////////////////////////////////
1039
* Force a Namespace declaration with a preferred prefix.
1041
* This is a convenience method that invokes {@link #setPrefix setPrefix}
1042
* then {@link #forceNSDecl(java.lang.String) forceNSDecl}.
1046
* The Namespace URI to declare on the root element.
1048
* The preferred prefix for the Namespace, or "" for the default
1051
* @see #forceNSDecl(java.lang.String)
1053
public void forceNSDecl(String uri, String prefix) {
1054
setPrefix(uri, prefix);
1059
* Force all Namespaces to be declared. This method is used on the root
1060
* element to ensure that the predeclared Namespaces all appear.
1062
private void forceNSDecls() {
1063
for (final String prefix : this.forcedDeclTable.keySet()) {
1064
doPrefix(prefix, null, true);
1069
* Return the current indent step.
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.
1075
* @return The number of spaces in each indentation step, or 0 or less for
1078
public int getIndentStep() {
1079
return this.indentStep;
1083
* Get the current or preferred prefix for a Namespace URI.
1086
* The Namespace URI.
1087
* @return The preferred prefix, or "" for the default Namespace.
1090
public String getPrefix(String uri) {
1091
return this.prefixTable.get(uri);
1095
* Returns the underlying writer.
1097
* @return The underlying writer.
1099
public Writer getWriter() {
1104
* Write ignorable whitespace. Pass the event on down the filter chain for
1105
* further processing.
1108
* The array of characters to write.
1110
* The starting position in the array.
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
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);
1126
* Internal initialization method.
1129
* All of the public constructors invoke this method.
1132
* The output destination, or null to use standard output.
1134
private void init(Writer 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>();
1142
public boolean isDataFormat() {
1143
return this.dataFormat;
1147
* Write a processing instruction. Pass the event on down the filter chain
1148
* for further processing.
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
1160
public void processingInstruction(String target, String data)
1161
throws SAXException {
1167
if (this.elementLevel < 1) {
1170
super.processingInstruction(target, data);
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.
1184
* This method is invoked automatically by the {@link #startDocument
1185
* startDocument} method before writing a new document.
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.
1195
public void reset() {
1196
if (isDataFormat()) {
1198
this.state = SEEN_NOTHING;
1199
this.stateStack = new Stack<Object>();
1202
this.elementLevel = 0;
1203
this.prefixCounter = 0;
1204
this.nsSupport.reset();
1207
public void setDataFormat(boolean dataFormat) {
1208
this.dataFormat = dataFormat;
1211
// //////////////////////////////////////////////////////////////////
1212
// Internal methods.
1213
// //////////////////////////////////////////////////////////////////
1216
* Set the current indent step.
1219
* The new indent step (0 or less for no indentation).
1221
public void setIndentStep(int indentStep) {
1222
this.indentStep = indentStep;
1226
* Set a new output destination for the document.
1229
* The output destination, or null to use standard output.
1232
public void setOutput(Writer writer) {
1233
if (writer == null) {
1234
this.output = new OutputStreamWriter(System.out);
1236
this.output = writer;
1241
* Specify a preferred prefix for a Namespace URI.
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.
1249
* The Namespace URI.
1251
* The preferred prefix, or "" to select the default Namespace.
1253
* @see #forceNSDecl(java.lang.String)
1254
* @see #forceNSDecl(java.lang.String,java.lang.String)
1256
public void setPrefix(String uri, String prefix) {
1257
this.prefixTable.put(uri, prefix);
1261
* Write the XML declaration at the beginning of the document. Pass the
1262
* event on down the filter chain for further processing.
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
1270
public void startDocument() throws SAXException {
1272
write("<?xml version=\"1.0\" standalone='yes'?>\n");
1273
super.startDocument();
1277
* Start a new element without a qname, attributes or a Namespace URI.
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.
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)
1292
public void startElement(String localName) throws SAXException {
1293
startElement("", localName, "", this.EMPTY_ATTS);
1297
* Start a new element without a qname or attributes.
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.
1306
* The element's Namespace URI.
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)
1314
public void startElement(String uri, String localName) throws SAXException {
1315
startElement(uri, localName, "", this.EMPTY_ATTS);
1319
* Write a start tag. Pass the event on down the filter chain for further
1323
* The Namespace URI, or the empty string if none is available.
1325
* The element's local (unprefixed) name (required).
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.
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
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) {
1350
this.elementLevel++;
1351
this.nsSupport.pushContext();
1353
writeName(uri, localName, qName, true);
1354
writeAttributes(atts);
1355
if (this.elementLevel == 1) {
1360
super.startElement(uri, localName, qName, atts);
1362
if (isDataFormat()) {
1368
* Write a raw character.
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.
1376
private void write(char c) throws SAXException {
1378
this.output.write(c);
1379
} catch (IOException e) {
1380
throw new SAXException(e);
1385
* Write a raw string.
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
1392
private void write(String s) throws SAXException {
1394
this.output.write(s);
1395
} catch (IOException e) {
1396
throw new SAXException(e);
1401
* Write out an attribute list, escaping values. The names will have
1402
* prefixes added to them.
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
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));
1422
final char ch[] = atts.getValue(i).toCharArray();
1424
writeName(atts.getURI(i), atts.getLocalName(i),
1425
atts.getQName(i), false);
1427
writeEsc(ch, 0, ch.length, true);
1434
* Write an array of data characters with escaping.
1437
* The array of characters.
1439
* The starting position.
1441
* The number of characters to use.
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.
1448
private void writeEsc(char ch[], int start, int length, boolean isAttVal)
1449
throws SAXException {
1450
for (int i = start; i < start + length; i++) {
1469
if (ch[i] > '\u007f') {
1471
write(Integer.toString(ch[i]));
1481
* Write an element or attribute name.
1484
* The Namespace URI.
1488
* The prefixed name, if available, or the empty string.
1490
* true if this is an element name, false if it is an attribute
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.
1496
private void writeName(String uri, String localName, String qName,
1497
boolean isElement) throws SAXException {
1499
final String prefix = doPrefix(uri, qName, isElement);
1500
if ((prefix != null) && !"".equals(prefix)) {
1508
* Write out the list of Namespace declarations.
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
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);
1525
final char ch[] = uri.toCharArray();
1527
if ("".equals(prefix)) {
1534
writeEsc(ch, 0, ch.length, true);