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