1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: ToUnknownStream.java 471981 2006-11-07 04:28:00Z minchau $ 20 */ 21 package org.apache.xml.serializer; 22 23 import java.io.IOException; 24 import java.io.OutputStream; 25 import java.io.Writer; 26 import java.util.Properties; 27 import java.util.Vector; 28 29 import javax.xml.transform.SourceLocator; 30 import javax.xml.transform.Transformer; 31 32 import org.w3c.dom.Node; 33 import org.xml.sax.Attributes; 34 import org.xml.sax.ContentHandler; 35 import org.xml.sax.Locator; 36 import org.xml.sax.SAXException; 37 38 39 /** 40 *This class wraps another SerializationHandler. The wrapped object will either 41 * handler XML or HTML, which is not known until a little later when the first XML 42 * tag is seen. If the first tag is <html> then the wrapped object is an HTML 43 * handler, otherwise it is an XML handler. 44 * 45 * This class effectively caches the first few calls to it then passes them 46 * on to the wrapped handler (once it exists). After that subsequent calls a 47 * simply passed directly to the wrapped handler. 48 * 49 * The user of this class doesn't know if the output is ultimatley XML or HTML. 50 * 51 * This class is not a public API, it is public because it is used within Xalan. 52 * @xsl.usage internal 53 */ 54 public final class ToUnknownStream extends SerializerBase 55 { 56 57 /** 58 * The wrapped handler, initially XML but possibly switched to HTML 59 */ 60 private SerializationHandler m_handler; 61 62 /** 63 * A String with no characters 64 */ 65 private static final String EMPTYSTRING = ""; 66 67 /** 68 * true if the underlying handler (XML or HTML) is fully initialized 69 */ 70 private boolean m_wrapped_handler_not_initialized = false; 71 72 73 /** 74 * the prefix of the very first tag in the document 75 */ 76 private String m_firstElementPrefix; 77 /** 78 * the element name (including any prefix) of the very first tag in the document 79 */ 80 private String m_firstElementName; 81 82 /** 83 * the namespace URI associated with the first element 84 */ 85 private String m_firstElementURI; 86 87 /** 88 * the local name (no prefix) associated with the first element 89 */ 90 private String m_firstElementLocalName = null; 91 92 /** 93 * true if the first tag has been emitted to the wrapped handler 94 */ 95 private boolean m_firstTagNotEmitted = true; 96 97 /** 98 * A collection of namespace URI's (only for first element). 99 * _namespacePrefix has the matching prefix for these URI's 100 */ 101 private Vector m_namespaceURI = null; 102 /** 103 * A collection of namespace Prefix (only for first element) 104 * _namespaceURI has the matching URIs for these prefix' 105 */ 106 private Vector m_namespacePrefix = null; 107 108 /** 109 * true if startDocument() was called before the underlying handler 110 * was initialized 111 */ 112 private boolean m_needToCallStartDocument = false; 113 /** 114 * true if setVersion() was called before the underlying handler 115 * was initialized 116 */ 117 private boolean m_setVersion_called = false; 118 /** 119 * true if setDoctypeSystem() was called before the underlying handler 120 * was initialized 121 */ 122 private boolean m_setDoctypeSystem_called = false; 123 /** 124 * true if setDoctypePublic() was called before the underlying handler 125 * was initialized 126 */ 127 private boolean m_setDoctypePublic_called = false; 128 /** 129 * true if setMediaType() was called before the underlying handler 130 * was initialized 131 */ 132 private boolean m_setMediaType_called = false; 133 134 /** 135 * Default constructor. 136 * Initially this object wraps an XML Stream object, so _handler is never null. 137 * That may change later to an HTML Stream object. 138 */ ToUnknownStream()139 public ToUnknownStream() 140 { 141 m_handler = new ToXMLStream(); 142 } 143 144 /** 145 * @see Serializer#asContentHandler() 146 * @return the wrapped XML or HTML handler 147 */ asContentHandler()148 public ContentHandler asContentHandler() throws IOException 149 { 150 /* don't return the real handler ( m_handler ) because 151 * that would expose the real handler to the outside. 152 * Keep m_handler private so it can be internally swapped 153 * to an HTML handler. 154 */ 155 return this; 156 } 157 158 /** 159 * @see SerializationHandler#close() 160 */ close()161 public void close() 162 { 163 m_handler.close(); 164 } 165 166 /** 167 * @see Serializer#getOutputFormat() 168 * @return the properties of the underlying handler 169 */ getOutputFormat()170 public Properties getOutputFormat() 171 { 172 return m_handler.getOutputFormat(); 173 } 174 175 /** 176 * @see Serializer#getOutputStream() 177 * @return the OutputStream of the underlying XML or HTML handler 178 */ getOutputStream()179 public OutputStream getOutputStream() 180 { 181 return m_handler.getOutputStream(); 182 } 183 184 /** 185 * @see Serializer#getWriter() 186 * @return the Writer of the underlying XML or HTML handler 187 */ getWriter()188 public Writer getWriter() 189 { 190 return m_handler.getWriter(); 191 } 192 193 /** 194 * passes the call on to the underlying HTML or XML handler 195 * @see Serializer#reset() 196 * @return ??? 197 */ reset()198 public boolean reset() 199 { 200 return m_handler.reset(); 201 } 202 203 /** 204 * Converts the DOM node to output 205 * @param node the DOM node to transform to output 206 * @see DOMSerializer#serialize(Node) 207 * 208 */ serialize(Node node)209 public void serialize(Node node) throws IOException 210 { 211 if (m_firstTagNotEmitted) 212 { 213 flush(); 214 } 215 m_handler.serialize(node); 216 } 217 218 /** 219 * @see SerializationHandler#setEscaping(boolean) 220 */ setEscaping(boolean escape)221 public boolean setEscaping(boolean escape) throws SAXException 222 { 223 return m_handler.setEscaping(escape); 224 } 225 226 /** 227 * Set the properties of the handler 228 * @param format the output properties to set 229 * @see Serializer#setOutputFormat(Properties) 230 */ setOutputFormat(Properties format)231 public void setOutputFormat(Properties format) 232 { 233 m_handler.setOutputFormat(format); 234 } 235 236 /** 237 * Sets the output stream to write to 238 * @param output the OutputStream to write to 239 * @see Serializer#setOutputStream(OutputStream) 240 */ setOutputStream(OutputStream output)241 public void setOutputStream(OutputStream output) 242 { 243 m_handler.setOutputStream(output); 244 } 245 246 /** 247 * Sets the writer to write to 248 * @param writer the writer to write to 249 * @see Serializer#setWriter(Writer) 250 */ setWriter(Writer writer)251 public void setWriter(Writer writer) 252 { 253 m_handler.setWriter(writer); 254 } 255 256 /** 257 * Adds an attribute to the currenly open tag 258 * @param uri the URI of a namespace 259 * @param localName the attribute name, without prefix 260 * @param rawName the attribute name, with prefix (if any) 261 * @param type the type of the attribute, typically "CDATA" 262 * @param value the value of the parameter 263 * @param XSLAttribute true if this attribute is coming from an xsl:attribute element 264 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) 265 */ addAttribute( String uri, String localName, String rawName, String type, String value, boolean XSLAttribute)266 public void addAttribute( 267 String uri, 268 String localName, 269 String rawName, 270 String type, 271 String value, 272 boolean XSLAttribute) 273 throws SAXException 274 { 275 if (m_firstTagNotEmitted) 276 { 277 flush(); 278 } 279 m_handler.addAttribute(uri, localName, rawName, type, value, XSLAttribute); 280 } 281 /** 282 * Adds an attribute to the currenly open tag 283 * @param rawName the attribute name, with prefix (if any) 284 * @param value the value of the parameter 285 * @see ExtendedContentHandler#addAttribute(String, String) 286 */ addAttribute(String rawName, String value)287 public void addAttribute(String rawName, String value) 288 { 289 if (m_firstTagNotEmitted) 290 { 291 flush(); 292 } 293 m_handler.addAttribute(rawName, value); 294 295 } 296 297 /** 298 * Adds a unique attribute to the currenly open tag 299 */ addUniqueAttribute(String rawName, String value, int flags)300 public void addUniqueAttribute(String rawName, String value, int flags) 301 throws SAXException 302 { 303 if (m_firstTagNotEmitted) 304 { 305 flush(); 306 } 307 m_handler.addUniqueAttribute(rawName, value, flags); 308 309 } 310 311 /** 312 * Converts the String to a character array and calls the SAX method 313 * characters(char[],int,int); 314 * 315 * @see ExtendedContentHandler#characters(String) 316 */ characters(String chars)317 public void characters(String chars) throws SAXException 318 { 319 final int length = chars.length(); 320 if (length > m_charsBuff.length) 321 { 322 m_charsBuff = new char[length*2 + 1]; 323 } 324 chars.getChars(0, length, m_charsBuff, 0); 325 this.characters(m_charsBuff, 0, length); 326 } 327 328 /** 329 * Pass the call on to the underlying handler 330 * @see ExtendedContentHandler#endElement(String) 331 */ endElement(String elementName)332 public void endElement(String elementName) throws SAXException 333 { 334 if (m_firstTagNotEmitted) 335 { 336 flush(); 337 } 338 m_handler.endElement(elementName); 339 } 340 341 342 /** 343 * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String) 344 * @param prefix The prefix that maps to the URI 345 * @param uri The URI for the namespace 346 */ startPrefixMapping(String prefix, String uri)347 public void startPrefixMapping(String prefix, String uri) throws SAXException 348 { 349 this.startPrefixMapping(prefix,uri, true); 350 } 351 352 /** 353 * This method is used when a prefix/uri namespace mapping 354 * is indicated after the element was started with a 355 * startElement() and before and endElement(). 356 * startPrefixMapping(prefix,uri) would be used before the 357 * startElement() call. 358 * @param uri the URI of the namespace 359 * @param prefix the prefix associated with the given URI. 360 * 361 * @see ExtendedContentHandler#namespaceAfterStartElement(String, String) 362 */ namespaceAfterStartElement(String prefix, String uri)363 public void namespaceAfterStartElement(String prefix, String uri) 364 throws SAXException 365 { 366 // hack for XSLTC with finding URI for default namespace 367 if (m_firstTagNotEmitted && m_firstElementURI == null && m_firstElementName != null) 368 { 369 String prefix1 = getPrefixPart(m_firstElementName); 370 if (prefix1 == null && EMPTYSTRING.equals(prefix)) 371 { 372 // the elements URI is not known yet, and it 373 // doesn't have a prefix, and we are currently 374 // setting the uri for prefix "", so we have 375 // the uri for the element... lets remember it 376 m_firstElementURI = uri; 377 } 378 } 379 startPrefixMapping(prefix,uri, false); 380 } 381 startPrefixMapping(String prefix, String uri, boolean shouldFlush)382 public boolean startPrefixMapping(String prefix, String uri, boolean shouldFlush) 383 throws SAXException 384 { 385 boolean pushed = false; 386 if (m_firstTagNotEmitted) 387 { 388 if (m_firstElementName != null && shouldFlush) 389 { 390 /* we've already seen a startElement, and this is a prefix mapping 391 * for the up coming element, so flush the old element 392 * then send this event on its way. 393 */ 394 flush(); 395 pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush); 396 } 397 else 398 { 399 if (m_namespacePrefix == null) 400 { 401 m_namespacePrefix = new Vector(); 402 m_namespaceURI = new Vector(); 403 } 404 m_namespacePrefix.addElement(prefix); 405 m_namespaceURI.addElement(uri); 406 407 if (m_firstElementURI == null) 408 { 409 if (prefix.equals(m_firstElementPrefix)) 410 m_firstElementURI = uri; 411 } 412 } 413 414 } 415 else 416 { 417 pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush); 418 } 419 return pushed; 420 } 421 422 /** 423 * This method cannot be cached because default is different in 424 * HTML and XML (we need more than a boolean). 425 */ 426 setVersion(String version)427 public void setVersion(String version) 428 { 429 m_handler.setVersion(version); 430 431 // Cache call to setVersion() 432 // super.setVersion(version); 433 m_setVersion_called = true; 434 } 435 436 /** 437 * @see org.xml.sax.ContentHandler#startDocument() 438 */ startDocument()439 public void startDocument() throws SAXException 440 { 441 m_needToCallStartDocument = true; 442 } 443 444 445 startElement(String qName)446 public void startElement(String qName) throws SAXException 447 { 448 this.startElement(null, null, qName, null); 449 } 450 startElement(String namespaceURI, String localName, String qName)451 public void startElement(String namespaceURI, String localName, String qName) throws SAXException 452 { 453 this.startElement(namespaceURI, localName, qName, null); 454 } 455 startElement( String namespaceURI, String localName, String elementName, Attributes atts)456 public void startElement( 457 String namespaceURI, 458 String localName, 459 String elementName, 460 Attributes atts) throws SAXException 461 { 462 /* we are notified of the start of an element */ 463 if (m_firstTagNotEmitted) 464 { 465 /* we have not yet sent the first element on its way */ 466 if (m_firstElementName != null) 467 { 468 /* this is not the first element, but a later one. 469 * But we have the old element pending, so flush it out, 470 * then send this one on its way. 471 */ 472 flush(); 473 m_handler.startElement(namespaceURI, localName, elementName, atts); 474 } 475 else 476 { 477 /* this is the very first element that we have seen, 478 * so save it for flushing later. We may yet get to know its 479 * URI due to added attributes. 480 */ 481 482 m_wrapped_handler_not_initialized = true; 483 m_firstElementName = elementName; 484 485 // null if not known 486 m_firstElementPrefix = getPrefixPartUnknown(elementName); 487 488 // null if not known 489 m_firstElementURI = namespaceURI; 490 491 // null if not known 492 m_firstElementLocalName = localName; 493 494 if (m_tracer != null) 495 firePseudoElement(elementName); 496 497 /* we don't want to call our own addAttributes, which 498 * merely delegates to the wrapped handler, but we want to 499 * add these attributes to m_attributes. So me must call super. 500 * addAttributes() In this case m_attributes is only used for the 501 * first element, after that this class totally delegates to the 502 * wrapped handler which is either XML or HTML. 503 */ 504 if (atts != null) 505 super.addAttributes(atts); 506 507 // if there are attributes, then lets make the flush() 508 // call the startElement on the handler and send the 509 // attributes on their way. 510 if (atts != null) 511 flush(); 512 513 } 514 } 515 else 516 { 517 // this is not the first element, but a later one, so just 518 // send it on its way. 519 m_handler.startElement(namespaceURI, localName, elementName, atts); 520 } 521 } 522 523 /** 524 * Pass the call on to the underlying handler 525 * @see ExtendedLexicalHandler#comment(String) 526 */ comment(String comment)527 public void comment(String comment) throws SAXException 528 { 529 if (m_firstTagNotEmitted && m_firstElementName != null) 530 { 531 emitFirstTag(); 532 } 533 else if (m_needToCallStartDocument) 534 { 535 m_handler.startDocument(); 536 m_needToCallStartDocument = false; 537 } 538 539 m_handler.comment(comment); 540 } 541 542 /** 543 * Pass the call on to the underlying handler 544 * @see XSLOutputAttributes#getDoctypePublic() 545 */ getDoctypePublic()546 public String getDoctypePublic() 547 { 548 549 return m_handler.getDoctypePublic(); 550 } 551 552 /** 553 * Pass the call on to the underlying handler 554 * @see XSLOutputAttributes#getDoctypeSystem() 555 */ getDoctypeSystem()556 public String getDoctypeSystem() 557 { 558 return m_handler.getDoctypeSystem(); 559 } 560 561 /** 562 * Pass the call on to the underlying handler 563 * @see XSLOutputAttributes#getEncoding() 564 */ getEncoding()565 public String getEncoding() 566 { 567 return m_handler.getEncoding(); 568 } 569 570 /** 571 * Pass the call on to the underlying handler 572 * @see XSLOutputAttributes#getIndent() 573 */ getIndent()574 public boolean getIndent() 575 { 576 return m_handler.getIndent(); 577 } 578 579 /** 580 * Pass the call on to the underlying handler 581 * @see XSLOutputAttributes#getIndentAmount() 582 */ getIndentAmount()583 public int getIndentAmount() 584 { 585 return m_handler.getIndentAmount(); 586 } 587 588 /** 589 * Pass the call on to the underlying handler 590 * @see XSLOutputAttributes#getMediaType() 591 */ getMediaType()592 public String getMediaType() 593 { 594 return m_handler.getMediaType(); 595 } 596 597 /** 598 * Pass the call on to the underlying handler 599 * @see XSLOutputAttributes#getOmitXMLDeclaration() 600 */ getOmitXMLDeclaration()601 public boolean getOmitXMLDeclaration() 602 { 603 return m_handler.getOmitXMLDeclaration(); 604 } 605 606 /** 607 * Pass the call on to the underlying handler 608 * @see XSLOutputAttributes#getStandalone() 609 */ getStandalone()610 public String getStandalone() 611 { 612 return m_handler.getStandalone(); 613 } 614 615 /** 616 * Pass the call on to the underlying handler 617 * @see XSLOutputAttributes#getVersion() 618 */ getVersion()619 public String getVersion() 620 { 621 return m_handler.getVersion(); 622 } 623 624 /** 625 * @see XSLOutputAttributes#setDoctype(String, String) 626 */ setDoctype(String system, String pub)627 public void setDoctype(String system, String pub) 628 { 629 m_handler.setDoctypePublic(pub); 630 m_handler.setDoctypeSystem(system); 631 } 632 633 /** 634 * Set the doctype in the underlying XML handler. Remember that this method 635 * was called, just in case we need to transfer this doctype to an HTML handler 636 * @param doctype the public doctype to set 637 * @see XSLOutputAttributes#setDoctypePublic(String) 638 */ setDoctypePublic(String doctype)639 public void setDoctypePublic(String doctype) 640 { 641 m_handler.setDoctypePublic(doctype); 642 m_setDoctypePublic_called = true; 643 } 644 645 /** 646 * Set the doctype in the underlying XML handler. Remember that this method 647 * was called, just in case we need to transfer this doctype to an HTML handler 648 * @param doctype the system doctype to set 649 * @see XSLOutputAttributes#setDoctypeSystem(String) 650 */ setDoctypeSystem(String doctype)651 public void setDoctypeSystem(String doctype) 652 { 653 m_handler.setDoctypeSystem(doctype); 654 m_setDoctypeSystem_called = true; 655 } 656 657 /** 658 * Pass the call on to the underlying handler 659 * @see XSLOutputAttributes#setEncoding(String) 660 */ setEncoding(String encoding)661 public void setEncoding(String encoding) 662 { 663 m_handler.setEncoding(encoding); 664 } 665 666 /** 667 * Pass the call on to the underlying handler 668 * @see XSLOutputAttributes#setIndent(boolean) 669 */ setIndent(boolean indent)670 public void setIndent(boolean indent) 671 { 672 m_handler.setIndent(indent); 673 } 674 675 /** 676 * Pass the call on to the underlying handler 677 */ setIndentAmount(int value)678 public void setIndentAmount(int value) 679 { 680 m_handler.setIndentAmount(value); 681 } 682 683 /** 684 * @see XSLOutputAttributes#setMediaType(String) 685 */ setMediaType(String mediaType)686 public void setMediaType(String mediaType) 687 { 688 m_handler.setMediaType(mediaType); 689 m_setMediaType_called = true; 690 } 691 692 /** 693 * Pass the call on to the underlying handler 694 * @see XSLOutputAttributes#setOmitXMLDeclaration(boolean) 695 */ setOmitXMLDeclaration(boolean b)696 public void setOmitXMLDeclaration(boolean b) 697 { 698 m_handler.setOmitXMLDeclaration(b); 699 } 700 701 /** 702 * Pass the call on to the underlying handler 703 * @see XSLOutputAttributes#setStandalone(String) 704 */ setStandalone(String standalone)705 public void setStandalone(String standalone) 706 { 707 m_handler.setStandalone(standalone); 708 } 709 710 /** 711 * @see XSLOutputAttributes#setVersion(String) 712 */ 713 714 /** 715 * Pass the call on to the underlying handler 716 * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String) 717 */ attributeDecl( String arg0, String arg1, String arg2, String arg3, String arg4)718 public void attributeDecl( 719 String arg0, 720 String arg1, 721 String arg2, 722 String arg3, 723 String arg4) 724 throws SAXException 725 { 726 m_handler.attributeDecl(arg0, arg1, arg2, arg3, arg4); 727 } 728 729 /** 730 * Pass the call on to the underlying handler 731 * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String) 732 */ elementDecl(String arg0, String arg1)733 public void elementDecl(String arg0, String arg1) throws SAXException 734 { 735 if (m_firstTagNotEmitted) 736 { 737 emitFirstTag(); 738 } 739 m_handler.elementDecl(arg0, arg1); 740 } 741 742 /** 743 * Pass the call on to the underlying handler 744 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String) 745 */ externalEntityDecl( String name, String publicId, String systemId)746 public void externalEntityDecl( 747 String name, 748 String publicId, 749 String systemId) 750 throws SAXException 751 { 752 if (m_firstTagNotEmitted) 753 { 754 flush(); 755 } 756 m_handler.externalEntityDecl(name, publicId, systemId); 757 } 758 759 /** 760 * Pass the call on to the underlying handler 761 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String) 762 */ internalEntityDecl(String arg0, String arg1)763 public void internalEntityDecl(String arg0, String arg1) 764 throws SAXException 765 { 766 if (m_firstTagNotEmitted) 767 { 768 flush(); 769 } 770 m_handler.internalEntityDecl(arg0, arg1); 771 } 772 773 /** 774 * Pass the call on to the underlying handler 775 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 776 */ characters(char[] characters, int offset, int length)777 public void characters(char[] characters, int offset, int length) 778 throws SAXException 779 { 780 if (m_firstTagNotEmitted) 781 { 782 flush(); 783 } 784 785 m_handler.characters(characters, offset, length); 786 787 } 788 789 /** 790 * Pass the call on to the underlying handler 791 * @see org.xml.sax.ContentHandler#endDocument() 792 */ endDocument()793 public void endDocument() throws SAXException 794 { 795 if (m_firstTagNotEmitted) 796 { 797 flush(); 798 } 799 800 m_handler.endDocument(); 801 802 803 } 804 805 /** 806 * Pass the call on to the underlying handler 807 * @see org.xml.sax.ContentHandler#endElement(String, String, String) 808 */ endElement(String namespaceURI, String localName, String qName)809 public void endElement(String namespaceURI, String localName, String qName) 810 throws SAXException 811 { 812 if (m_firstTagNotEmitted) 813 { 814 flush(); 815 if (namespaceURI == null && m_firstElementURI != null) 816 namespaceURI = m_firstElementURI; 817 818 819 if (localName == null && m_firstElementLocalName != null) 820 localName = m_firstElementLocalName; 821 } 822 823 m_handler.endElement(namespaceURI, localName, qName); 824 } 825 826 /** 827 * Pass the call on to the underlying handler 828 * @see org.xml.sax.ContentHandler#endPrefixMapping(String) 829 */ endPrefixMapping(String prefix)830 public void endPrefixMapping(String prefix) throws SAXException 831 { 832 m_handler.endPrefixMapping(prefix); 833 } 834 835 /** 836 * Pass the call on to the underlying handler 837 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 838 */ ignorableWhitespace(char[] ch, int start, int length)839 public void ignorableWhitespace(char[] ch, int start, int length) 840 throws SAXException 841 { 842 if (m_firstTagNotEmitted) 843 { 844 flush(); 845 } 846 m_handler.ignorableWhitespace(ch, start, length); 847 } 848 849 /** 850 * Pass the call on to the underlying handler 851 * @see org.xml.sax.ContentHandler#processingInstruction(String, String) 852 */ processingInstruction(String target, String data)853 public void processingInstruction(String target, String data) 854 throws SAXException 855 { 856 if (m_firstTagNotEmitted) 857 { 858 flush(); 859 } 860 861 m_handler.processingInstruction(target, data); 862 } 863 864 /** 865 * Pass the call on to the underlying handler 866 * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator) 867 */ setDocumentLocator(Locator locator)868 public void setDocumentLocator(Locator locator) 869 { 870 m_handler.setDocumentLocator(locator); 871 } 872 873 /** 874 * Pass the call on to the underlying handler 875 * @see org.xml.sax.ContentHandler#skippedEntity(String) 876 */ skippedEntity(String name)877 public void skippedEntity(String name) throws SAXException 878 { 879 m_handler.skippedEntity(name); 880 } 881 882 883 884 /** 885 * Pass the call on to the underlying handler 886 * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) 887 */ comment(char[] ch, int start, int length)888 public void comment(char[] ch, int start, int length) throws SAXException 889 { 890 if (m_firstTagNotEmitted) 891 { 892 flush(); 893 } 894 895 m_handler.comment(ch, start, length); 896 } 897 898 /** 899 * Pass the call on to the underlying handler 900 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 901 */ endCDATA()902 public void endCDATA() throws SAXException 903 { 904 905 m_handler.endCDATA(); 906 } 907 908 /** 909 * Pass the call on to the underlying handler 910 * @see org.xml.sax.ext.LexicalHandler#endDTD() 911 */ endDTD()912 public void endDTD() throws SAXException 913 { 914 915 m_handler.endDTD(); 916 } 917 918 /** 919 * Pass the call on to the underlying handler 920 * @see org.xml.sax.ext.LexicalHandler#endEntity(String) 921 */ endEntity(String name)922 public void endEntity(String name) throws SAXException 923 { 924 if (m_firstTagNotEmitted) 925 { 926 emitFirstTag(); 927 } 928 m_handler.endEntity(name); 929 } 930 931 /** 932 * Pass the call on to the underlying handler 933 * @see org.xml.sax.ext.LexicalHandler#startCDATA() 934 */ startCDATA()935 public void startCDATA() throws SAXException 936 { 937 m_handler.startCDATA(); 938 } 939 940 /** 941 * Pass the call on to the underlying handler 942 * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String) 943 */ startDTD(String name, String publicId, String systemId)944 public void startDTD(String name, String publicId, String systemId) 945 throws SAXException 946 { 947 m_handler.startDTD(name, publicId, systemId); 948 } 949 950 /** 951 * Pass the call on to the underlying handler 952 * @see org.xml.sax.ext.LexicalHandler#startEntity(String) 953 */ startEntity(String name)954 public void startEntity(String name) throws SAXException 955 { 956 m_handler.startEntity(name); 957 } 958 959 /** 960 * Initialize the wrapped output stream (XML or HTML). 961 * If the stream handler should be HTML, then replace the XML handler with 962 * an HTML handler. After than send the starting method calls that were cached 963 * to the wrapped handler. 964 * 965 */ initStreamOutput()966 private void initStreamOutput() throws SAXException 967 { 968 969 // Try to rule out if this is an not to be an HTML document based on prefix 970 boolean firstElementIsHTML = isFirstElemHTML(); 971 972 if (firstElementIsHTML) 973 { 974 // create an HTML output handler, and initialize it 975 976 // keep a reference to the old handler, ... it will soon be gone 977 SerializationHandler oldHandler = m_handler; 978 979 /* We have to make sure we get an output properties with the proper 980 * defaults for the HTML method. The easiest way to do this is to 981 * have the OutputProperties class do it. 982 */ 983 984 Properties htmlProperties = 985 OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML); 986 Serializer serializer = 987 SerializerFactory.getSerializer(htmlProperties); 988 989 // The factory should be returning a ToStream 990 // Don't know what to do if it doesn't 991 // i.e. the user has over-ridden the content-handler property 992 // for html 993 m_handler = (SerializationHandler) serializer; 994 //m_handler = new ToHTMLStream(); 995 996 Writer writer = oldHandler.getWriter(); 997 998 if (null != writer) 999 m_handler.setWriter(writer); 1000 else 1001 { 1002 OutputStream os = oldHandler.getOutputStream(); 1003 1004 if (null != os) 1005 m_handler.setOutputStream(os); 1006 } 1007 1008 // need to copy things from the old handler to the new one here 1009 1010 // if (_setVersion_called) 1011 // { 1012 m_handler.setVersion(oldHandler.getVersion()); 1013 // } 1014 // if (_setDoctypeSystem_called) 1015 // { 1016 m_handler.setDoctypeSystem(oldHandler.getDoctypeSystem()); 1017 // } 1018 // if (_setDoctypePublic_called) 1019 // { 1020 m_handler.setDoctypePublic(oldHandler.getDoctypePublic()); 1021 // } 1022 // if (_setMediaType_called) 1023 // { 1024 m_handler.setMediaType(oldHandler.getMediaType()); 1025 // } 1026 1027 m_handler.setTransformer(oldHandler.getTransformer()); 1028 } 1029 1030 /* Now that we have a real wrapped handler (XML or HTML) lets 1031 * pass any cached calls to it 1032 */ 1033 // Call startDocument() if necessary 1034 if (m_needToCallStartDocument) 1035 { 1036 m_handler.startDocument(); 1037 m_needToCallStartDocument = false; 1038 } 1039 1040 // the wrapped handler is now fully initialized 1041 m_wrapped_handler_not_initialized = false; 1042 } 1043 emitFirstTag()1044 private void emitFirstTag() throws SAXException 1045 { 1046 if (m_firstElementName != null) 1047 { 1048 if (m_wrapped_handler_not_initialized) 1049 { 1050 initStreamOutput(); 1051 m_wrapped_handler_not_initialized = false; 1052 } 1053 // Output first tag 1054 m_handler.startElement(m_firstElementURI, null, m_firstElementName, m_attributes); 1055 // don't need the collected attributes of the first element anymore. 1056 m_attributes = null; 1057 1058 // Output namespaces of first tag 1059 if (m_namespacePrefix != null) 1060 { 1061 final int n = m_namespacePrefix.size(); 1062 for (int i = 0; i < n; i++) 1063 { 1064 final String prefix = 1065 (String) m_namespacePrefix.elementAt(i); 1066 final String uri = (String) m_namespaceURI.elementAt(i); 1067 m_handler.startPrefixMapping(prefix, uri, false); 1068 } 1069 m_namespacePrefix = null; 1070 m_namespaceURI = null; 1071 } 1072 m_firstTagNotEmitted = false; 1073 } 1074 } 1075 1076 /** 1077 * Utility function for calls to local-name(). 1078 * 1079 * Don't want to override static function on SerializerBase 1080 * So added Unknown suffix to method name. 1081 */ getLocalNameUnknown(String value)1082 private String getLocalNameUnknown(String value) 1083 { 1084 int idx = value.lastIndexOf(':'); 1085 if (idx >= 0) 1086 value = value.substring(idx + 1); 1087 idx = value.lastIndexOf('@'); 1088 if (idx >= 0) 1089 value = value.substring(idx + 1); 1090 return (value); 1091 } 1092 1093 /** 1094 * Utility function to return prefix 1095 * 1096 * Don't want to override static function on SerializerBase 1097 * So added Unknown suffix to method name. 1098 */ getPrefixPartUnknown(String qname)1099 private String getPrefixPartUnknown(String qname) 1100 { 1101 final int index = qname.indexOf(':'); 1102 return (index > 0) ? qname.substring(0, index) : EMPTYSTRING; 1103 } 1104 1105 /** 1106 * Determine if the firts element in the document is <html> or <HTML> 1107 * This uses the cached first element name, first element prefix and the 1108 * cached namespaces from previous method calls 1109 * 1110 * @return true if the first element is an opening <html> tag 1111 */ isFirstElemHTML()1112 private boolean isFirstElemHTML() 1113 { 1114 boolean isHTML; 1115 1116 // is the first tag html, not considering the prefix ? 1117 isHTML = 1118 getLocalNameUnknown(m_firstElementName).equalsIgnoreCase("html"); 1119 1120 // Try to rule out if this is not to be an HTML document based on URI 1121 if (isHTML 1122 && m_firstElementURI != null 1123 && !EMPTYSTRING.equals(m_firstElementURI)) 1124 { 1125 // the <html> element has a non-trivial namespace 1126 isHTML = false; 1127 } 1128 // Try to rule out if this is an not to be an HTML document based on prefix 1129 if (isHTML && m_namespacePrefix != null) 1130 { 1131 /* the first element has a name of "html", but lets check the prefix. 1132 * If the prefix points to a namespace with a URL that is not "" 1133 * then the doecument doesn't start with an <html> tag, and isn't html 1134 */ 1135 final int max = m_namespacePrefix.size(); 1136 for (int i = 0; i < max; i++) 1137 { 1138 final String prefix = (String) m_namespacePrefix.elementAt(i); 1139 final String uri = (String) m_namespaceURI.elementAt(i); 1140 1141 if (m_firstElementPrefix != null 1142 && m_firstElementPrefix.equals(prefix) 1143 && !EMPTYSTRING.equals(uri)) 1144 { 1145 // The first element has a prefix, so it can't be <html> 1146 isHTML = false; 1147 break; 1148 } 1149 } 1150 1151 } 1152 return isHTML; 1153 } 1154 /** 1155 * @see Serializer#asDOMSerializer() 1156 */ asDOMSerializer()1157 public DOMSerializer asDOMSerializer() throws IOException 1158 { 1159 return m_handler.asDOMSerializer(); 1160 } 1161 1162 /** 1163 * @param URI_and_localNames Vector a list of pairs of URI/localName 1164 * specified in the cdata-section-elements attribute. 1165 * @see SerializationHandler#setCdataSectionElements(java.util.Vector) 1166 */ setCdataSectionElements(Vector URI_and_localNames)1167 public void setCdataSectionElements(Vector URI_and_localNames) 1168 { 1169 m_handler.setCdataSectionElements(URI_and_localNames); 1170 } 1171 /** 1172 * @see ExtendedContentHandler#addAttributes(org.xml.sax.Attributes) 1173 */ addAttributes(Attributes atts)1174 public void addAttributes(Attributes atts) throws SAXException 1175 { 1176 m_handler.addAttributes(atts); 1177 } 1178 1179 /** 1180 * Get the current namespace mappings. 1181 * Simply returns the mappings of the wrapped handler. 1182 * @see ExtendedContentHandler#getNamespaceMappings() 1183 */ getNamespaceMappings()1184 public NamespaceMappings getNamespaceMappings() 1185 { 1186 NamespaceMappings mappings = null; 1187 if (m_handler != null) 1188 { 1189 mappings = m_handler.getNamespaceMappings(); 1190 } 1191 return mappings; 1192 } 1193 /** 1194 * @see SerializationHandler#flushPending() 1195 */ flushPending()1196 public void flushPending() throws SAXException 1197 { 1198 1199 flush(); 1200 1201 m_handler.flushPending(); 1202 } 1203 flush()1204 private void flush() 1205 { 1206 try 1207 { 1208 if (m_firstTagNotEmitted) 1209 { 1210 emitFirstTag(); 1211 } 1212 if (m_needToCallStartDocument) 1213 { 1214 m_handler.startDocument(); 1215 m_needToCallStartDocument = false; 1216 } 1217 } 1218 catch(SAXException e) 1219 { 1220 throw new RuntimeException(e.toString()); 1221 } 1222 1223 1224 } 1225 1226 /** 1227 * @see ExtendedContentHandler#getPrefix 1228 */ getPrefix(String namespaceURI)1229 public String getPrefix(String namespaceURI) 1230 { 1231 return m_handler.getPrefix(namespaceURI); 1232 } 1233 /** 1234 * @see ExtendedContentHandler#entityReference(java.lang.String) 1235 */ entityReference(String entityName)1236 public void entityReference(String entityName) throws SAXException 1237 { 1238 m_handler.entityReference(entityName); 1239 } 1240 1241 /** 1242 * @see ExtendedContentHandler#getNamespaceURI(java.lang.String, boolean) 1243 */ getNamespaceURI(String qname, boolean isElement)1244 public String getNamespaceURI(String qname, boolean isElement) 1245 { 1246 return m_handler.getNamespaceURI(qname, isElement); 1247 } 1248 getNamespaceURIFromPrefix(String prefix)1249 public String getNamespaceURIFromPrefix(String prefix) 1250 { 1251 return m_handler.getNamespaceURIFromPrefix(prefix); 1252 } 1253 setTransformer(Transformer t)1254 public void setTransformer(Transformer t) 1255 { 1256 m_handler.setTransformer(t); 1257 if ((t instanceof SerializerTrace) && 1258 (((SerializerTrace) t).hasTraceListeners())) { 1259 m_tracer = (SerializerTrace) t; 1260 } else { 1261 m_tracer = null; 1262 } 1263 } getTransformer()1264 public Transformer getTransformer() 1265 { 1266 return m_handler.getTransformer(); 1267 } 1268 1269 /** 1270 * @see SerializationHandler#setContentHandler(org.xml.sax.ContentHandler) 1271 */ setContentHandler(ContentHandler ch)1272 public void setContentHandler(ContentHandler ch) 1273 { 1274 m_handler.setContentHandler(ch); 1275 } 1276 /** 1277 * This method is used to set the source locator, which might be used to 1278 * generated an error message. 1279 * @param locator the source locator 1280 * 1281 * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator) 1282 */ setSourceLocator(SourceLocator locator)1283 public void setSourceLocator(SourceLocator locator) 1284 { 1285 m_handler.setSourceLocator(locator); 1286 } 1287 firePseudoElement(String elementName)1288 protected void firePseudoElement(String elementName) 1289 { 1290 1291 if (m_tracer != null) { 1292 StringBuffer sb = new StringBuffer(); 1293 1294 sb.append('<'); 1295 sb.append(elementName); 1296 1297 // convert the StringBuffer to a char array and 1298 // emit the trace event that these characters "might" 1299 // be written 1300 char ch[] = sb.toString().toCharArray(); 1301 m_tracer.fireGenerateEvent( 1302 SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS, 1303 ch, 1304 0, 1305 ch.length); 1306 } 1307 } 1308 1309 /** 1310 * @see org.apache.xml.serializer.Serializer#asDOM3Serializer() 1311 */ asDOM3Serializer()1312 public Object asDOM3Serializer() throws IOException 1313 { 1314 return m_handler.asDOM3Serializer(); 1315 } 1316 } 1317