View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath.ri;
19  
20  import java.io.Serializable;
21  import java.util.HashMap;
22  
23  import org.apache.commons.jxpath.Pointer;
24  import org.apache.commons.jxpath.ri.model.NodeIterator;
25  import org.apache.commons.jxpath.ri.model.NodePointer;
26  
27  /**
28   * Namespace resolver for {@link JXPathContextReferenceImpl}.
29   */
30  public class NamespaceResolver implements Cloneable, Serializable {
31  
32      private static final long serialVersionUID = 1085590057838651311L;
33  
34      /**
35       * Find the namespace prefix for the specified namespace URI and NodePointer.
36       *
37       * @param pointer      location
38       * @param namespaceURI to check
39       * @return prefix if found
40       * @since JXPath 1.3
41       */
42      protected static String getPrefix(final NodePointer pointer, final String namespaceURI) {
43          NodePointer currentPointer = pointer;
44          while (currentPointer != null) {
45              final NodeIterator ni = currentPointer.namespaceIterator();
46              for (int position = 1; ni != null && ni.setPosition(position); position++) {
47                  final NodePointer nsPointer = ni.getNodePointer();
48                  final String uri = nsPointer.getNamespaceURI();
49                  if (uri.equals(namespaceURI)) {
50                      final String prefix = nsPointer.getName().getName();
51                      if (!"".equals(prefix)) {
52                          return prefix;
53                      }
54                  }
55              }
56              currentPointer = currentPointer.getParent();
57          }
58          return null;
59      }
60  
61      /** Parent NamespaceResolver. */
62      protected final NamespaceResolver parent;
63  
64      /** Namespace map. */
65      protected HashMap<String, String> namespaceMap = new HashMap<>();
66  
67      /** Reverse lookup map */
68      protected HashMap<String, String> reverseMap = new HashMap<>();
69  
70      /** Node pointer. */
71      protected NodePointer pointer;
72  
73      /**
74       * Whether this instance is sealed.
75       */
76      private boolean sealed;
77  
78      /**
79       * Constructs a new NamespaceResolver.
80       */
81      public NamespaceResolver() {
82          this(null);
83      }
84  
85      /**
86       * Constructs a new NamespaceResolver.
87       *
88       * @param parent NamespaceResolver
89       */
90      public NamespaceResolver(final NamespaceResolver parent) {
91          this.parent = parent;
92      }
93  
94      @Override
95      public Object clone() {
96          try {
97              final NamespaceResolver result = (NamespaceResolver) super.clone();
98              result.sealed = false;
99              return result;
100         } catch (final CloneNotSupportedException e) {
101             // Of course, it's supported.
102             e.printStackTrace();
103             return null;
104         }
105     }
106 
107     /**
108      * Given a prefix, returns an externally registered namespace URI.
109      *
110      * @param prefix The namespace prefix to look up
111      * @return namespace URI or null if the prefix is undefined.
112      * @since JXPath 1.3
113      */
114     protected synchronized String getExternallyRegisteredNamespaceURI(final String prefix) {
115         final String uri = namespaceMap.get(prefix);
116         return uri == null && parent != null ? parent.getExternallyRegisteredNamespaceURI(prefix) : uri;
117     }
118 
119     /**
120      * Gets the nearest prefix found that matches an externally-registered namespace.
121      *
122      * @param namespaceURI the ns URI to check.
123      * @return String prefix if found.
124      * @since JXPath 1.3
125      */
126     protected synchronized String getExternallyRegisteredPrefix(final String namespaceURI) {
127         final String prefix = reverseMap.get(namespaceURI);
128         return prefix == null && parent != null ? parent.getExternallyRegisteredPrefix(namespaceURI) : prefix;
129     }
130 
131     /**
132      * Gets the namespace context pointer.
133      *
134      * @return Pointer
135      */
136     public Pointer getNamespaceContextPointer() {
137         if (pointer == null && parent != null) {
138             return parent.getNamespaceContextPointer();
139         }
140         return pointer;
141     }
142 
143     /**
144      * Given a prefix, returns a registered namespace URI. If the requested prefix was not defined explicitly using the registerNamespace method, JXPathContext
145      * will then check the context node to see if the prefix is defined there. See {@link #setNamespaceContextPointer(NodePointer) setNamespaceContextPointer}.
146      *
147      * @param prefix The namespace prefix to look up
148      * @return namespace URI or null if the prefix is undefined.
149      */
150     public synchronized String getNamespaceURI(final String prefix) {
151         final String uri = getExternallyRegisteredNamespaceURI(prefix);
152         return uri == null && pointer != null ? pointer.getNamespaceURI(prefix) : uri;
153     }
154 
155     /**
156      * Gets the prefix associated with the specifed namespace URI.
157      *
158      * @param namespaceURI the ns URI to check.
159      * @return String prefix
160      */
161     public synchronized String getPrefix(final String namespaceURI) {
162         final String prefix = getExternallyRegisteredPrefix(namespaceURI);
163         return prefix == null && pointer != null ? getPrefix(pointer, namespaceURI) : prefix;
164     }
165 
166     /**
167      * Tests whether this NamespaceResolver has been sealed.
168      *
169      * @return boolean
170      */
171     public boolean isSealed() {
172         return sealed;
173     }
174 
175     /**
176      * Registers a namespace prefix.
177      *
178      * @param prefix       A namespace prefix
179      * @param namespaceURI A URI for that prefix
180      */
181     public synchronized void registerNamespace(final String prefix, final String namespaceURI) {
182         if (isSealed()) {
183             throw new IllegalStateException("Cannot register namespaces on a sealed NamespaceResolver");
184         }
185         namespaceMap.put(prefix, namespaceURI);
186         reverseMap.put(namespaceURI, prefix);
187     }
188 
189     /**
190      * Seal this {@link NamespaceResolver}.
191      */
192     public void seal() {
193         sealed = true;
194         if (parent != null) {
195             parent.seal();
196         }
197     }
198 
199     /**
200      * Register a namespace for the expression context.
201      *
202      * @param pointer the Pointer to set.
203      */
204     public void setNamespaceContextPointer(final NodePointer pointer) {
205         this.pointer = pointer;
206     }
207 }