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 */ ToUnknownStream()118 public ToUnknownStream() { 119 m_handler = new ToXMLStream(); 120 } 121 122 /** 123 * @see Serializer#asContentHandler() 124 * @return the wrapped XML or HTML handler 125 */ asContentHandler()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 */ close()138 public void close() { 139 m_handler.close(); 140 } 141 142 /** 143 * @see Serializer#getOutputFormat() 144 * @return the properties of the underlying handler 145 */ getOutputFormat()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 */ getOutputStream()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 */ getWriter()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 */ reset()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 */ serialize(Node node)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 */ setEscaping(boolean escape)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 */ setOutputFormat(Properties format)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 */ setOutputStream(OutputStream output)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 */ setWriter(Writer writer)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 */ addAttribute(String uri, String localName, String rawName, String type, String value)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 */ addAttribute(String uri, String localName, String rawName, String type, String value, boolean XSLAttribute)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 */ addAttribute(String rawName, String value)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 */ addUniqueAttribute(String rawName, String value, int flags)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 */ characters(String chars)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 */ endElement(String elementName)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 */ startPrefixMapping(String prefix, String uri)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 */ namespaceAfterStartElement(String prefix, String uri)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 startPrefixMapping(String prefix, String uri, boolean shouldFlush)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 */ setVersion(String version)391 public void setVersion(String version) { 392 m_handler.setVersion(version); 393 } 394 395 /** 396 * @see org.xml.sax.ContentHandler#startDocument() 397 */ startDocument()398 public void startDocument() throws SAXException { 399 m_needToCallStartDocument = true; 400 } 401 startElement(String qName)402 public void startElement(String qName) throws SAXException { 403 this.startElement(null, null, qName, null); 404 } 405 startElement(String namespaceURI, String localName, String qName)406 public void startElement(String namespaceURI, String localName, 407 String qName) throws SAXException { 408 this.startElement(namespaceURI, localName, qName, null); 409 } 410 startElement(String namespaceURI, String localName, String elementName, Attributes atts)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 */ comment(String comment)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 */ getDoctypePublic()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 */ getDoctypeSystem()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 */ getEncoding()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 */ getIndent()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 */ getIndentAmount()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 */ getMediaType()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 */ getOmitXMLDeclaration()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 */ getStandalone()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 */ getVersion()575 public String getVersion() { 576 return m_handler.getVersion(); 577 } 578 579 /** 580 * @see XSLOutputAttributes#setDoctype(String, String) 581 */ setDoctype(String system, String pub)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 */ setDoctypePublic(String doctype)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 */ setDoctypeSystem(String doctype)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 */ setEncoding(String encoding)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 */ setIndent(boolean indent)619 public void setIndent(boolean indent) { 620 m_handler.setIndent(indent); 621 } 622 623 /** 624 * Pass the call on to the underlying handler 625 */ setIndentAmount(int value)626 public void setIndentAmount(int value) { 627 m_handler.setIndentAmount(value); 628 } 629 630 /** 631 * @see XSLOutputAttributes#setMediaType(String) 632 */ setMediaType(String mediaType)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 */ setOmitXMLDeclaration(boolean b)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 */ setStandalone(String standalone)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 */ attributeDecl(String arg0, String arg1, String arg2, String arg3, String arg4)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 */ elementDecl(String arg0, String arg1)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 */ externalEntityDecl( String name, String publicId, String systemId)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 */ internalEntityDecl(String arg0, String arg1)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 */ characters(char[] characters, int offset, int length)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 */ endDocument()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 */ endElement(String namespaceURI, String localName, String qName)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 */ endPrefixMapping(String prefix)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 */ ignorableWhitespace(char[] ch, int start, int length)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 */ processingInstruction(String target, String data)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 */ setDocumentLocator(Locator locator)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 */ skippedEntity(String name)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 */ comment(char[] ch, int start, int length)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 */ endCDATA()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 */ endDTD()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 */ endEntity(String name)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 */ startCDATA()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 */ startDTD(String name, String publicId, String systemId)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 */ startEntity(String name)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 */ initStreamOutput()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 emitFirstTag()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 */ getLocalNameUnknown(String value)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 */ getPrefixPartUnknown(String qname)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 */ isFirstElemHTML()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 */ asDOMSerializer()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 */ setCdataSectionElements(List<String> URI_and_localNames)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 */ addAttributes(Attributes atts)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 */ getNamespaceMappings()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 */ flushPending()1102 public void flushPending() throws SAXException { 1103 flush(); 1104 m_handler.flushPending(); 1105 } 1106 flush()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 */ getPrefix(String namespaceURI)1124 public String getPrefix(String namespaceURI) { 1125 return m_handler.getPrefix(namespaceURI); 1126 } 1127 1128 /** 1129 * @see ExtendedContentHandler#entityReference(java.lang.String) 1130 */ entityReference(String entityName)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 */ getNamespaceURI(String qname, boolean isElement)1138 public String getNamespaceURI(String qname, boolean isElement) { 1139 return m_handler.getNamespaceURI(qname, isElement); 1140 } 1141 getNamespaceURIFromPrefix(String prefix)1142 public String getNamespaceURIFromPrefix(String prefix) { 1143 return m_handler.getNamespaceURIFromPrefix(prefix); 1144 } 1145 setTransformer(Transformer t)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 getTransformer()1157 public Transformer getTransformer() { 1158 return m_handler.getTransformer(); 1159 } 1160 1161 /** 1162 * @see SerializationHandler#setContentHandler(org.xml.sax.ContentHandler) 1163 */ setContentHandler(ContentHandler ch)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 */ setSourceLocator(SourceLocator locator)1175 public void setSourceLocator(SourceLocator locator) { 1176 m_handler.setSourceLocator(locator); 1177 } 1178 firePseudoElement(String elementName)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 */ asDOM3Serializer()1201 public Object asDOM3Serializer() throws IOException 1202 { 1203 return m_handler.asDOM3Serializer(); 1204 } 1205 } 1206