Home | History | Annotate | Download | only in helpers
      1 // NamespaceSupport.java - generic Namespace support for SAX.
      2 // http://www.saxproject.org
      3 // Written by David Megginson
      4 // This class is in the Public Domain.  NO WARRANTY!
      5 // $Id: NamespaceSupport.java,v 1.15 2004/04/26 17:34:35 dmegginson Exp $
      6 
      7 package org.xml.sax.helpers;
      8 
      9 import java.util.ArrayList;
     10 import java.util.Collections;
     11 import java.util.EmptyStackException;
     12 import java.util.Enumeration;
     13 import java.util.Hashtable;
     14 
     15 /**
     16  * Encapsulate Namespace logic for use by applications using SAX,
     17  * or internally by SAX drivers.
     18  *
     19  * <blockquote>
     20  * <em>This module, both source code and documentation, is in the
     21  * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
     22  * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
     23  * for further information.
     24  * </blockquote>
     25  *
     26  * <p>This class encapsulates the logic of Namespace processing: it
     27  * tracks the declarations currently in force for each context and
     28  * automatically processes qualified XML names into their Namespace
     29  * parts; it can also be used in reverse for generating XML qnames
     30  * from Namespaces.</p>
     31  *
     32  * <p>Namespace support objects are reusable, but the reset method
     33  * must be invoked between each session.</p>
     34  *
     35  * <p>Here is a simple session:</p>
     36  *
     37  * <pre>
     38  * String parts[] = new String[3];
     39  * NamespaceSupport support = new NamespaceSupport();
     40  *
     41  * support.pushContext();
     42  * support.declarePrefix("", "http://www.w3.org/1999/xhtml");
     43  * support.declarePrefix("dc", "http://www.purl.org/dc#");
     44  *
     45  * parts = support.processName("p", parts, false);
     46  * System.out.println("Namespace URI: " + parts[0]);
     47  * System.out.println("Local name: " + parts[1]);
     48  * System.out.println("Raw name: " + parts[2]);
     49  *
     50  * parts = support.processName("dc:title", parts, false);
     51  * System.out.println("Namespace URI: " + parts[0]);
     52  * System.out.println("Local name: " + parts[1]);
     53  * System.out.println("Raw name: " + parts[2]);
     54  *
     55  * support.popContext();
     56  * </pre>
     57  *
     58  * <p>Note that this class is optimized for the use case where most
     59  * elements do not contain Namespace declarations: if the same
     60  * prefix/URI mapping is repeated for each context (for example), this
     61  * class will be somewhat less efficient.</p>
     62  *
     63  * <p>Although SAX drivers (parsers) may choose to use this class to
     64  * implement namespace handling, they are not required to do so.
     65  * Applications must track namespace information themselves if they
     66  * want to use namespace information.
     67  *
     68  * @since SAX 2.0
     69  * @author David Megginson
     70  * @version 2.0.1 (sax2r2)
     71  */
     72 public class NamespaceSupport
     73 {
     74 
     75 
     76     ////////////////////////////////////////////////////////////////////
     78     // Constants.
     79     ////////////////////////////////////////////////////////////////////
     80 
     81 
     82     /**
     83      * The XML Namespace URI as a constant.
     84      * The value is <code>http://www.w3.org/XML/1998/namespace</code>
     85      * as defined in the "Namespaces in XML" * recommendation.
     86      *
     87      * <p>This is the Namespace URI that is automatically mapped
     88      * to the "xml" prefix.</p>
     89      */
     90     public static final String XMLNS =
     91     "http://www.w3.org/XML/1998/namespace";
     92 
     93 
     94     /**
     95      * The namespace declaration URI as a constant.
     96      * The value is <code>http://www.w3.org/xmlns/2000/</code>, as defined
     97      * in a backwards-incompatible erratum to the "Namespaces in XML"
     98      * recommendation.  Because that erratum postdated SAX2, SAX2 defaults
     99      * to the original recommendation, and does not normally use this URI.
    100      *
    101      *
    102      * <p>This is the Namespace URI that is optionally applied to
    103      * <em>xmlns</em> and <em>xmlns:*</em> attributes, which are used to
    104      * declare namespaces.  </p>
    105      *
    106      * @since SAX 2.1alpha
    107      * @see #setNamespaceDeclUris
    108      * @see #isNamespaceDeclUris
    109      */
    110     public static final String NSDECL =
    111     "http://www.w3.org/xmlns/2000/";
    112 
    113 
    114     /**
    115      * An empty enumeration.
    116      */
    117     private static final Enumeration EMPTY_ENUMERATION = Collections.enumeration(Collections.emptyList());
    118 
    119 
    120     ////////////////////////////////////////////////////////////////////
    122     // Constructor.
    123     ////////////////////////////////////////////////////////////////////
    124 
    125 
    126     /**
    127      * Create a new Namespace support object.
    128      */
    129     public NamespaceSupport ()
    130     {
    131     reset();
    132     }
    133 
    134 
    135 
    136     ////////////////////////////////////////////////////////////////////
    138     // Context management.
    139     ////////////////////////////////////////////////////////////////////
    140 
    141 
    142     /**
    143      * Reset this Namespace support object for reuse.
    144      *
    145      * <p>It is necessary to invoke this method before reusing the
    146      * Namespace support object for a new session.  If namespace
    147      * declaration URIs are to be supported, that flag must also
    148      * be set to a non-default value.
    149      * </p>
    150      *
    151      * @see #setNamespaceDeclUris
    152      */
    153     public void reset ()
    154     {
    155     contexts = new Context[32];
    156     namespaceDeclUris = false;
    157     contextPos = 0;
    158     contexts[contextPos] = currentContext = new Context();
    159     currentContext.declarePrefix("xml", XMLNS);
    160     }
    161 
    162 
    163     /**
    164      * Start a new Namespace context.
    165      * The new context will automatically inherit
    166      * the declarations of its parent context, but it will also keep
    167      * track of which declarations were made within this context.
    168      *
    169      * <p>Event callback code should start a new context once per element.
    170      * This means being ready to call this in either of two places.
    171      * For elements that don't include namespace declarations, the
    172      * <em>ContentHandler.startElement()</em> callback is the right place.
    173      * For elements with such a declaration, it'd done in the first
    174      * <em>ContentHandler.startPrefixMapping()</em> callback.
    175      * A boolean flag can be used to
    176      * track whether a context has been started yet.  When either of
    177      * those methods is called, it checks the flag to see if a new context
    178      * needs to be started.  If so, it starts the context and sets the
    179      * flag.  After <em>ContentHandler.startElement()</em>
    180      * does that, it always clears the flag.
    181      *
    182      * <p>Normally, SAX drivers would push a new context at the beginning
    183      * of each XML element.  Then they perform a first pass over the
    184      * attributes to process all namespace declarations, making
    185      * <em>ContentHandler.startPrefixMapping()</em> callbacks.
    186      * Then a second pass is made, to determine the namespace-qualified
    187      * names for all attributes and for the element name.
    188      * Finally all the information for the
    189      * <em>ContentHandler.startElement()</em> callback is available,
    190      * so it can then be made.
    191      *
    192      * <p>The Namespace support object always starts with a base context
    193      * already in force: in this context, only the "xml" prefix is
    194      * declared.</p>
    195      *
    196      * @see org.xml.sax.ContentHandler
    197      * @see #popContext
    198      */
    199     public void pushContext ()
    200     {
    201     int max = contexts.length;
    202 
    203     contexts [contextPos].declsOK = false;
    204     contextPos++;
    205 
    206                 // Extend the array if necessary
    207     if (contextPos >= max) {
    208         Context newContexts[] = new Context[max*2];
    209         System.arraycopy(contexts, 0, newContexts, 0, max);
    210         max *= 2;
    211         contexts = newContexts;
    212     }
    213 
    214                 // Allocate the context if necessary.
    215     currentContext = contexts[contextPos];
    216     if (currentContext == null) {
    217         contexts[contextPos] = currentContext = new Context();
    218     }
    219 
    220                 // Set the parent, if any.
    221     if (contextPos > 0) {
    222         currentContext.setParent(contexts[contextPos - 1]);
    223     }
    224     }
    225 
    226 
    227     /**
    228      * Revert to the previous Namespace context.
    229      *
    230      * <p>Normally, you should pop the context at the end of each
    231      * XML element.  After popping the context, all Namespace prefix
    232      * mappings that were previously in force are restored.</p>
    233      *
    234      * <p>You must not attempt to declare additional Namespace
    235      * prefixes after popping a context, unless you push another
    236      * context first.</p>
    237      *
    238      * @see #pushContext
    239      */
    240     public void popContext ()
    241     {
    242     contexts[contextPos].clear();
    243     contextPos--;
    244     if (contextPos < 0) {
    245         throw new EmptyStackException();
    246     }
    247     currentContext = contexts[contextPos];
    248     }
    249 
    250 
    251 
    252     ////////////////////////////////////////////////////////////////////
    254     // Operations within a context.
    255     ////////////////////////////////////////////////////////////////////
    256 
    257 
    258     /**
    259      * Declare a Namespace prefix.  All prefixes must be declared
    260      * before they are referenced.  For example, a SAX driver (parser)
    261      * would scan an element's attributes
    262      * in two passes:  first for namespace declarations,
    263      * then a second pass using {@link #processName processName()} to
    264      * interpret prefixes against (potentially redefined) prefixes.
    265      *
    266      * <p>This method declares a prefix in the current Namespace
    267      * context; the prefix will remain in force until this context
    268      * is popped, unless it is shadowed in a descendant context.</p>
    269      *
    270      * <p>To declare the default element Namespace, use the empty string as
    271      * the prefix.</p>
    272      *
    273      * <p>Note that you must <em>not</em> declare a prefix after
    274      * you've pushed and popped another Namespace context, or
    275      * treated the declarations phase as complete by processing
    276      * a prefixed name.</p>
    277      *
    278      * <p>Note that there is an asymmetry in this library: {@link
    279      * #getPrefix getPrefix} will not return the "" prefix,
    280      * even if you have declared a default element namespace.
    281      * To check for a default namespace,
    282      * you have to look it up explicitly using {@link #getURI getURI}.
    283      * This asymmetry exists to make it easier to look up prefixes
    284      * for attribute names, where the default prefix is not allowed.</p>
    285      *
    286      * @param prefix The prefix to declare, or the empty string to
    287      *    indicate the default element namespace.  This may never have
    288      *    the value "xml" or "xmlns".
    289      * @param uri The Namespace URI to associate with the prefix.
    290      * @return true if the prefix was legal, false otherwise
    291      *
    292      * @see #processName
    293      * @see #getURI
    294      * @see #getPrefix
    295      */
    296     public boolean declarePrefix (String prefix, String uri)
    297     {
    298     if (prefix.equals("xml") || prefix.equals("xmlns")) {
    299         return false;
    300     } else {
    301         currentContext.declarePrefix(prefix, uri);
    302         return true;
    303     }
    304     }
    305 
    306 
    307     /**
    308      * Process a raw XML qualified name, after all declarations in the
    309      * current context have been handled by {@link #declarePrefix
    310      * declarePrefix()}.
    311      *
    312      * <p>This method processes a raw XML qualified name in the
    313      * current context by removing the prefix and looking it up among
    314      * the prefixes currently declared.  The return value will be the
    315      * array supplied by the caller, filled in as follows:</p>
    316      *
    317      * <dl>
    318      * <dt>parts[0]</dt>
    319      * <dd>The Namespace URI, or an empty string if none is
    320      *  in use.</dd>
    321      * <dt>parts[1]</dt>
    322      * <dd>The local name (without prefix).</dd>
    323      * <dt>parts[2]</dt>
    324      * <dd>The original raw name.</dd>
    325      * </dl>
    326      *
    327      * <p>All of the strings in the array will be internalized.  If
    328      * the raw name has a prefix that has not been declared, then
    329      * the return value will be null.</p>
    330      *
    331      * <p>Note that attribute names are processed differently than
    332      * element names: an unprefixed element name will receive the
    333      * default Namespace (if any), while an unprefixed attribute name
    334      * will not.</p>
    335      *
    336      * @param qName The XML qualified name to be processed.
    337      * @param parts An array supplied by the caller, capable of
    338      *        holding at least three members.
    339      * @param isAttribute A flag indicating whether this is an
    340      *        attribute name (true) or an element name (false).
    341      * @return The supplied array holding three internalized strings
    342      *        representing the Namespace URI (or empty string), the
    343      *        local name, and the XML qualified name; or null if there
    344      *        is an undeclared prefix.
    345      * @see #declarePrefix
    346      * @see java.lang.String#intern */
    347     public String [] processName (String qName, String parts[],
    348                   boolean isAttribute)
    349     {
    350     String myParts[] = currentContext.processName(qName, isAttribute);
    351     if (myParts == null) {
    352         return null;
    353     } else {
    354         parts[0] = myParts[0];
    355         parts[1] = myParts[1];
    356         parts[2] = myParts[2];
    357         return parts;
    358     }
    359     }
    360 
    361 
    362     /**
    363      * Look up a prefix and get the currently-mapped Namespace URI.
    364      *
    365      * <p>This method looks up the prefix in the current context.
    366      * Use the empty string ("") for the default Namespace.</p>
    367      *
    368      * @param prefix The prefix to look up.
    369      * @return The associated Namespace URI, or null if the prefix
    370      *         is undeclared in this context.
    371      * @see #getPrefix
    372      * @see #getPrefixes
    373      */
    374     public String getURI (String prefix)
    375     {
    376     return currentContext.getURI(prefix);
    377     }
    378 
    379 
    380     /**
    381      * Return an enumeration of all prefixes whose declarations are
    382      * active in the current context.
    383      * This includes declarations from parent contexts that have
    384      * not been overridden.
    385      *
    386      * <p><strong>Note:</strong> if there is a default prefix, it will not be
    387      * returned in this enumeration; check for the default prefix
    388      * using the {@link #getURI getURI} with an argument of "".</p>
    389      *
    390      * @return An enumeration of prefixes (never empty).
    391      * @see #getDeclaredPrefixes
    392      * @see #getURI
    393      */
    394     public Enumeration getPrefixes ()
    395     {
    396     return currentContext.getPrefixes();
    397     }
    398 
    399 
    400     /**
    401      * Return one of the prefixes mapped to a Namespace URI.
    402      *
    403      * <p>If more than one prefix is currently mapped to the same
    404      * URI, this method will make an arbitrary selection; if you
    405      * want all of the prefixes, use the {@link #getPrefixes}
    406      * method instead.</p>
    407      *
    408      * <p><strong>Note:</strong> this will never return the empty (default) prefix;
    409      * to check for a default prefix, use the {@link #getURI getURI}
    410      * method with an argument of "".</p>
    411      *
    412      * @param uri the namespace URI
    413      * @return one of the prefixes currently mapped to the URI supplied,
    414      *         or null if none is mapped or if the URI is assigned to
    415      *         the default namespace
    416      * @see #getPrefixes(java.lang.String)
    417      * @see #getURI
    418      */
    419     public String getPrefix (String uri)
    420     {
    421     return currentContext.getPrefix(uri);
    422     }
    423 
    424 
    425     /**
    426      * Return an enumeration of all prefixes for a given URI whose
    427      * declarations are active in the current context.
    428      * This includes declarations from parent contexts that have
    429      * not been overridden.
    430      *
    431      * <p>This method returns prefixes mapped to a specific Namespace
    432      * URI.  The xml: prefix will be included.  If you want only one
    433      * prefix that's mapped to the Namespace URI, and you don't care
    434      * which one you get, use the {@link #getPrefix getPrefix}
    435      *  method instead.</p>
    436      *
    437      * <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included
    438      * in this enumeration; to check for the presence of a default
    439      * Namespace, use the {@link #getURI getURI} method with an
    440      * argument of "".</p>
    441      *
    442      * @param uri The Namespace URI.
    443      * @return An enumeration of prefixes (never empty).
    444      * @see #getPrefix
    445      * @see #getDeclaredPrefixes
    446      * @see #getURI
    447      */
    448     public Enumeration getPrefixes(String uri) {
    449         ArrayList<String> prefixes = new ArrayList<String>();
    450         Enumeration allPrefixes = getPrefixes();
    451         while (allPrefixes.hasMoreElements()) {
    452             String prefix = (String) allPrefixes.nextElement();
    453             if (uri.equals(getURI(prefix))) {
    454                 prefixes.add(prefix);
    455             }
    456         }
    457         return Collections.enumeration(prefixes);
    458     }
    459 
    460 
    461     /**
    462      * Return an enumeration of all prefixes declared in this context.
    463      *
    464      * <p>The empty (default) prefix will be included in this
    465      * enumeration; note that this behaviour differs from that of
    466      * {@link #getPrefix} and {@link #getPrefixes}.</p>
    467      *
    468      * @return An enumeration of all prefixes declared in this
    469      *         context.
    470      * @see #getPrefixes
    471      * @see #getURI
    472      */
    473     public Enumeration getDeclaredPrefixes ()
    474     {
    475     return currentContext.getDeclaredPrefixes();
    476     }
    477 
    478     /**
    479      * Controls whether namespace declaration attributes are placed
    480      * into the {@link #NSDECL NSDECL} namespace
    481      * by {@link #processName processName()}.  This may only be
    482      * changed before any contexts have been pushed.
    483      *
    484      * @param value the namespace declaration attribute state. A value of true
    485      *              enables this feature, a value of false disables it.
    486      *
    487      * @since SAX 2.1alpha
    488      *
    489      * @exception IllegalStateException when attempting to set this
    490      *    after any context has been pushed.
    491      */
    492     public void setNamespaceDeclUris (boolean value)
    493     {
    494     if (contextPos != 0)
    495         throw new IllegalStateException ();
    496     if (value == namespaceDeclUris)
    497         return;
    498     namespaceDeclUris = value;
    499     if (value)
    500         currentContext.declarePrefix ("xmlns", NSDECL);
    501     else {
    502         contexts[contextPos] = currentContext = new Context();
    503         currentContext.declarePrefix("xml", XMLNS);
    504     }
    505     }
    506 
    507     /**
    508      * Returns true if namespace declaration attributes are placed into
    509      * a namespace.  This behavior is not the default.
    510      *
    511      * @return true if namespace declaration attributes are enabled, false
    512      *         otherwise.
    513      * @since SAX 2.1alpha
    514      */
    515     public boolean isNamespaceDeclUris ()
    516     { return namespaceDeclUris; }
    517 
    518 
    519 
    520     ////////////////////////////////////////////////////////////////////
    522     // Internal state.
    523     ////////////////////////////////////////////////////////////////////
    524 
    525     private Context contexts[];
    526     private Context currentContext;
    527     private int contextPos;
    528     private boolean namespaceDeclUris;
    529 
    530 
    531     ////////////////////////////////////////////////////////////////////
    533     // Internal classes.
    534     ////////////////////////////////////////////////////////////////////
    535 
    536     /**
    537      * Internal class for a single Namespace context.
    538      *
    539      * <p>This module caches and reuses Namespace contexts,
    540      * so the number allocated
    541      * will be equal to the element depth of the document, not to the total
    542      * number of elements (i.e. 5-10 rather than tens of thousands).
    543      * Also, data structures used to represent contexts are shared when
    544      * possible (child contexts without declarations) to further reduce
    545      * the amount of memory that's consumed.
    546      * </p>
    547      */
    548     final class Context {
    549 
    550     /**
    551      * Create the root-level Namespace context.
    552      */
    553     Context ()
    554     {
    555         copyTables();
    556     }
    557 
    558 
    559     /**
    560      * (Re)set the parent of this Namespace context.
    561      * The context must either have been freshly constructed,
    562      * or must have been cleared.
    563      *
    564      * @param context The parent Namespace context object.
    565      */
    566     void setParent (Context parent)
    567     {
    568         this.parent = parent;
    569         declarations = null;
    570         prefixTable = parent.prefixTable;
    571         uriTable = parent.uriTable;
    572         elementNameTable = parent.elementNameTable;
    573         attributeNameTable = parent.attributeNameTable;
    574         defaultNS = parent.defaultNS;
    575         declSeen = false;
    576         declsOK = true;
    577     }
    578 
    579     /**
    580      * Makes associated state become collectible,
    581      * invalidating this context.
    582      * {@link #setParent} must be called before
    583      * this context may be used again.
    584      */
    585     void clear ()
    586     {
    587         parent = null;
    588         prefixTable = null;
    589         uriTable = null;
    590         elementNameTable = null;
    591         attributeNameTable = null;
    592         defaultNS = null;
    593     }
    594 
    595 
    596     /**
    597      * Declare a Namespace prefix for this context.
    598      *
    599      * @param prefix The prefix to declare.
    600      * @param uri The associated Namespace URI.
    601      * @see org.xml.sax.helpers.NamespaceSupport#declarePrefix
    602      */
    603     void declarePrefix(String prefix, String uri) {
    604         // Lazy processing...
    605         if (!declsOK) {
    606             throw new IllegalStateException ("can't declare any more prefixes in this context");
    607         }
    608         if (!declSeen) {
    609             copyTables();
    610         }
    611         if (declarations == null) {
    612             declarations = new ArrayList<String>();
    613         }
    614 
    615         prefix = prefix.intern();
    616         uri = uri.intern();
    617         if ("".equals(prefix)) {
    618             if ("".equals(uri)) {
    619                 defaultNS = null;
    620             } else {
    621                 defaultNS = uri;
    622             }
    623         } else {
    624             prefixTable.put(prefix, uri);
    625             uriTable.put(uri, prefix); // may wipe out another prefix
    626         }
    627         declarations.add(prefix);
    628     }
    629 
    630 
    631     /**
    632      * Process an XML qualified name in this context.
    633      *
    634      * @param qName The XML qualified name.
    635      * @param isAttribute true if this is an attribute name.
    636      * @return An array of three strings containing the
    637      *         URI part (or empty string), the local part,
    638      *         and the raw name, all internalized, or null
    639      *         if there is an undeclared prefix.
    640      * @see org.xml.sax.helpers.NamespaceSupport#processName
    641      */
    642     String [] processName (String qName, boolean isAttribute)
    643     {
    644         String name[];
    645         Hashtable table;
    646 
    647                     // detect errors in call sequence
    648         declsOK = false;
    649 
    650                 // Select the appropriate table.
    651         if (isAttribute) {
    652         table = attributeNameTable;
    653         } else {
    654         table = elementNameTable;
    655         }
    656 
    657                 // Start by looking in the cache, and
    658                 // return immediately if the name
    659                 // is already known in this content
    660         name = (String[])table.get(qName);
    661         if (name != null) {
    662         return name;
    663         }
    664 
    665                 // We haven't seen this name in this
    666                 // context before.  Maybe in the parent
    667                 // context, but we can't assume prefix
    668                 // bindings are the same.
    669         name = new String[3];
    670         name[2] = qName.intern();
    671         int index = qName.indexOf(':');
    672 
    673 
    674                 // No prefix.
    675         if (index == -1) {
    676         if (isAttribute) {
    677             if (qName == "xmlns" && namespaceDeclUris)
    678             name[0] = NSDECL;
    679             else
    680             name[0] = "";
    681         } else if (defaultNS == null) {
    682             name[0] = "";
    683         } else {
    684             name[0] = defaultNS;
    685         }
    686         name[1] = name[2];
    687         }
    688 
    689                 // Prefix
    690         else {
    691         String prefix = qName.substring(0, index);
    692         String local = qName.substring(index+1);
    693         String uri;
    694         if ("".equals(prefix)) {
    695             uri = defaultNS;
    696         } else {
    697             uri = (String)prefixTable.get(prefix);
    698         }
    699         if (uri == null
    700             || (!isAttribute && "xmlns".equals (prefix))) {
    701             return null;
    702         }
    703         name[0] = uri;
    704         name[1] = local.intern();
    705         }
    706 
    707                 // Save in the cache for future use.
    708                 // (Could be shared with parent context...)
    709         table.put(name[2], name);
    710         return name;
    711     }
    712 
    713 
    714     /**
    715      * Look up the URI associated with a prefix in this context.
    716      *
    717      * @param prefix The prefix to look up.
    718      * @return The associated Namespace URI, or null if none is
    719      *         declared.
    720      * @see org.xml.sax.helpers.NamespaceSupport#getURI
    721      */
    722     String getURI (String prefix)
    723     {
    724         if ("".equals(prefix)) {
    725         return defaultNS;
    726         } else if (prefixTable == null) {
    727         return null;
    728         } else {
    729         return (String)prefixTable.get(prefix);
    730         }
    731     }
    732 
    733 
    734     /**
    735      * Look up one of the prefixes associated with a URI in this context.
    736      *
    737      * <p>Since many prefixes may be mapped to the same URI,
    738      * the return value may be unreliable.</p>
    739      *
    740      * @param uri The URI to look up.
    741      * @return The associated prefix, or null if none is declared.
    742      * @see org.xml.sax.helpers.NamespaceSupport#getPrefix
    743      */
    744     String getPrefix (String uri)
    745     {
    746         if (uriTable == null) {
    747         return null;
    748         } else {
    749         return (String)uriTable.get(uri);
    750         }
    751     }
    752 
    753 
    754     /**
    755      * Return an enumeration of prefixes declared in this context.
    756      *
    757      * @return An enumeration of prefixes (possibly empty).
    758      * @see org.xml.sax.helpers.NamespaceSupport#getDeclaredPrefixes
    759      */
    760     Enumeration getDeclaredPrefixes() {
    761         return (declarations == null) ? EMPTY_ENUMERATION : Collections.enumeration(declarations);
    762     }
    763 
    764 
    765     /**
    766      * Return an enumeration of all prefixes currently in force.
    767      *
    768      * <p>The default prefix, if in force, is <em>not</em>
    769      * returned, and will have to be checked for separately.</p>
    770      *
    771      * @return An enumeration of prefixes (never empty).
    772      * @see org.xml.sax.helpers.NamespaceSupport#getPrefixes
    773      */
    774     Enumeration getPrefixes ()
    775     {
    776         if (prefixTable == null) {
    777         return EMPTY_ENUMERATION;
    778         } else {
    779         return prefixTable.keys();
    780         }
    781     }
    782 
    783 
    784 
    785     ////////////////////////////////////////////////////////////////
    787     // Internal methods.
    788     ////////////////////////////////////////////////////////////////
    789 
    790 
    791     /**
    792      * Copy on write for the internal tables in this context.
    793      *
    794      * <p>This class is optimized for the normal case where most
    795      * elements do not contain Namespace declarations.</p>
    796      */
    797     private void copyTables ()
    798     {
    799         if (prefixTable != null) {
    800         prefixTable = (Hashtable)prefixTable.clone();
    801         } else {
    802         prefixTable = new Hashtable();
    803         }
    804         if (uriTable != null) {
    805         uriTable = (Hashtable)uriTable.clone();
    806         } else {
    807         uriTable = new Hashtable();
    808         }
    809         elementNameTable = new Hashtable();
    810         attributeNameTable = new Hashtable();
    811         declSeen = true;
    812     }
    813 
    814 
    815 
    816     ////////////////////////////////////////////////////////////////
    818     // Protected state.
    819     ////////////////////////////////////////////////////////////////
    820 
    821     Hashtable prefixTable;
    822     Hashtable uriTable;
    823     Hashtable elementNameTable;
    824     Hashtable attributeNameTable;
    825     String defaultNS = null;
    826     boolean declsOK = true;
    827 
    828 
    829 
    830     ////////////////////////////////////////////////////////////////
    832     // Internal state.
    833     ////////////////////////////////////////////////////////////////
    834 
    835     private ArrayList<String> declarations = null;
    836     private boolean declSeen = false;
    837     private Context parent = null;
    838     }
    839 }
    840 
    841 // end of NamespaceSupport.java
    842