1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.serializer; 23 24 import java.io.IOException; 25 import java.io.OutputStream; 26 import java.io.Writer; 27 import java.util.Properties; 28 29 import javax.xml.transform.Result; 30 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 import org.xml.sax.ext.LexicalHandler; 37 38 /** 39 * This class accepts SAX-like calls, then sends true SAX calls to a 40 * wrapped SAX handler. There is optimization done knowing that the ultimate 41 * output is HTML. 42 * 43 * This class is not a public API. 44 * 45 * @xsl.usage internal 46 */ 47 public final class ToHTMLSAXHandler extends ToSAXHandler 48 { 49 /** 50 * Handle document type declaration (for first element only) 51 */ 52 private boolean m_dtdHandled = false; 53 54 /** 55 * Keeps track of whether output escaping is currently enabled 56 */ 57 protected boolean m_escapeSetting = true; 58 59 /** 60 * Returns null. 61 * @return null 62 * @see Serializer#getOutputFormat() 63 */ getOutputFormat()64 public Properties getOutputFormat() 65 { 66 return null; 67 } 68 69 /** 70 * Reurns null 71 * @return null 72 * @see Serializer#getOutputStream() 73 */ getOutputStream()74 public OutputStream getOutputStream() 75 { 76 return null; 77 } 78 79 /** 80 * Returns null 81 * @return null 82 * @see Serializer#getWriter() 83 */ getWriter()84 public Writer getWriter() 85 { 86 return null; 87 } 88 89 /** 90 * Does nothing. 91 * 92 */ indent(int n)93 public void indent(int n) throws SAXException 94 { 95 } 96 97 /** 98 * Does nothing. 99 * @see DOMSerializer#serialize(Node) 100 */ serialize(Node node)101 public void serialize(Node node) throws IOException 102 { 103 return; 104 } 105 106 /** 107 * Turns special character escaping on/off. 108 * 109 * 110 * @param escape true if escaping is to be set on. 111 * 112 * @see SerializationHandler#setEscaping(boolean) 113 */ setEscaping(boolean escape)114 public boolean setEscaping(boolean escape) throws SAXException 115 { 116 boolean oldEscapeSetting = m_escapeSetting; 117 m_escapeSetting = escape; 118 119 if (escape) { 120 processingInstruction(Result.PI_ENABLE_OUTPUT_ESCAPING, ""); 121 } else { 122 processingInstruction(Result.PI_DISABLE_OUTPUT_ESCAPING, ""); 123 } 124 125 return oldEscapeSetting; 126 } 127 128 /** 129 * Does nothing 130 * @param indent the number of spaces to indent per indentation level 131 * (ignored) 132 * @see SerializationHandler#setIndent(boolean) 133 */ setIndent(boolean indent)134 public void setIndent(boolean indent) 135 { 136 } 137 138 /** 139 * Does nothing. 140 * @param format this parameter is not used 141 * @see Serializer#setOutputFormat(Properties) 142 */ setOutputFormat(Properties format)143 public void setOutputFormat(Properties format) 144 { 145 } 146 147 /** 148 * Does nothing. 149 * @param output this parameter is ignored 150 * @see Serializer#setOutputStream(OutputStream) 151 */ setOutputStream(OutputStream output)152 public void setOutputStream(OutputStream output) 153 { 154 } 155 156 157 /** 158 * Does nothing. 159 * @param writer this parameter is ignored. 160 * @see Serializer#setWriter(Writer) 161 */ setWriter(Writer writer)162 public void setWriter(Writer writer) 163 { 164 } 165 166 /** 167 * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String) 168 */ 169 /** 170 * Does nothing. 171 * 172 * @param eName this parameter is ignored 173 * @param aName this parameter is ignored 174 * @param type this parameter is ignored 175 * @param valueDefault this parameter is ignored 176 * @param value this parameter is ignored 177 * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String,String,String) 178 */ attributeDecl( String eName, String aName, String type, String valueDefault, String value)179 public void attributeDecl( 180 String eName, 181 String aName, 182 String type, 183 String valueDefault, 184 String value) 185 throws SAXException 186 { 187 } 188 189 190 /** 191 * Does nothing. 192 * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String) 193 */ elementDecl(String name, String model)194 public void elementDecl(String name, String model) throws SAXException 195 { 196 return; 197 } 198 199 /** 200 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String) 201 */ externalEntityDecl(String arg0, String arg1, String arg2)202 public void externalEntityDecl(String arg0, String arg1, String arg2) 203 throws SAXException 204 { 205 } 206 207 /** 208 * Does nothing. 209 * 210 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 211 */ internalEntityDecl(String name, String value)212 public void internalEntityDecl(String name, String value) 213 throws SAXException 214 { 215 } 216 217 /** 218 * Receive notification of the end of an element. 219 * 220 * <p>The SAX parser will invoke this method at the end of every 221 * element in the XML document; there will be a corresponding 222 * startElement() event for every endElement() event (even when the 223 * element is empty).</p> 224 * 225 * <p>If the element name has a namespace prefix, the prefix will 226 * still be attached to the name.</p> 227 * 228 * 229 * @param uri The Namespace URI, or the empty string if the 230 * element has no Namespace URI or if Namespace 231 * processing is not being performed. 232 * @param localName The local name (without prefix), or the 233 * empty string if Namespace processing is not being 234 * performed. 235 * @param qName The qualified name (with prefix), or the 236 * empty string if qualified names are not available. 237 * @throws org.xml.sax.SAXException Any SAX exception, possibly 238 * wrapping another exception. 239 * @see org.xml.sax.ContentHandler#endElement(String, String, String) 240 */ endElement(String uri, String localName, String qName)241 public void endElement(String uri, String localName, String qName) 242 throws SAXException 243 { 244 flushPending(); 245 m_saxHandler.endElement(uri, localName, qName); 246 247 // time to fire off endElement event 248 if (m_tracer != null) 249 super.fireEndElem(qName); 250 } 251 252 /** 253 * Does nothing. 254 */ endPrefixMapping(String prefix)255 public void endPrefixMapping(String prefix) throws SAXException 256 { 257 } 258 259 /** 260 * Does nothing. 261 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 262 */ ignorableWhitespace(char[] ch, int start, int length)263 public void ignorableWhitespace(char[] ch, int start, int length) 264 throws SAXException 265 { 266 } 267 268 /** 269 * Receive notification of a processing instruction. 270 * 271 * <p>The Parser will invoke this method once for each processing 272 * instruction found: note that processing instructions may occur 273 * before or after the main document element.</p> 274 * 275 * <p>A SAX parser should never report an XML declaration (XML 1.0, 276 * section 2.8) or a text declaration (XML 1.0, section 4.3.1) 277 * using this method.</p> 278 * 279 * @param target The processing instruction target. 280 * @param data The processing instruction data, or null if 281 * none was supplied. 282 * @throws org.xml.sax.SAXException Any SAX exception, possibly 283 * wrapping another exception. 284 * 285 * @throws org.xml.sax.SAXException 286 * @see org.xml.sax.ContentHandler#processingInstruction(String, String) 287 */ processingInstruction(String target, String data)288 public void processingInstruction(String target, String data) 289 throws SAXException 290 { 291 flushPending(); 292 m_saxHandler.processingInstruction(target,data); 293 294 // time to fire off processing instruction event 295 296 if (m_tracer != null) 297 super.fireEscapingEvent(target,data); 298 } 299 300 /** 301 * Does nothing. 302 * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator) 303 */ setDocumentLocator(Locator arg0)304 public void setDocumentLocator(Locator arg0) 305 { 306 super.setDocumentLocator(arg0); 307 } 308 309 /** 310 * Does nothing. 311 * @see org.xml.sax.ContentHandler#skippedEntity(String) 312 */ skippedEntity(String arg0)313 public void skippedEntity(String arg0) throws SAXException 314 { 315 } 316 317 /** 318 * Receive notification of the beginning of an element, although this is a 319 * SAX method additional namespace or attribute information can occur before 320 * or after this call, that is associated with this element. 321 * 322 * 323 * @param namespaceURI The Namespace URI, or the empty string if the 324 * element has no Namespace URI or if Namespace 325 * processing is not being performed. 326 * @param localName The local name (without prefix), or the 327 * empty string if Namespace processing is not being 328 * performed. 329 * @param qName The elements name. 330 * @param atts The attributes attached to the element, if any. 331 * @throws org.xml.sax.SAXException Any SAX exception, possibly 332 * wrapping another exception. 333 * @see org.xml.sax.ContentHandler#startElement 334 * @see org.xml.sax.ContentHandler#endElement 335 * @see org.xml.sax.AttributeList 336 * 337 * @throws org.xml.sax.SAXException 338 * 339 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes) 340 */ startElement( String namespaceURI, String localName, String qName, Attributes atts)341 public void startElement( 342 String namespaceURI, 343 String localName, 344 String qName, 345 Attributes atts) 346 throws SAXException 347 { 348 flushPending(); 349 super.startElement(namespaceURI, localName, qName, atts); 350 m_saxHandler.startElement(namespaceURI, localName, qName, atts); 351 m_elemContext.m_startTagOpen = false; 352 } 353 354 /** 355 * Receive notification of a comment anywhere in the document. This callback 356 * will be used for comments inside or outside the document element. 357 * @param ch An array holding the characters in the comment. 358 * @param start The starting position in the array. 359 * @param length The number of characters to use from the array. 360 * @throws org.xml.sax.SAXException The application may raise an exception. 361 * 362 * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) 363 */ comment(char[] ch, int start, int length)364 public void comment(char[] ch, int start, int length) throws SAXException 365 { 366 flushPending(); 367 if (m_lexHandler != null) 368 m_lexHandler.comment(ch, start, length); 369 370 // time to fire off comment event 371 if (m_tracer != null) 372 super.fireCommentEvent(ch, start, length); 373 return; 374 } 375 376 /** 377 * Does nothing. 378 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 379 */ endCDATA()380 public void endCDATA() throws SAXException 381 { 382 return; 383 } 384 385 /** 386 * Does nothing. 387 * @see org.xml.sax.ext.LexicalHandler#endDTD() 388 */ endDTD()389 public void endDTD() throws SAXException 390 { 391 } 392 393 /** 394 * Does nothing. 395 * @see org.xml.sax.ext.LexicalHandler#startCDATA() 396 */ startCDATA()397 public void startCDATA() throws SAXException 398 { 399 } 400 401 /** 402 * Does nothing. 403 * @see org.xml.sax.ext.LexicalHandler#startEntity(String) 404 */ startEntity(String arg0)405 public void startEntity(String arg0) throws SAXException 406 { 407 } 408 409 /** 410 * Receive notification of the end of a document. 411 * 412 * <p>The SAX parser will invoke this method only once, and it will 413 * be the last method invoked during the parse. The parser shall 414 * not invoke this method until it has either abandoned parsing 415 * (because of an unrecoverable error) or reached the end of 416 * input.</p> 417 * 418 * @throws org.xml.sax.SAXException Any SAX exception, possibly 419 * wrapping another exception. 420 * 421 * @throws org.xml.sax.SAXException 422 * 423 * 424 */ endDocument()425 public void endDocument() throws SAXException 426 { 427 flushPending(); 428 429 // Close output document 430 m_saxHandler.endDocument(); 431 432 if (m_tracer != null) 433 super.fireEndDoc(); 434 } 435 436 /** 437 * This method is called when all the data needed for a call to the 438 * SAX handler's startElement() method has been gathered. 439 */ closeStartTag()440 protected void closeStartTag() throws SAXException 441 { 442 443 m_elemContext.m_startTagOpen = false; 444 445 // Now is time to send the startElement event 446 m_saxHandler.startElement( 447 EMPTYSTRING, 448 m_elemContext.m_elementName, 449 m_elemContext.m_elementName, 450 m_attributes); 451 m_attributes.clear(); 452 453 } 454 455 /** 456 * Do nothing. 457 * @see SerializationHandler#close() 458 */ close()459 public void close() 460 { 461 return; 462 } 463 464 /** 465 * Receive notification of character data. 466 * 467 * @param chars The string of characters to process. 468 * 469 * @throws org.xml.sax.SAXException 470 * 471 * @see ExtendedContentHandler#characters(String) 472 */ characters(final String chars)473 public void characters(final String chars) throws SAXException 474 { 475 final int length = chars.length(); 476 if (length > m_charsBuff.length) 477 { 478 m_charsBuff = new char[length * 2 + 1]; 479 } 480 chars.getChars(0, length, m_charsBuff, 0); 481 this.characters(m_charsBuff, 0, length); 482 } 483 484 485 /** 486 * A constructor 487 * @param handler the wrapped SAX content handler 488 * @param encoding the encoding of the output HTML document 489 */ ToHTMLSAXHandler(ContentHandler handler, String encoding)490 public ToHTMLSAXHandler(ContentHandler handler, String encoding) 491 { 492 super(handler,encoding); 493 } 494 /** 495 * A constructor. 496 * @param handler the wrapped SAX content handler 497 * @param lex the wrapped lexical handler 498 * @param encoding the encoding of the output HTML document 499 */ ToHTMLSAXHandler( ContentHandler handler, LexicalHandler lex, String encoding)500 public ToHTMLSAXHandler( 501 ContentHandler handler, 502 LexicalHandler lex, 503 String encoding) 504 { 505 super(handler,lex,encoding); 506 } 507 508 /** 509 * An element starts, but attributes are not fully known yet. 510 * 511 * @param elementNamespaceURI the URI of the namespace of the element 512 * (optional) 513 * @param elementLocalName the element name, but without prefix 514 * (optional) 515 * @param elementName the element name, with prefix, if any (required) 516 * 517 * @see ExtendedContentHandler#startElement(String) 518 */ startElement( String elementNamespaceURI, String elementLocalName, String elementName)519 public void startElement( 520 String elementNamespaceURI, 521 String elementLocalName, 522 String elementName) throws SAXException 523 { 524 525 super.startElement(elementNamespaceURI, elementLocalName, elementName); 526 527 flushPending(); 528 529 // Handle document type declaration (for first element only) 530 if (!m_dtdHandled) 531 { 532 String doctypeSystem = getDoctypeSystem(); 533 String doctypePublic = getDoctypePublic(); 534 if ((doctypeSystem != null) || (doctypePublic != null)) { 535 if (m_lexHandler != null) 536 m_lexHandler.startDTD( 537 elementName, 538 doctypePublic, 539 doctypeSystem); 540 } 541 m_dtdHandled = true; 542 } 543 m_elemContext = m_elemContext.push(elementNamespaceURI, elementLocalName, elementName); 544 } 545 /** 546 * An element starts, but attributes are not fully known yet. 547 * 548 * @param elementName the element name, with prefix, if any 549 * 550 * @see ExtendedContentHandler#startElement(String) 551 */ startElement(String elementName)552 public void startElement(String elementName) throws SAXException 553 { 554 this.startElement(null,null, elementName); 555 } 556 557 /** 558 * Receive notification of the end of an element. 559 * @param elementName The element type name 560 * @throws org.xml.sax.SAXException Any SAX exception, possibly 561 * wrapping another exception. 562 * 563 * @see ExtendedContentHandler#endElement(String) 564 */ endElement(String elementName)565 public void endElement(String elementName) throws SAXException 566 { 567 flushPending(); 568 m_saxHandler.endElement(EMPTYSTRING, elementName, elementName); 569 570 // time to fire off endElement event 571 if (m_tracer != null) 572 super.fireEndElem(elementName); 573 } 574 575 /** 576 * Receive notification of character data. 577 * 578 * <p>The Parser will call this method to report each chunk of 579 * character data. SAX parsers may return all contiguous character 580 * data in a single chunk, or they may split it into several 581 * chunks; however, all of the characters in any single event 582 * must come from the same external entity, so that the Locator 583 * provides useful information.</p> 584 * 585 * <p>The application must not attempt to read from the array 586 * outside of the specified range.</p> 587 * 588 * <p>Note that some parsers will report whitespace using the 589 * ignorableWhitespace() method rather than this one (validating 590 * parsers must do so).</p> 591 * 592 * @param ch The characters from the XML document. 593 * @param off The start position in the array. 594 * @param len The number of characters to read from the array. 595 * @throws org.xml.sax.SAXException Any SAX exception, possibly 596 * wrapping another exception. 597 * @see #ignorableWhitespace 598 * @see org.xml.sax.Locator 599 * 600 * @throws org.xml.sax.SAXException 601 * 602 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 603 */ characters(char[] ch, int off, int len)604 public void characters(char[] ch, int off, int len) throws SAXException 605 { 606 607 flushPending(); 608 m_saxHandler.characters(ch, off, len); 609 610 // time to fire off characters event 611 if (m_tracer != null) 612 super.fireCharEvent(ch, off, len); 613 } 614 615 /** 616 * This method flushes any pending events, which can be startDocument() 617 * closing the opening tag of an element, or closing an open CDATA section. 618 */ flushPending()619 public void flushPending() throws SAXException 620 { 621 if (m_needToCallStartDocument) 622 { 623 startDocumentInternal(); 624 m_needToCallStartDocument = false; 625 } 626 // Close any open element 627 if (m_elemContext.m_startTagOpen) 628 { 629 closeStartTag(); 630 m_elemContext.m_startTagOpen = false; 631 } 632 } 633 /** 634 * Handle a prefix/uri mapping, which is associated with a startElement() 635 * that is soon to follow. Need to close any open start tag to make 636 * sure than any name space attributes due to this event are associated wih 637 * the up comming element, not the current one. 638 * @see ExtendedContentHandler#startPrefixMapping 639 * 640 * @param prefix The Namespace prefix being declared. 641 * @param uri The Namespace URI the prefix is mapped to. 642 * @param shouldFlush true if any open tags need to be closed first, this 643 * will impact which element the mapping applies to (open parent, or its up 644 * comming child) 645 * @return returns true if the call made a change to the current 646 * namespace information, false if it did not change anything, e.g. if the 647 * prefix/namespace mapping was already in scope from before. 648 * 649 * @throws org.xml.sax.SAXException The client may throw 650 * an exception during processing. 651 */ startPrefixMapping( String prefix, String uri, boolean shouldFlush)652 public boolean startPrefixMapping( 653 String prefix, 654 String uri, 655 boolean shouldFlush) 656 throws SAXException 657 { 658 // no namespace support for HTML 659 if (shouldFlush) 660 flushPending(); 661 m_saxHandler.startPrefixMapping(prefix,uri); 662 return false; 663 } 664 665 /** 666 * Begin the scope of a prefix-URI Namespace mapping 667 * just before another element is about to start. 668 * This call will close any open tags so that the prefix mapping 669 * will not apply to the current element, but the up comming child. 670 * 671 * @see org.xml.sax.ContentHandler#startPrefixMapping 672 * 673 * @param prefix The Namespace prefix being declared. 674 * @param uri The Namespace URI the prefix is mapped to. 675 * 676 * @throws org.xml.sax.SAXException The client may throw 677 * an exception during processing. 678 * 679 */ startPrefixMapping(String prefix, String uri)680 public void startPrefixMapping(String prefix, String uri) 681 throws org.xml.sax.SAXException 682 { 683 startPrefixMapping(prefix,uri,true); 684 } 685 686 /** 687 * This method is used when a prefix/uri namespace mapping 688 * is indicated after the element was started with a 689 * startElement() and before and endElement(). 690 * startPrefixMapping(prefix,uri) would be used before the 691 * startElement() call. 692 * @param prefix the prefix associated with the given URI. 693 * @param uri the URI of the namespace 694 * 695 * @see ExtendedContentHandler#namespaceAfterStartElement(String, String) 696 */ namespaceAfterStartElement( final String prefix, final String uri)697 public void namespaceAfterStartElement( 698 final String prefix, 699 final String uri) 700 throws SAXException 701 { 702 // hack for XSLTC with finding URI for default namespace 703 if (m_elemContext.m_elementURI == null) 704 { 705 String prefix1 = getPrefixPart(m_elemContext.m_elementName); 706 if (prefix1 == null && EMPTYSTRING.equals(prefix)) 707 { 708 // the elements URI is not known yet, and it 709 // doesn't have a prefix, and we are currently 710 // setting the uri for prefix "", so we have 711 // the uri for the element... lets remember it 712 m_elemContext.m_elementURI = uri; 713 } 714 } 715 startPrefixMapping(prefix,uri,false); 716 } 717 718 /** 719 * Try's to reset the super class and reset this class for 720 * re-use, so that you don't need to create a new serializer 721 * (mostly for performance reasons). 722 * 723 * @return true if the class was successfuly reset. 724 * @see Serializer#reset() 725 */ reset()726 public boolean reset() 727 { 728 boolean wasReset = false; 729 if (super.reset()) 730 { 731 resetToHTMLSAXHandler(); 732 wasReset = true; 733 } 734 return wasReset; 735 } 736 737 /** 738 * Reset all of the fields owned by ToHTMLSAXHandler class 739 * 740 */ resetToHTMLSAXHandler()741 private void resetToHTMLSAXHandler() 742 { 743 this.m_escapeSetting = true; 744 } 745 } 746