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