001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: SerializerBase.java 471981 2006-11-07 04:28:00Z minchau $ 020 */ 021 package org.apache.xml.serializer; 022 023 import java.io.IOException; 024 import java.util.HashMap; 025 import java.util.Set; 026 027 import javax.xml.transform.OutputKeys; 028 import javax.xml.transform.SourceLocator; 029 import javax.xml.transform.Transformer; 030 031 import org.apache.xml.serializer.utils.MsgKey; 032 import org.apache.xml.serializer.utils.Utils; 033 import org.xml.sax.Attributes; 034 import org.xml.sax.ContentHandler; 035 import org.xml.sax.Locator; 036 import org.xml.sax.SAXException; 037 import org.xml.sax.SAXParseException; 038 039 040 /** 041 * This class acts as a base class for the XML "serializers" 042 * and the stream serializers. 043 * It contains a number of common fields and methods. 044 * 045 * @xsl.usage internal 046 */ 047 public abstract class SerializerBase 048 implements SerializationHandler, SerializerConstants 049 { 050 SerializerBase() { 051 return; 052 } 053 054 /** 055 * The name of the package that this class is in. 056 * <p> 057 * Not a public API. 058 */ 059 public static final String PKG_NAME; 060 061 /** 062 * The same as the name of the package that this class is in 063 * except that '.' are replaced with '/'. 064 * <p> 065 * Not a public API. 066 */ 067 public static final String PKG_PATH; 068 069 static { 070 String fullyQualifiedName = SerializerBase.class.getName(); 071 int lastDot = fullyQualifiedName.lastIndexOf('.'); 072 if (lastDot < 0) { 073 PKG_NAME = ""; 074 } else { 075 PKG_NAME = fullyQualifiedName.substring(0, lastDot); 076 } 077 078 StringBuffer sb = new StringBuffer(); 079 for (int i = 0; i < PKG_NAME.length(); i++) { 080 char ch = PKG_NAME.charAt(i); 081 if (ch == '.') 082 sb.append('/'); 083 else 084 sb.append(ch); 085 } 086 PKG_PATH = sb.toString(); 087 } 088 089 090 091 /** 092 * To fire off the end element trace event 093 * @param name Name of element 094 */ 095 protected void fireEndElem(String name) 096 throws org.xml.sax.SAXException 097 { 098 if (m_tracer != null) 099 { 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 540 public void close() 541 { 542 // do nothing (base behavior) 543 } 544 545 /** 546 * Initialize global variables 547 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 969 public void error(SAXParseException exc) throws SAXException { 970 } 971 972 /** 973 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) 974 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1235 public void setNamespaceMappings(NamespaceMappings mappings) { 1236 m_prefixMap = mappings; 1237 } 1238 1239 public boolean reset() 1240 { 1241 resetSerializerBase(); 1242 return true; 1243 } 1244 1245 /** 1246 * Reset all of the fields owned by SerializerBase 1247 * 1248 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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; 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; 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 1627 Set getOutputPropDefaultKeys() { 1628 return m_OutputPropsDefault.keySet(); 1629 } 1630 Set getOutputPropKeys() { 1631 return m_OutputProps.keySet(); 1632 } 1633 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 */ 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 */ 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