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