KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > znerd > xmlenc > XMLOutputter


1 /*
2  * $Id: XMLOutputter.java,v 1.113 2004/03/17 21:23:03 znerd Exp $
3  */

4 package org.znerd.xmlenc;
5
6 import java.io.IOException JavaDoc;
7 import java.io.UnsupportedEncodingException JavaDoc;
8 import java.io.Writer JavaDoc;
9
10 /**
11  * Stream-based XML outputter. Instances of this class are able to write XML
12  * output to {@link Writer Writers}.
13  *
14  * <h3>Standards compliance</h3>
15  *
16  * This class is intended to produce output that conforms to the
17  * <a HREF="http://www.w3.org/TR/2000/REC-xml-20001006">XML 1.0
18  * Specification</a>. However, not all applicable restrictions are validated.
19  * For example, it is currently not checked if names contain characters that
20  * are invalid within a <em>Name</em> production.
21  *
22  * <p />Furthermore, not all possible XML documents can be produced. The
23  * following limitations apply:
24  *
25  * <ul>
26  * <li>The name of the applicable encoding is always printed in the XML
27  * declaration, even though it may not be necessary.</li>
28  * <li>The <code>standalone</code> attribute is not supported in the XML
29  * declaration.</li>
30  * <li>Internal DTD subsets are not supported.</li>
31  * <li>Spacing is fixed, whitespace is always kept to the minimum.</li>
32  * </ul>
33  *
34  * <h3>Supported encodings</h3>
35  *
36  * The following encodings are supported:
37  *
38  * <ul>
39  * <li>UTF-8</li>
40  * <li>UTF-16</li>
41  * <li>ISO-10646-UCS-2</li>
42  * <li>ISO-10646-UCS-4</li>
43  * <li>ISO-10646-UTF-1</li>
44  * <li>US-ASCII (also known as ASCII)</li>
45  * <li>ISO-8859-<em>n</em>, where <em>n</em> is the part number</li>
46  * </ul>
47  *
48  * <h3>Multi-threading</h3>
49  *
50  * This class is <em>not</em> thread-safe.
51  *
52  * <h3>Exceptions</h3>
53  *
54  * Note that all methods check the state first and then check the
55  * arguments. This means that if the state is incorrect and the arguments are
56  * incorrect, then an {@link IllegalStateException} will be thrown.
57  *
58  * <p />If any of the writing methods generates an {@link IOException}, then
59  * the state will be set to {@link #ERROR_STATE} and no more output can be
60  * performed.
61  *
62  * <h3>Performance hints</h3>
63  *
64  * It is usually a good idea to let <code>XMLOutputter</code> instances
65  * write to buffered {@link Writer Writers}. This typically improves
66  * performance on large documents or relatively slow or blocking output
67  * streams.
68  *
69  * <p />Instances of this class can be cached in a pool to reduce object
70  * creations. Call {@link #reset()} (with no arguments) when storing an
71  * instance in the pool. Use {@link #reset(Writer,String)} (with 2 arguments)
72  * to re-initialize the instance after fetching it from the pool.
73  *
74  * @version $Revision: 1.113 $ $Date: 2004/03/17 21:23:03 $
75  * @author Ernst de Haan (<a HREF="mailto:znerd@FreeBSD.org">znerd@FreeBSD.org</a>)
76  * @author Jochen Schwoerer (j.schwoerer [at] web.de)
77  *
78  * @since xmlenc 0.19
79  */

