1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 2001-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * 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 * $Id: SerializerBase.java,v 1.5 2006/04/14 12:09:19 sunithareddy Exp $ 22 */ 23 package com.sun.org.apache.xml.internal.serializer; 24 25 import java.io.IOException; 26 import java.util.Vector; 27 28 import javax.xml.transform.SourceLocator; 29 import javax.xml.transform.Transformer; 30 31 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; 32 import com.sun.org.apache.xml.internal.serializer.utils.Utils; 33 import org.xml.sax.Attributes; 34 import org.xml.sax.ContentHandler; 35 import org.xml.sax.Locator; 36 import org.xml.sax.SAXException; 37 import org.xml.sax.SAXParseException; 38 import org.xml.sax.ext.Locator2; 39 40 41 /** 42 * This class acts as a base class for the XML "serializers" 43 * and the stream serializers. 44 * It contains a number of common fields and methods. 45 * 46 * @xsl.usage internal 47 */ 48 public abstract class SerializerBase 49 implements SerializationHandler, SerializerConstants 50 { 51 52 53 /** 54 * To fire off the end element trace event 55 * @param name Name of element 56 */ 57 protected void fireEndElem(String name) 58 throws org.xml.sax.SAXException 59 { 60 if (m_tracer != null) 61 { 62 flushMyWriter(); 63 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null); 64 } 65 } 66 67 /** 68 * Report the characters trace event 69 * @param chars content of characters 70 * @param start starting index of characters to output 71 * @param length number of characters to output 72 */ 73 protected void fireCharEvent(char[] chars, int start, int length) 74 throws org.xml.sax.SAXException 75 { 76 if (m_tracer != null) 77 { 78 flushMyWriter(); 79 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length); 80 } 81 } 82 83 /** 84 * true if we still need to call startDocumentInternal() 85 */ 86 protected boolean m_needToCallStartDocument = true; 87 88 /** True if a trailing "]]>" still needs to be written to be 89 * written out. Used to merge adjacent CDATA sections 90 */ 91 protected boolean m_cdataTagOpen = false; 92 93 /** 94 * All the attributes of the current element, collected from 95 * startPrefixMapping() calls, or addAddtribute() calls, or 96 * from the SAX attributes in a startElement() call. 97 */ 98 protected AttributesImplSerializer m_attributes = new AttributesImplSerializer(); 99 100 /** 101 * Tells if we're in an EntityRef event. 102 */ 103 protected boolean m_inEntityRef = false; 104 105 /** This flag is set while receiving events from the external DTD */ 106 protected boolean m_inExternalDTD = false; 107 108 /** 109 * The System ID for the doc type. 110 */ 111 private String m_doctypeSystem; 112 113 /** 114 * The public ID for the doc type. 115 */ 116 private String m_doctypePublic; 117 118 /** 119 * Flag to tell that we need to add the doctype decl, which we can't do 120 * until the first element is encountered. 121 */ 122 boolean m_needToOutputDocTypeDecl = true; 123 124 /** 125 * The character encoding. Must match the encoding used for the 126 * printWriter. 127 */ 128 private String m_encoding = null; 129 130 /** 131 * Tells if we should write the XML declaration. 132 */ 133 private boolean m_shouldNotWriteXMLHeader = false; 134 135 /** 136 * The standalone value for the doctype. 137 */ 138 private String m_standalone; 139 140 /** 141 * True if standalone was specified. 142 */ 143 protected boolean m_standaloneWasSpecified = false; 144 145 /** 146 * Determine if the output is a standalone. 147 */ 148 protected boolean m_isStandalone = false; 149 150 /** 151 * Flag to tell if indenting (pretty-printing) is on. 152 */ 153 protected boolean m_doIndent = false; 154 /** 155 * Amount to indent. 156 */ 157 protected int m_indentAmount = 0; 158 159 /** 160 * Tells the XML version, for writing out to the XML decl. 161 */ 162 private String m_version = null; 163 164 /** 165 * The mediatype. Not used right now. 166 */ 167 private String m_mediatype; 168 169 /** 170 * The transformer that was around when this output handler was created (if 171 * any). 172 */ 173 private Transformer m_transformer; 174 175 /** 176 * Pairs of local names and corresponding URIs of CDATA sections. This list 177 * comes from the cdata-section-elements attribute. Every second one is a 178 * local name, and every other second one is the URI for the local name. 179 */ 180 protected Vector m_cdataSectionElements = null; 181 182 /** 183 * Namespace support, that keeps track of currently defined 184 * prefix/uri mappings. As processed elements come and go, so do 185 * the associated mappings for that element. 186 */ 187 protected NamespaceMappings m_prefixMap; 188 189 /** 190 * Handle for firing generate events. This interface may be implemented 191 * by the referenced transformer object. 192 */ 193 protected SerializerTrace m_tracer; 194 195 protected SourceLocator m_sourceLocator; 196 197 198 /** 199 * The writer to send output to. This field is only used in the ToStream 200 * serializers, but exists here just so that the fireStartDoc() and 201 * other fire... methods can flush this writer when tracing. 202 */ 203 protected java.io.Writer m_writer = null; 204 205 /** 206 * A reference to "stack frame" corresponding to 207 * the current element. Such a frame is pushed at a startElement() 208 * and popped at an endElement(). This frame contains information about 209 * the element, such as its namespace URI. 210 */ 211 protected ElemContext m_elemContext = new ElemContext(); 212 213 /** 214 * A utility buffer for converting Strings passed to 215 * character() methods to character arrays. 216 * Reusing this buffer means not creating a new character array 217 * everytime and it runs faster. 218 */ 219 protected char[] m_charsBuff = new char[60]; 220 221 /** 222 * A utility buffer for converting Strings passed to 223 * attribute methods to character arrays. 224 * Reusing this buffer means not creating a new character array 225 * everytime and it runs faster. 226 */ 227 protected char[] m_attrBuff = new char[30]; 228 229 private Locator m_locator = null; 230 231 protected boolean m_needToCallSetDocumentInfo = true; 232 233 /** 234 * Receive notification of a comment. 235 * 236 * @see ExtendedLexicalHandler#comment(String) 237 */ 238 public void comment(String data) throws SAXException 239 { 240 final int length = data.length(); 241 if (length > m_charsBuff.length) 242 { 243 m_charsBuff = new char[length * 2 + 1]; 244 } 245 data.getChars(0, length, m_charsBuff, 0); 246 comment(m_charsBuff, 0, length); 247 } 248 249 /** 250 * If at runtime, when the qname of the attribute is 251 * known, another prefix is specified for the attribute, then we can 252 * patch or hack the name with this method. For 253 * a qname of the form "ns?:otherprefix:name", this function patches the 254 * qname by simply ignoring "otherprefix". 255 * TODO: This method is a HACK! We do not have access to the 256 * XML file, it sometimes generates a NS prefix of the form "ns?" for 257 * an attribute. 258 */ 259 protected String patchName(String qname) 260 { 261 262 263 final int lastColon = qname.lastIndexOf(':'); 264 265 if (lastColon > 0) { 266 final int firstColon = qname.indexOf(':'); 267 final String prefix = qname.substring(0, firstColon); 268 final String localName = qname.substring(lastColon + 1); 269 270 // If uri is "" then ignore prefix 271 final String uri = m_prefixMap.lookupNamespace(prefix); 272 if (uri != null && uri.length() == 0) { 273 return localName; 274 } 275 else if (firstColon != lastColon) { 276 return prefix + ':' + localName; 277 } 278 } 279 return qname; 280 } 281 282 /** 283 * Returns the local name of a qualified name. If the name has no prefix, 284 * then it works as the identity (SAX2). 285 * @param qname the qualified name 286 * @return the name, but excluding any prefix and colon. 287 */ 288 protected static String getLocalName(String qname) 289 { 290 final int col = qname.lastIndexOf(':'); 291 return (col > 0) ? qname.substring(col + 1) : qname; 292 } 293 294 /** 295 * Receive an object for locating the origin of SAX document events. 296 * 297 * @param locator An object that can return the location of any SAX document 298 * event. 299 * 300 * Receive an object for locating the origin of SAX document events. 301 * 302 * <p>SAX parsers are strongly encouraged (though not absolutely 303 * required) to supply a locator: if it does so, it must supply 304 * the locator to the application by invoking this method before 305 * invoking any of the other methods in the DocumentHandler 306 * interface.</p> 307 * 308 * <p>The locator allows the application to determine the end 309 * position of any document-related event, even if the parser is 310 * not reporting an error. Typically, the application will 311 * use this information for reporting its own errors (such as 312 * character content that does not match an application's 313 * business rules). The information returned by the locator 314 * is probably not sufficient for use with a search engine.</p> 315 * 316 * <p>Note that the locator will return correct information only 317 * during the invocation of the events in this interface. The 318 * application should not attempt to use it at any other time.</p> 319 */ 320 public void setDocumentLocator(Locator locator) 321 { 322 m_locator = locator; 323 } 324 325 /** 326 * Adds the given attribute to the set of collected attributes , but only if 327 * there is a currently open element. 328 * 329 * An element is currently open if a startElement() notification has 330 * occured but the start of the element has not yet been written to the 331 * output. In the stream case this means that we have not yet been forced 332 * to close the elements opening tag by another notification, such as a 333 * character notification. 334 * 335 * @param uri the URI of the attribute 336 * @param localName the local name of the attribute 337 * @param rawName the qualified name of the attribute 338 * @param type the type of the attribute (probably CDATA) 339 * @param value the value of the attribute 340 * @param XSLAttribute true if this attribute is coming from an xsl:attriute element 341 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) 342 */ 343 public void addAttribute( 344 String uri, 345 String localName, 346 String rawName, 347 String type, 348 String value, 349 boolean XSLAttribute) 350 throws SAXException 351 { 352 if (m_elemContext.m_startTagOpen) 353 { 354 addAttributeAlways(uri, localName, rawName, type, value, XSLAttribute); 355 } 356 357 } 358 359 /** 360 * Adds the given attribute to the set of attributes, even if there is 361 * no currently open element. This is useful if a SAX startPrefixMapping() 362 * should need to add an attribute before the element name is seen. 363 * 364 * @param uri the URI of the attribute 365 * @param localName the local name of the attribute 366 * @param rawName the qualified name of the attribute 367 * @param type the type of the attribute (probably CDATA) 368 * @param value the value of the attribute 369 * @param XSLAttribute true if this attribute is coming from an xsl:attribute element 370 * @return true if the attribute was added, 371 * false if an existing value was replaced. 372 */ 373 public boolean addAttributeAlways( 374 String uri, 375 String localName, 376 String rawName, 377 String type, 378 String value, 379 boolean XSLAttribute) 380 { 381 boolean was_added; 382 // final int index = 383 // (localName == null || uri == null) ? 384 // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName); 385 int index; 386 // if (localName == null || uri == null){ 387 // index = m_attributes.getIndex(rawName); 388 // } 389 // else { 390 // index = m_attributes.getIndex(uri, localName); 391 // } 392 393 if (localName == null || uri == null || uri.length() == 0) 394 index = m_attributes.getIndex(rawName); 395 else { 396 index = m_attributes.getIndex(uri,localName); 397 } 398 if (index >= 0) 399 { 400 /* We've seen the attribute before. 401 * We may have a null uri or localName, but all 402 * we really want to re-set is the value anyway. 403 */ 404 m_attributes.setValue(index,value); 405 was_added = false; 406 } 407 else 408 { 409 // the attribute doesn't exist yet, create it 410 m_attributes.addAttribute(uri, localName, rawName, type, value); 411 was_added = true; 412 } 413 return was_added; 414 415 } 416 417 418 /** 419 * Adds the given attribute to the set of collected attributes, 420 * but only if there is a currently open element. 421 * 422 * @param name the attribute's qualified name 423 * @param value the value of the attribute 424 */ 425 public void addAttribute(String name, final String value) 426 { 427 if (m_elemContext.m_startTagOpen) 428 { 429 final String patchedName = patchName(name); 430 final String localName = getLocalName(patchedName); 431 final String uri = getNamespaceURI(patchedName, false); 432 433 addAttributeAlways(uri,localName, patchedName, "CDATA", value, false); 434 } 435 } 436 437 /** 438 * Adds the given xsl:attribute to the set of collected attributes, 439 * but only if there is a currently open element. 440 * 441 * @param name the attribute's qualified name (prefix:localName) 442 * @param value the value of the attribute 443 * @param uri the URI that the prefix of the name points to 444 */ 445 public void addXSLAttribute(String name, final String value, final String uri) 446 { 447 if (m_elemContext.m_startTagOpen) 448 { 449 final String patchedName = patchName(name); 450 final String localName = getLocalName(patchedName); 451 452 addAttributeAlways(uri,localName, patchedName, "CDATA", value, true); 453 } 454 } 455 456 /** 457 * Add the given attributes to the currently collected ones. These 458 * attributes are always added, regardless of whether on not an element 459 * is currently open. 460 * @param atts List of attributes to add to this list 461 */ 462 public void addAttributes(Attributes atts) throws SAXException 463 { 464 465 int nAtts = atts.getLength(); 466 for (int i = 0; i < nAtts; i++) 467 { 468 String uri = atts.getURI(i); 469 470 if (null == uri) 471 uri = ""; 472 473 addAttributeAlways( 474 uri, 475 atts.getLocalName(i), 476 atts.getQName(i), 477 atts.getType(i), 478 atts.getValue(i), 479 false); 480 481 } 482 } 483 484 /** 485 * Return a {@link ContentHandler} interface into this serializer. 486 * If the serializer does not support the {@link ContentHandler} 487 * interface, it should return null. 488 * 489 * @return A {@link ContentHandler} interface into this serializer, 490 * or null if the serializer is not SAX 2 capable 491 * @throws IOException An I/O exception occured 492 */ 493 public ContentHandler asContentHandler() throws IOException 494 { 495 return this; 496 } 497 498 /** 499 * Report the end of an entity. 500 * 501 * @param name The name of the entity that is ending. 502 * @throws org.xml.sax.SAXException The application may raise an exception. 503 * @see #startEntity 504 */ 505 public void endEntity(String name) throws org.xml.sax.SAXException 506 { 507 if (name.equals("[dtd]")) 508 m_inExternalDTD = false; 509 m_inEntityRef = false; 510 511 if (m_tracer != null) 512 this.fireEndEntity(name); 513 } 514 515 /** 516 * Flush and close the underlying java.io.Writer. This method applies to 517 * ToStream serializers, not ToSAXHandler serializers. 518 * @see ToStream 519 */ 520 public void close() 521 { 522 // do nothing (base behavior) 523 } 524 525 /** 526 * Initialize global variables 527 */ 528 protected void initCDATA() 529 { 530 // CDATA stack 531 // _cdataStack = new Stack(); 532 // _cdataStack.push(new Integer(-1)); // push dummy value 533 } 534 535 /** 536 * Returns the character encoding to be used in the output document. 537 * @return the character encoding to be used in the output document. 538 */ 539 public String getEncoding() 540 { 541 return m_encoding; 542 } 543 544 /** 545 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute. 546 * @param m_encoding the character encoding 547 */ 548 public void setEncoding(String m_encoding) 549 { 550 this.m_encoding = m_encoding; 551 } 552 553 /** 554 * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute 555 * @param b true if the XML declaration is to be omitted from the output 556 * document. 557 */ 558 public void setOmitXMLDeclaration(boolean b) 559 { 560 this.m_shouldNotWriteXMLHeader = b; 561 } 562 563 564 /** 565 * @return true if the XML declaration is to be omitted from the output 566 * document. 567 */ 568 public boolean getOmitXMLDeclaration() 569 { 570 return m_shouldNotWriteXMLHeader; 571 } 572 573 /** 574 * Returns the previously set value of the value to be used as the public 575 * identifier in the document type declaration (DTD). 576 * 577 *@return the public identifier to be used in the DOCTYPE declaration in the 578 * output document. 579 */ 580 public String getDoctypePublic() 581 { 582 return m_doctypePublic; 583 } 584 585 /** Set the value coming from the xsl:output doctype-public stylesheet attribute. 586 * @param doctypePublic the public identifier to be used in the DOCTYPE 587 * declaration in the output document. 588 */ 589 public void setDoctypePublic(String doctypePublic) 590 { 591 this.m_doctypePublic = doctypePublic; 592 } 593 594 595 /** 596 * Returns the previously set value of the value to be used 597 * as the system identifier in the document type declaration (DTD). 598 * @return the system identifier to be used in the DOCTYPE declaration in 599 * the output document. 600 * 601 */ 602 public String getDoctypeSystem() 603 { 604 return m_doctypeSystem; 605 } 606 607 /** Set the value coming from the xsl:output doctype-system stylesheet attribute. 608 * @param doctypeSystem the system identifier to be used in the DOCTYPE 609 * declaration in the output document. 610 */ 611 public void setDoctypeSystem(String doctypeSystem) 612 { 613 this.m_doctypeSystem = doctypeSystem; 614 } 615 616 /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties 617 * @param doctypeSystem the system identifier to be used in the DOCTYPE 618 * declaration in the output document. 619 * @param doctypePublic the public identifier to be used in the DOCTYPE 620 * declaration in the output document. 621 */ 622 public void setDoctype(String doctypeSystem, String doctypePublic) 623 { 624 this.m_doctypeSystem = doctypeSystem; 625 this.m_doctypePublic = doctypePublic; 626 } 627 628 /** 629 * Sets the value coming from the xsl:output standalone stylesheet attribute. 630 * @param standalone a value of "yes" indicates that the 631 * <code>standalone</code> delaration is to be included in the output 632 * document. This method remembers if the value was explicitly set using 633 * this method, verses if the value is the default value. 634 */ 635 public void setStandalone(String standalone) 636 { 637 if (standalone != null) 638 { 639 m_standaloneWasSpecified = true; 640 setStandaloneInternal(standalone); 641 } 642 } 643 /** 644 * Sets the XSL standalone attribute, but does not remember if this is a 645 * default or explicite setting. 646 * @param standalone "yes" | "no" 647 */ 648 protected void setStandaloneInternal(String standalone) 649 { 650 if ("yes".equals(standalone)) 651 m_standalone = "yes"; 652 else 653 m_standalone = "no"; 654 655 } 656 657 /** 658 * Gets the XSL standalone attribute 659 * @return a value of "yes" if the <code>standalone</code> delaration is to 660 * be included in the output document. 661 * @see XSLOutputAttributes#getStandalone() 662 */ 663 public String getStandalone() 664 { 665 return m_standalone; 666 } 667 668 /** 669 * @return true if the output document should be indented to visually 670 * indicate its structure. 671 */ 672 public boolean getIndent() 673 { 674 return m_doIndent; 675 } 676 /** 677 * Gets the mediatype the media-type or MIME type associated with the output 678 * document. 679 * @return the mediatype the media-type or MIME type associated with the 680 * output document. 681 */ 682 public String getMediaType() 683 { 684 return m_mediatype; 685 } 686 687 /** 688 * Gets the version of the output format. 689 * @return the version of the output format. 690 */ 691 public String getVersion() 692 { 693 return m_version; 694 } 695 696 /** 697 * Sets the value coming from the xsl:output version attribute. 698 * @param version the version of the output format. 699 * @see SerializationHandler#setVersion(String) 700 */ 701 public void setVersion(String version) 702 { 703 m_version = version; 704 } 705 706 /** 707 * Sets the value coming from the xsl:output media-type stylesheet attribute. 708 * @param mediaType the non-null media-type or MIME type associated with the 709 * output document. 710 * @see javax.xml.transform.OutputKeys#MEDIA_TYPE 711 * @see SerializationHandler#setMediaType(String) 712 */ 713 public void setMediaType(String mediaType) 714 { 715 m_mediatype = mediaType; 716 } 717 718 /** 719 * @return the number of spaces to indent for each indentation level. 720 */ 721 public int getIndentAmount() 722 { 723 return m_indentAmount; 724 } 725 726 /** 727 * Sets the indentation amount. 728 * @param m_indentAmount The m_indentAmount to set 729 */ 730 public void setIndentAmount(int m_indentAmount) 731 { 732 this.m_indentAmount = m_indentAmount; 733 } 734 735 /** 736 * Sets the value coming from the xsl:output indent stylesheet 737 * attribute. 738 * @param doIndent true if the output document should be indented to 739 * visually indicate its structure. 740 * @see XSLOutputAttributes#setIndent(boolean) 741 */ 742 public void setIndent(boolean doIndent) 743 { 744 m_doIndent = doIndent; 745 } 746 747 /** 748 * Sets the isStandalone property 749 * @param isStandalone true if the ORACLE_IS_STANDALONE is set to yes 750 * @see OutputPropertiesFactory ORACLE_IS_STANDALONE 751 */ 752 public void setIsStandalone(boolean isStandalone) 753 { 754 m_isStandalone = isStandalone; 755 } 756 757 /** 758 * This method is used when a prefix/uri namespace mapping 759 * is indicated after the element was started with a 760 * startElement() and before and endElement(). 761 * startPrefixMapping(prefix,uri) would be used before the 762 * startElement() call. 763 * @param uri the URI of the namespace 764 * @param prefix the prefix associated with the given URI. 765 * 766 * @see ExtendedContentHandler#namespaceAfterStartElement(String, String) 767 */ 768 public void namespaceAfterStartElement(String uri, String prefix) 769 throws SAXException 770 { 771 // default behavior is to do nothing 772 } 773 774 /** 775 * Return a {@link DOMSerializer} interface into this serializer. If the 776 * serializer does not support the {@link DOMSerializer} interface, it should 777 * return null. 778 * 779 * @return A {@link DOMSerializer} interface into this serializer, or null 780 * if the serializer is not DOM capable 781 * @throws IOException An I/O exception occured 782 * @see Serializer#asDOMSerializer() 783 */ 784 public DOMSerializer asDOMSerializer() throws IOException 785 { 786 return this; 787 } 788 789 /** 790 * Push a boolean state based on if the name of the current element 791 * is found in the list of qnames. A state is only pushed if 792 * there were some cdata-section-names were specified. 793 * <p> 794 * Hidden parameters are the vector of qualified elements specified in 795 * cdata-section-names attribute, and the m_cdataSectionStates stack 796 * onto which whether the current element is in the list is pushed (true or 797 * false). Other hidden parameters are the current elements namespaceURI, 798 * localName and qName 799 */ 800 protected boolean isCdataSection() 801 { 802 803 boolean b = false; 804 805 if (null != m_cdataSectionElements) 806 { 807 if (m_elemContext.m_elementLocalName == null) 808 m_elemContext.m_elementLocalName = 809 getLocalName(m_elemContext.m_elementName); 810 if (m_elemContext.m_elementURI == null) 811 { 812 String prefix = getPrefixPart(m_elemContext.m_elementName); 813 if (prefix != null) 814 m_elemContext.m_elementURI = 815 m_prefixMap.lookupNamespace(prefix); 816 817 } 818 819 if ((null != m_elemContext.m_elementURI) 820 && m_elemContext.m_elementURI.length() == 0) 821 m_elemContext.m_elementURI = null; 822 823 int nElems = m_cdataSectionElements.size(); 824 825 // loop through 2 at a time, as these are pairs of URI and localName 826 for (int i = 0; i < nElems; i += 2) 827 { 828 String uri = (String) m_cdataSectionElements.elementAt(i); 829 String loc = (String) m_cdataSectionElements.elementAt(i + 1); 830 if (loc.equals(m_elemContext.m_elementLocalName) 831 && subPartMatch(m_elemContext.m_elementURI, uri)) 832 { 833 b = true; 834 835 break; 836 } 837 } 838 } 839 return b; 840 } 841 842 /** 843 * Tell if two strings are equal, without worry if the first string is null. 844 * 845 * @param p String reference, which may be null. 846 * @param t String reference, which may be null. 847 * 848 * @return true if strings are equal. 849 */ 850 private static final boolean subPartMatch(String p, String t) 851 { 852 return (p == t) || ((null != p) && (p.equals(t))); 853 } 854 855 /** 856 * Returns the local name of a qualified name. 857 * If the name has no prefix, 858 * then it works as the identity (SAX2). 859 * 860 * @param qname a qualified name 861 * @return returns the prefix of the qualified name, 862 * or null if there is no prefix. 863 */ 864 protected static final String getPrefixPart(String qname) 865 { 866 final int col = qname.indexOf(':'); 867 return (col > 0) ? qname.substring(0, col) : null; 868 //return (col > 0) ? qname.substring(0,col) : ""; 869 } 870 871 /** 872 * Some users of the serializer may need the current namespace mappings 873 * @return the current namespace mappings (prefix/uri) 874 * @see ExtendedContentHandler#getNamespaceMappings() 875 */ 876 public NamespaceMappings getNamespaceMappings() 877 { 878 return m_prefixMap; 879 } 880 881 /** 882 * Returns the prefix currently pointing to the given URI (if any). 883 * @param namespaceURI the uri of the namespace in question 884 * @return a prefix pointing to the given URI (if any). 885 * @see ExtendedContentHandler#getPrefix(String) 886 */ 887 public String getPrefix(String namespaceURI) 888 { 889 String prefix = m_prefixMap.lookupPrefix(namespaceURI); 890 return prefix; 891 } 892 893 /** 894 * Returns the URI of an element or attribute. Note that default namespaces 895 * do not apply directly to attributes. 896 * @param qname a qualified name 897 * @param isElement true if the qualified name is the name of 898 * an element. 899 * @return returns the namespace URI associated with the qualified name. 900 */ 901 public String getNamespaceURI(String qname, boolean isElement) 902 { 903 String uri = EMPTYSTRING; 904 int col = qname.lastIndexOf(':'); 905 final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING; 906 907 if (!EMPTYSTRING.equals(prefix) || isElement) 908 { 909 if (m_prefixMap != null) 910 { 911 uri = m_prefixMap.lookupNamespace(prefix); 912 if (uri == null && !prefix.equals(XMLNS_PREFIX)) 913 { 914 throw new RuntimeException( 915 Utils.messages.createMessage( 916 MsgKey.ER_NAMESPACE_PREFIX, 917 new Object[] { qname.substring(0, col) } )); 918 } 919 } 920 } 921 return uri; 922 } 923 924 /** 925 * Returns the URI of prefix (if any) 926 * 927 * @param prefix the prefix whose URI is searched for 928 * @return the namespace URI currently associated with the 929 * prefix, null if the prefix is undefined. 930 */ 931 public String getNamespaceURIFromPrefix(String prefix) 932 { 933 String uri = null; 934 if (m_prefixMap != null) 935 uri = m_prefixMap.lookupNamespace(prefix); 936 return uri; 937 } 938 939 /** 940 * Entity reference event. 941 * 942 * @param name Name of entity 943 * 944 * @throws org.xml.sax.SAXException 945 */ 946 public void entityReference(String name) throws org.xml.sax.SAXException 947 { 948 949 flushPending(); 950 951 startEntity(name); 952 endEntity(name); 953 954 if (m_tracer != null) 955 fireEntityReference(name); 956 } 957 958 /** 959 * Sets the transformer associated with this serializer 960 * @param t the transformer associated with this serializer. 961 * @see SerializationHandler#setTransformer(Transformer) 962 */ 963 public void setTransformer(Transformer t) 964 { 965 m_transformer = t; 966 967 // If this transformer object implements the SerializerTrace interface 968 // then assign m_tracer to the transformer object so it can be used 969 // to fire trace events. 970 if ((m_transformer instanceof SerializerTrace) && 971 (((SerializerTrace) m_transformer).hasTraceListeners())) { 972 m_tracer = (SerializerTrace) m_transformer; 973 } else { 974 m_tracer = null; 975 } 976 } 977 /** 978 * Gets the transformer associated with this serializer 979 * @return returns the transformer associated with this serializer. 980 * @see SerializationHandler#getTransformer() 981 */ 982 public Transformer getTransformer() 983 { 984 return m_transformer; 985 } 986 987 /** 988 * This method gets the nodes value as a String and uses that String as if 989 * it were an input character notification. 990 * @param node the Node to serialize 991 * @throws org.xml.sax.SAXException 992 */ 993 public void characters(org.w3c.dom.Node node) 994 throws org.xml.sax.SAXException 995 { 996 flushPending(); 997 String data = node.getNodeValue(); 998 if (data != null) 999 { 1000 final int length = data.length(); 1001 if (length > m_charsBuff.length) 1002 { 1003 m_charsBuff = new char[length * 2 + 1]; 1004 } 1005 data.getChars(0, length, m_charsBuff, 0); 1006 characters(m_charsBuff, 0, length); 1007 } 1008 } 1009 1010 1011 /** 1012 * @see org.xml.sax.ErrorHandler#error(SAXParseException) 1013 */ 1014 public void error(SAXParseException exc) throws SAXException { 1015 } 1016 1017 /** 1018 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) 1019 */ 1020 public void fatalError(SAXParseException exc) throws SAXException { 1021 1022 m_elemContext.m_startTagOpen = false; 1023 1024 } 1025 1026 /** 1027 * @see org.xml.sax.ErrorHandler#warning(SAXParseException) 1028 */ 1029 public void warning(SAXParseException exc) throws SAXException 1030 { 1031 } 1032 1033 /** 1034 * To fire off start entity trace event 1035 * @param name Name of entity 1036 */ 1037 protected void fireStartEntity(String name) 1038 throws org.xml.sax.SAXException 1039 { 1040 if (m_tracer != null) 1041 { 1042 flushMyWriter(); 1043 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name); 1044 } 1045 } 1046 1047 /** 1048 * Report the characters event 1049 * @param chars content of characters 1050 * @param start starting index of characters to output 1051 * @param length number of characters to output 1052 */ 1053 // protected void fireCharEvent(char[] chars, int start, int length) 1054 // throws org.xml.sax.SAXException 1055 // { 1056 // if (m_tracer != null) 1057 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length); 1058 // } 1059 // 1060 1061 /** 1062 * This method is only used internally when flushing the writer from the 1063 * various fire...() trace events. Due to the writer being wrapped with 1064 * SerializerTraceWriter it may cause the flush of these trace events: 1065 * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS 1066 * EVENTTYPE_OUTPUT_CHARACTERS 1067 * which trace the output written to the output stream. 1068 * 1069 */ 1070 private void flushMyWriter() 1071 { 1072 if (m_writer != null) 1073 { 1074 try 1075 { 1076 m_writer.flush(); 1077 } 1078 catch(IOException ioe) 1079 { 1080 1081 } 1082 } 1083 } 1084 /** 1085 * Report the CDATA trace event 1086 * @param chars content of CDATA 1087 * @param start starting index of characters to output 1088 * @param length number of characters to output 1089 */ 1090 protected void fireCDATAEvent(char[] chars, int start, int length) 1091 throws org.xml.sax.SAXException 1092 { 1093 if (m_tracer != null) 1094 { 1095 flushMyWriter(); 1096 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length); 1097 } 1098 } 1099 1100 /** 1101 * Report the comment trace event 1102 * @param chars content of comment 1103 * @param start starting index of comment to output 1104 * @param length number of characters to output 1105 */ 1106 protected void fireCommentEvent(char[] chars, int start, int length) 1107 throws org.xml.sax.SAXException 1108 { 1109 if (m_tracer != null) 1110 { 1111 flushMyWriter(); 1112 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length)); 1113 } 1114 } 1115 1116 1117 /** 1118 * To fire off end entity trace event 1119 * @param name Name of entity 1120 */ 1121 public void fireEndEntity(String name) 1122 throws org.xml.sax.SAXException 1123 { 1124 if (m_tracer != null) 1125 flushMyWriter(); 1126 // we do not need to handle this. 1127 } 1128 1129 /** 1130 * To fire off start document trace event 1131 */ 1132 protected void fireStartDoc() 1133 throws org.xml.sax.SAXException 1134 { 1135 if (m_tracer != null) 1136 { 1137 flushMyWriter(); 1138 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT); 1139 } 1140 } 1141 1142 1143 /** 1144 * To fire off end document trace event 1145 */ 1146 protected void fireEndDoc() 1147 throws org.xml.sax.SAXException 1148 { 1149 if (m_tracer != null) 1150 { 1151 flushMyWriter(); 1152 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT); 1153 } 1154 } 1155 1156 /** 1157 * Report the start element trace event. This trace method needs to be 1158 * called just before the attributes are cleared. 1159 * 1160 * @param elemName the qualified name of the element 1161 * 1162 */ 1163 protected void fireStartElem(String elemName) 1164 throws org.xml.sax.SAXException 1165 { 1166 if (m_tracer != null) 1167 { 1168 flushMyWriter(); 1169 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT, 1170 elemName, m_attributes); 1171 } 1172 } 1173 1174 1175 /** 1176 * To fire off the end element event 1177 * @param name Name of element 1178 */ 1179 // protected void fireEndElem(String name) 1180 // throws org.xml.sax.SAXException 1181 // { 1182 // if (m_tracer != null) 1183 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null); 1184 // } 1185 1186 1187 /** 1188 * To fire off the PI trace event 1189 * @param name Name of PI 1190 */ 1191 protected void fireEscapingEvent(String name, String data) 1192 throws org.xml.sax.SAXException 1193 { 1194 1195 if (m_tracer != null) 1196 { 1197 flushMyWriter(); 1198 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data); 1199 } 1200 } 1201 1202 1203 /** 1204 * To fire off the entity reference trace event 1205 * @param name Name of entity reference 1206 */ 1207 protected void fireEntityReference(String name) 1208 throws org.xml.sax.SAXException 1209 { 1210 if (m_tracer != null) 1211 { 1212 flushMyWriter(); 1213 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null); 1214 } 1215 } 1216 1217 /** 1218 * Receive notification of the beginning of a document. 1219 * This method is never a self generated call, 1220 * but only called externally. 1221 * 1222 * <p>The SAX parser will invoke this method only once, before any 1223 * other methods in this interface or in DTDHandler (except for 1224 * setDocumentLocator).</p> 1225 * 1226 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1227 * wrapping another exception. 1228 * 1229 * @throws org.xml.sax.SAXException 1230 */ 1231 public void startDocument() throws org.xml.sax.SAXException 1232 { 1233 1234 // if we do get called with startDocument(), handle it right away 1235 startDocumentInternal(); 1236 m_needToCallStartDocument = false; 1237 return; 1238 } 1239 1240 /** 1241 * This method handles what needs to be done at a startDocument() call, 1242 * whether from an external caller, or internally called in the 1243 * serializer. For historical reasons the serializer is flexible to 1244 * startDocument() not always being called. 1245 * Even if no external call is 1246 * made into startDocument() this method will always be called as a self 1247 * generated internal startDocument, it handles what needs to be done at a 1248 * startDocument() call. 1249 * 1250 * This method exists just to make sure that startDocument() is only ever 1251 * called from an external caller, which in principle is just a matter of 1252 * style. 1253 * 1254 * @throws SAXException 1255 */ 1256 protected void startDocumentInternal() throws org.xml.sax.SAXException 1257 { 1258 if (m_tracer != null) 1259 this.fireStartDoc(); 1260 1261 } 1262 1263 /* This method extracts version and encoding information from SAX events. 1264 */ 1265 protected void setDocumentInfo() { 1266 if (m_locator == null) 1267 return; 1268 try{ 1269 String strVersion = ((Locator2)m_locator).getXMLVersion(); 1270 if (strVersion != null) 1271 setVersion(strVersion); 1272 /*String strEncoding = ((Locator2)m_locator).getEncoding(); 1273 if (strEncoding != null) 1274 setEncoding(strEncoding); */ 1275 1276 }catch(ClassCastException e){} 1277 } 1278 1279 /** 1280 * This method is used to set the source locator, which might be used to 1281 * generated an error message. 1282 * @param locator the source locator 1283 * 1284 * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator) 1285 */ 1286 public void setSourceLocator(SourceLocator locator) 1287 { 1288 m_sourceLocator = locator; 1289 } 1290 1291 1292 /** 1293 * Used only by TransformerSnapshotImpl to restore the serialization 1294 * to a previous state. 1295 * 1296 * @param mappings NamespaceMappings 1297 */ 1298 public void setNamespaceMappings(NamespaceMappings mappings) { 1299 m_prefixMap = mappings; 1300 } 1301 1302 public boolean reset() 1303 { 1304 resetSerializerBase(); 1305 return true; 1306 } 1307 1308 /** 1309 * Reset all of the fields owned by SerializerBase 1310 * 1311 */ 1312 private void resetSerializerBase() 1313 { 1314 this.m_attributes.clear(); 1315 this.m_cdataSectionElements = null; 1316 this.m_elemContext = new ElemContext(); 1317 this.m_doctypePublic = null; 1318 this.m_doctypeSystem = null; 1319 this.m_doIndent = false; 1320 this.m_encoding = null; 1321 this.m_indentAmount = 0; 1322 this.m_inEntityRef = false; 1323 this.m_inExternalDTD = false; 1324 this.m_mediatype = null; 1325 this.m_needToCallStartDocument = true; 1326 this.m_needToOutputDocTypeDecl = false; 1327 if (this.m_prefixMap != null) 1328 this.m_prefixMap.reset(); 1329 this.m_shouldNotWriteXMLHeader = false; 1330 this.m_sourceLocator = null; 1331 this.m_standalone = null; 1332 this.m_standaloneWasSpecified = false; 1333 this.m_tracer = null; 1334 this.m_transformer = null; 1335 this.m_version = null; 1336 // don't set writer to null, so that it might be re-used 1337 //this.m_writer = null; 1338 } 1339 1340 /** 1341 * Returns true if the serializer is used for temporary output rather than 1342 * final output. 1343 * 1344 * This concept is made clear in the XSLT 2.0 draft. 1345 */ 1346 final boolean inTemporaryOutputState() 1347 { 1348 /* This is a hack. We should really be letting the serializer know 1349 * that it is in temporary output state with an explicit call, but 1350 * from a pragmatic point of view (for now anyways) having no output 1351 * encoding at all, not even the default UTF-8 indicates that the serializer 1352 * is being used for temporary RTF. 1353 */ 1354 return (getEncoding() == null); 1355 1356 } 1357 1358 /** 1359 * This method adds an attribute the the current element, 1360 * but should not be used for an xsl:attribute child. 1361 * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) 1362 */ 1363 public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException 1364 { 1365 if (m_elemContext.m_startTagOpen) 1366 { 1367 addAttributeAlways(uri, localName, rawName, type, value, false); 1368 } 1369 } 1370 1371 /** 1372 * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String) 1373 */ 1374 public void notationDecl(String arg0, String arg1, String arg2) 1375 throws SAXException { 1376 // This method just provides a definition to satisfy the interface 1377 // A particular sub-class of SerializerBase provides the implementation (if desired) 1378 } 1379 1380 /** 1381 * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String) 1382 */ 1383 public void unparsedEntityDecl( 1384 String arg0, 1385 String arg1, 1386 String arg2, 1387 String arg3) 1388 throws SAXException { 1389 // This method just provides a definition to satisfy the interface 1390 // A particular sub-class of SerializerBase provides the implementation (if desired) 1391 } 1392 1393 /** 1394 * If set to false the serializer does not expand DTD entities, 1395 * but leaves them as is, the default value is true. 1396 */ 1397 public void setDTDEntityExpansion(boolean expand) { 1398 // This method just provides a definition to satisfy the interface 1399 // A particular sub-class of SerializerBase provides the implementation (if desired) 1400 } 1401 1402 }