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