001: /*
002: * Fast Infoset ver. 0.1 software ("Software")
003: *
004: * Copyright, 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * Software is licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License. You may
008: * 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, WITHOUT
014: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
015: * License for the specific language governing permissions and limitations.
016: *
017: * Sun supports and benefits from the global community of open source
018: * developers, and thanks the community for its important contributions and
019: * open standards-based technology, which Sun has adopted into many of its
020: * products.
021: *
022: * Please note that portions of Software may be provided with notices and
023: * open source licenses from such communities and third parties that govern the
024: * use of those portions, and any licenses granted hereunder do not alter any
025: * rights and obligations you may have under such open source licenses,
026: * however, the disclaimer of warranty and limitation of liability provisions
027: * in this License will apply to all Software in this distribution.
028: *
029: * You acknowledge that the Software is not designed, licensed or intended
030: * for use in the design, construction, operation or maintenance of any nuclear
031: * facility.
032: *
033: * Apache License
034: * Version 2.0, January 2004
035: * http://www.apache.org/licenses/
036: *
037: */
038:
039: package com.sun.xml.stream.buffer.stax;
040:
041: import java.util.ArrayList;
042: import java.util.Collections;
043: import java.util.Iterator;
044: import java.util.List;
045: import org.jvnet.staxex.NamespaceContextEx;
046:
047: /**
048: * A helper class for managing the declaration of namespaces.
049: * <p>
050: * A namespace is declared on a namespace context.
051: * Namespace contexts are pushed on and popped off the namespace context stack.
052: * <p>
053: * A declared namespace will be in scope iff the context that it was declared on
054: * has not been popped off the stack.
055: * <p>
056: * When instantiated the namespace stack consists of the root namespace context,
057: * which contains, by default, the "xml" and "xmlns" declarations.
058: * Namespaces may be declarations may be declared on the root context.
059: * The root context cannot be popped but can be reset to contain just the
060: * "xml" and "xmlns" declarations.
061: * <p>
062: * Implementation note: determining the prefix from a namespace URI
063: * (or vice versa) is efficient when there are few namespace
064: * declarations i.e. what is considered to be the case for namespace
065: * declarations in 'average' XML documents. The look up of a namespace URI
066: * given a prefix is performed in O(n) time. The look up of a prefix given
067: * a namespace URI is performed in O(2n) time.
068: * <p>
069: * The implementation does not scale when there are many namespace
070: * declarations. TODO: Use a hash map when there are many namespace
071: * declarations.
072: *
073: * @author Paul.Sandoz@Sun.Com
074: */
075: final public class NamespaceContexHelper implements NamespaceContextEx {
076: private static int DEFAULT_SIZE = 8;
077:
078: // The prefixes of the namespace declarations
079: private String[] prefixes = new String[DEFAULT_SIZE];
080: // The URIs of the namespace declarations
081: private String[] namespaceURIs = new String[DEFAULT_SIZE];
082: // Current position to store the next namespace declaration
083: private int namespacePosition;
084:
085: // The namespace contexts
086: private int[] contexts = new int[DEFAULT_SIZE];
087: // Current position to store the next namespace context
088: private int contextPosition;
089:
090: // The current namespace context
091: private int currentContext;
092:
093: /**
094: * Create a new NamespaceContexHelper.
095: *
096: */
097: public NamespaceContexHelper() {
098: // The default namespace declarations that are always in scope
099: prefixes[0] = "xml";
100: namespaceURIs[0] = "http://www.w3.org/XML/1998/namespace";
101: prefixes[1] = "xmlns";
102: namespaceURIs[1] = "http://www.w3.org/2000/xmlns/";
103:
104: currentContext = namespacePosition = 2;
105: }
106:
107: // NamespaceContext interface
108:
109: public String getNamespaceURI(String prefix) {
110: if (prefix == null)
111: throw new IllegalArgumentException();
112:
113: prefix = prefix.intern();
114:
115: for (int i = namespacePosition - 1; i >= 0; i--) {
116: final String declaredPrefix = prefixes[i];
117: if (declaredPrefix == prefix) {
118: return namespaceURIs[i];
119: }
120: }
121:
122: return "";
123: }
124:
125: public String getPrefix(String namespaceURI) {
126: if (namespaceURI == null)
127: throw new IllegalArgumentException();
128:
129: for (int i = namespacePosition - 1; i >= 0; i--) {
130: final String declaredNamespaceURI = namespaceURIs[i];
131: if (declaredNamespaceURI == namespaceURI
132: || declaredNamespaceURI.equals(namespaceURI)) {
133: final String declaredPrefix = prefixes[i];
134:
135: // Check if prefix is out of scope
136: for (++i; i < namespacePosition; i++)
137: if (declaredPrefix == prefixes[i])
138: return null;
139:
140: return declaredPrefix;
141: }
142: }
143:
144: return null;
145: }
146:
147: public Iterator getPrefixes(String namespaceURI) {
148: if (namespaceURI == null)
149: throw new IllegalArgumentException();
150:
151: List<String> l = new ArrayList<String>();
152:
153: NAMESPACE_LOOP: for (int i = namespacePosition - 1; i >= 0; i--) {
154: final String declaredNamespaceURI = namespaceURIs[i];
155: if (declaredNamespaceURI == namespaceURI
156: || declaredNamespaceURI.equals(namespaceURI)) {
157: final String declaredPrefix = prefixes[i];
158:
159: // Check if prefix is out of scope
160: for (int j = i + 1; j < namespacePosition; j++)
161: if (declaredPrefix == prefixes[j])
162: continue NAMESPACE_LOOP;
163:
164: l.add(declaredPrefix);
165: }
166: }
167:
168: return l.iterator();
169: }
170:
171: // NamespaceContextEx interface
172:
173: public Iterator<NamespaceContextEx.Binding> iterator() {
174: if (namespacePosition == 2)
175: return Collections.EMPTY_LIST.iterator();
176:
177: final List<NamespaceContextEx.Binding> namespaces = new ArrayList<NamespaceContextEx.Binding>(
178: namespacePosition);
179:
180: NAMESPACE_LOOP: for (int i = namespacePosition - 1; i >= 2; i--) {
181: final String declaredPrefix = prefixes[i];
182:
183: // Check if prefix is out of scope
184: for (int j = i + 1; j < namespacePosition; j++) {
185: if (declaredPrefix == prefixes[j])
186: continue NAMESPACE_LOOP;
187:
188: namespaces.add(new NamespaceBindingImpl(i));
189: }
190: }
191:
192: return namespaces.iterator();
193: }
194:
195: final private class NamespaceBindingImpl implements
196: NamespaceContextEx.Binding {
197: int index;
198:
199: NamespaceBindingImpl(int index) {
200: this .index = index;
201: }
202:
203: public String getPrefix() {
204: return prefixes[index];
205: }
206:
207: public String getNamespaceURI() {
208: return namespaceURIs[index];
209: }
210: }
211:
212: /**
213: * Declare a default namespace.
214: * <p>
215: * @param namespaceURI the namespace URI to declare, may be null.
216: */
217: public void declareDefaultNamespace(String namespaceURI) {
218: declareNamespace("", namespaceURI);
219: }
220:
221: /**
222: * Declare a namespace.
223: * <p>
224: * The namespace will be declared on the current namespace context.
225: * <p>
226: * The namespace can be removed by popping the current namespace
227: * context, or, if the declaration occured in the root context, by
228: * reseting the namespace context.
229: * <p>
230: * A default namespace can be declared by passing <code>""</code> as
231: * the value of the prefix parameter.
232: * A namespace may be undeclared by passing <code>null</code> as the
233: * value of the namespaceURI parameter.
234: * <p>
235: * @param prefix the namespace prefix to declare, may not be null.
236: * @param namespaceURI the namespace URI to declare, may be null.
237: * @throws IllegalArgumentException, if the prefix is null.
238: */
239: public void declareNamespace(String prefix, String namespaceURI) {
240: if (prefix == null)
241: throw new IllegalArgumentException();
242:
243: prefix = prefix.intern();
244: // Ignore the "xml" or "xmlns" declarations
245: if (prefix == "xml" || prefix == "xmlns")
246: return;
247:
248: // Check for undeclaration
249: if (namespaceURI != null)
250: namespaceURI = namespaceURI.intern();
251:
252: if (namespacePosition == namespaceURIs.length)
253: resizeNamespaces();
254:
255: // Add new declaration
256: prefixes[namespacePosition] = prefix;
257: namespaceURIs[namespacePosition++] = namespaceURI;
258: }
259:
260: private void resizeNamespaces() {
261: final int newLength = namespaceURIs.length * 3 / 2 + 1;
262:
263: String[] newPrefixes = new String[newLength];
264: System.arraycopy(prefixes, 0, newPrefixes, 0, prefixes.length);
265: prefixes = newPrefixes;
266:
267: String[] newNamespaceURIs = new String[newLength];
268: System.arraycopy(namespaceURIs, 0, newNamespaceURIs, 0,
269: namespaceURIs.length);
270: namespaceURIs = newNamespaceURIs;
271: }
272:
273: /**
274: * Push a namespace context on the stack.
275: */
276: public void pushContext() {
277: if (contextPosition == contexts.length)
278: resizeContexts();
279:
280: contexts[contextPosition++] = currentContext = namespacePosition;
281: }
282:
283: private void resizeContexts() {
284: int[] newContexts = new int[contexts.length * 3 / 2 + 1];
285: System.arraycopy(contexts, 0, newContexts, 0, contexts.length);
286: contexts = newContexts;
287: }
288:
289: /**
290: * Pop the namespace context off the stack.
291: * <p>
292: * Namespaces declared within the context (to be popped)
293: * will be removed and no longer be in scope.
294: */
295: public void popContext() {
296: if (contextPosition > 0) {
297: namespacePosition = currentContext = contexts[--contextPosition];
298: }
299: }
300:
301: /**
302: * Reset namespace contexts.
303: * <p>
304: * Pop all namespace contexts and reset the root context.
305: */
306: public void resetContexts() {
307: currentContext = namespacePosition = 2;
308: }
309: }
|