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