Home | History | Annotate | Download | only in utils
      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: NamespaceSupport2.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xml.utils;
     22 
     23 import java.util.EmptyStackException;
     24 import java.util.Enumeration;
     25 import java.util.Hashtable;
     26 import java.util.Vector;
     27 
     28 /**
     29  * Encapsulate Namespace tracking logic for use by SAX drivers.
     30  *
     31  * <p>This class is an attempt to rewrite the SAX NamespaceSupport
     32  * "helper" class for improved efficiency. It can be used to track the
     33  * namespace declarations currently in scope, providing lookup
     34  * routines to map prefixes to URIs and vice versa.</p>
     35  *
     36  * <p>ISSUE: For testing purposes, I've extended NamespaceSupport even
     37  * though I'm completely reasserting all behaviors and fields.
     38  * Wasteful.... But SAX did not put an interface under that object and
     39  * we seem to have written that SAX class into our APIs... and I don't
     40  * want to argue with it right now. </p>
     41  *
     42  * @see org.xml.sax.helpers.NamespaceSupport
     43  * */
     44 public class NamespaceSupport2
     45     extends org.xml.sax.helpers.NamespaceSupport
     46 {
     47     ////////////////////////////////////////////////////////////////////
     48     // Internal state.
     49     ////////////////////////////////////////////////////////////////////
     50 
     51     private Context2 currentContext; // Current point on the double-linked stack
     52 
     53 
     54     ////////////////////////////////////////////////////////////////////
     55     // Constants.
     56     ////////////////////////////////////////////////////////////////////
     57 
     58 
     59     /**
     60      * The XML Namespace as a constant.
     61      *
     62      * <p>This is the Namespace URI that is automatically mapped
     63      * to the "xml" prefix.</p>
     64      */
     65     public final static String XMLNS =
     66         "http://www.w3.org/XML/1998/namespace";
     67 
     68 
     69     ////////////////////////////////////////////////////////////////////
     70     // Constructor.
     71     ////////////////////////////////////////////////////////////////////
     72 
     73 
     74     /**
     75      * Create a new Namespace support object.
     76      */
     77     public NamespaceSupport2 ()
     78     {
     79         reset();
     80     }
     81 
     82 
     83     ////////////////////////////////////////////////////////////////////
     84     // Context management.
     85     ////////////////////////////////////////////////////////////////////
     86 
     87 
     88     /**
     89      * Reset this Namespace support object for reuse.
     90      *
     91      * <p>It is necessary to invoke this method before reusing the
     92      * Namespace support object for a new session.</p>
     93      */
     94     public void reset ()
     95     {
     96         // Discarding the whole stack doesn't save us a lot versus
     97         // creating a new NamespaceSupport. Do we care, or should we
     98         // change this to just reset the root context?
     99         currentContext = new Context2(null);
    100         currentContext.declarePrefix("xml", XMLNS);
    101     }
    102 
    103 
    104     /**
    105      * Start a new Namespace context.
    106      *
    107      * <p>Normally, you should push a new context at the beginning
    108      * of each XML element: the new context will automatically inherit
    109      * the declarations of its parent context, but it will also keep
    110      * track of which declarations were made within this context.</p>
    111      *
    112      * <p>The Namespace support object always starts with a base context
    113      * already in force: in this context, only the "xml" prefix is
    114      * declared.</p>
    115      *
    116      * @see #popContext
    117      */
    118     public void pushContext ()
    119     {
    120         // JJK: Context has a parent pointer.
    121         // That means we don't need a stack to pop.
    122         // We may want to retain for reuse, but that can be done via
    123         // a child pointer.
    124 
    125         Context2 parentContext=currentContext;
    126         currentContext = parentContext.getChild();
    127         if (currentContext == null){
    128                 currentContext = new Context2(parentContext);
    129             }
    130         else{
    131             // JJK: This will wipe out any leftover data
    132             // if we're reusing a previously allocated Context.
    133             currentContext.setParent(parentContext);
    134         }
    135     }
    136 
    137 
    138     /**
    139      * Revert to the previous Namespace context.
    140      *
    141      * <p>Normally, you should pop the context at the end of each
    142      * XML element.  After popping the context, all Namespace prefix
    143      * mappings that were previously in force are restored.</p>
    144      *
    145      * <p>You must not attempt to declare additional Namespace
    146      * prefixes after popping a context, unless you push another
    147      * context first.</p>
    148      *
    149      * @see #pushContext
    150      */
    151     public void popContext ()
    152     {
    153         Context2 parentContext=currentContext.getParent();
    154         if(parentContext==null)
    155             throw new EmptyStackException();
    156         else
    157             currentContext = parentContext;
    158     }
    159 
    160 
    161 
    162     ////////////////////////////////////////////////////////////////////
    163     // Operations within a context.
    164     ////////////////////////////////////////////////////////////////////
    165 
    166 
    167     /**
    168      * Declare a Namespace prefix.
    169      *
    170      * <p>This method declares a prefix in the current Namespace
    171      * context; the prefix will remain in force until this context
    172      * is popped, unless it is shadowed in a descendant context.</p>
    173      *
    174      * <p>To declare a default Namespace, use the empty string.  The
    175      * prefix must not be "xml" or "xmlns".</p>
    176      *
    177      * <p>Note that you must <em>not</em> declare a prefix after
    178      * you've pushed and popped another Namespace.</p>
    179      *
    180      * <p>Note that there is an asymmetry in this library: while {@link
    181      * #getPrefix getPrefix} will not return the default "" prefix,
    182      * even if you have declared one; to check for a default prefix,
    183      * you have to look it up explicitly using {@link #getURI getURI}.
    184      * This asymmetry exists to make it easier to look up prefixes
    185      * for attribute names, where the default prefix is not allowed.</p>
    186      *
    187      * @param prefix The prefix to declare, or null for the empty
    188      *        string.
    189      * @param uri The Namespace URI to associate with the prefix.
    190      * @return true if the prefix was legal, false otherwise
    191      * @see #processName
    192      * @see #getURI
    193      * @see #getPrefix
    194      */
    195     public boolean declarePrefix (String prefix, String uri)
    196     {
    197         if (prefix.equals("xml") || prefix.equals("xmlns")) {
    198             return false;
    199         } else {
    200             currentContext.declarePrefix(prefix, uri);
    201             return true;
    202         }
    203     }
    204 
    205 
    206     /**
    207      * Process a raw XML 1.0 name.
    208      *
    209      * <p>This method processes a raw XML 1.0 name in the current
    210      * context by removing the prefix and looking it up among the
    211      * prefixes currently declared.  The return value will be the
    212      * array supplied by the caller, filled in as follows:</p>
    213      *
    214      * <dl>
    215      * <dt>parts[0]</dt>
    216      * <dd>The Namespace URI, or an empty string if none is
    217      *  in use.</dd>
    218      * <dt>parts[1]</dt>
    219      * <dd>The local name (without prefix).</dd>
    220      * <dt>parts[2]</dt>
    221      * <dd>The original raw name.</dd>
    222      * </dl>
    223      *
    224      * <p>All of the strings in the array will be internalized.  If
    225      * the raw name has a prefix that has not been declared, then
    226      * the return value will be null.</p>
    227      *
    228      * <p>Note that attribute names are processed differently than
    229      * element names: an unprefixed element name will received the
    230      * default Namespace (if any), while an unprefixed element name
    231      * will not.</p>
    232      *
    233      * @param qName The raw XML 1.0 name to be processed.
    234      * @param parts A string array supplied by the caller, capable of
    235      *        holding at least three members.
    236      * @param isAttribute A flag indicating whether this is an
    237      *        attribute name (true) or an element name (false).
    238      * @return The supplied array holding three internalized strings
    239      *        representing the Namespace URI (or empty string), the
    240      *        local name, and the raw XML 1.0 name; or null if there
    241      *        is an undeclared prefix.
    242      * @see #declarePrefix
    243      * @see java.lang.String#intern */
    244     public String [] processName (String qName, String[] parts,
    245                                   boolean isAttribute)
    246     {
    247         String[] name=currentContext.processName(qName, isAttribute);
    248         if(name==null)
    249             return null;
    250 
    251         // JJK: This recopying is required because processName may return
    252         // a cached result. I Don't Like It. *****
    253         System.arraycopy(name,0,parts,0,3);
    254         return parts;
    255     }
    256 
    257 
    258     /**
    259      * Look up a prefix and get the currently-mapped Namespace URI.
    260      *
    261      * <p>This method looks up the prefix in the current context.
    262      * Use the empty string ("") for the default Namespace.</p>
    263      *
    264      * @param prefix The prefix to look up.
    265      * @return The associated Namespace URI, or null if the prefix
    266      *         is undeclared in this context.
    267      * @see #getPrefix
    268      * @see #getPrefixes
    269      */
    270     public String getURI (String prefix)
    271     {
    272         return currentContext.getURI(prefix);
    273     }
    274 
    275 
    276     /**
    277      * Return an enumeration of all prefixes currently declared.
    278      *
    279      * <p><strong>Note:</strong> if there is a default prefix, it will not be
    280      * returned in this enumeration; check for the default prefix
    281      * using the {@link #getURI getURI} with an argument of "".</p>
    282      *
    283      * @return An enumeration of all prefixes declared in the
    284      *         current context except for the empty (default)
    285      *         prefix.
    286      * @see #getDeclaredPrefixes
    287      * @see #getURI
    288      */
    289     public Enumeration getPrefixes ()
    290     {
    291         return currentContext.getPrefixes();
    292     }
    293 
    294 
    295     /**
    296      * Return one of the prefixes mapped to a Namespace URI.
    297      *
    298      * <p>If more than one prefix is currently mapped to the same
    299      * URI, this method will make an arbitrary selection; if you
    300      * want all of the prefixes, use the {@link #getPrefixes}
    301      * method instead.</p>
    302      *
    303      * <p><strong>Note:</strong> this will never return the empty
    304      * (default) prefix; to check for a default prefix, use the {@link
    305      * #getURI getURI} method with an argument of "".</p>
    306      *
    307      * @param uri The Namespace URI.
    308      * @return One of the prefixes currently mapped to the URI supplied,
    309      *         or null if none is mapped or if the URI is assigned to
    310      *         the default Namespace.
    311      * @see #getPrefixes(java.lang.String)
    312      * @see #getURI */
    313     public String getPrefix (String uri)
    314     {
    315         return currentContext.getPrefix(uri);
    316     }
    317 
    318 
    319     /**
    320      * Return an enumeration of all prefixes currently declared for a URI.
    321      *
    322      * <p>This method returns prefixes mapped to a specific Namespace
    323      * URI.  The xml: prefix will be included.  If you want only one
    324      * prefix that's mapped to the Namespace URI, and you don't care
    325      * which one you get, use the {@link #getPrefix getPrefix}
    326      *  method instead.</p>
    327      *
    328      * <p><strong>Note:</strong> the empty (default) prefix is
    329      * <em>never</em> included in this enumeration; to check for the
    330      * presence of a default Namespace, use the {@link #getURI getURI}
    331      * method with an argument of "".</p>
    332      *
    333      * @param uri The Namespace URI.
    334      * @return An enumeration of all prefixes declared in the
    335      *         current context.
    336      * @see #getPrefix
    337      * @see #getDeclaredPrefixes
    338      * @see #getURI */
    339     public Enumeration getPrefixes (String uri)
    340     {
    341         // JJK: The old code involved creating a vector, filling it
    342         // with all the matching prefixes, and then getting its
    343         // elements enumerator. Wastes storage, wastes cycles if we
    344         // don't actually need them all. Better to either implement
    345         // a specific enumerator for these prefixes... or a filter
    346         // around the all-prefixes enumerator, which comes out to
    347         // roughly the same thing.
    348         //
    349         // **** Currently a filter. That may not be most efficient
    350         // when I'm done restructuring storage!
    351         return new PrefixForUriEnumerator(this,uri,getPrefixes());
    352     }
    353 
    354 
    355     /**
    356      * Return an enumeration of all prefixes declared in this context.
    357      *
    358      * <p>The empty (default) prefix will be included in this
    359      * enumeration; note that this behaviour differs from that of
    360      * {@link #getPrefix} and {@link #getPrefixes}.</p>
    361      *
    362      * @return An enumeration of all prefixes declared in this
    363      *         context.
    364      * @see #getPrefixes
    365      * @see #getURI
    366      */
    367     public Enumeration getDeclaredPrefixes ()
    368     {
    369         return currentContext.getDeclaredPrefixes();
    370     }
    371 
    372 
    373 
    374 }
    375 
    376 ////////////////////////////////////////////////////////////////////
    377 // Local classes.
    378 // These were _internal_ classes... but in fact they don't have to be,
    379 // and may be more efficient if they aren't.
    380 ////////////////////////////////////////////////////////////////////
    381 
    382 /**
    383  * Implementation of Enumeration filter, wrapped
    384  * aroung the get-all-prefixes version of the operation. This is NOT
    385  * necessarily the most efficient approach; finding the URI and then asking
    386  * what prefixes apply to it might make much more sense.
    387  */
    388 class PrefixForUriEnumerator implements Enumeration
    389 {
    390     private Enumeration allPrefixes;
    391     private String uri;
    392     private String lookahead=null;
    393     private NamespaceSupport2 nsup;
    394 
    395     // Kluge: Since one can't do a constructor on an
    396     // anonymous class (as far as I know)...
    397     PrefixForUriEnumerator(NamespaceSupport2 nsup,String uri, Enumeration allPrefixes)
    398     {
    399 	this.nsup=nsup;
    400         this.uri=uri;
    401         this.allPrefixes=allPrefixes;
    402     }
    403 
    404     public boolean hasMoreElements()
    405     {
    406         if(lookahead!=null)
    407             return true;
    408 
    409         while(allPrefixes.hasMoreElements())
    410             {
    411                 String prefix=(String)allPrefixes.nextElement();
    412                 if(uri.equals(nsup.getURI(prefix)))
    413                     {
    414                         lookahead=prefix;
    415                         return true;
    416                     }
    417             }
    418         return false;
    419     }
    420 
    421     public Object nextElement()
    422     {
    423         if(hasMoreElements())
    424             {
    425                 String tmp=lookahead;
    426                 lookahead=null;
    427                 return tmp;
    428             }
    429         else
    430             throw new java.util.NoSuchElementException();
    431     }
    432 }
    433 
    434 /**
    435  * Internal class for a single Namespace context.
    436  *
    437  * <p>This module caches and reuses Namespace contexts, so the number allocated
    438  * will be equal to the element depth of the document, not to the total
    439  * number of elements (i.e. 5-10 rather than tens of thousands).</p>
    440  */
    441 final class Context2 {
    442 
    443     ////////////////////////////////////////////////////////////////
    444     // Manefest Constants
    445     ////////////////////////////////////////////////////////////////
    446 
    447     /**
    448      * An empty enumeration.
    449      */
    450     private final static Enumeration EMPTY_ENUMERATION =
    451         new Vector().elements();
    452 
    453     ////////////////////////////////////////////////////////////////
    454     // Protected state.
    455     ////////////////////////////////////////////////////////////////
    456 
    457     Hashtable prefixTable;
    458     Hashtable uriTable;
    459     Hashtable elementNameTable;
    460     Hashtable attributeNameTable;
    461     String defaultNS = null;
    462 
    463     ////////////////////////////////////////////////////////////////
    464     // Internal state.
    465     ////////////////////////////////////////////////////////////////
    466 
    467     private Vector declarations = null;
    468     private boolean tablesDirty = false;
    469     private Context2 parent = null;
    470     private Context2 child = null;
    471 
    472     /**
    473      * Create a new Namespace context.
    474      */
    475     Context2 (Context2 parent)
    476     {
    477         if(parent==null)
    478             {
    479                 prefixTable = new Hashtable();
    480                 uriTable = new Hashtable();
    481                 elementNameTable=null;
    482                 attributeNameTable=null;
    483             }
    484         else
    485             setParent(parent);
    486     }
    487 
    488 
    489     /**
    490      * @returns The child Namespace context object, or null if this
    491      * is the last currently on the chain.
    492      */
    493     Context2 getChild()
    494     {
    495         return child;
    496     }
    497 
    498     /**
    499      * @returns The parent Namespace context object, or null if this
    500      * is the root.
    501      */
    502     Context2 getParent()
    503     {
    504         return parent;
    505     }
    506 
    507     /**
    508      * (Re)set the parent of this Namespace context.
    509      * This is separate from the c'tor because it's re-applied
    510      * when a Context2 is reused by push-after-pop.
    511      *
    512      * @param context The parent Namespace context object.
    513      */
    514     void setParent (Context2 parent)
    515     {
    516         this.parent = parent;
    517         parent.child = this;        // JJK: Doubly-linked
    518         declarations = null;
    519         prefixTable = parent.prefixTable;
    520         uriTable = parent.uriTable;
    521         elementNameTable = parent.elementNameTable;
    522         attributeNameTable = parent.attributeNameTable;
    523         defaultNS = parent.defaultNS;
    524         tablesDirty = false;
    525     }
    526 
    527 
    528     /**
    529      * Declare a Namespace prefix for this context.
    530      *
    531      * @param prefix The prefix to declare.
    532      * @param uri The associated Namespace URI.
    533      * @see org.xml.sax.helpers.NamespaceSupport2#declarePrefix
    534      */
    535     void declarePrefix (String prefix, String uri)
    536     {
    537                                 // Lazy processing...
    538         if (!tablesDirty) {
    539             copyTables();
    540         }
    541         if (declarations == null) {
    542             declarations = new Vector();
    543         }
    544 
    545         prefix = prefix.intern();
    546         uri = uri.intern();
    547         if ("".equals(prefix)) {
    548             if ("".equals(uri)) {
    549                 defaultNS = null;
    550             } else {
    551                 defaultNS = uri;
    552             }
    553         } else {
    554             prefixTable.put(prefix, uri);
    555             uriTable.put(uri, prefix); // may wipe out another prefix
    556         }
    557         declarations.addElement(prefix);
    558     }
    559 
    560 
    561     /**
    562      * Process a raw XML 1.0 name in this context.
    563      *
    564      * @param qName The raw XML 1.0 name.
    565      * @param isAttribute true if this is an attribute name.
    566      * @return An array of three strings containing the
    567      *         URI part (or empty string), the local part,
    568      *         and the raw name, all internalized, or null
    569      *         if there is an undeclared prefix.
    570      * @see org.xml.sax.helpers.NamespaceSupport2#processName
    571      */
    572     String [] processName (String qName, boolean isAttribute)
    573     {
    574         String name[];
    575         Hashtable table;
    576 
    577                                 // Select the appropriate table.
    578         if (isAttribute) {
    579             if(elementNameTable==null)
    580                 elementNameTable=new Hashtable();
    581             table = elementNameTable;
    582         } else {
    583             if(attributeNameTable==null)
    584                 attributeNameTable=new Hashtable();
    585             table = attributeNameTable;
    586         }
    587 
    588                                 // Start by looking in the cache, and
    589                                 // return immediately if the name
    590                                 // is already known in this content
    591         name = (String[])table.get(qName);
    592         if (name != null) {
    593             return name;
    594         }
    595 
    596                                 // We haven't seen this name in this
    597                                 // context before.
    598         name = new String[3];
    599         int index = qName.indexOf(':');
    600 
    601 
    602                                 // No prefix.
    603         if (index == -1) {
    604             if (isAttribute || defaultNS == null) {
    605                 name[0] = "";
    606             } else {
    607                 name[0] = defaultNS;
    608             }
    609             name[1] = qName.intern();
    610             name[2] = name[1];
    611         }
    612 
    613                                 // Prefix
    614         else {
    615             String prefix = qName.substring(0, index);
    616             String local = qName.substring(index+1);
    617             String uri;
    618             if ("".equals(prefix)) {
    619                 uri = defaultNS;
    620             } else {
    621                 uri = (String)prefixTable.get(prefix);
    622             }
    623             if (uri == null) {
    624                 return null;
    625             }
    626             name[0] = uri;
    627             name[1] = local.intern();
    628             name[2] = qName.intern();
    629         }
    630 
    631                                 // Save in the cache for future use.
    632         table.put(name[2], name);
    633         tablesDirty = true;
    634         return name;
    635     }
    636 
    637 
    638     /**
    639      * Look up the URI associated with a prefix in this context.
    640      *
    641      * @param prefix The prefix to look up.
    642      * @return The associated Namespace URI, or null if none is
    643      *         declared.
    644      * @see org.xml.sax.helpers.NamespaceSupport2#getURI
    645      */
    646     String getURI (String prefix)
    647     {
    648         if ("".equals(prefix)) {
    649             return defaultNS;
    650         } else if (prefixTable == null) {
    651             return null;
    652         } else {
    653             return (String)prefixTable.get(prefix);
    654         }
    655     }
    656 
    657 
    658     /**
    659      * Look up one of the prefixes associated with a URI in this context.
    660      *
    661      * <p>Since many prefixes may be mapped to the same URI,
    662      * the return value may be unreliable.</p>
    663      *
    664      * @param uri The URI to look up.
    665      * @return The associated prefix, or null if none is declared.
    666      * @see org.xml.sax.helpers.NamespaceSupport2#getPrefix
    667      */
    668     String getPrefix (String uri)
    669     {
    670         if (uriTable == null) {
    671             return null;
    672         } else {
    673             return (String)uriTable.get(uri);
    674         }
    675     }
    676 
    677 
    678     /**
    679      * Return an enumeration of prefixes declared in this context.
    680      *
    681      * @return An enumeration of prefixes (possibly empty).
    682      * @see org.xml.sax.helpers.NamespaceSupport2#getDeclaredPrefixes
    683      */
    684     Enumeration getDeclaredPrefixes ()
    685     {
    686         if (declarations == null) {
    687             return EMPTY_ENUMERATION;
    688         } else {
    689             return declarations.elements();
    690         }
    691     }
    692 
    693 
    694     /**
    695      * Return an enumeration of all prefixes currently in force.
    696      *
    697      * <p>The default prefix, if in force, is <em>not</em>
    698      * returned, and will have to be checked for separately.</p>
    699      *
    700      * @return An enumeration of prefixes (never empty).
    701      * @see org.xml.sax.helpers.NamespaceSupport2#getPrefixes
    702      */
    703     Enumeration getPrefixes ()
    704     {
    705         if (prefixTable == null) {
    706             return EMPTY_ENUMERATION;
    707         } else {
    708             return prefixTable.keys();
    709         }
    710     }
    711 
    712     ////////////////////////////////////////////////////////////////
    713     // Internal methods.
    714     ////////////////////////////////////////////////////////////////
    715 
    716     /**
    717      * Copy on write for the internal tables in this context.
    718      *
    719      * <p>This class is optimized for the normal case where most
    720      * elements do not contain Namespace declarations. In that case,
    721      * the Context2 will share data structures with its parent.
    722      * New tables are obtained only when new declarations are issued,
    723      * so they can be popped off the stack.</p>
    724      *
    725      * <p> JJK: **** Alternative: each Context2 might declare
    726      *  _only_ its local bindings, and delegate upward if not found.</p>
    727      */
    728     private void copyTables ()
    729     {
    730         // Start by copying our parent's bindings
    731         prefixTable = (Hashtable)prefixTable.clone();
    732         uriTable = (Hashtable)uriTable.clone();
    733 
    734         // Replace the caches with empty ones, rather than
    735         // trying to determine which bindings should be flushed.
    736         // As far as I can tell, these caches are never actually
    737         // used in Xalan... More efficient to remove the whole
    738         // cache system? ****
    739         if(elementNameTable!=null)
    740             elementNameTable=new Hashtable();
    741         if(attributeNameTable!=null)
    742             attributeNameTable=new Hashtable();
    743         tablesDirty = true;
    744     }
    745 
    746 }
    747 
    748 
    749 // end of NamespaceSupport2.java
    750