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