80 public class XMLOutputter
81 extends Object JavaDoc
82 implements StatefulXMLEventListener {
83
84    //-------------------------------------------------------------------------
85
// Class functions
86
//-------------------------------------------------------------------------
87

88    //-------------------------------------------------------------------------
89
// Class fields
90
//-------------------------------------------------------------------------
91

92    /**
93     * Default indentation. This is the empty string, <code>""</code>, since by
94     * default no indentation is performed.
95     */

96    public static final String JavaDoc DEFAULT_INDENTATION = "";
97
98
99    //-------------------------------------------------------------------------
100
// Constructor
101
//-------------------------------------------------------------------------
102

103    /**
104     * Constructs a new <code>XMLOutputter</code>. This sets the state to
105     * {@link #UNINITIALIZED}.
106     */

107    public XMLOutputter() {
108       _elementStack = new String JavaDoc[16];
109       _quotationMark = '"';
110    }
111
112    /**
113     * Constructs a new <code>XMLOutputter</code> for the specified
114     * <code>Writer</code> and encoding. This sets the state to
115     * {@link #BEFORE_XML_DECLARATION}.
116     *
117     * <p />The encoding will be stored exactly as passed, leaving the case
118     * intact.
119     *
120     * @param out
121     * the output stream to write to, not <code>null</code>.
122     *
123     * @param encoding
124     * the encoding, not <code>null</code>.
125     *
126     * @throws IllegalStateException
127     * if <code>getState() != {@link #UNINITIALIZED} &amp;&amp;
128     * getState() != {@link #AFTER_ROOT_ELEMENT} &amp;&amp;
129     * getState() != {@link #ERROR_STATE}</code>.
130     *
131     * @throws IllegalArgumentException
132     * if <code>out == null || encoding == null</code>.
133     *
134     * @throws UnsupportedEncodingException
135     * if the specified encoding is not supported.
136     */

137    public XMLOutputter(Writer JavaDoc out, String JavaDoc encoding)
138    throws IllegalStateException JavaDoc,
139           IllegalArgumentException JavaDoc,
140           UnsupportedEncodingException JavaDoc {
141
142       this();
143
144       // Initialize
145
reset(out, encoding);
146    }
147
148
149    /**
150     * Constructs a new <code>XMLOutputter</code> for the specified
151     * <code>Writer</code> and <code>encoder</code>. This sets the state to
152     * {@link #BEFORE_XML_DECLARATION}.
153     *
154     * @param out
155     * the output stream to write to, not <code>null</code>.
156     *
157     * @param encoder
158     * the encoder, not <code>null</code>.
159     *
160     * @throws IllegalStateException
161     * if <code>getState() != {@link #UNINITIALIZED} &amp;&amp;
162     * getState() != {@link #AFTER_ROOT_ELEMENT} &amp;&amp;
163     * getState() != {@link #ERROR_STATE}</code>.
164     *
165     * @throws IllegalArgumentException
166     * if <code>out == null || encoder == null</code>.
167     *
168     * @throws UnsupportedEncodingException
169     * if the specified encoding is not supported.
170     */

171    public XMLOutputter(Writer JavaDoc out, XMLEncoder encoder)
172    throws IllegalStateException JavaDoc,
173           IllegalArgumentException JavaDoc,
174           UnsupportedEncodingException JavaDoc {
175
176       this();
177
178       // Initialize
179
reset(out, encoder);
180    }
181
182    //-------------------------------------------------------------------------
183
// Fields
184
//-------------------------------------------------------------------------
185

186    /**
187     * The output stream this outputter will write to.
188     *
189     * <p>This field is initialized by the constructor. It can never be
190     * <code>null</code>.
191     *
192     * <p />The value of this field is returned by {@link #getWriter()}.
193     */

194    private Writer JavaDoc _out;
195
196    /**
197     * The encoder used to actually encode character streams.
198     */

199    private XMLEncoder _encoder;
200
201    /**
202     * The state of this outputter.
203     */

204    private XMLEventListenerState _state = UNINITIALIZED;
205
206    /**
207     * Stack of open elements.
208     *
209     * <p>This field is initialized by the constructor. It can never be
210     * <code>null</code>.
211     *
212     * @since xmlenc 0.22
213     */

214    private String JavaDoc[] _elementStack;
215
216    /**
217     * The size of the element stack. The actual capacity is
218     * {@link #_elementStack}<code>.length</code>.
219     *
220     * @since xmlenc 0.22
221     */

222    private int _elementStackSize;
223
224    /**
225     * The current quotation mark.
226     *
227     * <p />The value of this field can be set using
228     * {@link #setQuotationMark(char)} and can be retrieved
229     * using {@link #getQuotationMark()}.
230     */

231    private char _quotationMark;
232
233    /**
234     * Flag that indicates if ampersands should be escaped.
235     */

236    private boolean _escapeAmpersands = true;
237
238    /**
239     * The line break that is currently in use. Should never become
240     * <code>null</code>.
241     */

242    private LineBreak _lineBreak = LineBreak.NONE;
243
244    /**
245     * The line break as a char array. Should never become <code>null</code>,
246     * but can be a zero-length array.
247     */

248    private char[] _lineBreakChars = _lineBreak._lineBreakChars;
249
250    /**
251     * The currently used indentation string. Can never become
252     * <code>null</code>.
253     */

254    private String JavaDoc _indentation;
255
256
257    //-------------------------------------------------------------------------
258
// Methods
259
//-------------------------------------------------------------------------
260

261    /**
262     * Checks all invariants. This check should be performed at the end of
263     * every method that changes the internal state of this object.
264     *
265     * @throws Error
266     * if the state of this <code>XMLOutputter</code> is invalid.
267     */

268    private final void checkInvariants()
269    throws Error JavaDoc {
270       // TODO
271
}
272
273    /**
274     * Writes the indentation to the output stream.
275     *
276     * @throws IOException
277     * if an I/O error occurs.
278     */

279    private final void writeIndentation()
280    throws IOException JavaDoc {
281
282       // Write indentation only if there is any
283
if (_indentation.length() != 0) {
284          int count = _elementStackSize - 1;
285          for (int i = 0; i < count; i++) {
286             _out.write(_indentation);
287          }
288       }
289    }
290
291    /**
292     * Returns the output stream this outputter uses.
293     *
294     * @return
295     * the output stream of this encoding, only <code>null</code> if and
296     * only if the state is {@link #UNINITIALIZED}.
297     */

298    public final Writer JavaDoc getWriter() {
299       return _out;
300    }
301
302    /**
303     * Returns the encoding of this outputter.
304     *
305     * @return
306     * the encoding used by this outputter, only <code>null</code> if and
307     * only if the state is {@link #UNINITIALIZED}.
308     */

309    public final String JavaDoc getEncoding() {
310       if (_encoder == null) {
311          return null;
312       } else {
313          return _encoder.getEncoding();
314       }
315    }
316
317    /**
318     * Resets this <code>XMLOutputter</code>. The <code>Writer</code> and the
319     * encoding will be set to <code>null</code>, the element stack will be
320     * cleared, the state will be set to {@link #UNINITIALIZED}, the line break
321     * will be set to {@link LineBreak#NONE} and the indentation will be set to
322     * {@link #DEFAULT_INDENTATION} (an empty string).
323     */

324    public void reset() {
325       _out = null;
326       _encoder = null;
327       _elementStackSize = 0;
328       _state = UNINITIALIZED;
329       _lineBreak = LineBreak.NONE;
330       _lineBreakChars = _lineBreak._lineBreakChars;
331       _indentation = DEFAULT_INDENTATION;
332
333       // State has changed, check
334
checkInvariants();
335    }
336
337    /**
338     * Resets this <code>XMLOutputter</code> and configures it for the
339     * specified output stream. This sets the state to
340     * {@link #BEFORE_XML_DECLARATION} and clears the stack of open elements.
341     *
342     * @param out
343     * the new output stream, cannot be <code>null</code>.
344     *
345     * @throws IllegalArgumentException
346     * if <code>out == null</code>.
347     */

348    private final void reset(Writer JavaDoc out)
349    throws IllegalArgumentException JavaDoc {
350
351       // Check preconditions
352
if (out == null) {
353          throw new IllegalArgumentException JavaDoc("out == null");
354       }
355
356       // Reset the fields
357
_out = out;
358       _state = BEFORE_XML_DECLARATION;
359       _elementStackSize = 0;
360       _lineBreak = LineBreak.NONE;
361       _lineBreakChars = _lineBreak._lineBreakChars;
362       _indentation = DEFAULT_INDENTATION;
363
364       // State has changed, check
365
checkInvariants();
366    }
367
368    /**
369     * Resets this <code>XMLOutputter</code> and configures it for the
370     * specified output stream and encoding. This resets the state to
371     * {@link #BEFORE_XML_DECLARATION} and clears the stack of open elements.
372     *
373     * @param out
374     * the output stream to write to, not <code>null</code>.
375     *
376     * @param encoding
377     * the encoding, not <code>null</code>.
378     *
379     * @throws IllegalArgumentException
380     * if <code>out == null || encoding == null</code>.
381     *
382     * @throws UnsupportedEncodingException
383     * if the specified encoding is not supported.
384     */

385    public final void reset(Writer JavaDoc out, String JavaDoc encoding)
386    throws IllegalArgumentException JavaDoc,
387           UnsupportedEncodingException JavaDoc {
388
389       // Check arguments
390
if (encoding == null) {
391          throw new IllegalArgumentException JavaDoc("encoding == null");
392       }
393
394       reset(out);
395
396       // Store the fields
397
_encoder = XMLEncoder.getEncoder(encoding);
398
399       // State has changed, check
400
checkInvariants();
401    }
402
403    /**
404     * Resets this <code>XMLOutputter</code> and configures it for the
405     * specified output stream and encoder. This resets the state to
406     * {@link #BEFORE_XML_DECLARATION} and clears the stack of open elements.
407     *
408     * @param out
409     * the output stream to write to, not <code>null</code>.
410     *
411     * @param encoder
412     * the encoder, not <code>null</code>.
413     *
414     * @throws IllegalArgumentException
415     * if <code>out == null || encoder == null</code>.
416     *
417     * @throws UnsupportedEncodingException
418     * if the specified encoding is not supported.
419     */

420    public final void reset(Writer JavaDoc out, XMLEncoder encoder)
421    throws IllegalArgumentException JavaDoc,
422           UnsupportedEncodingException JavaDoc {
423
424       // Check arguments
425
if (encoder == null) {
426          throw new IllegalArgumentException JavaDoc("encoder == null");
427       }
428
429       reset(out);
430
431       // Store the fields
432
_encoder = encoder;
433
434       // State has changed, check
435
checkInvariants();
436    }
437
438    /**
439     * Sets the state of this outputter. Normally, it is not necessary to call
440     * this method.
441     *
442     * <p />Calling this method with {@link #UNINITIALIZED} as the state is
443     * equivalent to calling {@link #reset()}.
444     *
445     * <p />Caution: This method can be used to let this class generate invalid
446     * XML.
447     *
448     * @param newState
449     * the new state, not <code>null</code>.
450     *
451     * @param newElementStack
452     * the new element stack, if <code>newState == START_TAG_OPEN
453     * || newState == WITHIN_ELEMENT</code> then it should be
454     * non-<code>null</code> and containing no <code>null</code> elements,
455     * otherwise it must be <code>null</code>.
456     *
457     * @throws IllegalArgumentException
458     * if <code>newState == null
459     * || (newState == {@link #START_TAG_OPEN} &amp;&amp; newElementStack == null)
460     * || (newState == {@link #WITHIN_ELEMENT} &amp;&amp; newElementStack == null)
461     * || (newState != {@link #START_TAG_OPEN} &amp;&amp; newState != {@link #WITHIN_ELEMENT} &amp;&amp; newElementStack != null)
462     * || newElementStack[<i>n</i>] == null</code> (where <code>0 &lt;= <i>n</i> &lt; newElementStack.length</code>).
463     *
464     * @since xmlenc 0.22
465     */

466    public final void setState(XMLEventListenerState newState, String JavaDoc[] newElementStack)
467    throws IllegalArgumentException JavaDoc {
468
469       // Check arguments
470
if (newState == null) {
471          throw new IllegalArgumentException JavaDoc("newState == null");
472       } else if (newState == START_TAG_OPEN && newElementStack == null) {
473          throw new IllegalArgumentException JavaDoc("newState == START_TAG_OPEN && newElementStack == null");
474       } else if (newState == WITHIN_ELEMENT && newElementStack == null) {
475          throw new IllegalArgumentException JavaDoc("newState == WITHIN_ELEMENT && newElementStack == null");
476       } else if (newState != START_TAG_OPEN && newState != WITHIN_ELEMENT && newElementStack != null) {
477          throw new IllegalArgumentException JavaDoc("newState != START_TAG_OPEN && newState != WITHIN_ELEMENT && newElementStack != null");
478       }
479
480       if (newElementStack != null) {
481          for (int i = 0; i < newElementStack.length; i++) {
482             if (newElementStack[i] == null) {
483                throw new IllegalArgumentException JavaDoc("newElementStack[" + i + "] == null");
484             }
485          }
486
487          if (newElementStack.length > _elementStack.length) {
488             try {
489                _elementStack = new String JavaDoc[newElementStack.length + 16];
490             } catch (OutOfMemoryError JavaDoc error) {
491                _elementStack = new String JavaDoc[newElementStack.length];
492             }
493          }
494          System.arraycopy(newElementStack, 0, _elementStack, 0, newElementStack.length);
495       }
496
497       if (newState == UNINITIALIZED) {
498          reset();
499       } else {
500          _state = newState;
501          _elementStackSize = newElementStack == null ? 0 : newElementStack.length;
502       }
503
504       // State has changed, check
505
checkInvariants();
506    }
507
508    /**
509     * Returns the current state of this outputter.
510     *
511     * @return
512     * the current state, cannot be <code>null</code>.
513     */

514    public final XMLEventListenerState getState() {
515       return _state;
516    }
517
518    /**
519     * Checks if escaping is currently enabled. If escaping is enabled, then
520     * all ampersand characters (<code>'&amp;'</code>) are replaced by the
521     * character entity reference <code>"&amp;amp;"</code>. This affects
522     * PCDATA string printing ({@link #pcdata(String)} and
523     * {@link #pcdata(char[],int,int)}) and attribute value printing
524     * ({@link #attribute(String,String)}).
525     *
526     * @return
527     * <code>true</code> if escaping is enabled, <code>false</code>
528     * otherwise.
529     */

530    public final boolean isEscaping() {
531       return _escapeAmpersands;
532    }
533
534    /**
535     * Sets if ampersands should be escaped. This affects PCDATA string
536     * printing ({@link #pcdata(String)} and
537     * {@link #pcdata(char[],int,int)}) and attribute value printing
538     * ({@link #attribute(String,String)}).
539     *
540     * <p />If ampersands are not escaped, then entity references can be
541     * printed.
542     *
543     * @param escapeAmpersands
544     * <code>true</code> if ampersands should be escaped, <code>false</code>
545     * otherwise.
546     *
547     * @since xmlenc 0.24
548     */

549    public final void setEscaping(boolean escapeAmpersands) {
550       _escapeAmpersands = escapeAmpersands;
551
552       // State has changed, check
553
checkInvariants();
554    }
555
556    /**
557     * Returns a copy of the element stack. The returned array will be a new
558     * array. The size of the array will be equal to the element stack size
559     * (see {@link #getElementStackSize()}.
560     *
561     * @return
562     * a newly constructed array that contains all the element types
563     * currently on the element stack, or <code>null</code> if there are no
564     * elements on the stack.
565     *
566     * @since xmlenc 0.22
567     */

568    public final String JavaDoc[] getElementStack() {
569       if (_elementStackSize == 0) {
570          return null;
571       } else {
572          String JavaDoc[] newStack = new String JavaDoc[_elementStackSize];
573          System.arraycopy(_elementStack, 0, newStack, 0, _elementStackSize);
574          return newStack;
575       }
576    }
577
578    /**
579     * Returns the current depth of open elements.
580     *
581     * @return
582     * the open element depth, always &gt;= 0.
583     *
584     * @since xmlenc 0.22
585     */

586    public final int getElementStackSize() {
587       return _elementStackSize;
588    }
589
590    /**
591     * Returns the current capacity for the stack of open elements.
592     *
593     * @return
594     * the open element stack capacity, always &gt;=
595     * {@link #getElementStackSize()}.
596     *
597     * @since xmlenc 0.28
598     */

599    public final int getElementStackCapacity() {
600       return _elementStack.length;
601    }
602
603    /**
604     * Sets the capacity for the stack of open elements. The new capacity must
605     * at least allow the stack to contain the current open elements.
606     *
607     * @param newCapacity
608     * the new capacity, &gt;= {@link #getElementStackSize()}.
609     *
610     * @throws IllegalArgumentException
611     * if <code>newCapacity &lt; {@link #getElementStackSize()}</code>.
612     *
613     * @throws OutOfMemoryError
614     * if a new array cannot be allocated; this object will still be usable,
615     * but the capacity will remain unchanged.
616     */

617    public final void setElementStackCapacity(int newCapacity)
618    throws IllegalArgumentException JavaDoc, OutOfMemoryError JavaDoc {
619
620       // Check argument
621
if (newCapacity < _elementStack.length) {
622          throw new IllegalArgumentException JavaDoc("newCapacity < getElementStackSize()");
623       }
624
625       int currentCapacity = _elementStack.length;
626
627       // Short-circuit if possible
628
if (currentCapacity == newCapacity) {
629          return;
630       }
631
632       String JavaDoc[] newStack = new String JavaDoc[newCapacity];
633       System.arraycopy(_elementStack, 0, newStack, 0, _elementStackSize);
634       _elementStack = newStack;
635
636       // State has changed, check
637
checkInvariants();
638    }
639
640    /**
641     * Sets the quotation mark character to use. This character is printed
642     * before and after an attribute value. It can be either the single or the
643     * double quote character.
644     *
645     * <p />The default quotation mark character is <code>'"'</code>.
646     *
647     * @param c
648     * the character to put around attribute values, either
649     * <code>'\''</code> or <code>'"'</code>.
650     *
651     * @throws IllegalArgumentException
652     * if <code>c != '\'' &amp;&amp; c != '"'</code>.
653     */

654    public final void setQuotationMark(char c)
655    throws IllegalArgumentException JavaDoc {
656
657       // Accept apostrophe and quote
658
if (c == '\'' || c == '"') {
659          _quotationMark = c;
660
661       // Deny any other character
662
} else {
663          throw new IllegalArgumentException JavaDoc("c != '\\'' && c != '\"'");
664       }
665
666       // State has changed, check
667
checkInvariants();
668    }
669
670    /**
671     * Gets the quotation mark character. This character is used to mark the
672     * start and end of an attribute value.
673     *
674     * <p />The default quotation mark character is <code>'"'</code>.
675     *
676     * @return
677     * the character to put around attribute values, either
678     * <code>'\''</code> or <code>'"'</code>.
679     */

680    public final char getQuotationMark() {
681       return _quotationMark;
682    }
683
684    /**
685     * Sets the type of line break to use.
686     *
687     * @param lineBreak
688     * the line break to use; specifying <code>null</code> as the argument
689     * is equivalent to specifying {@link LineBreak#NONE}.
690     */

691    public final void setLineBreak(LineBreak lineBreak) {
692       _lineBreak = lineBreak != null
693                  ? lineBreak
694                  : LineBreak.NONE;
695       _lineBreakChars = _lineBreak._lineBreakChars;
696
697       // State has changed, check
698
checkInvariants();
699    }
700
701    /**
702     * Returns the currently used line break.
703     *
704     * @return
705     * the currently used line break, never <code>null</code>.
706     */

707    public final LineBreak getLineBreak() {
708       return _lineBreak;
709    }
710   
711    /**
712     * Sets the string to be used for indentation.
713     *
714     * @param indentation
715     * the character string used for indentation, or <code>null</code> if
716     * {@link #DEFAULT_INDENTATION the default indentation} should be used.
717     */

718    public final void setIndentation(String JavaDoc indentation) {
719       _indentation = indentation != null
720                    ? indentation
721                    : DEFAULT_INDENTATION;
722
723       // State has changed, check
724
checkInvariants();
725    }
726
727    /**
728     * Returns the string currently used for indentation.
729     *
730     * @return
731     * the character string used for indentation, never <code>null</code>.
732     */

733    public final String JavaDoc getIndentation() {
734       return _indentation;
735    }
736
737    /**
738     * Closes an open start tag.
739     *
740     * @throws IOException
741     * if an I/O error occurs.
742     */

743    private void closeStartTag()
744    throws IOException JavaDoc {
745       _out.write('>');
746    }
747
748    /**
749     * Writes the XML declaration. This method always prints the name of the
750     * encoding. The case of the encoding is as it was specified during
751     * initialization (or re-initialization).
752     *
753     * <p />If the encoding is set to <code>"ISO-8859-1"</code>, then this
754     * method will produce the following output:
755     *
756     * <blockquote><code>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;</code></blockquote>
757     *
758     * @throws IllegalStateException
759     * if <code>getState() != BEFORE_XML_DECLARATION</code>.
760     *
761     * @throws IOException
762     * if an I/O error occurs; this will set the state to
763     * {@link #ERROR_STATE}.
764     */

765    public final void declaration() throws IllegalStateException JavaDoc, IOException JavaDoc {
766
767       // Check state
768
if (_state != BEFORE_XML_DECLARATION) {
769          throw new IllegalStateException JavaDoc("getState() == " + _state);
770       }
771
772       // Temporarily set the state to ERROR_STATE, unless an exception is
773
// thrown in the write methods, it will be reset to a valid state
774
_state = ERROR_STATE;
775
776       // Write the output
777
_encoder.declaration(_out);
778
779       // Write the line break
780
_out.write(_lineBreakChars);
781
782       // Change the state
783
_state = BEFORE_DTD_DECLARATION;
784
785       // State has changed, check
786
checkInvariants();
787    }
788
789    /**
790     * Writes a document type declaration.
791     *
792     * <p />An external subset can be specified using either a
793     * <em>system identifier</em> (alone), or using both a
794     * <em>public identifier</em> and a <em>system identifier</em>. It can
795     * never be specified using a <em>public identifier</em> alone.
796     *
797     * <p />For example, for XHTML 1.0 the public identifier is:
798     *
799     * <blockquote><code>-//W3C//DTD XHTML 1.0 Transitional//EN</code></blockquote>
800     *
801     * <p />while the system identifier is:
802     *
803     * <blockquote><code>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</code></blockquote>
804     *
805     * <p />The output is typically similar to this:
806     *
807     * <blockquote><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</code></blockquote>
808     *
809     * or alternatively, if only the <em>system identifier</em> is specified:
810     *
811     * <blockquote><code>&lt;!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</code></blockquote>
812     *
813     * @param name
814     * the name of the document type, not <code>null</code>.
815     *
816     * @param publicID
817     * the public identifier, can be <code>null</code>.
818     *
819     * @param systemID
820     * the system identifier, can be <code>null</code>, but otherwise
821     * it should be a properly formatted URL, see
822     * <a HREF="http://www.w3.org/TR/2000/REC-xml-20001006#sec-external-ent">section 4.2.2 External Entities</a>
823     * in the XML 1.0 Specification.
824     *
825     * @throws IllegalStateException
826     * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
827     * getState() != {@link #BEFORE_DTD_DECLARATION}</code>.
828     *
829     * @throws IllegalArgumentException
830     * if <code>name == null ||
831     * (publicID != null &amp;&amp; systemID == null)</code>.
832     *
833     * @throws InvalidXMLException
834     * if the specified name does not match the
835     * <a HREF="http://www.w3.org/TR/REC-xml#NT-Name"><em>Name</em> production</a>
836     * (see {@link XMLChecker#checkName(String)}).
837     *
838     * @throws IOException
839     * if an I/O error occurs; this will set the state to
840     * {@link #ERROR_STATE}.
841     */

842    public final void dtd(String JavaDoc name, String JavaDoc publicID, String JavaDoc systemID)
843    throws IllegalStateException JavaDoc,
844           IllegalArgumentException JavaDoc,
845           InvalidXMLException,
846           IOException JavaDoc {
847
848       // Check state
849
if (_state != BEFORE_XML_DECLARATION
850        && _state != BEFORE_DTD_DECLARATION) {
851          throw new IllegalStateException JavaDoc("getState() == " + _state);
852       }
853
854       // Check arguments
855
if (name == null) {
856          throw new IllegalArgumentException JavaDoc("name == null");
857       } else if (publicID != null && systemID == null) {
858          throw new IllegalArgumentException JavaDoc("Found public identifier, but no system identifier.");
859       }
860
861       // Check productions
862
XMLChecker.checkName(name);
863       // TODO: More
864

865       // Temporarily set the state to ERROR_STATE, unless an exception is
866
// thrown in the write methods, it will be reset to a valid state
867
_state = ERROR_STATE;
868
869       // Write the DTD reference
870
_out.write("<!DOCTYPE ");
871       _out.write(name);
872       if (publicID != null) {
873          _out.write(" PUBLIC \"");
874          _out.write(publicID);
875          _out.write('"');
876          _out.write(' ');
877          _out.write('"');
878          _out.write(systemID);
879          _out.write('"');
880       } else if (systemID != null) {
881          _out.write(" SYSTEM \"");
882          _out.write(systemID);
883          _out.write('"');
884       }
885       closeStartTag();
886
887       // Change the state
888
_state = BEFORE_ROOT_ELEMENT;
889
890       // State has changed, check
891
checkInvariants();
892    }
893
894    /**
895     * Writes an element start tag. The element type name will be stored in the
896     * internal element stack. If necessary, the capacity of this stack will be
897     * extended.
898     *
899     * @param type
900     * the type of the tag to start, not <code>null</code>.
901     *
902     * @throws IllegalStateException
903     * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
904     * getState() != {@link #BEFORE_DTD_DECLARATION} &amp;&amp;
905     * getState() != {@link #BEFORE_ROOT_ELEMENT} &amp;&amp;
906     * getState() != {@link #START_TAG_OPEN} &amp;&amp;
907     * getState() != {@link #WITHIN_ELEMENT}</code>.
908     *
909     * @throws IllegalArgumentException
910     * if <code>type == null</code>.
911     *
912     * @throws IOException
913     * if an I/O error occurs; this will set the state to
914     * {@link #ERROR_STATE}.
915     */

916    public final void startTag(String JavaDoc type)
917    throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IOException JavaDoc {
918
919       // Check state
920
if (_state != BEFORE_XML_DECLARATION &&
921           _state != BEFORE_DTD_DECLARATION &&
922           _state != BEFORE_ROOT_ELEMENT &&
923           _state != START_TAG_OPEN &&
924           _state != WITHIN_ELEMENT) {
925          throw new IllegalStateException JavaDoc("getState() == " + _state);
926
927       // Check arguments
928
} else if (type == null) {
929          throw new IllegalArgumentException JavaDoc("type == null");
930       }
931
932       boolean startTagOpen = _state == START_TAG_OPEN;
933
934       // Temporarily set the state to ERROR_STATE, unless an exception is
935
// thrown in the write methods, it will be reset to a valid state
936
_state = ERROR_STATE;
937
938       // Increase the stack size with 100% if necessary
939
if (_elementStackSize == _elementStack.length) {
940          String JavaDoc[] newStack;
941          try {
942             newStack = new String JavaDoc[(_elementStackSize + 1) * 2];
943          } catch (OutOfMemoryError JavaDoc error) {
944             newStack = new String JavaDoc[_elementStackSize + 1];
945          }
946          System.arraycopy(_elementStack, 0, newStack, 0, _elementStackSize);
947          _elementStack = newStack;
948       }
949
950       // Store the element type name on the stack
951
_elementStack[_elementStackSize] = type;
952       _elementStackSize++;
953
954       // Close start tag if necessary and then write a raw '<'
955
// followed by the type
956
if (startTagOpen) {
957          _out.write('>');
958       }
959       
960       // Write the line break and the indentation
961
_out.write(_lineBreakChars);
962       writeIndentation();
963
964       _out.write('<');
965
966       // Escape the element name, if necessary
967
_out.write(type);
968
969       // Change the state
970
_state = START_TAG_OPEN;
971
972       // State has changed, check
973
checkInvariants();
974    }
975
976    /**
977     * Adds an attribute to the current element. There must currently be an
978     * open element.
979     *
980     * <p />The attribute value is surrounded by single quotes.
981     *
982     * @param name
983     * the name of the attribute, not <code>null</code>.
984     *
985     * @param value
986     * the value of the attribute, not <code>null</code>.
987     *
988     * @throws IllegalStateException
989     * if <code>getState() != {@link #START_TAG_OPEN}</code>.
990     *
991     * @throws IllegalArgumentException
992     * if <code>name == null || value == null</code>.
993     *
994     * @throws IOException
995     * if an I/O error occurs; this will set the state to
996     * {@link #ERROR_STATE}.
997     */

998    public final void attribute(String JavaDoc name, String JavaDoc value)
999    throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IOException JavaDoc {
1000
1001      // Check state
1002
if (getState() != START_TAG_OPEN) {
1003         throw new IllegalStateException JavaDoc("getState() == " + _state);
1004
1005      // Check arguments
1006
} else if (name == null || value == null) {
1007         if (name == null && value == null) {
1008            throw new IllegalArgumentException JavaDoc("name == null && value == null");
1009         } else if (name == null) {
1010            throw new IllegalArgumentException JavaDoc("name == null");
1011         } else {
1012            throw new IllegalArgumentException JavaDoc("value == null");
1013         }
1014      }
1015
1016      // Temporarily set the state to ERROR_STATE, unless an exception is
1017
// thrown in the write methods, it will be reset to a valid state
1018
_state = ERROR_STATE;
1019
1020      // Write output
1021
_encoder.attribute(_out, name, value, _quotationMark, _escapeAmpersands);
1022
1023      // Reset the state
1024
_state = START_TAG_OPEN;
1025
1026      // State has changed, check
1027
checkInvariants();
1028   }
1029
1030   /**
1031    * Writes an element end tag.
1032    *
1033    * @throws IllegalStateException
1034    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1035    * getState() != {@link #WITHIN_ELEMENT}</code>
1036    *
1037    * @throws IOException
1038    * if an I/O error occurs; this will set the state to
1039    * {@link #ERROR_STATE}.
1040    */

1041   public final void endTag()
1042   throws IllegalStateException JavaDoc, IOException JavaDoc {
1043
1044      // Check state
1045
if (_state != WITHIN_ELEMENT
1046       && _state != START_TAG_OPEN) {
1047         throw new IllegalStateException JavaDoc("getState() == " + _state);
1048      }
1049
1050      boolean startTagOpen = _state == START_TAG_OPEN;
1051
1052      // Temporarily set the state to ERROR_STATE, unless an exception is
1053
// thrown in the write methods, it will be reset to a valid state
1054
_state = ERROR_STATE;
1055
1056      String JavaDoc type = _elementStack[_elementStackSize-1];
1057
1058      // Write output
1059
if (startTagOpen) {
1060         _out.write('/');
1061         _out.write('>');
1062         _out.write(_lineBreakChars);
1063      } else {
1064         _out.write(_lineBreakChars);
1065         if (_lineBreak != LineBreak.NONE) {
1066            writeIndentation();
1067         }
1068
1069         _out.write('<');
1070         _out.write('/');
1071         _out.write(type);
1072         closeStartTag();
1073      }
1074
1075      _elementStackSize--;
1076
1077      // Change the state
1078
if (_elementStackSize == 0) {
1079         _state = AFTER_ROOT_ELEMENT;
1080      } else {
1081         _state = WITHIN_ELEMENT;
1082      }
1083
1084      // State has changed, check
1085
checkInvariants();
1086   }
1087
1088   /**
1089    * Writes the specified <code>String</code> as PCDATA.
1090    *
1091    * @param text
1092    * the PCDATA text to be written, not <code>null</code>.
1093    *
1094    * @throws IllegalStateException
1095    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1096    * getState() != {@link #WITHIN_ELEMENT}</code>
1097    *
1098    * @throws IllegalArgumentException
1099    * if <code>text == null</code>.
1100    *
1101    * @throws InvalidXMLException
1102    * if the specified text contains an invalid character.
1103    *
1104    * @throws IOException
1105    * if an I/O error occurs; this will set the state to
1106    * {@link #ERROR_STATE}.
1107    */

1108   public final void pcdata(String JavaDoc text)
1109   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, InvalidXMLException, IOException JavaDoc {
1110
1111      // Check state
1112
if (_state != START_TAG_OPEN
1113       && _state != WITHIN_ELEMENT) {
1114         throw new IllegalStateException JavaDoc("getState() == " + _state);
1115
1116      // Check arguments
1117
} else if (text == null) {
1118         throw new IllegalArgumentException JavaDoc("text == null");
1119      }
1120
1121      boolean startTagOpen = _state == START_TAG_OPEN;
1122
1123      // Temporarily set the state to ERROR_STATE, unless an exception is
1124
// thrown in the write methods, it will be reset to a valid state
1125
_state = ERROR_STATE;
1126
1127      // Write output
1128
if (startTagOpen) {
1129         closeStartTag();
1130         _out.write(_lineBreakChars);
1131      }
1132      _encoder.text(_out, text, _escapeAmpersands);
1133
1134      // Change the state
1135
_state = WITHIN_ELEMENT;
1136
1137      // State has changed, check
1138
checkInvariants();
1139   }
1140
1141   /**
1142    * Writes the specified character array as PCDATA.
1143    *
1144    * @param ch
1145    * the character array containing the text to be written, not
1146    * <code>null</code>.
1147    *
1148    * @param start
1149    * the start index in the array, must be &gt;= 0 and it must be &lt;
1150    * <code>ch.length</code>.
1151    *
1152    * @param length
1153    * the number of characters to read from the array, must be &gt; 0.
1154    *
1155    * @throws IllegalStateException
1156    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1157    * getState() != {@link #WITHIN_ELEMENT}</code>
1158    *
1159    * @throws IllegalArgumentException
1160    * if <code>ch == null
1161    * || start &lt; 0
1162    * || start &gt;= ch.length
1163    * || length &lt; 0</code>.
1164    *
1165    * @throws IndexOutOfBoundsException
1166    * if <code>start + length &gt; ch.length</code>.
1167    *
1168    * @throws InvalidXMLException
1169    * if the specified text contains an invalid character.
1170    *
1171    * @throws IOException
1172    * if an I/O error occurs; this will set the state to
1173    * {@link #ERROR_STATE}.
1174    */

1175   public final void pcdata(char[] ch, int start, int length)
1176   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IndexOutOfBoundsException JavaDoc, InvalidXMLException, IOException JavaDoc {
1177
1178      // Check state
1179
if (_state != START_TAG_OPEN
1180       && _state != WITHIN_ELEMENT) {
1181         throw new IllegalStateException JavaDoc("getState() == " + _state);
1182
1183      // Check arguments
1184
} else if (ch == null) {
1185         throw new IllegalArgumentException JavaDoc("ch == null");
1186      } else if (start < 0) {
1187         throw new IllegalArgumentException JavaDoc("start (" + start + ") < 0");
1188      } else if (start >= ch.length) {
1189         throw new IllegalArgumentException JavaDoc("start (" + start + ") >= ch.length (" + ch.length + ')');
1190      } else if (length < 0) {
1191         throw new IllegalArgumentException JavaDoc("length < 0");
1192      }
1193
1194      boolean startTagOpen = _state == START_TAG_OPEN;
1195
1196      // Temporarily set the state to ERROR_STATE, unless an exception is
1197
// thrown in the write methods, it will be reset to a valid state
1198
_state = ERROR_STATE;
1199
1200      // Write output
1201
if (startTagOpen) {
1202         closeStartTag();
1203      }
1204      _encoder.text(_out, ch, start, length, _escapeAmpersands);
1205
1206      // Change the state
1207
_state = WITHIN_ELEMENT;
1208
1209      // State has changed, check
1210
checkInvariants();
1211   }
1212
1213   /**
1214    * Writes the specified ignorable whitespace. Ignorable whitespace may be
1215    * written anywhere in XML output stream, except above the XML declaration.
1216    *
1217    * <p />If the state equals {@link #BEFORE_XML_DECLARATION}, then it will be set to
1218    * {@link #BEFORE_DTD_DECLARATION}, otherwise if the state is
1219    * {@link #START_TAG_OPEN} then it will be set to {@link #WITHIN_ELEMENT},
1220    * otherwise the state will not be changed.
1221    *
1222    * @param whitespace
1223    * the ignorable whitespace to be written, not <code>null</code>.
1224    *
1225    * @throws IllegalStateException
1226    * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
1227    * getState() != {@link #BEFORE_DTD_DECLARATION} &amp;&amp;
1228    * getState() != {@link #BEFORE_ROOT_ELEMENT} &amp;&amp;
1229    * getState() != {@link #START_TAG_OPEN} &amp;&amp;
1230    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1231    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>.
1232    *
1233    * @throws IllegalArgumentException
1234    * if <code>whitespace == null</code>.
1235    *
1236    * @throws InvalidXMLException
1237    * if the specified character string contains a character that is
1238    * invalid as whitespace.
1239    *
1240    * @throws IOException
1241    * if an I/O error occurs; this will set the state to
1242    * {@link #ERROR_STATE}.
1243    */

1244   public final void whitespace(String JavaDoc whitespace)
1245   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, InvalidXMLException, IOException JavaDoc {
1246
1247      // Check state
1248
if (_state != BEFORE_XML_DECLARATION &&
1249          _state != BEFORE_DTD_DECLARATION &&
1250          _state != BEFORE_ROOT_ELEMENT &&
1251          _state != START_TAG_OPEN &&
1252          _state != WITHIN_ELEMENT &&
1253          _state != AFTER_ROOT_ELEMENT) {
1254         throw new IllegalStateException JavaDoc("getState() == " + _state);
1255
1256      // Check arguments
1257
} else if (whitespace == null) {
1258         throw new IllegalArgumentException JavaDoc("whitespace == null");
1259      }
1260
1261      XMLEventListenerState oldState = _state;
1262
1263      // Temporarily set the state to ERROR_STATE, unless an exception is
1264
// thrown in the write methods, it will be reset to a valid state
1265
_state = ERROR_STATE;
1266
1267      // Write output
1268
if (oldState == START_TAG_OPEN) {
1269         closeStartTag();
1270      }
1271
1272      // Do the actual output
1273
_encoder.whitespace(_out, whitespace);
1274
1275      // Change state
1276
if (oldState == BEFORE_XML_DECLARATION) {
1277         _state = BEFORE_DTD_DECLARATION;
1278      } else if (oldState == START_TAG_OPEN) {
1279         _state = WITHIN_ELEMENT;
1280      } else {
1281         _state = oldState;
1282      }
1283
1284      // State has changed, check
1285
checkInvariants();
1286   }
1287
1288   /**
1289    * Writes text from the specified character array as ignorable whitespace.
1290    * Ignorable whitespace may be written anywhere in XML output stream,
1291    * except above the XML declaration.
1292    *
1293    * <p />This method does not check if the string actually contains
1294    * whitespace.
1295    *
1296    * <p />If the state equals {@link #BEFORE_XML_DECLARATION}, then it will be set to
1297    * {@link #BEFORE_DTD_DECLARATION}, otherwise if the state is
1298    * {@link #START_TAG_OPEN} then it will be set to {@link #WITHIN_ELEMENT},
1299    * otherwise the state will not be changed.
1300    *
1301    * @param ch
1302    * the character array containing the text to be written, not
1303    * <code>null</code>.
1304    *
1305    * @param start
1306    * the start index in the array, must be &gt;= 0 and it must be &lt;
1307    * <code>ch.length</code>.
1308    *
1309    * @param length
1310    * the number of characters to read from the array, must be &gt; 0.
1311    *
1312    * @throws IllegalStateException
1313    * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
1314    * getState() != {@link #BEFORE_DTD_DECLARATION} &amp;&amp;
1315    * getState() != {@link #BEFORE_ROOT_ELEMENT} &amp;&amp;
1316    * getState() != {@link #START_TAG_OPEN} &amp;&amp;
1317    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1318    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>.
1319    *
1320    * @throws IllegalArgumentException
1321    * if <code>ch == null
1322    * || start &lt; 0
1323    * || start &gt;= ch.length
1324    * || length &lt; 0</code>.
1325    *
1326    * @throws IndexOutOfBoundsException
1327    * if <code>start + length &gt; ch.length</code>.
1328    *
1329    * @throws InvalidXMLException
1330    * if the specified character string contains a character that is
1331    * invalid as whitespace.
1332    *
1333    * @throws IOException
1334    * if an I/O error occurs; this will set the state to
1335    * {@link #ERROR_STATE}.
1336    */

1337   public final void whitespace(char[] ch, int start, int length)
1338   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IndexOutOfBoundsException JavaDoc, InvalidXMLException, IOException JavaDoc {
1339
1340      // Check state
1341
if (_state != BEFORE_XML_DECLARATION
1342       && _state != BEFORE_DTD_DECLARATION
1343       && _state != BEFORE_ROOT_ELEMENT
1344       && _state != START_TAG_OPEN
1345       && _state != WITHIN_ELEMENT
1346       && _state != AFTER_ROOT_ELEMENT) {
1347         throw new IllegalStateException JavaDoc("getState() == " + _state);
1348
1349      // Check arguments
1350
} else if (ch == null) {
1351         throw new IllegalArgumentException JavaDoc("ch == null");
1352      } else if (start < 0) {
1353         throw new IllegalArgumentException JavaDoc("start (" + start + ") < 0");
1354      } else if (start >= ch.length) {
1355         throw new IllegalArgumentException JavaDoc("start (" + start + ") >= ch.length (" + ch.length + ')');
1356      } else if (length < 0) {
1357         throw new IllegalArgumentException JavaDoc("length < 0");
1358      }
1359
1360      XMLEventListenerState oldState = _state;
1361
1362      // Temporarily set the state to ERROR_STATE, unless an exception is
1363
// thrown in the write methods, it will be reset to a valid state
1364
_state = ERROR_STATE;
1365
1366      // Write output
1367
if (oldState == START_TAG_OPEN) {
1368         closeStartTag();
1369      }
1370
1371      // Do the actual output
1372
_encoder.whitespace(_out, ch, start, length);
1373
1374      // Change state
1375
if (oldState == BEFORE_XML_DECLARATION) {
1376         _state = BEFORE_DTD_DECLARATION;
1377      } else if (oldState == START_TAG_OPEN) {
1378         _state = WITHIN_ELEMENT;
1379      } else {
1380         _state = oldState;
1381      }
1382
1383      // State has changed, check
1384
checkInvariants();
1385   }
1386
1387   /**
1388    * Writes the specified comment. The comment should not contain the string
1389    * <code>"--"</code>.
1390    *
1391    * <p />If the state equals {@link #BEFORE_XML_DECLARATION}, then it will be set to
1392    * {@link #BEFORE_DTD_DECLARATION}, otherwise if the state is
1393    * {@link #START_TAG_OPEN} then it will be set to {@link #WITHIN_ELEMENT},
1394    * otherwise the state will not be changed.
1395    *
1396    * @param text
1397    * the text for the comment be written, not <code>null</code>.
1398    *
1399    * @throws IllegalStateException
1400    * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
1401    * getState() != {@link #BEFORE_DTD_DECLARATION} &amp;&amp;
1402    * getState() != {@link #BEFORE_ROOT_ELEMENT} &amp;&amp;
1403    * getState() != {@link #START_TAG_OPEN} &amp;&amp;
1404    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1405    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>.
1406    *
1407    * @throws IllegalArgumentException
1408    * if <code>text == null</code>.
1409    *
1410    * @throws InvalidXMLException
1411    * if the specified text contains an invalid character.
1412    *
1413    * @throws IOException
1414    * if an I/O error occurs; this will set the state to
1415    * {@link #ERROR_STATE}.
1416    */

1417   public final void comment(String JavaDoc text)
1418   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, InvalidXMLException, IOException JavaDoc {
1419
1420      // Check arguments
1421
if (_state != BEFORE_XML_DECLARATION
1422       && _state != BEFORE_DTD_DECLARATION
1423       && _state != BEFORE_ROOT_ELEMENT
1424       && _state != START_TAG_OPEN
1425       && _state != WITHIN_ELEMENT
1426       && _state != AFTER_ROOT_ELEMENT) {
1427         throw new IllegalStateException JavaDoc("getState() == " + _state);
1428
1429      // Check arguments
1430
} else if (text == null) {
1431         throw new IllegalArgumentException JavaDoc("text == null");
1432      }
1433
1434      XMLEventListenerState oldState = _state;
1435
1436      // Temporarily set the state to ERROR_STATE, unless an exception is
1437
// thrown in the write methods, it will be reset to a valid state
1438
_state = ERROR_STATE;
1439
1440      // Write output
1441
if (oldState == START_TAG_OPEN) {
1442         _out.write('>');
1443         _out.write('<');
1444         _out.write('!');
1445         _out.write('-');
1446         _out.write('-');
1447      } else {
1448         _out.write('<');
1449         _out.write('!');
1450         _out.write('-');
1451         _out.write('-');
1452      }
1453      _encoder.text(_out, text, _escapeAmpersands);
1454      _out.write('-');
1455      _out.write('-');
1456      _out.write('>');
1457
1458      _out.write(_lineBreakChars);
1459
1460      // Change state
1461
if (oldState == BEFORE_XML_DECLARATION) {
1462         _state = BEFORE_DTD_DECLARATION;
1463      } else if (oldState == START_TAG_OPEN) {
1464         _state = WITHIN_ELEMENT;
1465      } else {
1466         _state = oldState;
1467      }
1468
1469      // State has changed, check
1470
checkInvariants();
1471   }
1472
1473   /**
1474    * Writes a processing instruction. A target and an optional instruction
1475    * should be specified.
1476    *
1477    * <p />A processing instruction can appear above and below the root
1478    * element, and between elements. It cannot appear inside an element start
1479    * or end tag, nor inside a comment. Processing instructions cannot be
1480    * nested.
1481    *
1482    * <p />If the state equals {@link #BEFORE_XML_DECLARATION}, then it will be set to
1483    * {@link #BEFORE_DTD_DECLARATION}, otherwise the state will not be
1484    * changed.
1485    *
1486    * @param target
1487    * an identification of the application at which the instruction is
1488    * targeted, not <code>null</code>.
1489    *
1490    * @param instruction
1491    * the instruction, can be <code>null</code>, which is equivalent to an
1492    * empty string.
1493    *
1494    * @throws IllegalStateException
1495    * if <code>getState() != {@link #BEFORE_XML_DECLARATION} &amp;&amp;
1496    * getState() != {@link #BEFORE_DTD_DECLARATION} &amp;&amp;
1497    * getState() != {@link #BEFORE_ROOT_ELEMENT} &amp;&amp;
1498    * getState() != {@link #START_TAG_OPEN} &amp;&amp;
1499    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1500    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>.
1501    *
1502    * @throws IllegalArgumentException
1503    * if <code>target == null</code>.
1504    *
1505    * @throws IOException
1506    * if an I/O error occurs; this will set the state to
1507    * {@link #ERROR_STATE}.
1508    */

1509   public final void pi(String JavaDoc target, String JavaDoc instruction)
1510   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IOException JavaDoc {
1511
1512      // Check state
1513
if (_state != BEFORE_XML_DECLARATION
1514       && _state != BEFORE_DTD_DECLARATION
1515       && _state != BEFORE_ROOT_ELEMENT
1516       && _state != START_TAG_OPEN
1517       && _state != WITHIN_ELEMENT
1518       && _state != AFTER_ROOT_ELEMENT) {
1519         throw new IllegalStateException JavaDoc("getState() == " + _state);
1520
1521      // Check arguments
1522
} else if (target == null) {
1523         throw new IllegalArgumentException JavaDoc("target == null");
1524      }
1525
1526      XMLEventListenerState oldState = _state;
1527
1528      // Temporarily set the state to ERROR_STATE, unless an exception is
1529
// thrown in the write methods, it will be reset to a valid state
1530
_state = ERROR_STATE;
1531
1532      // Complete the start tag if necessary
1533
if (oldState == START_TAG_OPEN) {
1534         closeStartTag();
1535      }
1536
1537      // Write the Processing Instruction
1538
_out.write('<');
1539      _out.write('?');
1540      _out.write(target);
1541      if (instruction != null) {
1542         _out.write(' ');
1543         _out.write(instruction);
1544      }
1545      _out.write('?');
1546      _out.write('>');
1547
1548      // Change the state
1549
if (oldState == BEFORE_XML_DECLARATION) {
1550         _state = BEFORE_DTD_DECLARATION;
1551      } else if (oldState == START_TAG_OPEN) {
1552         _state = WITHIN_ELEMENT;
1553      } else {
1554         _state = oldState;
1555      }
1556
1557      // State has changed, check
1558
checkInvariants();
1559   }
1560
1561   /**
1562    * Writes a CDATA section.
1563    *
1564    * <p />A CDATA section can contain any string, except
1565    * <code>"]]&gt;"</code>. This will, however, not be checked by this
1566    * method.
1567    *
1568    * <p />Left angle brackets and ampersands will be output in their literal
1569    * form; they need not (and cannot) be escaped using
1570    * <code>"&amp;lt;"</code> and <code>"&amp;amp;"</code>.
1571    *
1572    * <p />If the specified string is empty (i.e.
1573    * <code>"".equals(text)</code>, then nothing will be output.
1574    *
1575    * <p />If the specified string contains characters that cannot be printed
1576    * in this encoding, then the result is undefined.
1577    *
1578    * @param text
1579    * the contents of the CDATA section, not <code>null</code>.
1580    *
1581    * @throws IllegalStateException
1582    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1583    * getState() != {@link #WITHIN_ELEMENT}</code>
1584    *
1585    * @throws IllegalArgumentException
1586    * if <code>text == null</code>.
1587    *
1588    * @throws IOException
1589    * if an I/O error occurs; this will set the state to
1590    * {@link #ERROR_STATE}.
1591    */

1592   public final void cdata(String JavaDoc text)
1593   throws IllegalStateException JavaDoc, IllegalArgumentException JavaDoc, IOException JavaDoc {
1594
1595      // Check state
1596
if (_state != START_TAG_OPEN
1597       && _state != WITHIN_ELEMENT) {
1598         throw new IllegalStateException JavaDoc("getState() == " + _state);
1599
1600      // Check arguments
1601
} else if (text == null) {
1602         throw new IllegalArgumentException JavaDoc("text == null");
1603      }
1604
1605      boolean startTagOpen = _state == START_TAG_OPEN;
1606
1607      // Temporarily set the state to ERROR_STATE, unless an exception is
1608
// thrown in the write methods, it will be reset to a valid state
1609
_state = ERROR_STATE;
1610
1611      // Complete the start tag if necessary
1612
if (startTagOpen) {
1613         closeStartTag();
1614      }
1615
1616      _out.write("<![CDATA[");
1617      _out.write(text);
1618      _out.write(']');
1619      _out.write(']');
1620      _out.write('>');
1621
1622      // Change state
1623
_state = WITHIN_ELEMENT;
1624
1625      // State has changed, check
1626
checkInvariants();
1627   }
1628
1629   /**
1630    * Closes all open elements. After calling this method, only the
1631    * {@link #whitespace(String)} method can be called.
1632    *
1633    * <p />If you would like to flush the output stream as well, call
1634    * {@link #endDocument()} instead.
1635    *
1636    * @throws IllegalStateException
1637    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1638    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1639    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>
1640    *
1641    * @throws IOException
1642    * if an I/O error occurs; this will set the state to
1643    * {@link #ERROR_STATE}.
1644    */

1645   public final void close()
1646   throws IllegalStateException JavaDoc, IOException JavaDoc {
1647
1648      // Check state
1649
if (_state != START_TAG_OPEN
1650       && _state != WITHIN_ELEMENT
1651       && _state != AFTER_ROOT_ELEMENT) {
1652         throw new IllegalStateException JavaDoc("getState() == " + _state);
1653      }
1654
1655      while (_elementStackSize > 0) {
1656         endTag();
1657      }
1658
1659      // State has changed, check
1660
checkInvariants();
1661   }
1662
1663   /**
1664    * Ends the XML output. All open elements will be closed and the underlying
1665    * output stream will be flushed using
1666    * {@link #getWriter()}.{@link java.io.Writer#flush() flush()}.
1667    *
1668    * <p />After calling this method, no more output can be
1669    * written until this outputter is reset.
1670    *
1671    * @throws IllegalStateException
1672    * if <code>getState() != {@link #START_TAG_OPEN} &amp;&amp;
1673    * getState() != {@link #WITHIN_ELEMENT} &amp;&amp;
1674    * getState() != {@link #AFTER_ROOT_ELEMENT}</code>.
1675    *
1676    * @throws IOException
1677    * if an I/O error occurs; this will set the state to
1678    * {@link #ERROR_STATE}.
1679    */

1680   public final void endDocument()
1681   throws IllegalStateException JavaDoc, IOException JavaDoc {
1682
1683      // Check state
1684
if (_state != START_TAG_OPEN &&
1685          _state != WITHIN_ELEMENT &&
1686          _state != AFTER_ROOT_ELEMENT) {
1687         throw new IllegalStateException JavaDoc("getState() == " + _state);
1688      }
1689
1690      // Close all open elements
1691
close();
1692
1693      // Flush the output stream
1694
_out.flush();
1695
1696      // Finally change the state
1697
_state = DOCUMENT_ENDED;
1698
1699      // State has changed, check
1700
checkInvariants();
1701   }
1702}
1703
Popular Tags