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