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 }