1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // @@3RD PARTY CODE@@
  27 
  28 // XMLWriter.java - serialize an XML document.
  29 // Written by David Megginson, david@megginson.com
  30 // NO WARRANTY!  This class is in the public domain.
  31 
  32 // Id: XMLWriter.java,v 1.5 2000/09/17 01:08:16 david Exp
  33 
  34 package com.sun.xml.internal.bind.marshaller;
  35 
  36 import java.io.IOException;
  37 import java.io.OutputStreamWriter;
  38 import java.io.Writer;
  39 import java.util.HashMap;
  40 import java.util.Map;
  41 
  42 import org.xml.sax.Attributes;
  43 import org.xml.sax.SAXException;
  44 import org.xml.sax.helpers.AttributesImpl;
  45 import org.xml.sax.helpers.XMLFilterImpl;
  46 
  47 
  48 /**
  49  * Filter to write an XML document from a SAX event stream.
  50  *
  51  * <p>This class can be used by itself or as part of a SAX event
  52  * stream: it takes as input a series of SAX2 ContentHandler
  53  * events and uses the information in those events to write
  54  * an XML document.  Since this class is a filter, it can also
  55  * pass the events on down a filter chain for further processing
  56  * (you can use the XMLWriter to take a snapshot of the current
  57  * state at any point in a filter chain), and it can be
  58  * used directly as a ContentHandler for a SAX2 XMLReader.</p>
  59  *
  60  * <p>The client creates a document by invoking the methods for
  61  * standard SAX2 events, always beginning with the
  62  * {@link #startDocument startDocument} method and ending with
  63  * the {@link #endDocument endDocument} method.  There are convenience
  64  * methods provided so that clients to not have to create empty
  65  * attribute lists or provide empty strings as parameters; for
  66  * example, the method invocation</p>
  67  *
  68  * <pre>
  69  * w.startElement("foo");
  70  * </pre>
  71  *
  72  * <p>is equivalent to the regular SAX2 ContentHandler method</p>
  73  *
  74  * <pre>
  75  * w.startElement("", "foo", "", new AttributesImpl());
  76  * </pre>
  77  *
  78  * <p>Except that it is more efficient because it does not allocate
  79  * a new empty attribute list each time.  The following code will send
  80  * a simple XML document to standard output:</p>
  81  *
  82  * <pre>
  83  * XMLWriter w = new XMLWriter();
  84  *
  85  * w.startDocument();
  86  * w.startElement("greeting");
  87  * w.characters("Hello, world!");
  88  * w.endElement("greeting");
  89  * w.endDocument();
  90  * </pre>
  91  *
  92  * <p>The resulting document will look like this:</p>
  93  *
  94  * <pre>{@code
  95  * <?xml version="1.0" standalone="yes"?>
  96  *
  97  * <greeting>Hello, world!</greeting>
  98  * }</pre>
  99  *
 100  * <p>In fact, there is an even simpler convenience method,
 101  * <var>dataElement</var>, designed for writing elements that
 102  * contain only character data, so the code to generate the
 103  * document could be shortened to</p>
 104  *
 105  * <pre>
 106  * XMLWriter w = new XMLWriter();
 107  *
 108  * w.startDocument();
 109  * w.dataElement("greeting", "Hello, world!");
 110  * w.endDocument();
 111  * </pre>
 112  *
 113  * <h2>Whitespace</h2>
 114  *
 115  * <p>According to the XML Recommendation, <em>all</em> whitespace
 116  * in an XML document is potentially significant to an application,
 117  * so this class never adds newlines or indentation.  If you
 118  * insert three elements in a row, as in</p>
 119  *
 120  * <pre>
 121  * w.dataElement("item", "1");
 122  * w.dataElement("item", "2");
 123  * w.dataElement("item", "3");
 124  * </pre>
 125  *
 126  * <p>you will end up with</p>
 127  *
 128  * <pre>{@code
 129  * <item>1</item><item>3</item><item>3</item>
 130  * }</pre>
 131  *
 132  * <p>You need to invoke one of the <var>characters</var> methods
 133  * explicitly to add newlines or indentation.  Alternatively, you
 134  * can use {@link DataWriter}, which
 135  * is derived from this class -- it is optimized for writing
 136  * purely data-oriented (or field-oriented) XML, and does automatic
 137  * linebreaks and indentation (but does not support mixed content
 138  * properly).</p>
 139  *
 140  *
 141  * <h2>Namespace Support</h2>
 142  *
 143  * <p>The writer contains extensive support for XML Namespaces, so that
 144  * a client application does not have to keep track of prefixes and
 145  * supply <var>xmlns</var> attributes.  By default, the XML writer will
 146  * generate Namespace declarations in the form _NS1, _NS2, etc., wherever
 147  * they are needed, as in the following example:</p>
 148  *
 149  * <pre>
 150  * w.startDocument();
 151  * w.emptyElement("http://www.foo.com/ns/", "foo");
 152  * w.endDocument();
 153  * </pre>
 154  *
 155  * <p>The resulting document will look like this:</p>
 156  *
 157  * <pre>{@code
 158  * <?xml version="1.0" standalone="yes"?>
 159  *
 160  * <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
 161  * }</pre>
 162  *
 163  * <p>In many cases, document authors will prefer to choose their
 164  * own prefixes rather than using the (ugly) default names.  The
 165  * XML writer allows two methods for selecting prefixes:</p>
 166  *
 167  * <ol>
 168  * <li>the qualified name</li>
 169  * </ol>
 170  *
 171  * <p>Whenever the XML writer finds a new Namespace URI, it checks
 172  * to see if a qualified (prefixed) name is also available; if so
 173  * it attempts to use the name's prefix (as long as the prefix is
 174  * not already in use for another Namespace URI).</p>
 175  *
 176  * <p>The resulting document will look like this:</p>
 177  *
 178  * <pre>{@code
 179  * <?xml version="1.0" standalone="yes"?>
 180  *
 181  * <foo:foo xmlns:foo="http://www.foo.com/ns/"/>
 182  * }</pre>
 183  *
 184  * <p>The default Namespace simply uses an empty string as the prefix:</p>
 185  *
 186  * <pre>
 187  * w.setPrefix("http://www.foo.com/ns/", "");
 188  * w.startDocument();
 189  * w.emptyElement("http://www.foo.com/ns/", "foo");
 190  * w.endDocument();
 191  * </pre>
 192  *
 193  * <p>The resulting document will look like this:</p>
 194  *
 195  * <pre>{@code
 196  * <?xml version="1.0" standalone="yes"?>
 197  *
 198  * <foo xmlns="http://www.foo.com/ns/"/>
 199  * }</pre>
 200  *
 201  * <p>By default, the XML writer will not declare a Namespace until
 202  * it is actually used.  Sometimes, this approach will create
 203  * a large number of Namespace declarations, as in the following
 204  * example:</p>
 205  *
 206  * <pre>{@code
 207  * <xml version="1.0" standalone="yes"?>
 208  *
 209  * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 210  *  <rdf:Description about="http://www.foo.com/ids/books/12345">
 211  *   <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title>
 212  *   <dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith</dc:title>
 213  *   <dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09</dc:title>
 214  *  </rdf:Description>
 215  * </rdf:RDF>
 216  * }</pre>
 217  *
 218  * <p>The "rdf" prefix is declared only once, because the RDF Namespace
 219  * is used by the root element and can be inherited by all of its
 220  * descendants; the "dc" prefix, on the other hand, is declared three
 221  * times, because no higher element uses the Namespace.  To solve this
 222  * problem, you can instruct the XML writer to predeclare Namespaces
 223  * on the root element even if they are not used there:</p>
 224  *
 225  * <pre>
 226  * w.forceNSDecl("http://www.purl.org/dc/");
 227  * </pre>
 228  *
 229  * <p>Now, the "dc" prefix will be declared on the root element even
 230  * though it's not needed there, and can be inherited by its
 231  * descendants:</p>
 232  *
 233  * <pre>{@code
 234  * <xml version="1.0" standalone="yes"?>
 235  *
 236  * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 237  *             xmlns:dc="http://www.purl.org/dc/">
 238  *  <rdf:Description about="http://www.foo.com/ids/books/12345">
 239  *   <dc:title>A Dark Night</dc:title>
 240  *   <dc:creator>Jane Smith</dc:title>
 241  *   <dc:date>2000-09-09</dc:title>
 242  *  </rdf:Description>
 243  * </rdf:RDF>
 244  * }</pre>
 245  *
 246  * <p>This approach is also useful for declaring Namespace prefixes
 247  * that be used by qualified names appearing in attribute values or
 248  * character data.</p>
 249  *
 250  * @author David Megginson, david@megginson.com
 251  * @version 0.2
 252  * @since JAXB 1.0
 253  * @see org.xml.sax.XMLFilter
 254  * @see org.xml.sax.ContentHandler
 255  */
 256 public class XMLWriter extends XMLFilterImpl
 257 {
 258 
 259     ////////////////////////////////////////////////////////////////////
 260     // Constructors.
 261     ////////////////////////////////////////////////////////////////////
 262 
 263 
 264 
 265 
 266     /**
 267      * Create a new XML writer.
 268      *
 269      * <p>Write to the writer provided.</p>
 270      *
 271      * @param writer
 272      *      The output destination, or null to use standard output.
 273      * @param encoding
 274      *      If non-null string is specified, it is written as a part
 275      *      of the XML declaration.
 276      */
 277     public XMLWriter (Writer writer, String encoding, CharacterEscapeHandler _escapeHandler )
 278     {
 279         init(writer,encoding);
 280         this.escapeHandler = _escapeHandler;
 281     }
 282 
 283     public XMLWriter (Writer writer, String encoding ) {
 284         this( writer, encoding, DumbEscapeHandler.theInstance );
 285     }
 286 
 287 
 288 
 289     /**
 290      * Internal initialization method.
 291      *
 292      * <p>All of the public constructors invoke this method.
 293      *
 294      * @param writer The output destination, or null to use
 295      *        standard output.
 296      */
 297     private void init (Writer writer,String encoding)
 298     {
 299         setOutput(writer,encoding);
 300     }
 301 
 302 
 303 
 304     ////////////////////////////////////////////////////////////////////
 305     // Public methods.
 306     ////////////////////////////////////////////////////////////////////
 307 
 308 
 309     /**
 310      * Reset the writer.
 311      *
 312      * <p>This method is especially useful if the writer throws an
 313      * exception before it is finished, and you want to reuse the
 314      * writer for a new document.  It is usually a good idea to
 315      * invoke {@link #flush flush} before resetting the writer,
 316      * to make sure that no output is lost.</p>
 317      *
 318      * <p>This method is invoked automatically by the
 319      * {@link #startDocument startDocument} method before writing
 320      * a new document.</p>
 321      *
 322      * <p><strong>Note:</strong> this method will <em>not</em>
 323      * clear the prefix or URI information in the writer or
 324      * the selected output writer.</p>
 325      *
 326      * @see #flush()
 327      */
 328     public void reset ()
 329     {
 330         elementLevel = 0;
 331         startTagIsClosed = true;
 332     }
 333 
 334 
 335     /**
 336      * Flush the output.
 337      *
 338      * <p>This method flushes the output stream.  It is especially useful
 339      * when you need to make certain that the entire document has
 340      * been written to output but do not want to close the output
 341      * stream.</p>
 342      *
 343      * <p>This method is invoked automatically by the
 344      * {@link #endDocument endDocument} method after writing a
 345      * document.</p>
 346      *
 347      * @see #reset()
 348      */
 349     public void flush ()
 350         throws IOException
 351     {
 352         output.flush();
 353     }
 354 
 355 
 356     /**
 357      * Set a new output destination for the document.
 358      *
 359      * @param writer The output destination, or null to use
 360      *        standard output.
 361      * @see #flush()
 362      */
 363     public void setOutput (Writer writer,String _encoding)
 364     {
 365         if (writer == null) {
 366             output = new OutputStreamWriter(System.out);
 367         } else {
 368             output = writer;
 369         }
 370         encoding = _encoding;
 371     }
 372 
 373     /**
 374      * Set whether the writer should print out the XML declaration
 375      * ({@code <?xml version='1.0' ... ?>}).
 376      * <p>
 377      * This option is set to true by default.
 378      */
 379     public void setXmlDecl( boolean _writeXmlDecl ) {
 380         this.writeXmlDecl = _writeXmlDecl;
 381     }
 382 
 383     /**
 384      * Sets the header string.
 385      *
 386      * This string will be written right after the xml declaration
 387      * without any escaping. Useful for generating a boiler-plate
 388      * DOCTYPE decl, PIs, and comments.
 389      *
 390      * @param _header
 391      *      passing null will work as if the empty string is passed.
 392      */
 393     public void setHeader( String _header ) {
 394         this.header = _header;
 395     }
 396 
 397 
 398     private final HashMap<String,String> locallyDeclaredPrefix = new HashMap<String,String>();
 399     public void startPrefixMapping( String prefix, String uri ) throws SAXException {
 400         locallyDeclaredPrefix.put(prefix,uri);
 401     }
 402 
 403 
 404     ////////////////////////////////////////////////////////////////////
 405     // Methods from org.xml.sax.ContentHandler.
 406     ////////////////////////////////////////////////////////////////////
 407 
 408     /**
 409      * Write the XML declaration at the beginning of the document.
 410      *
 411      * Pass the event on down the filter chain for further processing.
 412      *
 413      * @exception org.xml.sax.SAXException If there is an error
 414      *            writing the XML declaration, or if a handler further down
 415      *            the filter chain raises an exception.
 416      * @see org.xml.sax.ContentHandler#startDocument()
 417      */
 418     public void startDocument ()
 419         throws SAXException
 420     {
 421         try {
 422             reset();
 423 
 424             if(writeXmlDecl) {
 425                 String e="";
 426                 if(encoding!=null)
 427                     e = " encoding=\""+encoding+'\"';
 428 
 429                 writeXmlDecl("<?xml version=\"1.0\""+e +" standalone=\"yes\"?>");
 430             }
 431 
 432             if(header!=null)
 433                 write(header);
 434 
 435             super.startDocument();
 436         } catch( IOException e ) {
 437             throw new SAXException(e);
 438         }
 439     }
 440 
 441     protected void writeXmlDecl(String decl) throws IOException {
 442         write(decl);
 443     }
 444 
 445 
 446     /**
 447      * Write a newline at the end of the document.
 448      *
 449      * Pass the event on down the filter chain for further processing.
 450      *
 451      * @exception org.xml.sax.SAXException If there is an error
 452      *            writing the newline, or if a handler further down
 453      *            the filter chain raises an exception.
 454      * @see org.xml.sax.ContentHandler#endDocument()
 455      */
 456     public void endDocument ()
 457         throws SAXException
 458     {
 459         try {
 460             super.endDocument();
 461             flush();
 462         } catch( IOException e ) {
 463             throw new SAXException(e);
 464         }
 465     }
 466 
 467 
 468     /**
 469      * Write a start tag.
 470      *
 471      * Pass the event on down the filter chain for further processing.
 472      *
 473      * @param uri The Namespace URI, or the empty string if none
 474      *        is available.
 475      * @param localName The element's local (unprefixed) name (required).
 476      * @param qName The element's qualified (prefixed) name, or the
 477      *        empty string is none is available.  This method will
 478      *        use the qName as a template for generating a prefix
 479      *        if necessary, but it is not guaranteed to use the
 480      *        same qName.
 481      * @param atts The element's attribute list (must not be null).
 482      * @exception org.xml.sax.SAXException If there is an error
 483      *            writing the start tag, or if a handler further down
 484      *            the filter chain raises an exception.
 485      * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
 486      */
 487     public void startElement (String uri, String localName,
 488                               String qName, Attributes atts)
 489         throws SAXException
 490     {
 491         try {
 492             if (!startTagIsClosed) {
 493                 write(">");
 494             }
 495             elementLevel++;
 496 //            nsSupport.pushContext();
 497 
 498             write('<');
 499             write(qName);
 500             writeAttributes(atts);
 501 
 502             // declare namespaces specified by the startPrefixMapping methods
 503             if(!locallyDeclaredPrefix.isEmpty()) {
 504                 for (Map.Entry<String,String> e : locallyDeclaredPrefix.entrySet()) {
 505                     String p = e.getKey();
 506                     String u = e.getValue();
 507                     if (u == null) {
 508                         u = "";
 509                     }
 510                     write(' ');
 511                     if ("".equals(p)) {
 512                         write("xmlns=\"");
 513                     } else {
 514                         write("xmlns:");
 515                         write(p);
 516                         write("=\"");
 517                     }
 518                     char ch[] = u.toCharArray();
 519                     writeEsc(ch, 0, ch.length, true);
 520                     write('\"');
 521                 }
 522                 locallyDeclaredPrefix.clear();  // clear the contents
 523             }
 524 
 525 //            if (elementLevel == 1) {
 526 //                forceNSDecls();
 527 //            }
 528 //            writeNSDecls();
 529             super.startElement(uri, localName, qName, atts);
 530             startTagIsClosed = false;
 531         } catch( IOException e ) {
 532             throw new SAXException(e);
 533         }
 534     }
 535 
 536 
 537     /**
 538      * Write an end tag.
 539      *
 540      * Pass the event on down the filter chain for further processing.
 541      *
 542      * @param uri The Namespace URI, or the empty string if none
 543      *        is available.
 544      * @param localName The element's local (unprefixed) name (required).
 545      * @param qName The element's qualified (prefixed) name, or the
 546      *        empty string is none is available.  This method will
 547      *        use the qName as a template for generating a prefix
 548      *        if necessary, but it is not guaranteed to use the
 549      *        same qName.
 550      * @exception org.xml.sax.SAXException If there is an error
 551      *            writing the end tag, or if a handler further down
 552      *            the filter chain raises an exception.
 553      * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
 554      */
 555     public void endElement (String uri, String localName, String qName)
 556         throws SAXException
 557     {
 558         try {
 559             if (startTagIsClosed) {
 560                 write("</");
 561                 write(qName);
 562                 write('>');
 563             } else {
 564                 write("/>");
 565                 startTagIsClosed = true;
 566             }
 567             super.endElement(uri, localName, qName);
 568 //            nsSupport.popContext();
 569             elementLevel--;
 570         } catch( IOException e ) {
 571             throw new SAXException(e);
 572         }
 573     }
 574 
 575 
 576     /**
 577      * Write character data.
 578      *
 579      * Pass the event on down the filter chain for further processing.
 580      *
 581      * @param ch The array of characters to write.
 582      * @param start The starting position in the array.
 583      * @param len The number of characters to write.
 584      * @exception org.xml.sax.SAXException If there is an error
 585      *            writing the characters, or if a handler further down
 586      *            the filter chain raises an exception.
 587      * @see org.xml.sax.ContentHandler#characters(char[], int, int)
 588      */
 589     public void characters (char ch[], int start, int len)
 590         throws SAXException
 591     {
 592         try {
 593             if (!startTagIsClosed) {
 594                 write('>');
 595                 startTagIsClosed = true;
 596             }
 597             writeEsc(ch, start, len, false);
 598             super.characters(ch, start, len);
 599         } catch( IOException e ) {
 600             throw new SAXException(e);
 601         }
 602     }
 603 
 604 
 605     /**
 606      * Write ignorable whitespace.
 607      *
 608      * Pass the event on down the filter chain for further processing.
 609      *
 610      * @param ch The array of characters to write.
 611      * @param start The starting position in the array.
 612      * @param length The number of characters to write.
 613      * @exception org.xml.sax.SAXException If there is an error
 614      *            writing the whitespace, or if a handler further down
 615      *            the filter chain raises an exception.
 616      * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
 617      */
 618     public void ignorableWhitespace (char ch[], int start, int length)
 619         throws SAXException
 620     {
 621         try {
 622             writeEsc(ch, start, length, false);
 623             super.ignorableWhitespace(ch, start, length);
 624         } catch( IOException e ) {
 625             throw new SAXException(e);
 626         }
 627     }
 628 
 629 
 630 
 631     /**
 632      * Write a processing instruction.
 633      *
 634      * Pass the event on down the filter chain for further processing.
 635      *
 636      * @param target The PI target.
 637      * @param data The PI data.
 638      * @exception org.xml.sax.SAXException If there is an error
 639      *            writing the PI, or if a handler further down
 640      *            the filter chain raises an exception.
 641      * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
 642      */
 643     public void processingInstruction (String target, String data)
 644         throws SAXException
 645     {
 646         try {
 647             if (!startTagIsClosed) {
 648                 write('>');
 649                 startTagIsClosed = true;
 650             }
 651             write("<?");
 652             write(target);
 653             write(' ');
 654             write(data);
 655             write("?>");
 656             if (elementLevel < 1) {
 657                 write('\n');
 658             }
 659             super.processingInstruction(target, data);
 660         } catch( IOException e ) {
 661             throw new SAXException(e);
 662         }
 663     }
 664 
 665 
 666 
 667     ////////////////////////////////////////////////////////////////////
 668     // Convenience methods.
 669     ////////////////////////////////////////////////////////////////////
 670 
 671 
 672 
 673     /**
 674      * Start a new element without a qname or attributes.
 675      *
 676      * <p>This method will provide a default empty attribute
 677      * list and an empty string for the qualified name.
 678      * It invokes {@link
 679      * #startElement(String, String, String, Attributes)}
 680      * directly.</p>
 681      *
 682      * @param uri The element's Namespace URI.
 683      * @param localName The element's local name.
 684      * @exception org.xml.sax.SAXException If there is an error
 685      *            writing the start tag, or if a handler further down
 686      *            the filter chain raises an exception.
 687      * @see #startElement(String, String, String, Attributes)
 688      */
 689     public void startElement (String uri, String localName)
 690         throws SAXException
 691     {
 692         startElement(uri, localName, "", EMPTY_ATTS);
 693     }
 694 
 695 
 696     /**
 697      * Start a new element without a qname, attributes or a Namespace URI.
 698      *
 699      * <p>This method will provide an empty string for the
 700      * Namespace URI, and empty string for the qualified name,
 701      * and a default empty attribute list. It invokes
 702      * #startElement(String, String, String, Attributes)}
 703      * directly.</p>
 704      *
 705      * @param localName The element's local name.
 706      * @exception org.xml.sax.SAXException If there is an error
 707      *            writing the start tag, or if a handler further down
 708      *            the filter chain raises an exception.
 709      * @see #startElement(String, String, String, Attributes)
 710      */
 711     public void startElement (String localName)
 712         throws SAXException
 713     {
 714         startElement("", localName, "", EMPTY_ATTS);
 715     }
 716 
 717 
 718     /**
 719      * End an element without a qname.
 720      *
 721      * <p>This method will supply an empty string for the qName.
 722      * It invokes {@link #endElement(String, String, String)}
 723      * directly.</p>
 724      *
 725      * @param uri The element's Namespace URI.
 726      * @param localName The element's local name.
 727      * @exception org.xml.sax.SAXException If there is an error
 728      *            writing the end tag, or if a handler further down
 729      *            the filter chain raises an exception.
 730      * @see #endElement(String, String, String)
 731      */
 732     public void endElement (String uri, String localName)
 733         throws SAXException
 734     {
 735         endElement(uri, localName, "");
 736     }
 737 
 738 
 739     /**
 740      * End an element without a Namespace URI or qname.
 741      *
 742      * <p>This method will supply an empty string for the qName
 743      * and an empty string for the Namespace URI.
 744      * It invokes {@link #endElement(String, String, String)}
 745      * directly.</p>
 746      *
 747      * @param localName The element's local name.
 748      * @exception org.xml.sax.SAXException If there is an error
 749      *            writing the end tag, or if a handler further down
 750      *            the filter chain raises an exception.
 751      * @see #endElement(String, String, String)
 752      */
 753     public void endElement (String localName)
 754         throws SAXException
 755     {
 756         endElement("", localName, "");
 757     }
 758 
 759 
 760     /**
 761      * Write an element with character data content.
 762      *
 763      * <p>This is a convenience method to write a complete element
 764      * with character data content, including the start tag
 765      * and end tag.</p>
 766      *
 767      * <p>This method invokes
 768      * {@link #startElement(String, String, String, Attributes)},
 769      * followed by
 770      * {@link #characters(String)}, followed by
 771      * {@link #endElement(String, String, String)}.</p>
 772      *
 773      * @param uri The element's Namespace URI.
 774      * @param localName The element's local name.
 775      * @param qName The element's default qualified name.
 776      * @param atts The element's attributes.
 777      * @param content The character data content.
 778      * @exception org.xml.sax.SAXException If there is an error
 779      *            writing the empty tag, or if a handler further down
 780      *            the filter chain raises an exception.
 781      * @see #startElement(String, String, String, Attributes)
 782      * @see #characters(String)
 783      * @see #endElement(String, String, String)
 784      */
 785     public void dataElement (String uri, String localName,
 786                              String qName, Attributes atts,
 787                              String content)
 788         throws SAXException
 789     {
 790         startElement(uri, localName, qName, atts);
 791         characters(content);
 792         endElement(uri, localName, qName);
 793     }
 794 
 795 
 796     /**
 797      * Write an element with character data content but no attributes.
 798      *
 799      * <p>This is a convenience method to write a complete element
 800      * with character data content, including the start tag
 801      * and end tag.  This method provides an empty string
 802      * for the qname and an empty attribute list.</p>
 803      *
 804      * <p>This method invokes
 805      * {@link #startElement(String, String, String, Attributes)},
 806      * followed by
 807      * {@link #characters(String)}, followed by
 808      * {@link #endElement(String, String, String)}.</p>
 809      *
 810      * @param uri The element's Namespace URI.
 811      * @param localName The element's local name.
 812      * @param content The character data content.
 813      * @exception org.xml.sax.SAXException If there is an error
 814      *            writing the empty tag, or if a handler further down
 815      *            the filter chain raises an exception.
 816      * @see #startElement(String, String, String, Attributes)
 817      * @see #characters(String)
 818      * @see #endElement(String, String, String)
 819      */
 820     public void dataElement (String uri, String localName, String content)
 821         throws SAXException
 822     {
 823         dataElement(uri, localName, "", EMPTY_ATTS, content);
 824     }
 825 
 826 
 827     /**
 828      * Write an element with character data content but no attributes or Namespace URI.
 829      *
 830      * <p>This is a convenience method to write a complete element
 831      * with character data content, including the start tag
 832      * and end tag.  The method provides an empty string for the
 833      * Namespace URI, and empty string for the qualified name,
 834      * and an empty attribute list.</p>
 835      *
 836      * <p>This method invokes
 837      * {@link #startElement(String, String, String, Attributes)},
 838      * followed by
 839      * {@link #characters(String)}, followed by
 840      * {@link #endElement(String, String, String)}.</p>
 841      *
 842      * @param localName The element's local name.
 843      * @param content The character data content.
 844      * @exception org.xml.sax.SAXException If there is an error
 845      *            writing the empty tag, or if a handler further down
 846      *            the filter chain raises an exception.
 847      * @see #startElement(String, String, String, Attributes)
 848      * @see #characters(String)
 849      * @see #endElement(String, String, String)
 850      */
 851     public void dataElement (String localName, String content)
 852         throws SAXException
 853     {
 854         dataElement("", localName, "", EMPTY_ATTS, content);
 855     }
 856 
 857 
 858     /**
 859      * Write a string of character data, with XML escaping.
 860      *
 861      * <p>This is a convenience method that takes an XML
 862      * String, converts it to a character array, then invokes
 863      * {@link #characters(char[], int, int)}.</p>
 864      *
 865      * @param data The character data.
 866      * @exception org.xml.sax.SAXException If there is an error
 867      *            writing the string, or if a handler further down
 868      *            the filter chain raises an exception.
 869      * @see #characters(char[], int, int)
 870      */
 871     public void characters (String data) throws SAXException {
 872         try {
 873             if (!startTagIsClosed) {
 874                 write('>');
 875                 startTagIsClosed = true;
 876             }
 877             char ch[] = data.toCharArray();
 878             characters(ch, 0, ch.length);
 879         } catch( IOException e ) {
 880             throw new SAXException(e);
 881         }
 882     }
 883 
 884 
 885 
 886     ////////////////////////////////////////////////////////////////////
 887     // Internal methods.
 888     ////////////////////////////////////////////////////////////////////
 889 
 890 
 891 
 892 
 893     /**
 894      * Write a raw character.
 895      *
 896      * @param c The character to write.
 897      */
 898     protected final void write (char c) throws IOException {
 899         output.write(c);
 900     }
 901 
 902 
 903     /**
 904      * Write a raw string.
 905      */
 906     protected final void write(String s) throws IOException {
 907         output.write(s);
 908     }
 909 
 910 
 911     /**
 912      * Write out an attribute list, escaping values.
 913      *
 914      * The names will have prefixes added to them.
 915      *
 916      * @param atts The attribute list to write.
 917      */
 918     private void writeAttributes (Attributes atts) throws IOException {
 919         int len = atts.getLength();
 920         for (int i = 0; i < len; i++) {
 921             char ch[] = atts.getValue(i).toCharArray();
 922             write(' ');
 923             write(atts.getQName(i));
 924             write("=\"");
 925             writeEsc(ch, 0, ch.length, true);
 926             write('"');
 927         }
 928     }
 929 
 930 
 931     /**
 932      * Write an array of data characters with escaping.
 933      *
 934      * @param ch The array of characters.
 935      * @param start The starting position.
 936      * @param length The number of characters to use.
 937      * @param isAttVal true if this is an attribute value literal.
 938      */
 939     private void writeEsc (char ch[], int start,
 940                              int length, boolean isAttVal)
 941         throws IOException
 942     {
 943         escapeHandler.escape(ch, start, length, isAttVal, output);
 944     }
 945 
 946 
 947 
 948     ////////////////////////////////////////////////////////////////////
 949     // Constants.
 950     ////////////////////////////////////////////////////////////////////
 951 
 952     private final Attributes EMPTY_ATTS = new AttributesImpl();
 953 
 954 
 955 
 956     ////////////////////////////////////////////////////////////////////
 957     // Internal state.
 958     ////////////////////////////////////////////////////////////////////
 959 
 960     private int elementLevel = 0;
 961     private Writer output;
 962     private String encoding;
 963     private boolean writeXmlDecl = true;
 964     /**
 965      * This string will be written right after the xml declaration
 966      * without any escaping. Useful for generating a boiler-plate DOCTYPE decl
 967      * , PIs, and comments.
 968      */
 969     private String header=null;
 970 
 971     private final CharacterEscapeHandler escapeHandler;
 972 
 973     private boolean startTagIsClosed = true;
 974 }
 975 
 976 // end of XMLWriter.java