View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2003-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: NamespaceMappings.java,v 1.2.4.1 2005/09/15 08:15:19 suresh_emailid Exp $
22   */
23  package com.sun.org.apache.xml.internal.serializer;
24  
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.Stack;
28  
29  import org.xml.sax.ContentHandler;
30  import org.xml.sax.SAXException;
31  
32  /**
33   * This class keeps track of the currently defined namespaces. Conceptually the
34   * prefix/uri/depth triplets are pushed on a stack pushed on a stack. The depth
35   * indicates the nesting depth of the element for which the mapping was made.
36   *
37   * <p>For example:
38   * <pre>
39   * <chapter xmlns:p1="def">
40   *   <paragraph xmlns:p2="ghi">
41   *      <sentance xmlns:p3="jkl">
42   *      </sentance>
43   *    </paragraph>
44   *    <paragraph xlmns:p4="mno">
45   *    </paragraph>
46   * </chapter>
47   * </pre>
48   *
49   * When the <chapter> element is encounted the prefix "p1" associated with uri
50   * "def" is pushed on the stack with depth 1.
51   * When the first <paragraph> is encountered "p2" and "ghi" are pushed with
52   * depth 2.
53   * When the <sentance> is encountered "p3" and "jkl" are pushed with depth 3.
54   * When </sentance> occurs the popNamespaces(3) will pop "p3"/"jkl" off the
55   * stack.  Of course popNamespaces(2) would pop anything with depth 2 or
56   * greater.
57   *
58   * So prefix/uri pairs are pushed and poped off the stack as elements are
59   * processed.  At any given moment of processing the currently visible prefixes
60   * are on the stack and a prefix can be found given a uri, or a uri can be found
61   * given a prefix.
62   *
63   * This class is public only because it is used by Xalan. It is not a public API
64   *
65   * @xsl.usage internal
66   */
67  public class NamespaceMappings
68  {
69      /**
70       * This member is continually incremented when new prefixes need to be
71       * generated. ("ns0"  "ns1" ...)
72       */
73      private int count;
74  
75      /**
76       * Each entry (prefix) in this hashmap points to a Stack of URIs
77       * This maps a prefix (String) to a Stack of prefix mappings.
78       * All mappings in that retrieved stack have the same prefix,
79       * though possibly different URI's or depths. Such a stack must have
80       * mappings at deeper depths push later on such a stack.  Mappings pushed
81       * earlier on the stack will have smaller values for MappingRecord.m_declarationDepth.
82       */
83      private HashMap m_namespaces = new HashMap();
84  
85      /**
86       * The top of this stack contains the MapRecord
87       * of the last declared a namespace.
88       * Used to know how many prefix mappings to pop when leaving
89       * the current element depth.
90       * For every prefix mapping the current element depth is
91       * pushed on this stack.
92       * That way all prefixes pushed at the current depth can be
93       * removed at the same time.
94       * Used to ensure prefix/uri map scopes are closed correctly
95       *
96       */
97      private Stack m_nodeStack = new Stack();
98  
99      private static final String EMPTYSTRING = "";
100     private static final String XML_PREFIX = "xml"; // was "xmlns"
101 
102     /**
103      * Default constructor
104      * @see java.lang.Object#Object()
105      */
106     public NamespaceMappings()
107     {
108         initNamespaces();
109     }
110 
111     /**
112      * This method initializes the namespace object with appropriate stacks
113      * and predefines a few prefix/uri pairs which always exist.
114      */
115     private void initNamespaces()
116     {
117 
118 
119         // Define the default namespace (initially maps to "" uri)
120         Stack stack;
121         m_namespaces.put(EMPTYSTRING, stack = new Stack());
122         stack.push(new MappingRecord(EMPTYSTRING,EMPTYSTRING,0));
123 
124         m_namespaces.put(XML_PREFIX, stack = new Stack());
125         stack.push(new MappingRecord( XML_PREFIX,
126             "http://www.w3.org/XML/1998/namespace",0));
127 
128         m_nodeStack.push(new MappingRecord(null,null,-1));
129 
130     }
131 
132     /**
133      * Use a namespace prefix to lookup a namespace URI.
134      *
135      * @param prefix String the prefix of the namespace
136      * @return the URI corresponding to the prefix
137      */
138     public String lookupNamespace(String prefix)
139     {
140         final Stack stack = (Stack) m_namespaces.get(prefix);
141         return stack != null && !stack.isEmpty() ?
142             ((MappingRecord) stack.peek()).m_uri : null;
143     }
144 
145     MappingRecord getMappingFromPrefix(String prefix) {
146         final Stack stack = (Stack) m_namespaces.get(prefix);
147         return stack != null && !stack.isEmpty() ?
148             ((MappingRecord) stack.peek()) : null;
149     }
150 
151     /**
152      * Given a namespace uri, and the namespaces mappings for the
153      * current element, return the current prefix for that uri.
154      *
155      * @param uri the namespace URI to be search for
156      * @return an existing prefix that maps to the given URI, null if no prefix
157      * maps to the given namespace URI.
158      */
159     public String lookupPrefix(String uri)
160     {
161         String foundPrefix = null;
162         Iterator<String> itr = m_namespaces.keySet().iterator();
163         while (itr.hasNext()) {
164             String prefix = itr.next();
165             String uri2 = lookupNamespace(prefix);
166             if (uri2 != null && uri2.equals(uri))
167             {
168                 foundPrefix = prefix;
169                 break;
170             }
171         }
172         return foundPrefix;
173     }
174 
175     MappingRecord getMappingFromURI(String uri)
176     {
177         MappingRecord foundMap = null;
178         Iterator<String> itr = m_namespaces.keySet().iterator();
179         while (itr.hasNext())
180         {
181             String prefix = itr.next();
182             MappingRecord map2 = getMappingFromPrefix(prefix);
183             if (map2 != null && (map2.m_uri).equals(uri))
184             {
185                 foundMap = map2;
186                 break;
187             }
188         }
189         return foundMap;
190     }
191 
192     /**
193      * Undeclare the namespace that is currently pointed to by a given prefix
194      */
195     boolean popNamespace(String prefix)
196     {
197         // Prefixes "xml" and "xmlns" cannot be redefined
198         if (prefix.startsWith(XML_PREFIX))
199         {
200             return false;
201         }
202 
203         Stack stack;
204         if ((stack = (Stack) m_namespaces.get(prefix)) != null)
205         {
206             stack.pop();
207             return true;
208         }
209         return false;
210     }
211 
212     /**
213      * Declare a mapping of a prefix to namespace URI at the given element depth.
214      * @param prefix a String with the prefix for a qualified name
215      * @param uri a String with the uri to which the prefix is to map
216      * @param elemDepth the depth of current declaration
217      */
218     boolean pushNamespace(String prefix, String uri, int elemDepth)
219     {
220         // Prefixes "xml" and "xmlns" cannot be redefined
221         if (prefix.startsWith(XML_PREFIX))
222         {
223             return false;
224         }
225 
226         Stack stack;
227         // Get the stack that contains URIs for the specified prefix
228         if ((stack = (Stack) m_namespaces.get(prefix)) == null)
229         {
230             m_namespaces.put(prefix, stack = new Stack());
231         }
232 
233         if (!stack.empty() && uri.equals(((MappingRecord)stack.peek()).m_uri))
234         {
235             return false;
236         }
237         MappingRecord map = new MappingRecord(prefix,uri,elemDepth);
238         stack.push(map);
239         m_nodeStack.push(map);
240         return true;
241     }
242 
243     /**
244      * Pop, or undeclare all namespace definitions that are currently
245      * declared at the given element depth, or deepter.
246      * @param elemDepth the element depth for which mappings declared at this
247      * depth or deeper will no longer be valid
248      * @param saxHandler The ContentHandler to notify of any endPrefixMapping()
249      * calls.  This parameter can be null.
250      */
251     void popNamespaces(int elemDepth, ContentHandler saxHandler)
252     {
253         while (true)
254         {
255             if (m_nodeStack.isEmpty())
256                 return;
257             MappingRecord map = (MappingRecord)(m_nodeStack.peek());
258             int depth = map.m_declarationDepth;
259             if (depth < elemDepth)
260                 return;
261             /* the depth of the declared mapping is elemDepth or deeper
262              * so get rid of it
263              */
264 
265             map = (MappingRecord) m_nodeStack.pop();
266             final String prefix = map.m_prefix;
267             popNamespace(prefix);
268             if (saxHandler != null)
269             {
270                 try
271                 {
272                     saxHandler.endPrefixMapping(prefix);
273                 }
274                 catch (SAXException e)
275                 {
276                     // not much we can do if they aren't willing to listen
277                 }
278             }
279 
280         }
281     }
282 
283     /**
284      * Generate a new namespace prefix ( ns0, ns1 ...) not used before
285      * @return String a new namespace prefix ( ns0, ns1, ns2 ...)
286      */
287     public String generateNextPrefix()
288     {
289         return "ns" + (count++);
290     }
291 
292 
293     /**
294      * This method makes a clone of this object.
295      *
296      */
297     public Object clone() throws CloneNotSupportedException {
298         NamespaceMappings clone = new NamespaceMappings();
299         clone.m_nodeStack = (Stack) m_nodeStack.clone();
300         clone.m_namespaces = (HashMap) m_namespaces.clone();
301         clone.count = count;
302         return clone;
303 
304     }
305 
306     final void reset()
307     {
308         this.count = 0;
309         this.m_namespaces.clear();
310         this.m_nodeStack.clear();
311         initNamespaces();
312     }
313 
314     class MappingRecord {
315         final String m_prefix;  // the prefix
316         final String m_uri;     // the uri
317         // the depth of the element where declartion was made
318         final int m_declarationDepth;
319         MappingRecord(String prefix, String uri, int depth) {
320             m_prefix = prefix;
321             m_uri = uri;
322             m_declarationDepth = depth;
323 
324         }
325     }
326 
327 }