1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xerces.internal.util;
22 
23 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.NoSuchElementException;
29 
30 /**
31  * Namespace support for XML document handlers. This class doesn't
32  * perform any error checking and assumes that all strings passed
33  * as arguments to methods are unique symbols. The SymbolTable class
34  * can be used for this purpose.
35  *
36  * @author Andy Clark, IBM
37  *
38  * @LastModified: Oct 2017
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. */
NamespaceSupport()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      */
NamespaceSupport(NamespaceContext context)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      */
reset()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      */
pushContext()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      */
popContext()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      */
declarePrefix(String prefix, String uri)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      */
getURI(String prefix)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      */
getPrefix(String uri)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      */
getDeclaredPrefixCount()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      */
getDeclaredPrefixAt(int index)239     public String getDeclaredPrefixAt(int index) {
240         return fNamespace[fContext[fCurrentContext] + index * 2];
241     } // getDeclaredPrefixAt(int):String
242 
getPrefixes()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      */
getAllPrefixes()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 
getPrefixes(String uri)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      */
containsPrefix(String prefix)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      */
containsPrefixInCurrentContext(String prefix)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          */
IteratorPrefixes(String [] prefixes, int size)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          */
hasNext()371         public boolean hasNext() {
372             return (counter < size);
373         }
374 
375         /**
376          * @see java.util.Enumeration#nextElement()
377          */
next()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 
toString()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 
remove()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          */
Prefixes(String [] prefixes, int size)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          */
hasMoreElements()417         public boolean hasMoreElements() {
418             return (counter< size);
419         }
420 
421         /**
422          * @see java.util.Enumeration#nextElement()
423          */
nextElement()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 
toString()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
445