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