1 /* 2 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.stream.buffer.stax; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.Iterator; 31 import java.util.List; 32 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx; 33 34 /** 35 * A helper class for managing the declaration of namespaces. 36 * <p> 37 * A namespace is declared on a namespace context. 38 * Namespace contexts are pushed on and popped off the namespace context stack. 39 * <p> 40 * A declared namespace will be in scope iff the context that it was declared on 41 * has not been popped off the stack. 42 * <p> 43 * When instantiated the namespace stack consists of the root namespace context, 44 * which contains, by default, the "xml" and "xmlns" declarations. 45 * Namespaces may be declarations may be declared on the root context. 46 * The root context cannot be popped but can be reset to contain just the 47 * "xml" and "xmlns" declarations. 48 * <p> 49 * Implementation note: determining the prefix from a namespace URI 50 * (or vice versa) is efficient when there are few namespace 51 * declarations i.e. what is considered to be the case for namespace 52 * declarations in 'average' XML documents. The look up of a namespace URI 53 * given a prefix is performed in O(n) time. The look up of a prefix given 54 * a namespace URI is performed in O(2n) time. 55 * <p> 56 * The implementation does not scale when there are many namespace 57 * declarations. TODO: Use a hash map when there are many namespace 58 * declarations. 59 * 60 * @author Paul.Sandoz@Sun.Com 61 */ 62 final public class NamespaceContexHelper implements NamespaceContextEx { 63 private static int DEFAULT_SIZE = 8; 64 65 // The prefixes of the namespace declarations 66 private String[] prefixes = new String[DEFAULT_SIZE]; 67 // The URIs of the namespace declarations 68 private String[] namespaceURIs = new String[DEFAULT_SIZE]; 69 // Current position to store the next namespace declaration 70 private int namespacePosition; 71 72 // The namespace contexts 73 private int[] contexts = new int[DEFAULT_SIZE]; 74 // Current position to store the next namespace context 75 private int contextPosition; 76 77 // The current namespace context 78 private int currentContext; 79 80 /** 81 * Create a new NamespaceContexHelper. 82 * 83 */ 84 public NamespaceContexHelper() { 85 // The default namespace declarations that are always in scope 86 prefixes[0] = "xml"; 87 namespaceURIs[0] = "http://www.w3.org/XML/1998/namespace"; 88 prefixes[1] = "xmlns"; 89 namespaceURIs[1] = "http://www.w3.org/2000/xmlns/"; 90 91 currentContext = namespacePosition = 2; 92 } 93 94 95 // NamespaceContext interface 96 97 public String getNamespaceURI(String prefix) { 98 if (prefix == null) throw new IllegalArgumentException(); 99 100 prefix = prefix.intern(); 101 102 for (int i = namespacePosition - 1; i >= 0; i--) { 103 final String declaredPrefix = prefixes[i]; 104 if (declaredPrefix == prefix) { 105 return namespaceURIs[i]; 106 } 107 } 108 109 return ""; 110 } 111 112 public String getPrefix(String namespaceURI) { 113 if (namespaceURI == null) throw new IllegalArgumentException(); 114 115 for (int i = namespacePosition - 1; i >= 0; i--) { 116 final String declaredNamespaceURI = namespaceURIs[i]; 117 if (declaredNamespaceURI == namespaceURI || declaredNamespaceURI.equals(namespaceURI)) { 118 final String declaredPrefix = prefixes[i]; 119 120 // Check if prefix is out of scope 121 for (++i; i < namespacePosition; i++) 122 if (declaredPrefix == prefixes[i]) 123 return null; 124 125 return declaredPrefix; 126 } 127 } 128 129 return null; 130 } 131 132 public Iterator getPrefixes(String namespaceURI) { 133 if (namespaceURI == null) throw new IllegalArgumentException(); 134 135 List<String> l = new ArrayList<String>(); 136 137 NAMESPACE_LOOP: for (int i = namespacePosition - 1; i >= 0; i--) { 138 final String declaredNamespaceURI = namespaceURIs[i]; 139 if (declaredNamespaceURI == namespaceURI || declaredNamespaceURI.equals(namespaceURI)) { 140 final String declaredPrefix = prefixes[i]; 141 142 // Check if prefix is out of scope 143 for (int j = i + 1; j < namespacePosition; j++) 144 if (declaredPrefix == prefixes[j]) 145 continue NAMESPACE_LOOP; 146 147 l.add(declaredPrefix); 148 } 149 } 150 151 return l.iterator(); 152 } 153 154 // NamespaceContextEx interface 155 156 public Iterator<NamespaceContextEx.Binding> iterator() { 157 if (namespacePosition == 2) 158 return Collections.EMPTY_LIST.iterator(); 159 160 final List<NamespaceContextEx.Binding> namespaces = 161 new ArrayList<NamespaceContextEx.Binding>(namespacePosition); 162 163 NAMESPACE_LOOP: for (int i = namespacePosition - 1; i >= 2; i--) { 164 final String declaredPrefix = prefixes[i]; 165 166 // Check if prefix is out of scope 167 for (int j = i + 1; j < namespacePosition; j++) { 168 if (declaredPrefix == prefixes[j]) 169 continue NAMESPACE_LOOP; 170 171 namespaces.add(new NamespaceBindingImpl(i)); 172 } 173 } 174 175 return namespaces.iterator(); 176 } 177 178 final private class NamespaceBindingImpl implements NamespaceContextEx.Binding { 179 int index; 180 181 NamespaceBindingImpl(int index) { 182 this.index = index; 183 } 184 185 public String getPrefix() { 186 return prefixes[index]; 187 } 188 189 public String getNamespaceURI() { 190 return namespaceURIs[index]; 191 } 192 } 193 194 /** 195 * Declare a default namespace. 196 * <p> 197 * @param namespaceURI the namespace URI to declare, may be null. 198 */ 199 public void declareDefaultNamespace(String namespaceURI) { 200 declareNamespace("", namespaceURI); 201 } 202 203 /** 204 * Declare a namespace. 205 * <p> 206 * The namespace will be declared on the current namespace context. 207 * <p> 208 * The namespace can be removed by popping the current namespace 209 * context, or, if the declaration occured in the root context, by 210 * reseting the namespace context. 211 * <p> 212 * A default namespace can be declared by passing <code>""</code> as 213 * the value of the prefix parameter. 214 * A namespace may be undeclared by passing <code>null</code> as the 215 * value of the namespaceURI parameter. 216 * <p> 217 * @param prefix the namespace prefix to declare, may not be null. 218 * @param namespaceURI the namespace URI to declare, may be null. 219 * @throws IllegalArgumentException, if the prefix is null. 220 */ 221 public void declareNamespace(String prefix, String namespaceURI) { 222 if (prefix == null) throw new IllegalArgumentException(); 223 224 prefix = prefix.intern(); 225 // Ignore the "xml" or "xmlns" declarations 226 if (prefix == "xml" || prefix == "xmlns") 227 return; 228 229 // Check for undeclaration 230 if (namespaceURI != null) 231 namespaceURI = namespaceURI.intern(); 232 233 if (namespacePosition == namespaceURIs.length) 234 resizeNamespaces(); 235 236 // Add new declaration 237 prefixes[namespacePosition] = prefix; 238 namespaceURIs[namespacePosition++] = namespaceURI; 239 } 240 241 private void resizeNamespaces() { 242 final int newLength = namespaceURIs.length * 3 / 2 + 1; 243 244 String[] newPrefixes = new String[newLength]; 245 System.arraycopy(prefixes, 0, newPrefixes, 0, prefixes.length); 246 prefixes = newPrefixes; 247 248 String[] newNamespaceURIs = new String[newLength]; 249 System.arraycopy(namespaceURIs, 0, newNamespaceURIs, 0, namespaceURIs.length); 250 namespaceURIs = newNamespaceURIs; 251 } 252 253 /** 254 * Push a namespace context on the stack. 255 */ 256 public void pushContext() { 257 if (contextPosition == contexts.length) 258 resizeContexts(); 259 260 contexts[contextPosition++] = currentContext = namespacePosition; 261 } 262 263 private void resizeContexts() { 264 int[] newContexts = new int[contexts.length * 3 / 2 + 1]; 265 System.arraycopy(contexts, 0, newContexts, 0, contexts.length); 266 contexts = newContexts; 267 } 268 269 /** 270 * Pop the namespace context off the stack. 271 * <p> 272 * Namespaces declared within the context (to be popped) 273 * will be removed and no longer be in scope. 274 */ 275 public void popContext() { 276 if (contextPosition > 0) { 277 namespacePosition = currentContext = contexts[--contextPosition]; 278 } 279 } 280 281 /** 282 * Reset namespace contexts. 283 * <p> 284 * Pop all namespace contexts and reset the root context. 285 */ 286 public void resetContexts() { 287 currentContext = namespacePosition = 2; 288 } 289 }