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: NamespaceMappings.java 1225444 2011-12-29 05:52:39Z mrglavas $
020     */
021    package org.apache.xml.serializer;
022    
023    import java.util.Enumeration;
024    import java.util.Hashtable;
025    
026    import org.xml.sax.ContentHandler;
027    import org.xml.sax.SAXException;
028    
029    /**
030     * This class keeps track of the currently defined namespaces. Conceptually the
031     * prefix/uri/depth triplets are pushed on a stack pushed on a stack. The depth
032     * indicates the nesting depth of the element for which the mapping was made.
033     * 
034     * <p>For example:
035     * <pre>
036     * <chapter xmlns:p1="def">
037     *   <paragraph xmlns:p2="ghi">
038     *      <sentance xmlns:p3="jkl">
039     *      </sentance>
040     *    </paragraph>
041     *    <paragraph xlmns:p4="mno">
042     *    </paragraph>
043     * </chapter>
044     * </pre>
045     * 
046     * When the <chapter> element is encounted the prefix "p1" associated with uri
047     * "def" is pushed on the stack with depth 1.
048     * When the first <paragraph> is encountered "p2" and "ghi" are pushed with
049     * depth 2.
050     * When the <sentance> is encountered "p3" and "jkl" are pushed with depth 3.
051     * When </sentance> occurs the popNamespaces(3) will pop "p3"/"jkl" off the
052     * stack.  Of course popNamespaces(2) would pop anything with depth 2 or
053     * greater.
054     * 
055     * So prefix/uri pairs are pushed and poped off the stack as elements are
056     * processed.  At any given moment of processing the currently visible prefixes
057     * are on the stack and a prefix can be found given a uri, or a uri can be found
058     * given a prefix.
059     *
060     * This class is intended for internal use only.  However, it is made public because
061     * other packages require it. 
062     * @xsl.usage internal
063     */
064    public class NamespaceMappings
065    {
066        /**
067         * This member is continually incremented when new prefixes need to be
068         * generated. ("ns0"  "ns1" ...)
069         */
070        private int count = 0;
071    
072        /**
073         * Each entry (prefix) in this hashtable points to a Stack of URIs
074         * This table maps a prefix (String) to a Stack of NamespaceNodes.
075         * All Namespace nodes in that retrieved stack have the same prefix,
076         * though possibly different URI's or depths. Such a stack must have
077         * mappings at deeper depths push later on such a stack.  Mappings pushed
078         * earlier on the stack will have smaller values for MappingRecord.m_declarationDepth.
079         */
080        private Hashtable m_namespaces = new Hashtable();
081    
082        /** 
083         * This stack is used as a convenience.
084         * It contains the pushed NamespaceNodes (shallowest
085         * to deepest) and is used to delete NamespaceNodes 
086         * when leaving the current element depth 
087         * to returning to the parent. The mappings of the deepest
088         * depth can be popped of the top and the same node
089         * can be removed from the appropriate prefix stack.
090         * 
091         * All prefixes pushed at the current depth can be 
092         * removed at the same time by using this stack to
093         * ensure prefix/uri map scopes are closed correctly.
094         */
095        private Stack m_nodeStack = new Stack();
096    
097        private static final String EMPTYSTRING = "";
098        private static final String XML_PREFIX = "xml"; // was "xmlns"
099    
100        /**
101         * Default constructor
102         * @see java.lang.Object#Object()
103         */
104        public NamespaceMappings()
105        {
106            initNamespaces();
107        }
108    
109        /**
110         * This method initializes the namespace object with appropriate stacks
111         * and predefines a few prefix/uri pairs which always exist.
112         */
113        private void initNamespaces()
114        {
115            // The initial prefix mappings will never be deleted because they are at element depth -1 
116            // (a kludge)
117            
118            // Define the default namespace (initially maps to "" uri)
119            Stack stack;
120            MappingRecord nn;
121            nn = new MappingRecord(EMPTYSTRING, EMPTYSTRING, -1);
122            stack = createPrefixStack(EMPTYSTRING);
123            stack.push(nn);
124    
125            // define "xml" namespace
126            nn = new MappingRecord(XML_PREFIX, "http://www.w3.org/XML/1998/namespace", -1);
127            stack = createPrefixStack(XML_PREFIX);
128            stack.push(nn);
129        }
130    
131        /**
132         * Use a namespace prefix to lookup a namespace URI.
133         * 
134         * @param prefix String the prefix of the namespace
135         * @return the URI corresponding to the prefix, returns ""
136         * if there is no visible mapping.
137         */
138        public String lookupNamespace(String prefix)
139        {
140            String uri = null;
141            final Stack stack = getPrefixStack(prefix);
142            if (stack != null && !stack.isEmpty()) {
143                uri = ((MappingRecord) stack.peek()).m_uri;
144            }
145            if (uri == null)
146                uri = EMPTYSTRING;
147            return uri;
148        }
149      
150        
151        MappingRecord getMappingFromPrefix(String prefix) {
152            final Stack stack = (Stack) m_namespaces.get(prefix);
153            return stack != null && !stack.isEmpty() ? 
154                ((MappingRecord) stack.peek()) : null;
155        }
156    
157        /**
158         * Given a namespace uri, and the namespaces mappings for the 
159         * current element, return the current prefix for that uri.
160         * 
161         * @param uri the namespace URI to be search for
162         * @return an existing prefix that maps to the given URI, null if no prefix
163         * maps to the given namespace URI.
164         */
165        public String lookupPrefix(String uri)
166        {
167            String foundPrefix = null;
168            Enumeration prefixes = m_namespaces.keys();
169            while (prefixes.hasMoreElements())
170            {
171                String prefix = (String) prefixes.nextElement();
172                String uri2 = lookupNamespace(prefix);
173                if (uri2 != null && uri2.equals(uri))
174                {
175                    foundPrefix = prefix;
176                    break;
177                }
178            }
179            return foundPrefix;
180        }
181        
182        MappingRecord getMappingFromURI(String uri)
183        {
184            MappingRecord foundMap = null;
185            Enumeration prefixes = m_namespaces.keys();
186            while (prefixes.hasMoreElements())
187            {
188                String prefix = (String) prefixes.nextElement();
189                MappingRecord map2 = getMappingFromPrefix(prefix);
190                if (map2 != null && (map2.m_uri).equals(uri))
191                {
192                    foundMap = map2;
193                    break;
194                }
195            }
196            return foundMap;
197        }
198    
199        /**
200         * Undeclare the namespace that is currently pointed to by a given prefix
201         */
202        boolean popNamespace(String prefix)
203        {
204            // Prefixes "xml" and "xmlns" cannot be redefined
205            if (prefix.startsWith(XML_PREFIX))
206            {
207                return false;
208            }
209    
210            Stack stack;
211            if ((stack = getPrefixStack(prefix)) != null)
212            {
213                stack.pop();
214                return true;
215            }
216            return false;
217        }
218    
219        /**
220         * Declare a mapping of a prefix to namespace URI at the given element depth.
221         * @param prefix a String with the prefix for a qualified name
222         * @param uri a String with the uri to which the prefix is to map
223         * @param elemDepth the depth of current declaration
224         */
225        public boolean pushNamespace(String prefix, String uri, int elemDepth)
226        {
227            // Prefixes "xml" and "xmlns" cannot be redefined
228            if (prefix.startsWith(XML_PREFIX))
229            {
230                return false;
231            }
232    
233            Stack stack;
234            // Get the stack that contains URIs for the specified prefix
235            if ((stack = (Stack) m_namespaces.get(prefix)) == null)
236            {
237                m_namespaces.put(prefix, stack = new Stack());
238            }
239    
240            if (!stack.empty())
241            {
242                MappingRecord mr = (MappingRecord)stack.peek();
243                if (uri.equals(mr.m_uri) || elemDepth == mr.m_declarationDepth) {
244                    // If the same prefix/uri mapping is already on the stack
245                    // don't push this one.
246                    // Or if we have a mapping at the same depth
247                    // don't replace by pushing this one. 
248                    return false;
249                }
250            }
251            MappingRecord map = new MappingRecord(prefix,uri,elemDepth);
252            stack.push(map);
253            m_nodeStack.push(map);
254            return true;
255        }
256    
257        /**
258         * Pop, or undeclare all namespace definitions that are currently
259         * declared at the given element depth, or deepter.
260         * @param elemDepth the element depth for which mappings declared at this
261         * depth or deeper will no longer be valid
262         * @param saxHandler The ContentHandler to notify of any endPrefixMapping()
263         * calls.  This parameter can be null.
264         */
265        void popNamespaces(int elemDepth, ContentHandler saxHandler)
266        {
267            while (true)
268            {
269                if (m_nodeStack.isEmpty())
270                    return;
271                MappingRecord map = (MappingRecord) (m_nodeStack.peek());
272                int depth = map.m_declarationDepth;
273                if (elemDepth < 1 || map.m_declarationDepth < elemDepth)
274                    break;
275                /* the depth of the declared mapping is elemDepth or deeper
276                 * so get rid of it
277                 */
278    
279                MappingRecord nm1 = (MappingRecord) m_nodeStack.pop();
280                // pop the node from the stack
281                String prefix = map.m_prefix;
282    
283                Stack prefixStack = getPrefixStack(prefix);
284                MappingRecord nm2 = (MappingRecord) prefixStack.peek();
285                if (nm1 == nm2)
286                {
287                    // It would be nice to always pop() but we
288                    // need to check that the prefix stack still has
289                    // the node we want to get rid of. This is because
290                    // the optimization of essentially this situation:
291                    // <a xmlns:x="abc"><b xmlns:x="" xmlns:x="abc" /></a>
292                    // will remove both mappings in <b> because the
293                    // new mapping is the same as the masked one and we get
294                    // <a xmlns:x="abc"><b/></a>
295                    // So we are only removing xmlns:x="" or
296                    // xmlns:x="abc" from the depth of element <b>
297                    // when going back to <a> if in fact they have
298                    // not been optimized away.
299                    // 
300                    prefixStack.pop();
301                    if (saxHandler != null)
302                    {
303                        try
304                        {
305                            saxHandler.endPrefixMapping(prefix);
306                        }
307                        catch (SAXException e)
308                        {
309                            // not much we can do if they aren't willing to listen
310                        }
311                    }
312                }
313    
314            }
315        }
316    
317        /**
318         * Generate a new namespace prefix ( ns0, ns1 ...) not used before
319         * @return String a new namespace prefix ( ns0, ns1, ns2 ...)
320         */
321        public String generateNextPrefix()
322        {
323            return "ns" + (count++);
324        }
325    
326     
327        /**
328         * This method makes a clone of this object.
329         *
330         */
331        public Object clone() throws CloneNotSupportedException {
332            NamespaceMappings clone = new NamespaceMappings();
333            clone.m_nodeStack = (NamespaceMappings.Stack) m_nodeStack.clone();        
334            clone.count = this.count;
335            clone.m_namespaces = (Hashtable) m_namespaces.clone();
336            
337            clone.count = count;
338            return clone;
339            
340        }
341        
342        final void reset()
343        {
344            this.count = 0;
345            this.m_namespaces.clear();
346            this.m_nodeStack.clear();        
347            
348            initNamespaces();
349        }
350        
351        /**
352         * Just a little class that ties the 3 fields together
353         * into one object, and this simplifies the pushing
354         * and popping of namespaces to one push or one pop on
355         * one stack rather than on 3 separate stacks.
356         */
357        static class MappingRecord {
358            final String m_prefix;  // the prefix
359            final String m_uri;     // the uri, possibly "" but never null
360            // the depth of the element where declartion was made
361            final int m_declarationDepth;
362            MappingRecord(String prefix, String uri, int depth) {
363                m_prefix = prefix;
364                m_uri = (uri==null)? EMPTYSTRING : uri;
365                m_declarationDepth = depth;
366            }
367        }    
368        
369        /**
370         * Rather than using java.util.Stack, this private class
371         * provides a minimal subset of methods and is faster
372         * because it is not thread-safe.
373         */
374        private class Stack {
375            private int top = -1;
376            private int max = 20;
377            Object[] m_stack = new Object[max];
378            
379            public Object clone() throws CloneNotSupportedException {
380                NamespaceMappings.Stack clone = new NamespaceMappings.Stack();  
381                clone.max = this.max;
382                clone.top = this.top;
383                clone.m_stack = new Object[clone.max];
384                for (int i=0; i <= top; i++) {
385                    // We are just copying references to immutable MappingRecord objects here
386                    // so it is OK if the clone has references to these.
387                    clone.m_stack[i] = this.m_stack[i];
388                }
389                return clone;            
390            }
391            
392            public Stack()
393            {
394            }
395            
396            public Object push(Object o) {
397                top++;
398                if (max <= top) {
399                    int newMax = 2*max + 1;
400                    Object[] newArray = new Object[newMax];
401                    System.arraycopy(m_stack,0, newArray, 0, max);
402                    max = newMax;
403                    m_stack = newArray;
404                }
405                m_stack[top] = o;
406                return o;
407            }
408            
409            public Object pop() {
410                Object o;
411                if (0 <= top) {
412                    o = m_stack[top];
413                    // m_stack[top] = null;  do we really care?
414                    top--;
415                }
416                else
417                    o = null;
418                return o;
419            }
420            
421            public Object peek() {
422                Object o;
423                if (0 <= top) {
424                    o = m_stack[top];
425                }
426                else
427                    o = null;
428                return o;
429            }
430            
431            public Object peek(int idx) {
432                return m_stack[idx];
433            }
434            
435            public boolean isEmpty() {
436                return (top < 0);
437            }
438            public boolean empty() {
439                return (top < 0);
440            }
441            
442            public void clear() {
443                for (int i=0; i<= top; i++)
444                    m_stack[i] = null;
445                top = -1;
446            }  
447            
448            public Object getElement(int index) {
449                return m_stack[index];      
450            }
451        }
452        /**
453         * A more type-safe way to get a stack of prefix mappings
454         * from the Hashtable m_namespaces
455         * (this is the only method that does the type cast).
456         */
457    
458        private Stack getPrefixStack(String prefix) {
459            Stack fs = (Stack) m_namespaces.get(prefix);
460            return fs;
461        }
462        
463        /**
464         * A more type-safe way of saving stacks under the
465         * m_namespaces Hashtable.
466         */
467        private Stack createPrefixStack(String prefix)
468        {
469            Stack fs = new Stack();
470            m_namespaces.put(prefix, fs);
471            return fs;
472        }
473        
474        /**
475         * Given a namespace uri, get all prefixes bound to the Namespace URI in the current scope. 
476         * 
477         * @param uri the namespace URI to be search for
478         * @return An array of Strings which are
479         * all prefixes bound to the namespace URI in the current scope.
480         * An array of zero elements is returned if no prefixes map to the given
481         * namespace URI.
482         */
483        public String[] lookupAllPrefixes(String uri)
484        {
485            java.util.ArrayList foundPrefixes = new java.util.ArrayList();
486            Enumeration prefixes = m_namespaces.keys();
487            while (prefixes.hasMoreElements())
488            {
489                String prefix = (String) prefixes.nextElement();
490                String uri2 = lookupNamespace(prefix);
491                if (uri2 != null && uri2.equals(uri))
492                {
493                    foundPrefixes.add(prefix);
494                }
495            }
496            String[] prefixArray = new String[foundPrefixes.size()];
497            foundPrefixes.toArray(prefixArray);
498            return prefixArray;
499        }
500    }