1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: SerializerBase.java 471981 2006-11-07 04:28:00Z minchau $ 20 */ 21 package org.apache.xml.serializer; 22 23 import java.io.IOException; 24 import java.util.HashMap; 25 import java.util.Set; 26 27 import javax.xml.transform.OutputKeys; 28 import javax.xml.transform.SourceLocator; 29 import javax.xml.transform.Transformer; 30 31 import org.apache.xml.serializer.utils.MsgKey; 32 import org.apache.xml.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 39 40 /** 41 * This class acts as a base class for the XML "serializers" 42 * and the stream serializers. 43 * It contains a number of common fields and methods. 44 * 45 * @xsl.usage internal 46 */ 47 public abstract class SerializerBase 48 implements SerializationHandler, SerializerConstants 49 { SerializerBase()50 SerializerBase() { 51 return; 52 } 53 54 /** 55 * The name of the package that this class is in. 56 * <p> 57 * Not a public API. 58 */ 59 public static final String PKG_NAME; 60 61 /** 62 * The same as the name of the package that this class is in 63 * except that '.' are replaced with '/'. 64 * <p> 65 * Not a public API. 66 */ 67 public static final String PKG_PATH; 68 69 static { 70 String fullyQualifiedName = SerializerBase.class.getName(); 71 int lastDot = fullyQualifiedName.lastIndexOf('.'); 72 if (lastDot < 0) { 73 PKG_NAME = ""; 74 } else { 75 PKG_NAME = fullyQualifiedName.substring(0, lastDot); 76 } 77 78 StringBuffer sb = new StringBuffer(); 79 for (int i = 0; i < PKG_NAME.length(); i++) { 80 char ch = PKG_NAME.charAt(i); 81 if (ch == '.') 82 sb.append('/'); 83 else 84 sb.append(ch); 85 } 86 PKG_PATH = sb.toString(); 87 } 88 89 90 91 /** 92 * To fire off the end element trace event 93 * @param name Name of element 94 */ fireEndElem(String name)95 protected void fireEndElem(String name) 96 throws org.xml.sax.SAXException 97 { 98 if (m_tracer != null) 99 { 100 flushMyWriter(); 101 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null); 102 } 103 } 104 105 /** 106 * Report the characters trace event 107 * @param chars content of characters 108 * @param start starting index of characters to output 109 * @param length number of characters to output 110 */ fireCharEvent(char[] chars, int start, int length)111 protected void fireCharEvent(char[] chars, int start, int length) 112 throws org.xml.sax.SAXException 113 { 114 if (m_tracer != null) 115 { 116 flushMyWriter(); 117 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length); 118 } 119 } 120 121 /** 122 * true if we still need to call startDocumentInternal() 123 */ 124 protected boolean m_needToCallStartDocument = true; 125 126 /** True if a trailing "]]>" still needs to be written to be 127 * written out. Used to merge adjacent CDATA sections 128 */ 129 protected boolean m_cdataTagOpen = false; 130 131 /** 132 * All the attributes of the current element, collected from 133 * startPrefixMapping() calls, or addAddtribute() calls, or 134 * from the SAX attributes in a startElement() call. 135 */ 136 protected AttributesImplSerializer m_attributes = new AttributesImplSerializer(); 137 138 /** 139 * Tells if we're in an EntityRef event. 140 */ 141 protected boolean m_inEntityRef = false; 142 143 /** This flag is set while receiving events from the external DTD */ 144 protected boolean m_inExternalDTD = false; 145 146 /** 147 * The System ID for the doc type. 148 */ 149 protected String m_doctypeSystem; 150 151 /** 152 * The public ID for the doc type. 153 */ 154 protected String m_doctypePublic; 155 156 /** 157 * Flag to tell that we need to add the doctype decl, which we can't do 158 * until the first element is encountered. 159 */ 160 boolean m_needToOutputDocTypeDecl = true; 161 162 /** 163 * Tells if we should write the XML declaration. 164 */ 165 protected boolean m_shouldNotWriteXMLHeader = false; 166 167 /** 168 * The standalone value for the doctype. 169 */ 170 private String m_standalone; 171 172 /** 173 * True if standalone was specified. 174 */ 175 protected boolean m_standaloneWasSpecified = false; 176 177 /** 178 * Flag to tell if indenting (pretty-printing) is on. 179 */ 180 protected boolean m_doIndent = false; 181 /** 182 * Amount to indent. 183 */ 184 protected int m_indentAmount = 0; 185 186 /** 187 * Tells the XML version, for writing out to the XML decl. 188 */ 189 protected String m_version = null; 190 191 /** 192 * The mediatype. Not used right now. 193 */ 194 protected String m_mediatype; 195 196 /** 197 * The transformer that was around when this output handler was created (if 198 * any). 199 */ 200 private Transformer m_transformer; 201 202 /** 203 * Namespace support, that keeps track of currently defined 204 * prefix/uri mappings. As processed elements come and go, so do 205 * the associated mappings for that element. 206 */ 207 protected NamespaceMappings m_prefixMap; 208 209 /** 210 * Handle for firing generate events. This interface may be implemented 211 * by the referenced transformer object. 212 */ 213 protected SerializerTrace m_tracer; 214 215 protected SourceLocator m_sourceLocator; 216 217 218 /** 219 * The writer to send output to. This field is only used in the ToStream 220 * serializers, but exists here just so that the fireStartDoc() and 221 * other fire... methods can flush this writer when tracing. 222 */ 223 protected java.io.Writer m_writer = null; 224 225 /** 226 * A reference to "stack frame" corresponding to 227 * the current element. Such a frame is pushed at a startElement() 228 * and popped at an endElement(). This frame contains information about 229 * the element, such as its namespace URI. 230 */ 231 protected ElemContext m_elemContext = new ElemContext(); 232 233 /** 234 * A utility buffer for converting Strings passed to 235 * character() methods to character arrays. 236 * Reusing this buffer means not creating a new character array 237 * everytime and it runs faster. 238 */ 239 protected char[] m_charsBuff = new char[60]; 240 241 /** 242 * A utility buffer for converting Strings passed to 243 * attribute methods to character arrays. 244 * Reusing this buffer means not creating a new character array 245 * everytime and it runs faster. 246 */ 247 protected char[] m_attrBuff = new char[30]; 248 249 /** 250 * Receive notification of a comment. 251 * 252 * @see ExtendedLexicalHandler#comment(String) 253 */ comment(String data)254 public void comment(String data) throws SAXException 255 { 256 m_docIsEmpty = false; 257 258 final int length = data.length(); 259 if (length > m_charsBuff.length) 260 { 261 m_charsBuff = new char[length * 2 + 1]; 262 } 263 data.getChars(0, length, m_charsBuff, 0); 264 comment(m_charsBuff, 0, length); 265 } 266 267 /** 268 * If at runtime, when the qname of the attribute is 269 * known, another prefix is specified for the attribute, then we can 270 * patch or hack the name with this method. For 271 * a qname of the form "ns?:otherprefix:name", this function patches the 272 * qname by simply ignoring "otherprefix". 273 * TODO: This method is a HACK! We do not have access to the 274 * XML file, it sometimes generates a NS prefix of the form "ns?" for 275 * an attribute. 276 */ patchName(String qname)277 protected String patchName(String qname) 278 { 279 280 281 final int lastColon = qname.lastIndexOf(':'); 282 283 if (lastColon > 0) { 284 final int firstColon = qname.indexOf(':'); 285 final String prefix = qname.substring(0, firstColon); 286 final String localName = qname.substring(lastColon + 1); 287 288 // If uri is "" then ignore prefix 289 final String uri = m_prefixMap.lookupNamespace(prefix); 290 if (uri != null && uri.length() == 0) { 291 return localName; 292 } 293 else if (firstColon != lastColon) { 294 return prefix + ':' + localName; 295 } 296 } 297 return qname; 298 } 299 300 /** 301 * Returns the local name of a qualified name. If the name has no prefix, 302 * then it works as the identity (SAX2). 303 * @param qname the qualified name 304 * @return the name, but excluding any prefix and colon. 305 */ getLocalName(String qname)306 protected static String getLocalName(String qname) 307 { 308 final int col = qname.lastIndexOf(':'); 309 return (col > 0) ? qname.substring(col + 1) : qname; 310 } 311 312 /** 313 * Receive an object for locating the origin of SAX document events. 314 * 315 * @param locator An object that can return the location of any SAX document 316 * event. 317 * 318 * Receive an object for locating the origin of SAX document events. 319 * 320 * <p>SAX parsers are strongly encouraged (though not absolutely 321 * required) to supply a locator: if it does so, it must supply 322 * the locator to the application by invoking this method before 323 * invoking any of the other methods in the DocumentHandler 324 * interface.</p> 325 * 326 * <p>The locator allows the application to determine the end 327 * position of any document-related event, even if the parser is 328 * not reporting an error. Typically, the application will 329 * use this information for reporting its own errors (such as 330 * character content that does not match an application's 331 * business rules). The information returned by the locator 332 * is probably not sufficient for use with a search engine.</p> 333 * 334 * <p>Note that the locator will return correct information only 335 * during the invocation of the events in this interface. The 336 * application should not attempt to use it at any other time.</p> 337 */ setDocumentLocator(Locator locator)338 public void setDocumentLocator(Locator locator) 339 { 340 return; 341 342 // I don't do anything with this yet. 343 } 344 345 /** 346 * Adds the given attribute to the set of collected attributes , but only if 347 * there is a currently open element. 348 * 349 * An element is currently open if a startElement() notification has 350 * occured but the start of the element has not yet been written to the 351 * output. In the stream case this means that we have not yet been forced 352 * to close the elements opening tag by another notification, such as a 353 * character notification. 354 * 355 * @param uri the URI of the attribute 356 * @param localName the local name of the attribute 357 * @param rawName the qualified name of the attribute 358 * @param type the type of the attribute (probably CDATA) 359 * @param value the value of the attribute 360 * @param XSLAttribute true if this attribute is coming from an xsl:attriute element 361 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) 362 */ addAttribute( String uri, String localName, String rawName, String type, String value, boolean XSLAttribute)363 public void addAttribute( 364 String uri, 365 String localName, 366 String rawName, 367 String type, 368 String value, 369 boolean XSLAttribute) 370 throws SAXException 371 { 372 if (m_elemContext.m_startTagOpen) 373 { 374 addAttributeAlways(uri, localName, rawName, type, value, XSLAttribute); 375 } 376 377 } 378 379 /** 380 * Adds the given attribute to the set of attributes, even if there is 381 * no currently open element. This is useful if a SAX startPrefixMapping() 382 * should need to add an attribute before the element name is seen. 383 * 384 * @param uri the URI of the attribute 385 * @param localName the local name of the attribute 386 * @param rawName the qualified name of the attribute 387 * @param type the type of the attribute (probably CDATA) 388 * @param value the value of the attribute 389 * @param XSLAttribute true if this attribute is coming from an xsl:attribute element 390 * @return true if the attribute was added, 391 * false if an existing value was replaced. 392 */ addAttributeAlways( String uri, String localName, String rawName, String type, String value, boolean XSLAttribute)393 public boolean addAttributeAlways( 394 String uri, 395 String localName, 396 String rawName, 397 String type, 398 String value, 399 boolean XSLAttribute) 400 { 401 boolean was_added; 402 // final int index = 403 // (localName == null || uri == null) ? 404 // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName); 405 int index; 406 // if (localName == null || uri == null){ 407 // index = m_attributes.getIndex(rawName); 408 // } 409 // else { 410 // index = m_attributes.getIndex(uri, localName); 411 // } 412 if (localName == null || uri == null || uri.length() == 0) 413 index = m_attributes.getIndex(rawName); 414 else { 415 index = m_attributes.getIndex(uri,localName); 416 } 417 if (index >= 0) 418 { 419 /* We've seen the attribute before. 420 * We may have a null uri or localName, but all 421 * we really want to re-set is the value anyway. 422 */ 423 m_attributes.setValue(index,value); 424 was_added = false; 425 } 426 else 427 { 428 // the attribute doesn't exist yet, create it 429 m_attributes.addAttribute(uri, localName, rawName, type, value); 430 was_added = true; 431 } 432 return was_added; 433 434 } 435 436 437 /** 438 * Adds the given 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 442 * @param value the value of the attribute 443 */ addAttribute(String name, final String value)444 public void addAttribute(String name, final String value) 445 { 446 if (m_elemContext.m_startTagOpen) 447 { 448 final String patchedName = patchName(name); 449 final String localName = getLocalName(patchedName); 450 final String uri = getNamespaceURI(patchedName, false); 451 452 addAttributeAlways(uri,localName, patchedName, "CDATA", value, false); 453 } 454 } 455 456 /** 457 * Adds the given xsl:attribute to the set of collected attributes, 458 * but only if there is a currently open element. 459 * 460 * @param name the attribute's qualified name (prefix:localName) 461 * @param value the value of the attribute 462 * @param uri the URI that the prefix of the name points to 463 */ addXSLAttribute(String name, final String value, final String uri)464 public void addXSLAttribute(String name, final String value, final String uri) 465 { 466 if (m_elemContext.m_startTagOpen) 467 { 468 final String patchedName = patchName(name); 469 final String localName = getLocalName(patchedName); 470 471 addAttributeAlways(uri,localName, patchedName, "CDATA", value, true); 472 } 473 } 474 475 /** 476 * Add the given attributes to the currently collected ones. These 477 * attributes are always added, regardless of whether on not an element 478 * is currently open. 479 * @param atts List of attributes to add to this list 480 */ addAttributes(Attributes atts)481 public void addAttributes(Attributes atts) throws SAXException 482 { 483 484 int nAtts = atts.getLength(); 485 486 for (int i = 0; i < nAtts; i++) 487 { 488 String uri = atts.getURI(i); 489 490 if (null == uri) 491 uri = ""; 492 493 addAttributeAlways( 494 uri, 495 atts.getLocalName(i), 496 atts.getQName(i), 497 atts.getType(i), 498 atts.getValue(i), 499 false); 500 501 } 502 } 503 504 /** 505 * Return a {@link ContentHandler} interface into this serializer. 506 * If the serializer does not support the {@link ContentHandler} 507 * interface, it should return null. 508 * 509 * @return A {@link ContentHandler} interface into this serializer, 510 * or null if the serializer is not SAX 2 capable 511 * @throws IOException An I/O exception occured 512 */ asContentHandler()513 public ContentHandler asContentHandler() throws IOException 514 { 515 return this; 516 } 517 518 /** 519 * Report the end of an entity. 520 * 521 * @param name The name of the entity that is ending. 522 * @throws org.xml.sax.SAXException The application may raise an exception. 523 * @see #startEntity 524 */ endEntity(String name)525 public void endEntity(String name) throws org.xml.sax.SAXException 526 { 527 if (name.equals("[dtd]")) 528 m_inExternalDTD = false; 529 m_inEntityRef = false; 530 531 if (m_tracer != null) 532 this.fireEndEntity(name); 533 } 534 535 /** 536 * Flush and close the underlying java.io.Writer. This method applies to 537 * ToStream serializers, not ToSAXHandler serializers. 538 * @see ToStream 539 */ close()540 public void close() 541 { 542 // do nothing (base behavior) 543 } 544 545 /** 546 * Initialize global variables 547 */ initCDATA()548 protected void initCDATA() 549 { 550 // CDATA stack 551 // _cdataStack = new Stack(); 552 // _cdataStack.push(new Integer(-1)); // push dummy value 553 } 554 555 /** 556 * Returns the character encoding to be used in the output document. 557 * @return the character encoding to be used in the output document. 558 */ getEncoding()559 public String getEncoding() 560 { 561 return getOutputProperty(OutputKeys.ENCODING); 562 } 563 564 /** 565 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute. 566 * @param m_encoding the character encoding 567 */ setEncoding(String encoding)568 public void setEncoding(String encoding) 569 { 570 setOutputProperty(OutputKeys.ENCODING,encoding); 571 } 572 573 /** 574 * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute 575 * @param b true if the XML declaration is to be omitted from the output 576 * document. 577 */ setOmitXMLDeclaration(boolean b)578 public void setOmitXMLDeclaration(boolean b) 579 { 580 String val = b ? "yes":"no"; 581 setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,val); 582 } 583 584 585 /** 586 * @return true if the XML declaration is to be omitted from the output 587 * document. 588 */ getOmitXMLDeclaration()589 public boolean getOmitXMLDeclaration() 590 { 591 return m_shouldNotWriteXMLHeader; 592 } 593 594 /** 595 * Returns the previously set value of the value to be used as the public 596 * identifier in the document type declaration (DTD). 597 * 598 *@return the public identifier to be used in the DOCTYPE declaration in the 599 * output document. 600 */ getDoctypePublic()601 public String getDoctypePublic() 602 { 603 return m_doctypePublic; 604 } 605 606 /** Set the value coming from the xsl:output doctype-public stylesheet attribute. 607 * @param doctypePublic the public identifier to be used in the DOCTYPE 608 * declaration in the output document. 609 */ setDoctypePublic(String doctypePublic)610 public void setDoctypePublic(String doctypePublic) 611 { 612 setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic); 613 } 614 615 616 /** 617 * Returns the previously set value of the value to be used 618 * as the system identifier in the document type declaration (DTD). 619 * @return the system identifier to be used in the DOCTYPE declaration in 620 * the output document. 621 * 622 */ getDoctypeSystem()623 public String getDoctypeSystem() 624 { 625 return m_doctypeSystem; 626 } 627 628 /** Set the value coming from the xsl:output doctype-system stylesheet attribute. 629 * @param doctypeSystem the system identifier to be used in the DOCTYPE 630 * declaration in the output document. 631 */ setDoctypeSystem(String doctypeSystem)632 public void setDoctypeSystem(String doctypeSystem) 633 { 634 setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem); 635 } 636 637 /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties 638 * @param doctypeSystem the system identifier to be used in the DOCTYPE 639 * declaration in the output document. 640 * @param doctypePublic the public identifier to be used in the DOCTYPE 641 * declaration in the output document. 642 */ setDoctype(String doctypeSystem, String doctypePublic)643 public void setDoctype(String doctypeSystem, String doctypePublic) 644 { 645 setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem); 646 setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic); 647 } 648 649 /** 650 * Sets the value coming from the xsl:output standalone stylesheet attribute. 651 * @param standalone a value of "yes" indicates that the 652 * <code>standalone</code> delaration is to be included in the output 653 * document. This method remembers if the value was explicitly set using 654 * this method, verses if the value is the default value. 655 */ setStandalone(String standalone)656 public void setStandalone(String standalone) 657 { 658 setOutputProperty(OutputKeys.STANDALONE, standalone); 659 } 660 /** 661 * Sets the XSL standalone attribute, but does not remember if this is a 662 * default or explicite setting. 663 * @param standalone "yes" | "no" 664 */ setStandaloneInternal(String standalone)665 protected void setStandaloneInternal(String standalone) 666 { 667 if ("yes".equals(standalone)) 668 m_standalone = "yes"; 669 else 670 m_standalone = "no"; 671 672 } 673 674 /** 675 * Gets the XSL standalone attribute 676 * @return a value of "yes" if the <code>standalone</code> delaration is to 677 * be included in the output document. 678 * @see XSLOutputAttributes#getStandalone() 679 */ getStandalone()680 public String getStandalone() 681 { 682 return m_standalone; 683 } 684 685 /** 686 * @return true if the output document should be indented to visually 687 * indicate its structure. 688 */ getIndent()689 public boolean getIndent() 690 { 691 return m_doIndent; 692 } 693 /** 694 * Gets the mediatype the media-type or MIME type associated with the output 695 * document. 696 * @return the mediatype the media-type or MIME type associated with the 697 * output document. 698 */ getMediaType()699 public String getMediaType() 700 { 701 return m_mediatype; 702 } 703 704 /** 705 * Gets the version of the output format. 706 * @return the version of the output format. 707 */ getVersion()708 public String getVersion() 709 { 710 return m_version; 711 } 712 713 /** 714 * Sets the value coming from the xsl:output version attribute. 715 * @param version the version of the output format. 716 * @see SerializationHandler#setVersion(String) 717 */ setVersion(String version)718 public void setVersion(String version) 719 { 720 setOutputProperty(OutputKeys.VERSION, version); 721 } 722 723 /** 724 * Sets the value coming from the xsl:output media-type stylesheet attribute. 725 * @param mediaType the non-null media-type or MIME type associated with the 726 * output document. 727 * @see javax.xml.transform.OutputKeys#MEDIA_TYPE 728 * @see SerializationHandler#setMediaType(String) 729 */ setMediaType(String mediaType)730 public void setMediaType(String mediaType) 731 { 732 setOutputProperty(OutputKeys.MEDIA_TYPE,mediaType); 733 } 734 735 /** 736 * @return the number of spaces to indent for each indentation level. 737 */ getIndentAmount()738 public int getIndentAmount() 739 { 740 return m_indentAmount; 741 } 742 743 /** 744 * Sets the indentation amount. 745 * @param m_indentAmount The m_indentAmount to set 746 */ setIndentAmount(int m_indentAmount)747 public void setIndentAmount(int m_indentAmount) 748 { 749 this.m_indentAmount = m_indentAmount; 750 } 751 752 /** 753 * Sets the value coming from the xsl:output indent stylesheet 754 * attribute. 755 * @param doIndent true if the output document should be indented to 756 * visually indicate its structure. 757 * @see XSLOutputAttributes#setIndent(boolean) 758 */ setIndent(boolean doIndent)759 public void setIndent(boolean doIndent) 760 { 761 String val = doIndent ? "yes":"no"; 762 setOutputProperty(OutputKeys.INDENT,val); 763 } 764 765 /** 766 * This method is used when a prefix/uri namespace mapping 767 * is indicated after the element was started with a 768 * startElement() and before and endElement(). 769 * startPrefixMapping(prefix,uri) would be used before the 770 * startElement() call. 771 * @param uri the URI of the namespace 772 * @param prefix the prefix associated with the given URI. 773 * 774 * @see ExtendedContentHandler#namespaceAfterStartElement(String, String) 775 */ namespaceAfterStartElement(String uri, String prefix)776 public void namespaceAfterStartElement(String uri, String prefix) 777 throws SAXException 778 { 779 // default behavior is to do nothing 780 } 781 782 /** 783 * Return a {@link DOMSerializer} interface into this serializer. If the 784 * serializer does not support the {@link DOMSerializer} interface, it should 785 * return null. 786 * 787 * @return A {@link DOMSerializer} interface into this serializer, or null 788 * if the serializer is not DOM capable 789 * @throws IOException An I/O exception occured 790 * @see Serializer#asDOMSerializer() 791 */ asDOMSerializer()792 public DOMSerializer asDOMSerializer() throws IOException 793 { 794 return this; 795 } 796 797 /** 798 * Tell if two strings are equal, without worry if the first string is null. 799 * 800 * @param p String reference, which may be null. 801 * @param t String reference, which may be null. 802 * 803 * @return true if strings are equal. 804 */ subPartMatch(String p, String t)805 private static final boolean subPartMatch(String p, String t) 806 { 807 return (p == t) || ((null != p) && (p.equals(t))); 808 } 809 810 /** 811 * Returns the local name of a qualified name. 812 * If the name has no prefix, 813 * then it works as the identity (SAX2). 814 * 815 * @param qname a qualified name 816 * @return returns the prefix of the qualified name, 817 * or null if there is no prefix. 818 */ getPrefixPart(String qname)819 protected static final String getPrefixPart(String qname) 820 { 821 final int col = qname.indexOf(':'); 822 return (col > 0) ? qname.substring(0, col) : null; 823 //return (col > 0) ? qname.substring(0,col) : ""; 824 } 825 826 /** 827 * Some users of the serializer may need the current namespace mappings 828 * @return the current namespace mappings (prefix/uri) 829 * @see ExtendedContentHandler#getNamespaceMappings() 830 */ getNamespaceMappings()831 public NamespaceMappings getNamespaceMappings() 832 { 833 return m_prefixMap; 834 } 835 836 /** 837 * Returns the prefix currently pointing to the given URI (if any). 838 * @param namespaceURI the uri of the namespace in question 839 * @return a prefix pointing to the given URI (if any). 840 * @see ExtendedContentHandler#getPrefix(String) 841 */ getPrefix(String namespaceURI)842 public String getPrefix(String namespaceURI) 843 { 844 String prefix = m_prefixMap.lookupPrefix(namespaceURI); 845 return prefix; 846 } 847 848 /** 849 * Returns the URI of an element or attribute. Note that default namespaces 850 * do not apply directly to attributes. 851 * @param qname a qualified name 852 * @param isElement true if the qualified name is the name of 853 * an element. 854 * @return returns the namespace URI associated with the qualified name. 855 */ getNamespaceURI(String qname, boolean isElement)856 public String getNamespaceURI(String qname, boolean isElement) 857 { 858 String uri = EMPTYSTRING; 859 int col = qname.lastIndexOf(':'); 860 final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING; 861 862 if (!EMPTYSTRING.equals(prefix) || isElement) 863 { 864 if (m_prefixMap != null) 865 { 866 uri = m_prefixMap.lookupNamespace(prefix); 867 if (uri == null && !prefix.equals(XMLNS_PREFIX)) 868 { 869 throw new RuntimeException( 870 Utils.messages.createMessage( 871 MsgKey.ER_NAMESPACE_PREFIX, 872 new Object[] { qname.substring(0, col) } )); 873 } 874 } 875 } 876 return uri; 877 } 878 879 /** 880 * Returns the URI of prefix (if any) 881 * 882 * @param prefix the prefix whose URI is searched for 883 * @return the namespace URI currently associated with the 884 * prefix, null if the prefix is undefined. 885 */ getNamespaceURIFromPrefix(String prefix)886 public String getNamespaceURIFromPrefix(String prefix) 887 { 888 String uri = null; 889 if (m_prefixMap != null) 890 uri = m_prefixMap.lookupNamespace(prefix); 891 return uri; 892 } 893 894 /** 895 * Entity reference event. 896 * 897 * @param name Name of entity 898 * 899 * @throws org.xml.sax.SAXException 900 */ entityReference(String name)901 public void entityReference(String name) throws org.xml.sax.SAXException 902 { 903 904 flushPending(); 905 906 startEntity(name); 907 endEntity(name); 908 909 if (m_tracer != null) 910 fireEntityReference(name); 911 } 912 913 /** 914 * Sets the transformer associated with this serializer 915 * @param t the transformer associated with this serializer. 916 * @see SerializationHandler#setTransformer(Transformer) 917 */ setTransformer(Transformer t)918 public void setTransformer(Transformer t) 919 { 920 m_transformer = t; 921 922 // If this transformer object implements the SerializerTrace interface 923 // then assign m_tracer to the transformer object so it can be used 924 // to fire trace events. 925 if ((m_transformer instanceof SerializerTrace) && 926 (((SerializerTrace) m_transformer).hasTraceListeners())) { 927 m_tracer = (SerializerTrace) m_transformer; 928 } else { 929 m_tracer = null; 930 } 931 } 932 /** 933 * Gets the transformer associated with this serializer 934 * @return returns the transformer associated with this serializer. 935 * @see SerializationHandler#getTransformer() 936 */ getTransformer()937 public Transformer getTransformer() 938 { 939 return m_transformer; 940 } 941 942 /** 943 * This method gets the nodes value as a String and uses that String as if 944 * it were an input character notification. 945 * @param node the Node to serialize 946 * @throws org.xml.sax.SAXException 947 */ characters(org.w3c.dom.Node node)948 public void characters(org.w3c.dom.Node node) 949 throws org.xml.sax.SAXException 950 { 951 flushPending(); 952 String data = node.getNodeValue(); 953 if (data != null) 954 { 955 final int length = data.length(); 956 if (length > m_charsBuff.length) 957 { 958 m_charsBuff = new char[length * 2 + 1]; 959 } 960 data.getChars(0, length, m_charsBuff, 0); 961 characters(m_charsBuff, 0, length); 962 } 963 } 964 965 966 /** 967 * @see org.xml.sax.ErrorHandler#error(SAXParseException) 968 */ error(SAXParseException exc)969 public void error(SAXParseException exc) throws SAXException { 970 } 971 972 /** 973 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) 974 */ fatalError(SAXParseException exc)975 public void fatalError(SAXParseException exc) throws SAXException { 976 977 m_elemContext.m_startTagOpen = false; 978 979 } 980 981 /** 982 * @see org.xml.sax.ErrorHandler#warning(SAXParseException) 983 */ warning(SAXParseException exc)984 public void warning(SAXParseException exc) throws SAXException 985 { 986 } 987 988 /** 989 * To fire off start entity trace event 990 * @param name Name of entity 991 */ fireStartEntity(String name)992 protected void fireStartEntity(String name) 993 throws org.xml.sax.SAXException 994 { 995 if (m_tracer != null) 996 { 997 flushMyWriter(); 998 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name); 999 } 1000 } 1001 1002 /** 1003 * Report the characters event 1004 * @param chars content of characters 1005 * @param start starting index of characters to output 1006 * @param length number of characters to output 1007 */ 1008 // protected void fireCharEvent(char[] chars, int start, int length) 1009 // throws org.xml.sax.SAXException 1010 // { 1011 // if (m_tracer != null) 1012 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length); 1013 // } 1014 // 1015 1016 /** 1017 * This method is only used internally when flushing the writer from the 1018 * various fire...() trace events. Due to the writer being wrapped with 1019 * SerializerTraceWriter it may cause the flush of these trace events: 1020 * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS 1021 * EVENTTYPE_OUTPUT_CHARACTERS 1022 * which trace the output written to the output stream. 1023 * 1024 */ flushMyWriter()1025 private void flushMyWriter() 1026 { 1027 if (m_writer != null) 1028 { 1029 try 1030 { 1031 m_writer.flush(); 1032 } 1033 catch(IOException ioe) 1034 { 1035 1036 } 1037 } 1038 } 1039 /** 1040 * Report the CDATA trace event 1041 * @param chars content of CDATA 1042 * @param start starting index of characters to output 1043 * @param length number of characters to output 1044 */ fireCDATAEvent(char[] chars, int start, int length)1045 protected void fireCDATAEvent(char[] chars, int start, int length) 1046 throws org.xml.sax.SAXException 1047 { 1048 if (m_tracer != null) 1049 { 1050 flushMyWriter(); 1051 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length); 1052 } 1053 } 1054 1055 /** 1056 * Report the comment trace event 1057 * @param chars content of comment 1058 * @param start starting index of comment to output 1059 * @param length number of characters to output 1060 */ fireCommentEvent(char[] chars, int start, int length)1061 protected void fireCommentEvent(char[] chars, int start, int length) 1062 throws org.xml.sax.SAXException 1063 { 1064 if (m_tracer != null) 1065 { 1066 flushMyWriter(); 1067 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length)); 1068 } 1069 } 1070 1071 1072 /** 1073 * To fire off end entity trace event 1074 * @param name Name of entity 1075 */ fireEndEntity(String name)1076 public void fireEndEntity(String name) 1077 throws org.xml.sax.SAXException 1078 { 1079 if (m_tracer != null) 1080 flushMyWriter(); 1081 // we do not need to handle this. 1082 } 1083 1084 /** 1085 * To fire off start document trace event 1086 */ fireStartDoc()1087 protected void fireStartDoc() 1088 throws org.xml.sax.SAXException 1089 { 1090 if (m_tracer != null) 1091 { 1092 flushMyWriter(); 1093 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT); 1094 } 1095 } 1096 1097 1098 /** 1099 * To fire off end document trace event 1100 */ fireEndDoc()1101 protected void fireEndDoc() 1102 throws org.xml.sax.SAXException 1103 { 1104 if (m_tracer != null) 1105 { 1106 flushMyWriter(); 1107 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT); 1108 } 1109 } 1110 1111 /** 1112 * Report the start element trace event. This trace method needs to be 1113 * called just before the attributes are cleared. 1114 * 1115 * @param elemName the qualified name of the element 1116 * 1117 */ fireStartElem(String elemName)1118 protected void fireStartElem(String elemName) 1119 throws org.xml.sax.SAXException 1120 { 1121 if (m_tracer != null) 1122 { 1123 flushMyWriter(); 1124 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT, 1125 elemName, m_attributes); 1126 } 1127 } 1128 1129 1130 /** 1131 * To fire off the end element event 1132 * @param name Name of element 1133 */ 1134 // protected void fireEndElem(String name) 1135 // throws org.xml.sax.SAXException 1136 // { 1137 // if (m_tracer != null) 1138 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null); 1139 // } 1140 1141 1142 /** 1143 * To fire off the PI trace event 1144 * @param name Name of PI 1145 */ fireEscapingEvent(String name, String data)1146 protected void fireEscapingEvent(String name, String data) 1147 throws org.xml.sax.SAXException 1148 { 1149 1150 if (m_tracer != null) 1151 { 1152 flushMyWriter(); 1153 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data); 1154 } 1155 } 1156 1157 1158 /** 1159 * To fire off the entity reference trace event 1160 * @param name Name of entity reference 1161 */ fireEntityReference(String name)1162 protected void fireEntityReference(String name) 1163 throws org.xml.sax.SAXException 1164 { 1165 if (m_tracer != null) 1166 { 1167 flushMyWriter(); 1168 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null); 1169 } 1170 } 1171 1172 /** 1173 * Receive notification of the beginning of a document. 1174 * This method is never a self generated call, 1175 * but only called externally. 1176 * 1177 * <p>The SAX parser will invoke this method only once, before any 1178 * other methods in this interface or in DTDHandler (except for 1179 * setDocumentLocator).</p> 1180 * 1181 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1182 * wrapping another exception. 1183 * 1184 * @throws org.xml.sax.SAXException 1185 */ startDocument()1186 public void startDocument() throws org.xml.sax.SAXException 1187 { 1188 1189 // if we do get called with startDocument(), handle it right away 1190 startDocumentInternal(); 1191 m_needToCallStartDocument = false; 1192 return; 1193 } 1194 1195 /** 1196 * This method handles what needs to be done at a startDocument() call, 1197 * whether from an external caller, or internally called in the 1198 * serializer. For historical reasons the serializer is flexible to 1199 * startDocument() not always being called. 1200 * Even if no external call is 1201 * made into startDocument() this method will always be called as a self 1202 * generated internal startDocument, it handles what needs to be done at a 1203 * startDocument() call. 1204 * 1205 * This method exists just to make sure that startDocument() is only ever 1206 * called from an external caller, which in principle is just a matter of 1207 * style. 1208 * 1209 * @throws SAXException 1210 */ startDocumentInternal()1211 protected void startDocumentInternal() throws org.xml.sax.SAXException 1212 { 1213 if (m_tracer != null) 1214 this.fireStartDoc(); 1215 } 1216 /** 1217 * This method is used to set the source locator, which might be used to 1218 * generated an error message. 1219 * @param locator the source locator 1220 * 1221 * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator) 1222 */ setSourceLocator(SourceLocator locator)1223 public void setSourceLocator(SourceLocator locator) 1224 { 1225 m_sourceLocator = locator; 1226 } 1227 1228 1229 /** 1230 * Used only by TransformerSnapshotImpl to restore the serialization 1231 * to a previous state. 1232 * 1233 * @param mappings NamespaceMappings 1234 */ setNamespaceMappings(NamespaceMappings mappings)1235 public void setNamespaceMappings(NamespaceMappings mappings) { 1236 m_prefixMap = mappings; 1237 } 1238 reset()1239 public boolean reset() 1240 { 1241 resetSerializerBase(); 1242 return true; 1243 } 1244 1245 /** 1246 * Reset all of the fields owned by SerializerBase 1247 * 1248 */ resetSerializerBase()1249 private void resetSerializerBase() 1250 { 1251 this.m_attributes.clear(); 1252 this.m_CdataElems = null; 1253 this.m_cdataTagOpen = false; 1254 this.m_docIsEmpty = true; 1255 this.m_doctypePublic = null; 1256 this.m_doctypeSystem = null; 1257 this.m_doIndent = false; 1258 this.m_elemContext = new ElemContext(); 1259 this.m_indentAmount = 0; 1260 this.m_inEntityRef = false; 1261 this.m_inExternalDTD = false; 1262 this.m_mediatype = null; 1263 this.m_needToCallStartDocument = true; 1264 this.m_needToOutputDocTypeDecl = false; 1265 if (m_OutputProps != null) 1266 this.m_OutputProps.clear(); 1267 if (m_OutputPropsDefault != null) 1268 this.m_OutputPropsDefault.clear(); 1269 if (this.m_prefixMap != null) 1270 this.m_prefixMap.reset(); 1271 this.m_shouldNotWriteXMLHeader = false; 1272 this.m_sourceLocator = null; 1273 this.m_standalone = null; 1274 this.m_standaloneWasSpecified = false; 1275 this.m_StringOfCDATASections = null; 1276 this.m_tracer = null; 1277 this.m_transformer = null; 1278 this.m_version = null; 1279 // don't set writer to null, so that it might be re-used 1280 //this.m_writer = null; 1281 } 1282 1283 /** 1284 * Returns true if the serializer is used for temporary output rather than 1285 * final output. 1286 * 1287 * This concept is made clear in the XSLT 2.0 draft. 1288 */ inTemporaryOutputState()1289 final boolean inTemporaryOutputState() 1290 { 1291 /* This is a hack. We should really be letting the serializer know 1292 * that it is in temporary output state with an explicit call, but 1293 * from a pragmatic point of view (for now anyways) having no output 1294 * encoding at all, not even the default UTF-8 indicates that the serializer 1295 * is being used for temporary RTF. 1296 */ 1297 return (getEncoding() == null); 1298 1299 } 1300 1301 /** 1302 * This method adds an attribute the the current element, 1303 * but should not be used for an xsl:attribute child. 1304 * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) 1305 */ addAttribute(String uri, String localName, String rawName, String type, String value)1306 public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException 1307 { 1308 if (m_elemContext.m_startTagOpen) 1309 { 1310 addAttributeAlways(uri, localName, rawName, type, value, false); 1311 } 1312 } 1313 1314 /** 1315 * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String) 1316 */ notationDecl(String arg0, String arg1, String arg2)1317 public void notationDecl(String arg0, String arg1, String arg2) 1318 throws SAXException { 1319 // This method just provides a definition to satisfy the interface 1320 // A particular sub-class of SerializerBase provides the implementation (if desired) 1321 } 1322 1323 /** 1324 * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String) 1325 */ unparsedEntityDecl( String arg0, String arg1, String arg2, String arg3)1326 public void unparsedEntityDecl( 1327 String arg0, 1328 String arg1, 1329 String arg2, 1330 String arg3) 1331 throws SAXException { 1332 // This method just provides a definition to satisfy the interface 1333 // A particular sub-class of SerializerBase provides the implementation (if desired) 1334 } 1335 1336 /** 1337 * If set to false the serializer does not expand DTD entities, 1338 * but leaves them as is, the default value is true. 1339 */ setDTDEntityExpansion(boolean expand)1340 public void setDTDEntityExpansion(boolean expand) { 1341 // This method just provides a definition to satisfy the interface 1342 // A particular sub-class of SerializerBase provides the implementation (if desired) 1343 } 1344 1345 1346 /** 1347 * The CDATA section names stored in a whitespace separateed list with 1348 * each element being a word of the form "{uri}localName" This list 1349 * comes from the cdata-section-elements attribute. 1350 * 1351 * This field replaces m_cdataSectionElements Vector. 1352 */ 1353 protected String m_StringOfCDATASections = null; 1354 1355 boolean m_docIsEmpty = true; initCdataElems(String s)1356 void initCdataElems(String s) 1357 { 1358 if (s != null) 1359 { 1360 int max = s.length(); 1361 1362 // true if we are in the middle of a pair of curly braces that delimit a URI 1363 boolean inCurly = false; 1364 1365 // true if we found a URI but haven't yet processed the local name 1366 boolean foundURI = false; 1367 1368 StringBuffer buf = new StringBuffer(); 1369 String uri = null; 1370 String localName = null; 1371 1372 // parse through string, breaking on whitespaces. I do this instead 1373 // of a tokenizer so I can track whitespace inside of curly brackets, 1374 // which theoretically shouldn't happen if they contain legal URLs. 1375 1376 1377 for (int i = 0; i < max; i++) 1378 { 1379 1380 char c = s.charAt(i); 1381 1382 if (Character.isWhitespace(c)) 1383 { 1384 if (!inCurly) 1385 { 1386 if (buf.length() > 0) 1387 { 1388 localName = buf.toString(); 1389 if (!foundURI) 1390 uri = ""; 1391 addCDATAElement(uri,localName); 1392 buf.setLength(0); 1393 foundURI = false; 1394 } 1395 continue; 1396 } 1397 else 1398 buf.append(c); // add whitespace to the URI 1399 } 1400 else if ('{' == c) // starting a URI 1401 inCurly = true; 1402 else if ('}' == c) 1403 { 1404 // we just ended a URI, add the URI to the vector 1405 foundURI = true; 1406 uri = buf.toString(); 1407 buf.setLength(0); 1408 inCurly = false; 1409 } 1410 else 1411 { 1412 // append non-whitespace, non-curly to current URI or localName being gathered. 1413 buf.append(c); 1414 } 1415 1416 } 1417 1418 if (buf.length() > 0) 1419 { 1420 // We have one last localName to process. 1421 localName = buf.toString(); 1422 if (!foundURI) 1423 uri = ""; 1424 addCDATAElement(uri,localName); 1425 } 1426 } 1427 } 1428 protected java.util.Hashtable m_CdataElems = null; addCDATAElement(String uri, String localName)1429 private void addCDATAElement(String uri, String localName) 1430 { 1431 if (m_CdataElems == null) { 1432 m_CdataElems = new java.util.Hashtable(); 1433 } 1434 1435 java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(localName); 1436 if (h == null) { 1437 h = new java.util.Hashtable(); 1438 m_CdataElems.put(localName,h); 1439 } 1440 h.put(uri,uri); 1441 1442 } 1443 1444 1445 /** 1446 * Return true if nothing has been sent to this result tree yet. 1447 * <p> 1448 * This is not a public API. 1449 * 1450 * @xsl.usage internal 1451 */ documentIsEmpty()1452 public boolean documentIsEmpty() { 1453 // If we haven't called startDocument() yet, then this document is empty 1454 return m_docIsEmpty && (m_elemContext.m_currentElemDepth == 0); 1455 } 1456 1457 /** 1458 * Return true if the current element in m_elemContext 1459 * is a CDATA section. 1460 * CDATA sections are specified in the <xsl:output> attribute 1461 * cdata-section-names or in the JAXP equivalent property. 1462 * In any case the format of the value of such a property is: 1463 * <pre> 1464 * "{uri1}localName1 {uri2}localName2 . . . " 1465 * </pre> 1466 * 1467 * <p> 1468 * This method is not a public API, but is only used internally by the serializer. 1469 */ isCdataSection()1470 protected boolean isCdataSection() 1471 { 1472 1473 boolean b = false; 1474 1475 if (null != m_StringOfCDATASections) 1476 { 1477 if (m_elemContext.m_elementLocalName == null) 1478 { 1479 String localName = getLocalName(m_elemContext.m_elementName); 1480 m_elemContext.m_elementLocalName = localName; 1481 } 1482 1483 if ( m_elemContext.m_elementURI == null) { 1484 1485 m_elemContext.m_elementURI = getElementURI(); 1486 } 1487 else if ( m_elemContext.m_elementURI.length() == 0) { 1488 if ( m_elemContext.m_elementName == null) { 1489 m_elemContext.m_elementName = m_elemContext.m_elementLocalName; 1490 // leave URI as "", meaning in no namespace 1491 } 1492 else if (m_elemContext.m_elementLocalName.length() < m_elemContext.m_elementName.length()){ 1493 // We were told the URI was "", yet the name has a prefix since the name is longer than the localname. 1494 // So we will fix that incorrect information here. 1495 m_elemContext.m_elementURI = getElementURI(); 1496 } 1497 } 1498 1499 java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(m_elemContext.m_elementLocalName); 1500 if (h != null) 1501 { 1502 Object obj = h.get(m_elemContext.m_elementURI); 1503 if (obj != null) 1504 b = true; 1505 } 1506 1507 } 1508 return b; 1509 } 1510 1511 /** 1512 * Before this call m_elementContext.m_elementURI is null, 1513 * which means it is not yet known. After this call it 1514 * is non-null, but possibly "" meaning that it is in the 1515 * default namespace. 1516 * 1517 * @return The URI of the element, never null, but possibly "". 1518 */ getElementURI()1519 private String getElementURI() { 1520 String uri = null; 1521 // At this point in processing we have received all the 1522 // namespace mappings 1523 // As we still don't know the elements namespace, 1524 // we now figure it out. 1525 1526 String prefix = getPrefixPart(m_elemContext.m_elementName); 1527 1528 if (prefix == null) { 1529 // no prefix so lookup the URI of the default namespace 1530 uri = m_prefixMap.lookupNamespace(""); 1531 } else { 1532 uri = m_prefixMap.lookupNamespace(prefix); 1533 } 1534 if (uri == null) { 1535 // We didn't find the namespace for the 1536 // prefix ... ouch, that shouldn't happen. 1537 // This is a hack, we really don't know 1538 // the namespace 1539 uri = EMPTYSTRING; 1540 } 1541 1542 return uri; 1543 } 1544 1545 1546 /** 1547 * Get the value of an output property, 1548 * the explicit value, if any, otherwise the 1549 * default value, if any, otherwise null. 1550 */ getOutputProperty(String name)1551 public String getOutputProperty(String name) { 1552 String val = getOutputPropertyNonDefault(name); 1553 // If no explicit value, try to get the default value 1554 if (val == null) 1555 val = getOutputPropertyDefault(name); 1556 return val; 1557 1558 } 1559 /** 1560 * Get the value of an output property, 1561 * not the default value. If there is a default 1562 * value, but no non-default value this method 1563 * will return null. 1564 * <p> 1565 * 1566 */ getOutputPropertyNonDefault(String name )1567 public String getOutputPropertyNonDefault(String name ) 1568 { 1569 return getProp(name,false); 1570 } 1571 1572 /** 1573 * Return a {@link DOM3Serializer} interface into this serializer. If the 1574 * serializer does not support the {@link DOM3Serializer} interface, it should 1575 * return null. 1576 * 1577 * @return A {@link DOM3Serializer} interface into this serializer, or null 1578 * if the serializer is not DOM capable 1579 * @throws IOException An I/O exception occured 1580 * @see org.apache.xml.serializer.Serializer#asDOM3Serializer() 1581 */ asDOM3Serializer()1582 public Object asDOM3Serializer() throws IOException 1583 { 1584 return new org.apache.xml.serializer.dom3.DOM3SerializerImpl(this); 1585 } 1586 /** 1587 * Get the default value of an xsl:output property, 1588 * which would be null only if no default value exists 1589 * for the property. 1590 */ getOutputPropertyDefault(String name)1591 public String getOutputPropertyDefault(String name) { 1592 return getProp(name, true); 1593 } 1594 1595 /** 1596 * Set the value for the output property, typically from 1597 * an xsl:output element, but this does not change what 1598 * the default value is. 1599 */ setOutputProperty(String name, String val)1600 public void setOutputProperty(String name, String val) { 1601 setProp(name,val,false); 1602 1603 } 1604 1605 /** 1606 * Set the default value for an output property, but this does 1607 * not impact any explicitly set value. 1608 */ setOutputPropertyDefault(String name, String val)1609 public void setOutputPropertyDefault(String name, String val) { 1610 setProp(name,val,true); 1611 1612 } 1613 1614 /** 1615 * A mapping of keys to explicitly set values, for example if 1616 * and <xsl:output/> has an "encoding" attribute, this 1617 * map will have what that attribute maps to. 1618 */ 1619 private HashMap m_OutputProps; 1620 /** 1621 * A mapping of keys to default values, for example if 1622 * the default value of the encoding is "UTF-8" then this 1623 * map will have that "encoding" maps to "UTF-8". 1624 */ 1625 private HashMap m_OutputPropsDefault; 1626 getOutputPropDefaultKeys()1627 Set getOutputPropDefaultKeys() { 1628 return m_OutputPropsDefault.keySet(); 1629 } getOutputPropKeys()1630 Set getOutputPropKeys() { 1631 return m_OutputProps.keySet(); 1632 } 1633 getProp(String name, boolean defaultVal)1634 private String getProp(String name, boolean defaultVal) { 1635 if (m_OutputProps == null) { 1636 m_OutputProps = new HashMap(); 1637 m_OutputPropsDefault = new HashMap(); 1638 } 1639 1640 String val; 1641 if (defaultVal) 1642 val = (String) m_OutputPropsDefault.get(name); 1643 else 1644 val = (String) m_OutputProps.get(name); 1645 1646 return val; 1647 1648 } 1649 /** 1650 * 1651 * @param name The name of the property, e.g. "{http://myprop}indent-tabs" or "indent". 1652 * @param val The value of the property, e.g. "4" 1653 * @param defaultVal true if this is a default value being set for the property as 1654 * opposed to a user define on, set say explicitly in the stylesheet or via JAXP 1655 */ setProp(String name, String val, boolean defaultVal)1656 void setProp(String name, String val, boolean defaultVal) { 1657 if (m_OutputProps == null) { 1658 m_OutputProps = new HashMap(); 1659 m_OutputPropsDefault = new HashMap(); 1660 } 1661 1662 if (defaultVal) 1663 m_OutputPropsDefault.put(name,val); 1664 else { 1665 if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name) && val != null) { 1666 initCdataElems(val); 1667 String oldVal = (String) m_OutputProps.get(name); 1668 String newVal; 1669 if (oldVal == null) 1670 newVal = oldVal + ' ' + val; 1671 else 1672 newVal = val; 1673 m_OutputProps.put(name,newVal); 1674 } 1675 else { 1676 m_OutputProps.put(name,val); 1677 } 1678 } 1679 1680 1681 } 1682 1683 /** 1684 * Get the first char of the local name 1685 * @param name Either a local name, or a local name 1686 * preceeded by a uri enclosed in curly braces. 1687 */ getFirstCharLocName(String name)1688 static char getFirstCharLocName(String name) { 1689 final char first; 1690 int i = name.indexOf('}'); 1691 if (i < 0) 1692 first = name.charAt(0); 1693 else 1694 first = name.charAt(i+1); 1695 return first; 1696 } 1697 } 1698 1699 1700