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