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