001: /*
002: * Copyright 2003-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: NamespaceMappings.java,v 1.9 2005/04/07 04:29:03 minchau Exp $
018: */
019: package org.apache.xml.serializer;
020:
021: import java.util.Enumeration;
022: import java.util.Hashtable;
023: import java.util.Stack;
024:
025: import org.xml.sax.ContentHandler;
026: import org.xml.sax.SAXException;
027:
028: /**
029: * This class keeps track of the currently defined namespaces. Conceptually the
030: * prefix/uri/depth triplets are pushed on a stack pushed on a stack. The depth
031: * indicates the nesting depth of the element for which the mapping was made.
032: *
033: * <p>For example:
034: * <pre>
035: * <chapter xmlns:p1="def">
036: * <paragraph xmlns:p2="ghi">
037: * <sentance xmlns:p3="jkl">
038: * </sentance>
039: * </paragraph>
040: * <paragraph xlmns:p4="mno">
041: * </paragraph>
042: * </chapter>
043: * </pre>
044: *
045: * When the <chapter> element is encounted the prefix "p1" associated with uri
046: * "def" is pushed on the stack with depth 1.
047: * When the first <paragraph> is encountered "p2" and "ghi" are pushed with
048: * depth 2.
049: * When the <sentance> is encountered "p3" and "jkl" are pushed with depth 3.
050: * When </sentance> occurs the popNamespaces(3) will pop "p3"/"jkl" off the
051: * stack. Of course popNamespaces(2) would pop anything with depth 2 or
052: * greater.
053: *
054: * So prefix/uri pairs are pushed and poped off the stack as elements are
055: * processed. At any given moment of processing the currently visible prefixes
056: * are on the stack and a prefix can be found given a uri, or a uri can be found
057: * given a prefix.
058: *
059: * This class is public only because it is used by Xalan. It is not a public API
060: *
061: * @xsl.usage internal
062: */
063: public class NamespaceMappings {
064: /**
065: * This member is continually incremented when new prefixes need to be
066: * generated. ("ns0" "ns1" ...)
067: */
068: private int count = 0;
069:
070: /**
071: * Each entry (prefix) in this hashtable points to a Stack of URIs
072: * This table maps a prefix (String) to a Stack of prefix mappings.
073: * All mappings in that retrieved stack have the same prefix,
074: * though possibly different URI's or depths. Such a stack must have
075: * mappings at deeper depths push later on such a stack. Mappings pushed
076: * earlier on the stack will have smaller values for MappingRecord.m_declarationDepth.
077: */
078: private Hashtable m_namespaces = new Hashtable();
079:
080: /**
081: * The top of this stack contains the MapRecord
082: * of the last declared a namespace.
083: * Used to know how many prefix mappings to pop when leaving
084: * the current element depth.
085: * For every prefix mapping the current element depth is
086: * pushed on this stack.
087: * That way all prefixes pushed at the current depth can be
088: * removed at the same time.
089: * Used to ensure prefix/uri map scopes are closed correctly
090: *
091: */
092: private Stack m_nodeStack = new Stack();
093:
094: private static final String EMPTYSTRING = "";
095: private static final String XML_PREFIX = "xml"; // was "xmlns"
096:
097: /**
098: * Default constructor
099: * @see java.lang.Object#Object()
100: */
101: public NamespaceMappings() {
102: initNamespaces();
103: }
104:
105: /**
106: * This method initializes the namespace object with appropriate stacks
107: * and predefines a few prefix/uri pairs which always exist.
108: */
109: private void initNamespaces() {
110:
111: // Define the default namespace (initially maps to "" uri)
112: Stack stack;
113: m_namespaces.put(EMPTYSTRING, stack = new Stack());
114: stack.push(new MappingRecord(EMPTYSTRING, EMPTYSTRING, 0));
115:
116: m_namespaces.put(XML_PREFIX, stack = new Stack());
117: stack.push(new MappingRecord(XML_PREFIX,
118: "http://www.w3.org/XML/1998/namespace", 0));
119:
120: m_nodeStack.push(new MappingRecord(null, null, -1));
121:
122: }
123:
124: /**
125: * Use a namespace prefix to lookup a namespace URI.
126: *
127: * @param prefix String the prefix of the namespace
128: * @return the URI corresponding to the prefix
129: */
130: public String lookupNamespace(String prefix) {
131: final Stack stack = (Stack) m_namespaces.get(prefix);
132: return stack != null && !stack.isEmpty() ? ((MappingRecord) stack
133: .peek()).m_uri
134: : null;
135: }
136:
137: MappingRecord getMappingFromPrefix(String prefix) {
138: final Stack stack = (Stack) m_namespaces.get(prefix);
139: return stack != null && !stack.isEmpty() ? ((MappingRecord) stack
140: .peek())
141: : null;
142: }
143:
144: /**
145: * Given a namespace uri, and the namespaces mappings for the
146: * current element, return the current prefix for that uri.
147: *
148: * @param uri the namespace URI to be search for
149: * @return an existing prefix that maps to the given URI, null if no prefix
150: * maps to the given namespace URI.
151: */
152: public String lookupPrefix(String uri) {
153: String foundPrefix = null;
154: Enumeration prefixes = m_namespaces.keys();
155: while (prefixes.hasMoreElements()) {
156: String prefix = (String) prefixes.nextElement();
157: String uri2 = lookupNamespace(prefix);
158: if (uri2 != null && uri2.equals(uri)) {
159: foundPrefix = prefix;
160: break;
161: }
162: }
163: return foundPrefix;
164: }
165:
166: MappingRecord getMappingFromURI(String uri) {
167: MappingRecord foundMap = null;
168: Enumeration prefixes = m_namespaces.keys();
169: while (prefixes.hasMoreElements()) {
170: String prefix = (String) prefixes.nextElement();
171: MappingRecord map2 = getMappingFromPrefix(prefix);
172: if (map2 != null && (map2.m_uri).equals(uri)) {
173: foundMap = map2;
174: break;
175: }
176: }
177: return foundMap;
178: }
179:
180: /**
181: * Undeclare the namespace that is currently pointed to by a given prefix
182: */
183: boolean popNamespace(String prefix) {
184: // Prefixes "xml" and "xmlns" cannot be redefined
185: if (prefix.startsWith(XML_PREFIX)) {
186: return false;
187: }
188:
189: Stack stack;
190: if ((stack = (Stack) m_namespaces.get(prefix)) != null) {
191: stack.pop();
192: return true;
193: }
194: return false;
195: }
196:
197: /**
198: * Declare a mapping of a prefix to namespace URI at the given element depth.
199: * @param prefix a String with the prefix for a qualified name
200: * @param uri a String with the uri to which the prefix is to map
201: * @param elemDepth the depth of current declaration
202: */
203: boolean pushNamespace(String prefix, String uri, int elemDepth) {
204: // Prefixes "xml" and "xmlns" cannot be redefined
205: if (prefix.startsWith(XML_PREFIX)) {
206: return false;
207: }
208:
209: Stack stack;
210: // Get the stack that contains URIs for the specified prefix
211: if ((stack = (Stack) m_namespaces.get(prefix)) == null) {
212: m_namespaces.put(prefix, stack = new Stack());
213: }
214:
215: if (!stack.empty()
216: && uri.equals(((MappingRecord) stack.peek()).m_uri)) {
217: return false;
218: }
219: MappingRecord map = new MappingRecord(prefix, uri, elemDepth);
220: stack.push(map);
221: m_nodeStack.push(map);
222: return true;
223: }
224:
225: /**
226: * Pop, or undeclare all namespace definitions that are currently
227: * declared at the given element depth, or deepter.
228: * @param elemDepth the element depth for which mappings declared at this
229: * depth or deeper will no longer be valid
230: * @param saxHandler The ContentHandler to notify of any endPrefixMapping()
231: * calls. This parameter can be null.
232: */
233: void popNamespaces(int elemDepth, ContentHandler saxHandler) {
234: while (true) {
235: if (m_nodeStack.isEmpty())
236: return;
237: MappingRecord map = (MappingRecord) (m_nodeStack.peek());
238: int depth = map.m_declarationDepth;
239: if (depth < elemDepth)
240: return;
241: /* the depth of the declared mapping is elemDepth or deeper
242: * so get rid of it
243: */
244:
245: map = (MappingRecord) m_nodeStack.pop();
246: final String prefix = map.m_prefix;
247: popNamespace(prefix);
248: if (saxHandler != null) {
249: try {
250: saxHandler.endPrefixMapping(prefix);
251: } catch (SAXException e) {
252: // not much we can do if they aren't willing to listen
253: }
254: }
255:
256: }
257: }
258:
259: /**
260: * Generate a new namespace prefix ( ns0, ns1 ...) not used before
261: * @return String a new namespace prefix ( ns0, ns1, ns2 ...)
262: */
263: public String generateNextPrefix() {
264: return "ns" + (count++);
265: }
266:
267: /**
268: * This method makes a clone of this object.
269: *
270: */
271: public Object clone() throws CloneNotSupportedException {
272: NamespaceMappings clone = new NamespaceMappings();
273: clone.m_nodeStack = (Stack) m_nodeStack.clone();
274: clone.m_namespaces = (Hashtable) m_namespaces.clone();
275:
276: clone.count = count;
277: return clone;
278:
279: }
280:
281: final void reset() {
282: this .count = 0;
283: this .m_namespaces.clear();
284: this .m_nodeStack.clear();
285: initNamespaces();
286: }
287:
288: class MappingRecord {
289: final String m_prefix; // the prefix
290: final String m_uri; // the uri
291: // the depth of the element where declartion was made
292: final int m_declarationDepth;
293:
294: MappingRecord(String prefix, String uri, int depth) {
295: m_prefix = prefix;
296: m_uri = uri;
297: m_declarationDepth = depth;
298:
299: }
300: }
301:
302: }
|