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