Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 1,177   Methods: 45
NCLOC: 376   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XMLWriter.java 73.2% 78.9% 62.2% 75.3%
coverage coverage
 1    // XMLWriter.java - serialize an XML document.
 2    // Written by David Megginson, david@megginson.com
 3    // NO WARRANTY! This class is in the public domain.
 4   
 5   
 6    package nu.xom.tests;
 7   
 8    import java.io.IOException;
 9    import java.io.OutputStreamWriter;
 10    import java.io.Writer;
 11    import java.util.Enumeration;
 12    import java.util.Hashtable;
 13   
 14    import org.xml.sax.Attributes;
 15    import org.xml.sax.SAXException;
 16    import org.xml.sax.ext.LexicalHandler;
 17    import org.xml.sax.helpers.AttributesImpl;
 18    import org.xml.sax.helpers.NamespaceSupport;
 19    import org.xml.sax.helpers.XMLFilterImpl;
 20   
 21   
 22    /**
 23    * <p>
 24    * The original version of this class was written and placed in the
 25    * public domain by David Megginson. Elliotte Rusty Harold added
 26    * <code>LexicalHandler</code> support. It is included here purely
 27    * for help with testing the <code>SAXConverter</code> class. It is
 28    * not part of the XOM API; nor is it used internally by XOM anywhere
 29    * except in the <code>SAXConverter</code> tests.
 30    * </p>
 31    *
 32    * <p>
 33    * This class does not properly preserve additional namespace
 34    * declarations in non-root elements. If you encounter that, the
 35    * bug is here, not in <code>SAXConverter</code>.
 36    * </p>
 37    *
 38    * @author David Megginson, Elliotte Rusty Harold
 39    * @version 1.0
 40    */
 41    class XMLWriter extends XMLFilterImpl implements LexicalHandler {
 42   
 43    ///////////////////////////////////////////////////////////////////
 44    // Constructors.
 45    ///////////////////////////////////////////////////////////////////
 46   
 47   
 48    /**
 49    * <p>
 50    * Create a new XML writer.
 51    * </p>
 52    *
 53    * <p>Write to standard output.</p>
 54    */
 55  1 public XMLWriter() {
 56  1 init(null);
 57    }
 58   
 59   
 60    /**
 61    * <p>
 62    * Create a new XML writer.
 63    * </p>
 64    *
 65    * <p>Write to the writer provided.</p>
 66    *
 67    * @param writer the output destination, or null to use standard
 68    * output
 69    */
 70  15 public XMLWriter (Writer writer) {
 71  15 init(writer);
 72    }
 73   
 74    /**
 75    * <p>
 76    * Internal initialization method.
 77    * </p>
 78    *
 79    * <p>All of the public constructors invoke this method.
 80    *
 81    * @param writer the output destination, or null to use
 82    * standard output.
 83    */
 84  16 private void init (Writer writer) {
 85  16 setOutput(writer);
 86  16 nsSupport = new NamespaceSupport();
 87  16 prefixTable = new Hashtable();
 88  16 forcedDeclTable = new Hashtable();
 89  16 doneDeclTable = new Hashtable();
 90    }
 91   
 92   
 93    ///////////////////////////////////////////////////////////////////
 94    // Public methods.
 95    ///////////////////////////////////////////////////////////////////
 96   
 97   
 98    /**
 99    * <p>
 100    * Reset the writer.
 101    * </p>
 102    *
 103    * <p>This method is especially useful if the writer throws an
 104    * exception before it is finished, and you want to reuse the
 105    * writer for a new document. It is usually a good idea to
 106    * invoke {@link #flush flush} before resetting the writer,
 107    * to make sure that no output is lost.</p>
 108    *
 109    * <p>This method is invoked automatically by the
 110    * {@link #startDocument startDocument} method before writing
 111    * a new document.</p>
 112    *
 113    * <p><strong>Note:</strong> this method will <em>not</em>
 114    * clear the prefix or URI information in the writer or
 115    * the selected output writer.</p>
 116    *
 117    * @see #flush
 118    */
 119  15 public void reset() {
 120  15 elementLevel = 0;
 121  15 prefixCounter = 0;
 122  15 nsSupport.reset();
 123    }
 124   
 125   
 126    /**
 127    * <p>
 128    * Flush the output.
 129    * </p>
 130    *
 131    * <p>This method flushes the output stream. It is especially useful
 132    * when you need to make certain that the entire document has
 133    * been written to output but do not want to close the output
 134    * stream.</p>
 135    *
 136    * <p>This method is invoked automatically by the
 137    * {@link #endDocument endDocument} method after writing a
 138    * document.</p>
 139    *
 140    * @throws IOException
 141    *
 142    * @see #reset
 143    *
 144    */
 145  15 public void flush() throws IOException {
 146  15 output.flush();
 147    }
 148   
 149   
 150    /**
 151    * <p>
 152    * Set a new output destination for the document.
 153    * </p>
 154    *
 155    * @param writer the output destination, or null to use
 156    * standard output
 157    *
 158    * @return the current output writer
 159    *
 160    * @see #flush
 161    */
 162  16 public void setOutput (Writer writer) {
 163  16 if (writer == null) {
 164  1 output = new OutputStreamWriter(System.out);
 165    }
 166    else {
 167  15 output = writer;
 168    }
 169    }
 170   
 171   
 172    /**
 173    * <p>
 174    * Specify a preferred prefix for a namespace URI.
 175    * </p>
 176    *
 177    * <p>Note that this method does not actually force the namespace
 178    * to be declared; to do that, use the {@link
 179    * #forceNSDecl(java.lang.String) forceNSDecl} method as well.</p>
 180    *
 181    * @param uri the namespace URI
 182    * @param prefix the preferred prefix, or "" to select
 183    * the default namespace
 184    *
 185    * @see #getPrefix
 186    * @see #forceNSDecl(java.lang.String)
 187    * @see #forceNSDecl(java.lang.String,java.lang.String)
 188    */
 189  9 public void setPrefix (String uri, String prefix) {
 190  9 prefixTable.put(uri, prefix);
 191    }
 192   
 193   
 194    /**
 195    * <p>
 196    * Get the current or preferred prefix for a namespace URI.
 197    * </p>
 198    *
 199    * @param uri the namespace URI
 200    *
 201    * @return the preferred prefix, or "" for the default namespace
 202    *
 203    * @see #setPrefix
 204    */
 205  0 public String getPrefix (String uri) {
 206  0 return (String)prefixTable.get(uri);
 207    }
 208   
 209  9 public void startPrefixMapping(String prefix, String uri) {
 210  9 this.forceNSDecl(uri, prefix);
 211    }
 212   
 213    /**
 214    * <p>
 215    * Force a namespace to be declared on the root element.
 216    * </p>
 217    *
 218    * <p>By default, the XMLWriter will declare only the namespaces
 219    * needed for an element; as a result, a namespace may be
 220    * declared many places in a document if it is not used on the
 221    * root element.</p>
 222    *
 223    * <p>This method forces a namespace to be declared on the root
 224    * element even if it is not used there, and reduces the number
 225    * of xmlns attributes in the document.</p>
 226    *
 227    * @param uri the namespace URI to declare
 228    *
 229    * @see #forceNSDecl(java.lang.String,java.lang.String)
 230    * @see #setPrefix
 231    */
 232  9 public void forceNSDecl (String uri) {
 233  9 forcedDeclTable.put(uri, Boolean.TRUE);
 234    }
 235   
 236   
 237    /**
 238    * <p>
 239    * Force a namespace declaration with a preferred prefix.
 240    * </p>
 241    *
 242    * <p>This is a convenience method that invokes {@link
 243    * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
 244    * forceNSDecl}.</p>
 245    *
 246    * @param uri the namespace URI to declare on the root element
 247    * @param prefix the preferred prefix for the namespace, or ""
 248    * for the default namespace
 249    *
 250    * @see #setPrefix
 251    * @see #forceNSDecl(java.lang.String)
 252    */
 253  9 public void forceNSDecl (String uri, String prefix) {
 254  9 setPrefix(uri, prefix);
 255  9 forceNSDecl(uri);
 256    }
 257   
 258   
 259    ///////////////////////////////////////////////////////////////////
 260    // Methods from org.xml.sax.ContentHandler.
 261    ///////////////////////////////////////////////////////////////////
 262   
 263   
 264    /**
 265    * <p>
 266    * Write the XML declaration at the beginning of the document.
 267    * </p>
 268    *
 269    * <p>
 270    * Pass the event on down the filter chain for further processing.
 271    * </p>
 272    *
 273    * @throws org.xml.sax.SAXException if there is an error
 274    * writing the XML declaration, or if a handler further
 275    * down the filter chain raises an exception
 276    *
 277    * @see org.xml.sax.ContentHandler#startDocument
 278    */
 279  15 public void startDocument() throws SAXException {
 280  15 reset();
 281  15 write("<?xml version=\"1.0\" standalone=\"yes\"?>\n\n");
 282  15 super.startDocument();
 283    }
 284   
 285   
 286    /**
 287    * <p>
 288    * Write a newline at the end of the document.
 289    * </p>
 290    *
 291    * <p>
 292    * Pass the event on down the filter chain for further processing.
 293    * </p>
 294    *
 295    * @throws org.xml.sax.SAXException if there is an error
 296    * writing the newline, or if a handler further down
 297    * the filter chain raises an exception
 298    *
 299    * @see org.xml.sax.ContentHandler#endDocument
 300    */
 301  15 public void endDocument() throws SAXException {
 302  15 write('\n');
 303  15 super.endDocument();
 304  15 try {
 305  15 flush();
 306    } catch (IOException ex) {
 307  0 throw new SAXException(ex);
 308    }
 309    }
 310   
 311   
 312    /**
 313    * <p>
 314    * Write a start-tag.
 315    * </p>
 316    *
 317    * <p>
 318    * Pass the event on down the filter chain for further processing.
 319    * </p>
 320    *
 321    * @param uri the namespace URI, or the empty string if none
 322    * is available
 323    * @param localName the element's local (unprefixed) name (required)
 324    * @param qualifiedName the element's qualified (prefixed) name,
 325    * or the empty string is none is available. This method
 326    * will use the qualified name as a template for generating
 327    * a prefix if necessary, but it is not guaranteed to use
 328    * the same qualified name.
 329    * @param atts the element's attribute list (must not be null)
 330    *
 331    * @throws org.xml.sax.SAXException if there is an error
 332    * writing the start-tag, or if a handler further down
 333    * the filter chain raises an exception
 334    *
 335    * @see org.xml.sax.ContentHandler#startElement
 336    */
 337  610 public void startElement (String uri, String localName,
 338    String qualifiedName, Attributes atts) throws SAXException {
 339  610 elementLevel++;
 340  610 nsSupport.pushContext();
 341  610 write('<');
 342  610 writeName(uri, localName, qualifiedName, true);
 343  610 writeAttributes(atts);
 344  610 if (elementLevel == 1) {
 345  15 forceNSDecls();
 346    }
 347  610 writeNSDecls();
 348  610 write('>');
 349  610 super.startElement(uri, localName, qualifiedName, atts);
 350    }
 351   
 352   
 353    /**
 354    * <p>
 355    * Write an end-tag.
 356    * </p>
 357    *
 358    * <p>
 359    * Pass the event on down the filter chain for further processing.
 360    * </p>
 361    *
 362    * @param uri the namespace URI, or the empty string if none
 363    * is available
 364    * @param localName the element's local (unprefixed) name (required)
 365    * @param qualifiedName the element's qualified (prefixed) name, or the
 366    * empty string is none is available. This method will
 367    * use the qName as a template for generating a prefix
 368    * if necessary, but it is not guaranteed to use the
 369    * same qualified name.
 370    *
 371    * @throws org.xml.sax.SAXException if there is an error
 372    * writing the end-tag, or if a handler further down
 373    * the filter chain raises an exception
 374    *
 375    * @see org.xml.sax.ContentHandler#endElement
 376    */
 377  610 public void endElement (String uri, String localName, String qualifiedName)
 378    throws SAXException {
 379  610 write("</");
 380  610 writeName(uri, localName, qualifiedName, true);
 381  610 write('>');
 382  610 if (elementLevel == 1) {
 383  15 write('\n');
 384    }
 385  610 super.endElement(uri, localName, qualifiedName);
 386  610 nsSupport.popContext();
 387  610 elementLevel--;
 388    }
 389   
 390   
 391    /**
 392    * <p>
 393    * Write character data.
 394    * </p>
 395    *
 396    * <p>
 397    * Pass the event on down the filter chain for further processing.
 398    * </p>
 399    *
 400    * @param ch the array of characters to write
 401    * @param start the starting position in the array
 402    * @param length the number of characters to write
 403    *
 404    * @throws org.xml.sax.SAXException if there is an error
 405    * writing the characters, or if a handler further down
 406    * the filter chain raises an exception
 407    *
 408    * @see org.xml.sax.ContentHandler#characters
 409    */
 410  852 public void characters (char[] ch, int start, int length)
 411    throws SAXException {
 412  852 writeEsc(ch, start, length, false);
 413  852 super.characters(ch, start, length);
 414    }
 415   
 416   
 417    /**
 418    * <p>
 419    * Write ignorable whitespace.
 420    * </p>
 421    *
 422    * <p>
 423    * Pass the event on down the filter chain for further processing.
 424    * </p>
 425    *
 426    * @param ch the array of characters to write
 427    * @param start the starting position in the array
 428    * @param length the number of characters to write
 429    *
 430    * @throws org.xml.sax.SAXException if there is an error
 431    * writing the whitespace, or if a handler further down
 432    * the filter chain raises an exception
 433    *
 434    * @see org.xml.sax.ContentHandler#ignorableWhitespace
 435    */
 436  0 public void ignorableWhitespace (char[] ch, int start, int length)
 437    throws SAXException {
 438  0 writeEsc(ch, start, length, false);
 439  0 super.ignorableWhitespace(ch, start, length);
 440    }
 441   
 442   
 443   
 444    /**
 445    * <p>
 446    * Write a processing instruction.
 447    * </p>
 448    *
 449    * <p>
 450    * Pass the event on down the filter chain for further processing.
 451    * </p>
 452    *
 453    * @param target the processing instruction target
 454    * @param data the processing instruction data
 455    *
 456    * @throws org.xml.sax.SAXException if there is an error
 457    * writing the PI, or if a handler further down
 458    * the filter chain raises an exception
 459    *
 460    * @see org.xml.sax.ContentHandler#processingInstruction
 461    */
 462  1 public void processingInstruction (String target, String data)
 463    throws SAXException {
 464  1 write("<?");
 465  1 write(target);
 466  1 write(' ');
 467  1 write(data);
 468  1 write("?>");
 469  1 if (elementLevel < 1) {
 470  1 write('\n');
 471    }
 472  1 super.processingInstruction(target, data);
 473    }
 474   
 475   
 476   
 477    ///////////////////////////////////////////////////////////////////
 478    // Additional markup.
 479    ///////////////////////////////////////////////////////////////////
 480   
 481    /**
 482    * <p>
 483    * Write an empty element.
 484    * </p>
 485    *
 486    * <p>
 487    * This method writes an empty-element tag rather than a start-tag
 488    * followed by an end-tag. Both a {@link #startElement
 489    * startElement} and an {@link #endElement endElement} event will
 490    * be passed on down the filter chain.
 491    * </p>
 492    *
 493    * @param uri the element's namespace URI, or the empty string
 494    * if the element has no namespace or if namespace
 495    * processing is not being performed
 496    * @param localName the element's local name (without prefix). This
 497    * parameter must be provided.
 498    * @param qualifiedName the element's qualified name (with prefix), or
 499    * the empty string if none is available. This parameter
 500    * is strictly advisory: the writer may or may not use
 501    * the prefix attached.
 502    * @param atts the element's attribute list
 503    *
 504    * @throws org.xml.sax.SAXException if there is an error
 505    * writing the empty tag, or if a handler further down
 506    * the filter chain raises an exception
 507    *
 508    * @see #startElement
 509    * @see #endElement
 510    */
 511  0 public void emptyElement (String uri, String localName,
 512    String qualifiedName, Attributes atts) throws SAXException {
 513  0 nsSupport.pushContext();
 514  0 write('<');
 515  0 writeName(uri, localName, qualifiedName, true);
 516  0 writeAttributes(atts);
 517  0 if (elementLevel == 1) {
 518  0 forceNSDecls();
 519    }
 520  0 writeNSDecls();
 521  0 write("/>");
 522  0 super.startElement(uri, localName, qualifiedName, atts);
 523  0 super.endElement(uri, localName, qualifiedName);
 524    }
 525   
 526   
 527   
 528    ///////////////////////////////////////////////////////////////////
 529    // Convenience methods.
 530    ///////////////////////////////////////////////////////////////////
 531   
 532   
 533   
 534    /**
 535    * <p>
 536    * Start a new element without a qualified name or attributes.
 537    * </p>
 538    *
 539    * <p>This method will provide a default empty attribute
 540    * list and an empty string for the qualified name.
 541    * It invokes {@link
 542    * #startElement(String, String, String, Attributes)}
 543    * directly.</p>
 544    *
 545    * @param uri the element's namespace URI
 546    * @param localName the element's local name
 547    *
 548    * @throws org.xml.sax.SAXException if there is an error
 549    * writing the start-tag, or if a handler further down
 550    * the filter chain raises an exception
 551    *
 552    * @see #startElement(String, String, String, Attributes)
 553    */
 554  0 public void startElement (String uri, String localName)
 555    throws SAXException {
 556  0 startElement(uri, localName, "", EMPTY_ATTS);
 557    }
 558   
 559   
 560    /**
 561    * <p>
 562    * Start a new element without a qualified name,
 563    * attributes or a namespace URI.</p>
 564    *
 565    * <p>This method will provide an empty string for the
 566    * namespace URI, and empty string for the qualified name,
 567    * and a default empty attribute list. It invokes
 568    * #startElement(String, String, String, Attributes)}
 569    * directly.</p>
 570    *
 571    * @param localName the element's local name
 572    *
 573    * @throws org.xml.sax.SAXException if there is an error
 574    * writing the start-tag, or if a handler further down
 575    * the filter chain raises an exception
 576    *
 577    * @see #startElement(String, String, String, Attributes)
 578    */
 579  0 public void startElement (String localName) throws SAXException {
 580  0 startElement("", localName, "", EMPTY_ATTS);
 581    }
 582   
 583   
 584    /**
 585    * <p>
 586    * End an element without a qualfied name.
 587    * </p>
 588    *
 589    * <p>This method will supply an empty string for the qName.
 590    * It invokes {@link #endElement(String, String, String)}
 591    * directly.</p>
 592    *
 593    * @param uri the element's namespace URI
 594    * @param localName the element's local name
 595    *
 596    * @throws org.xml.sax.SAXException if there is an error
 597    * writing the end-tag, or if a handler further down
 598    * the filter chain raises an exception
 599    *
 600    * @see #endElement(String, String, String)
 601    */
 602  0 public void endElement(String uri, String localName)
 603    throws SAXException {
 604  0 endElement(uri, localName, "");
 605    }
 606   
 607   
 608    /**
 609    * <p>
 610    * End an element without a namespace URI or qualfiied name.
 611    * </p>
 612    *
 613    * <p>This method will supply an empty string for the qName
 614    * and an empty string for the namespace URI.
 615    * It invokes {@link #endElement(String, String, String)}
 616    * directly.</p>
 617    *
 618    * @param localName the element's local name`
 619    *
 620    * @throws org.xml.sax.SAXException if there is an error
 621    * writing the end-tag, or if a handler further down
 622    * the filter chain raises an exception
 623    *
 624    * @see #endElement(String, String, String)
 625    */
 626  0 public void endElement(String localName) throws SAXException {
 627  0 endElement("", localName, "");
 628    }
 629   
 630   
 631    /**
 632    * <p>
 633    * Add an empty element without a qualified name or attributes.
 634    * </p>
 635    *
 636    * <p>This method will supply an empty string for the qualified name
 637    * and an empty attribute list. It invokes
 638    * {@link #emptyElement(String, String, String, Attributes)}
 639    * directly.</p>
 640    *
 641    * @param uri the element's namespace URI
 642    * @param localName the element's local name
 643    *
 644    * @throws org.xml.sax.SAXException if there is an error
 645    * writing the empty tag, or if a handler further down
 646    * the filter chain raises an exception
 647    *
 648    * @see #emptyElement(String, String, String, Attributes)
 649    */
 650  0 public void emptyElement (String uri, String localName)
 651    throws SAXException {
 652  0 emptyElement(uri, localName, "", EMPTY_ATTS);
 653    }
 654   
 655   
 656    /**
 657    * <p>
 658    * Add an empty element without a namespace URI, qualified
 659    * name or attributes.
 660    * </p>
 661    *
 662    * <p>This method will supply an empty string for the qualified
 663    * name, and empty string for the namespace URI, and an empty
 664    * attribute list. It invokes
 665    * {@link #emptyElement(String, String, String, Attributes)}
 666    * directly.</p>
 667    *
 668    * @param localName the element's local name
 669    *
 670    * @throws org.xml.sax.SAXException if there is an error
 671    * writing the empty tag, or if a handler further down
 672    * the filter chain raises an exception
 673    *
 674    * @see #emptyElement(String, String, String, Attributes)
 675    */
 676  0 public void emptyElement (String localName) throws SAXException {
 677  0 emptyElement("", localName, "", EMPTY_ATTS);
 678    }
 679   
 680   
 681    /**
 682    * <p>
 683    * Write an element with character data content.
 684    * </p>
 685    *
 686    * <p>This is a convenience method to write a complete element
 687    * with character data content, including the start-tag
 688    * and end-tag.</p>
 689    *
 690    * <p>This method invokes
 691    * {@link #startElement(String, String, String, Attributes)},
 692    * followed by
 693    * {@link #characters(String)}, followed by
 694    * {@link #endElement(String, String, String)}.</p>
 695    *
 696    * @param uri the element's namespace URI
 697    * @param localName the element's local name
 698    * @param qualifiedName the element's default qualified name
 699    * @param atts the element's attributes
 700    * @param content the character data content
 701    *
 702    * @throws org.xml.sax.SAXException if there is an error
 703    * writing the empty tag, or if a handler further down
 704    * the filter chain raises an exception
 705    *
 706    * @see #startElement(String, String, String, Attributes)
 707    * @see #characters(String)
 708    * @see #endElement(String, String, String)
 709    */
 710  0 public void dataElement (String uri, String localName,
 711    String qualifiedName, Attributes atts, String content)
 712    throws SAXException {
 713  0 startElement(uri, localName, qualifiedName, atts);
 714  0 characters(content);
 715  0 endElement(uri, localName, qualifiedName);
 716    }
 717   
 718   
 719    /**
 720    * <p>
 721    * Write an element with character data content but no attributes.
 722    * </p>
 723    *
 724    * <p>This is a convenience method to write a complete element
 725    * with character data content, including the start-tag
 726    * and end-tag. This method provides an empty string
 727    * for the qualified name and an empty attribute list.</p>
 728    *
 729    * <p>This method invokes
 730    * {@link #startElement(String, String, String, Attributes)},
 731    * followed by
 732    * {@link #characters(String)}, followed by
 733    * {@link #endElement(String, String, String)}.</p>
 734    *
 735    * @param uri the element's namespace URI
 736    * @param localName the element's local name
 737    * @param content the character data content
 738    *
 739    * @throws org.xml.sax.SAXException if there is an error
 740    * writing the empty tag, or if a handler further down
 741    * the filter chain raises an exception
 742    *
 743    * @see #startElement(String, String, String, Attributes)
 744    * @see #characters(String)
 745    * @see #endElement(String, String, String)
 746    */
 747  0 public void dataElement(String uri, String localName, String content)
 748    throws SAXException {
 749  0 dataElement(uri, localName, "", EMPTY_ATTS, content);
 750    }
 751   
 752   
 753    /**
 754    * <p>
 755    * Write an element with character data content but no attributes
 756    * or namespace URI.
 757    * </p>
 758    *
 759    * <p>This is a convenience method to write a complete element
 760    * with character data content, including the start-tag
 761    * and end-tag. The method provides an empty string for the
 762    * namespace URI, and empty string for the qualified name,
 763    * and an empty attribute list.</p>
 764    *
 765    * <p>This method invokes
 766    * {@link #startElement(String, String, String, Attributes)},
 767    * followed by
 768    * {@link #characters(String)}, followed by
 769    * {@link #endElement(String, String, String)}.</p>
 770    *
 771    * @param localName the element's local name
 772    * @param content the character data content
 773    *
 774    * @throws org.xml.sax.SAXException if there is an error
 775    * writing the empty tag, or if a handler further down
 776    * the filter chain raises an exception
 777    *
 778    * @see #startElement(String, String, String, Attributes)
 779    * @see #characters(String)
 780    * @see #endElement(String, String, String)
 781    */
 782  0 public void dataElement (String localName, String content)
 783    throws SAXException {
 784  0 dataElement("", localName, "", EMPTY_ATTS, content);
 785    }
 786   
 787    /**
 788    * <p>
 789    * Write a string of character data, with XML escaping.
 790    * </p>
 791    *
 792    * <p>This is a convenience method that takes an XML
 793    * String, converts it to a character array, then invokes
 794    * {@link #characters(char[], int, int)}.</p>
 795    *
 796    * @param data the character data
 797    * @throws org.xml.sax.SAXException if there is an error
 798    * writing the string, or if a handler further down
 799    * the filter chain raises an exception
 800    * @see #characters(char[], int, int)
 801    */
 802  0 public void characters(String data) throws SAXException {
 803  0 char[] ch = data.toCharArray();
 804  0 characters(ch, 0, ch.length);
 805    }
 806   
 807   
 808   
 809    ///////////////////////////////////////////////////////////////////
 810    // Internal methods.
 811    ///////////////////////////////////////////////////////////////////
 812   
 813   
 814    /**
 815    * <p>
 816    * Force all namespaces to be declared.
 817    * </p>
 818    *
 819    * <p>
 820    * This method is used on the root element to ensure that
 821    * the predeclared namespaces all appear.
 822    * </p>
 823    */
 824  15 private void forceNSDecls() {
 825  15 Enumeration prefixes = forcedDeclTable.keys();
 826  15 while (prefixes.hasMoreElements()) {
 827  7 String prefix = (String)prefixes.nextElement();
 828  7 doPrefix(prefix, null, true);
 829    }
 830    }
 831   
 832   
 833    /**
 834    * <p>
 835    * Determine the prefix for an element or attribute name.
 836    * </p>
 837    *
 838    * TODO: this method probably needs some cleanup.
 839    *
 840    * @param uri the namespace URI
 841    * @param qName the qualified name (optional); this will be used
 842    * to indicate the preferred prefix if none is currently
 843    * bound.
 844    * @param isElement true if this is an element name, false
 845    * if it is an attribute name (which cannot use the
 846    * default namespace).
 847    */
 848  1954 private String doPrefix (String uri, String qName, boolean isElement) {
 849  1954 String defaultNS = nsSupport.getURI("");
 850  1954 if ("".equals(uri)) {
 851  742 if (isElement && defaultNS != null)
 852  0 nsSupport.declarePrefix("", "");
 853  742 return null;
 854    }
 855  1212 String prefix;
 856  1212 if (isElement && defaultNS != null && uri.equals(defaultNS)) {
 857  1192 prefix = "";
 858    } else {
 859  20 prefix = nsSupport.getPrefix(uri);
 860    }
 861  1212 if (prefix != null) {
 862  1203 return prefix;
 863    }
 864  9 prefix = (String) doneDeclTable.get(uri);
 865  9 if (prefix != null &&
 866    ((!isElement || defaultNS != null) &&
 867    "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
 868  0 prefix = null;
 869    }
 870  9 if (prefix == null) {
 871  9 prefix = (String) prefixTable.get(uri);
 872  9 if (prefix != null &&
 873    ((!isElement || defaultNS != null) &&
 874    "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
 875  0 prefix = null;
 876    }
 877    }
 878  9 if (prefix == null && qName != null && !"".equals(qName)) {
 879  0 int i = qName.indexOf(':');
 880  0 if (i == -1) {
 881  0 if (isElement && defaultNS == null) {
 882  0 prefix = "";
 883    }
 884    } else {
 885  0 prefix = qName.substring(0, i);
 886    }
 887    }
 888  9 for (;
 889  9 prefix == null || nsSupport.getURI(prefix) != null;
 890    prefix = "__NS" + ++prefixCounter)
 891    ;
 892  9 nsSupport.declarePrefix(prefix, uri);
 893  9 doneDeclTable.put(uri, prefix);
 894  9 return prefix;
 895    }
 896   
 897   
 898    /**
 899    * <p>
 900    * Write a raw character.
 901    * </p>
 902    *
 903    * @param c the character to write
 904    *
 905    * @throws org.xml.sax.SAXException if there is an error writing
 906    * the character, this method will throw an IOException
 907    * wrapped in a SAXException
 908    */
 909  28980 private void write(char c) throws SAXException {
 910  28980 try {
 911  28980 output.write(c);
 912    }
 913    catch (IOException ex) {
 914  0 throw new SAXException(ex);
 915    }
 916    }
 917   
 918   
 919    /**
 920    * <p>
 921    * Write a raw string.
 922    * </p>
 923    *
 924    * @param s
 925    *
 926    * @throws org.xml.sax.SAXException if there is an error writing
 927    * the string, this method will throw an IOException
 928    * wrapped in a SAXException
 929    */
 930  3399 private void write (String s) throws SAXException {
 931  3399 try {
 932  3399 output.write(s);
 933    }
 934    catch (IOException e) {
 935  0 throw new SAXException(e);
 936    }
 937    }
 938   
 939   
 940    /**
 941    * <p>
 942    * Write out an attribute list, escaping values.
 943    *</p>
 944    *
 945    * <p>
 946    * The names will have prefixes added to them.
 947    * </p>
 948    *
 949    * @param atts the attribute list to write
 950    *
 951    * @throws org.xml.SAXException if there is an error writing
 952    * the attribute list, this method will throw an
 953    * IOException wrapped in a SAXException
 954    */
 955  610 private void writeAttributes (Attributes atts)
 956    throws SAXException {
 957  610 int len = atts.getLength();
 958  610 for (int i = 0; i < len; i++) {
 959  727 char[] ch = atts.getValue(i).toCharArray();
 960  727 write(' ');
 961  727 writeName(atts.getURI(i), atts.getLocalName(i),
 962    atts.getQName(i), false);
 963  727 write("=\"");
 964  727 writeEsc(ch, 0, ch.length, true);
 965  727 write('"');
 966    }
 967    }
 968   
 969   
 970    /**
 971    * <p>
 972    * Write an array of data characters with escaping.
 973    * </p>
 974    *
 975    * @param ch the array of characters
 976    * @param start the starting position
 977    * @param length the number of characters to use
 978    * @param isAttVal true if this is an attribute value literal
 979    *
 980    * @throws org.xml.SAXException if there is an error writing
 981    * the characters, this method will throw an
 982    * IOException wrapped in a SAXException
 983    */
 984  1588 private void writeEsc (char[] ch, int start, int length, boolean isAttVal)
 985    throws SAXException {
 986  1588 for (int i = start; i < start + length; i++) {
 987  25640 switch (ch[i]) {
 988  5 case '&':
 989  5 write("&amp;");
 990  5 break;
 991  2 case '<':
 992  2 write("&lt;");
 993  2 break;
 994  2 case '>':
 995  2 write("&gt;");
 996  2 break;
 997  10 case '\"':
 998  10 if (isAttVal) {
 999  0 write("&quot;");
 1000    }
 1001    else {
 1002  10 write('\"');
 1003    }
 1004  10 break;
 1005  25621 default:
 1006  25621 if (ch[i] > '\u007f') {
 1007  5 write("&#");
 1008  5 write(Integer.toString(ch[i]));
 1009  5 write(';');
 1010    }
 1011    else {
 1012  25616 write(ch[i]);
 1013    }
 1014    }
 1015    }
 1016    }
 1017   
 1018    /**
 1019    * <p>
 1020    * Write an array of data characters without escaping.
 1021    * </p>
 1022    *
 1023    * @param ch the array of characters
 1024    * @param start the starting position
 1025    * @param length the number of characters to use
 1026    *
 1027    * @throws org.xml.SAXException if there is an error writing
 1028    * the characters, this method will throw an
 1029    * IOException wrapped in a SAXException.
 1030    */
 1031  12 private void write(char[] ch, int start, int length)
 1032    throws SAXException {
 1033   
 1034  12 try {
 1035  12 output.write(ch, start, length);
 1036    }
 1037    catch (IOException e) {
 1038  0 throw new SAXException(e);
 1039    }
 1040   
 1041    }
 1042   
 1043   
 1044    /**
 1045    * <p>
 1046    * Write out the list of namespace declarations.
 1047    * </p>
 1048    *
 1049    * @throws org.xml.sax.SAXException This method will throw
 1050    * an IOException wrapped in a SAXException if
 1051    * there is an error writing the namespace
 1052    * declarations
 1053    */
 1054  610 private void writeNSDecls() throws SAXException {
 1055  610 Enumeration prefixes = nsSupport.getDeclaredPrefixes();
 1056  610 while (prefixes.hasMoreElements()) {
 1057  9 String prefix = (String) prefixes.nextElement();
 1058  9 String uri = nsSupport.getURI(prefix);
 1059  9 if (uri == null) {
 1060  0 uri = "";
 1061    }
 1062  9 char[] ch = uri.toCharArray();
 1063  9 write(' ');
 1064  9 if ("".equals(prefix)) {
 1065  2 write("xmlns=\"");
 1066    }
 1067    else {
 1068  7 write("xmlns:");
 1069  7 write(prefix);
 1070  7 write("=\"");
 1071    }
 1072  9 writeEsc(ch, 0, ch.length, true);
 1073  9 write('\"');
 1074    }
 1075    }
 1076   
 1077   
 1078    /**
 1079    * <p>
 1080    * Write an element or attribute name.
 1081    * </p>
 1082    *
 1083    * @param uri the namespace URI
 1084    * @param localName the local name
 1085    * @param qualifiedName the prefixed name, if available,
 1086    * or the empty string.
 1087    * @param isElement true if this is an element name, false if it
 1088    * is an attribute name
 1089    *
 1090    * @throws org.xml.sax.SAXException this method will throw an
 1091    * IOException wrapped in a SAXException if there is
 1092    * an error writing the name
 1093    */
 1094  1947 private void writeName (String uri, String localName,
 1095    String qualifiedName, boolean isElement)
 1096    throws SAXException {
 1097  1947 String prefix = doPrefix(uri, qualifiedName, isElement);
 1098  1947 if (prefix != null && !"".equals(prefix)) {
 1099  13 write(prefix);
 1100  13 write(':');
 1101    }
 1102  1947 write(localName);
 1103    }
 1104   
 1105   
 1106   
 1107    ////////////////////////////////////////////////////////////////////
 1108    // Constants.
 1109    ////////////////////////////////////////////////////////////////////
 1110   
 1111    private final Attributes EMPTY_ATTS = new AttributesImpl();
 1112   
 1113   
 1114    ////////////////////////////////////////////////////////////////////
 1115    // Internal state.
 1116    ////////////////////////////////////////////////////////////////////
 1117   
 1118    private Hashtable prefixTable;
 1119    private Hashtable forcedDeclTable;
 1120    private Hashtable doneDeclTable;
 1121    private int elementLevel = 0;
 1122    private Writer output;
 1123    private NamespaceSupport nsSupport;
 1124    private int prefixCounter = 0;
 1125   
 1126   
 1127    ///////////////////////////////////////////////////////////////////
 1128    // LexicalHandler methods.
 1129    ///////////////////////////////////////////////////////////////////
 1130   
 1131   
 1132  0 public void endCDATA() {}
 1133   
 1134  3 public void endDTD() throws SAXException {
 1135  3 write(">");
 1136    }
 1137   
 1138  0 public void startCDATA() {}
 1139   
 1140  12 public void comment(char[] ch, int start, int length)
 1141    throws SAXException {
 1142  12 write("<!--");
 1143  12 write(ch, start, length);
 1144  12 write("-->");
 1145  12 if (elementLevel < 1) {
 1146  2 write('\n');
 1147    }
 1148    }
 1149   
 1150  0 public void endEntity(String name) {}
 1151  0 public void startEntity(String name) {}
 1152   
 1153  3 public void startDTD(String name, String publicID, String systemID)
 1154    throws SAXException {
 1155  3 write("<!DOCTYPE ");
 1156  3 write(name);
 1157  3 if (systemID != null) {
 1158  2 if (publicID != null) {
 1159  1 write(" PUBLIC \"");
 1160  1 write(publicID);
 1161  1 write("\" \"");
 1162  1 write(systemID);
 1163  1 write("\"");
 1164    }
 1165    else {
 1166  1 write(" SYSTEM \"");
 1167  1 write(systemID);
 1168  1 write("\"");
 1169    }
 1170    }
 1171   
 1172    }
 1173   
 1174    }
 1175   
 1176    // end of XMLWriter.java
 1177