001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: NamespaceSupport2.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xml.utils;
022    
023    import java.util.EmptyStackException;
024    import java.util.Enumeration;
025    import java.util.Hashtable;
026    import java.util.Vector;
027    
028    /**
029     * Encapsulate Namespace tracking logic for use by SAX drivers.
030     *
031     * <p>This class is an attempt to rewrite the SAX NamespaceSupport
032     * "helper" class for improved efficiency. It can be used to track the
033     * namespace declarations currently in scope, providing lookup
034     * routines to map prefixes to URIs and vice versa.</p>
035     *
036     * <p>ISSUE: For testing purposes, I've extended NamespaceSupport even
037     * though I'm completely reasserting all behaviors and fields.
038     * Wasteful.... But SAX did not put an interface under that object and
039     * we seem to have written that SAX class into our APIs... and I don't
040     * want to argue with it right now. </p>
041     *
042     * @see org.xml.sax.helpers.NamespaceSupport
043     * */
044    public class NamespaceSupport2
045        extends org.xml.sax.helpers.NamespaceSupport
046    {
047        ////////////////////////////////////////////////////////////////////
048        // Internal state.
049        ////////////////////////////////////////////////////////////////////
050    
051        private Context2 currentContext; // Current point on the double-linked stack
052    
053    
054        ////////////////////////////////////////////////////////////////////
055        // Constants.
056        ////////////////////////////////////////////////////////////////////
057    
058    
059        /**
060         * The XML Namespace as a constant.
061         *
062         * <p>This is the Namespace URI that is automatically mapped
063         * to the "xml" prefix.</p>
064         */
065        public final static String XMLNS =
066            "http://www.w3.org/XML/1998/namespace";
067    
068    
069        ////////////////////////////////////////////////////////////////////
070        // Constructor.
071        ////////////////////////////////////////////////////////////////////
072    
073    
074        /**
075         * Create a new Namespace support object.
076         */
077        public NamespaceSupport2 ()
078        {
079            reset();
080        }
081    
082    
083        ////////////////////////////////////////////////////////////////////
084        // Context management.
085        ////////////////////////////////////////////////////////////////////
086    
087    
088        /**
089         * Reset this Namespace support object for reuse.
090         *
091         * <p>It is necessary to invoke this method before reusing the
092         * Namespace support object for a new session.</p>
093         */
094        public void reset ()
095        {
096            // Discarding the whole stack doesn't save us a lot versus
097            // creating a new NamespaceSupport. Do we care, or should we
098            // change this to just reset the root context?
099            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