1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xerces.internal.util; 23 24 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 25 import java.util.ArrayList; 26 import java.util.Enumeration; 27 import java.util.Iterator; 28 import java.util.List; 29 import java.util.NoSuchElementException; 30 31 /** 32 * Namespace support for XML document handlers. This class doesn't 33 * perform any error checking and assumes that all strings passed 34 * as arguments to methods are unique symbols. The SymbolTable class 35 * can be used for this purpose. 36 * 37 * @author Andy Clark, IBM 38 * 39 */ 40 public class NamespaceSupport implements NamespaceContext { 41 42 // 43 // Data 44 // 45 46 /** 47 * Namespace binding information. This array is composed of a 48 * series of tuples containing the namespace binding information: 49 * <prefix, uri>. The default size can be set to anything 50 * as long as it is a power of 2 greater than 1. 51 * 52 * @see #fNamespaceSize 53 * @see #fContext 54 */ 55 protected String[] fNamespace = new String[16 * 2]; 56 57 /** The top of the namespace information array. */ 58 protected int fNamespaceSize; 59 60 // NOTE: The constructor depends on the initial context size 61 // being at least 1. -Ac 62 63 /** 64 * Context indexes. This array contains indexes into the namespace 65 * information array. The index at the current context is the start 66 * index of declared namespace bindings and runs to the size of the 67 * namespace information array. 68 * 69 * @see #fNamespaceSize 70 */ 71 protected int[] fContext = new int[8]; 72 73 /** The current context. */ 74 protected int fCurrentContext; 75 76 protected String[] fPrefixes = new String[16]; 77 78 // 79 // Constructors 80 // 81 82 /** Default constructor. */ 83 public NamespaceSupport() { 84 } // <init>() 85 86 /** 87 * Constructs a namespace context object and initializes it with 88 * the prefixes declared in the specified context. 89 */ 90 public NamespaceSupport(NamespaceContext context) { 91 pushContext(); 92 // copy declaration in the context 93 Enumeration<String> prefixes = context.getAllPrefixes(); 94 while (prefixes.hasMoreElements()){ 95 String prefix = prefixes.nextElement(); 96 String uri = context.getURI(prefix); 97 declarePrefix(prefix, uri); 98 } 99 } // <init>(NamespaceContext) 100 101 102 // 103 // Public methods 104 // 105 106 /** 107 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#reset() 108 */ 109 public void reset() { 110 111 // reset namespace and context info 112 fNamespaceSize = 0; 113 fCurrentContext = 0; 114 115 116 // bind "xml" prefix to the XML uri 117 fNamespace[fNamespaceSize++] = XMLSymbols.PREFIX_XML; 118 fNamespace[fNamespaceSize++] = NamespaceContext.XML_URI; 119 // bind "xmlns" prefix to the XMLNS uri 120 fNamespace[fNamespaceSize++] = XMLSymbols.PREFIX_XMLNS; 121 fNamespace[fNamespaceSize++] = NamespaceContext.XMLNS_URI; 122 123 fContext[fCurrentContext] = fNamespaceSize; 124 //++fCurrentContext; 125 126 } // reset(SymbolTable) 127 128 129 /** 130 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#pushContext() 131 */ 132 public void pushContext() { 133 134 // extend the array, if necessary 135 if (fCurrentContext + 1 == fContext.length) { 136 int[] contextarray = new int[fContext.length * 2]; 137 System.arraycopy(fContext, 0, contextarray, 0, fContext.length); 138 fContext = contextarray; 139 } 140 141 // push context 142 fContext[++fCurrentContext] = fNamespaceSize; 143 //System.out.println("calling push context, current context = " + fCurrentContext); 144 } // pushContext() 145 146 147 /** 148 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#popContext() 149 */ 150 public void popContext() { 151 fNamespaceSize = fContext[fCurrentContext--]; 152 //System.out.println("Calling popContext, fCurrentContext = " + fCurrentContext); 153 } // popContext() 154 155 /** 156 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#declarePrefix(String, String) 157 */ 158 public boolean declarePrefix(String prefix, String uri) { 159 // ignore "xml" and "xmlns" prefixes 160 if (prefix == XMLSymbols.PREFIX_XML || prefix == XMLSymbols.PREFIX_XMLNS) { 161 return false; 162 } 163 164 // see if prefix already exists in current context 165 for (int i = fNamespaceSize; i > fContext[fCurrentContext]; i -= 2) { 166 if (fNamespace[i - 2] == prefix) { 167 // REVISIT: [Q] Should the new binding override the 168 // previously declared binding or should it 169 // it be ignored? -Ac 170 // NOTE: The SAX2 "NamespaceSupport" helper allows 171 // re-bindings with the new binding overwriting 172 // the previous binding. -Ac 173 fNamespace[i - 1] = uri; 174 return true; 175 } 176 } 177 178 // resize array, if needed 179 if (fNamespaceSize == fNamespace.length) { 180 String[] namespacearray = new String[fNamespaceSize * 2]; 181 System.arraycopy(fNamespace, 0, namespacearray, 0, fNamespaceSize); 182 fNamespace = namespacearray; 183 } 184 185 // bind prefix to uri in current context 186 fNamespace[fNamespaceSize++] = prefix; 187 fNamespace[fNamespaceSize++] = uri; 188 189 return true; 190 191 } // declarePrefix(String,String):boolean 192 193 /** 194 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#getURI(String) 195 */ 196 public String getURI(String prefix) { 197 198 // find prefix in current context 199 for (int i = fNamespaceSize; i > 0; i -= 2) { 200 if (fNamespace[i - 2] == prefix) { 201 return fNamespace[i - 1]; 202 } 203 } 204 205 // prefix not found 206 return null; 207 208 } // getURI(String):String 209 210 211 /** 212 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#getPrefix(String) 213 */ 214 public String getPrefix(String uri) { 215 216 // find uri in current context 217 for (int i = fNamespaceSize; i > 0; i -= 2) { 218 if (fNamespace[i - 1] == uri) { 219 if (getURI(fNamespace[i - 2]) == uri) 220 return fNamespace[i - 2]; 221 } 222 } 223 224 // uri not found 225 return null; 226 227 } // getPrefix(String):String 228 229 /** 230 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#getDeclaredPrefixCount() 231 */ 232 public int getDeclaredPrefixCount() { 233 return (fNamespaceSize - fContext[fCurrentContext]) / 2; 234 } // getDeclaredPrefixCount():int 235 236 /** 237 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#getDeclaredPrefixAt(int) 238 */ 239 public String getDeclaredPrefixAt(int index) { 240 return fNamespace[fContext[fCurrentContext] + index * 2]; 241 } // getDeclaredPrefixAt(int):String 242 243 public Iterator<String> getPrefixes(){ 244 int count = 0; 245 if (fPrefixes.length < (fNamespace.length/2)) { 246 // resize prefix array 247 String[] prefixes = new String[fNamespaceSize]; 248 fPrefixes = prefixes; 249 } 250 String prefix = null; 251 boolean unique = true; 252 for (int i = 2; i < (fNamespaceSize-2); i += 2) { 253 prefix = fNamespace[i + 2]; 254 for (int k=0;k<count;k++){ 255 if (fPrefixes[k]==prefix){ 256 unique = false; 257 break; 258 } 259 } 260 if (unique){ 261 fPrefixes[count++] = prefix; 262 } 263 unique = true; 264 } 265 return new IteratorPrefixes(fPrefixes, count); 266 }//getPrefixes 267 /** 268 * @see com.sun.org.apache.xerces.internal.xni.NamespaceContext#getAllPrefixes() 269 */ 270 public Enumeration<String> getAllPrefixes() { 271 int count = 0; 272 if (fPrefixes.length < (fNamespace.length/2)) { 273 // resize prefix array 274 String[] prefixes = new String[fNamespaceSize]; 275 fPrefixes = prefixes; 276 } 277 String prefix = null; 278 boolean unique = true; 279 for (int i = 2; i < (fNamespaceSize-2); i += 2) { 280 prefix = fNamespace[i + 2]; 281 for (int k=0;k<count;k++){ 282 if (fPrefixes[k]==prefix){ 283 unique = false; 284 break; 285 } 286 } 287 if (unique){ 288 fPrefixes[count++] = prefix; 289 } 290 unique = true; 291 } 292 return new Prefixes(fPrefixes, count); 293 } 294 295 public List<String> getPrefixes(String uri){ 296 int count = 0; 297 String prefix = null; 298 boolean unique = true; 299 List<String> prefixList = new ArrayList<>(); 300 for (int i = fNamespaceSize; i >0 ; i -= 2) { 301 if(fNamespace[i-1] == uri){ 302 if(!prefixList.contains(fNamespace[i-2])) 303 prefixList.add(fNamespace[i-2]); 304 } 305 } 306 return prefixList; 307 } 308 309 /* 310 * non-NamespaceContext methods 311 */ 312 313 /** 314 * Checks whether a binding or unbinding for 315 * the given prefix exists in the context. 316 * 317 * @param prefix The prefix to look up. 318 * 319 * @return true if the given prefix exists in the context 320 */ 321 public boolean containsPrefix(String prefix) { 322 323 // find prefix in context 324 for (int i = fNamespaceSize; i > 0; i -= 2) { 325 if (fNamespace[i - 2] == prefix) { 326 return true; 327 } 328 } 329 330 // prefix not found 331 return false; 332 } 333 334 /** 335 * Checks whether a binding or unbinding for 336 * the given prefix exists in the current context. 337 * 338 * @param prefix The prefix to look up. 339 * 340 * @return true if the given prefix exists in the current context 341 */ 342 public boolean containsPrefixInCurrentContext(String prefix) { 343 344 // find prefix in current context 345 for (int i = fContext[fCurrentContext]; i < fNamespaceSize; i += 2) { 346 if (fNamespace[i] == prefix) { 347 return true; 348 } 349 } 350 351 // prefix not found 352 return false; 353 } 354 355 protected final class IteratorPrefixes implements Iterator<String> { 356 private String[] prefixes; 357 private int counter = 0; 358 private int size = 0; 359 360 /** 361 * Constructor for Prefixes. 362 */ 363 public IteratorPrefixes(String [] prefixes, int size) { 364 this.prefixes = prefixes; 365 this.size = size; 366 } 367 368 /** 369 * @see java.util.Enumeration#hasMoreElements() 370 */ 371 public boolean hasNext() { 372 return (counter < size); 373 } 374 375 /** 376 * @see java.util.Enumeration#nextElement() 377 */ 378 public String next() { 379 if (counter< size){ 380 return fPrefixes[counter++]; 381 } 382 throw new NoSuchElementException("Illegal access to Namespace prefixes enumeration."); 383 } 384 385 public String toString(){ 386 StringBuilder buf = new StringBuilder(); 387 for (int i=0;i<size;i++){ 388 buf.append(prefixes[i]); 389 buf.append(" "); 390 } 391 392 return buf.toString(); 393 } 394 395 public void remove(){ 396 throw new UnsupportedOperationException(); 397 } 398 } 399 400 401 protected final class Prefixes implements Enumeration<String> { 402 private String[] prefixes; 403 private int counter = 0; 404 private int size = 0; 405 406 /** 407 * Constructor for Prefixes. 408 */ 409 public Prefixes(String [] prefixes, int size) { 410 this.prefixes = prefixes; 411 this.size = size; 412 } 413 414 /** 415 * @see java.util.Enumeration#hasMoreElements() 416 */ 417 public boolean hasMoreElements() { 418 return (counter< size); 419 } 420 421 /** 422 * @see java.util.Enumeration#nextElement() 423 */ 424 public String nextElement() { 425 if (counter< size){ 426 return fPrefixes[counter++]; 427 } 428 throw new NoSuchElementException("Illegal access to Namespace prefixes enumeration."); 429 } 430 431 public String toString(){ 432 StringBuilder buf = new StringBuilder(); 433 for (int i=0;i<size;i++){ 434 buf.append(prefixes[i]); 435 buf.append(" "); 436 } 437 438 return buf.toString(); 439 } 440 441 442 } 443 444 } // class NamespaceSupport