001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: XPathEvaluatorImpl.java 1225443 2011-12-29 05:44:18Z mrglavas $
020 */
021
022 package org.apache.xpath.domapi;
023
024 import javax.xml.transform.TransformerException;
025
026 import org.apache.xml.utils.PrefixResolver;
027 import org.apache.xpath.XPath;
028 import org.apache.xpath.res.XPATHErrorResources;
029 import org.apache.xpath.res.XPATHMessages;
030 import org.w3c.dom.DOMException;
031 import org.w3c.dom.Document;
032 import org.w3c.dom.Node;
033 import org.w3c.dom.xpath.XPathEvaluator;
034 import org.w3c.dom.xpath.XPathException;
035 import org.w3c.dom.xpath.XPathExpression;
036 import org.w3c.dom.xpath.XPathNSResolver;
037
038 /**
039 *
040 * The class provides an implementation of XPathEvaluator according
041 * to the DOM L3 XPath Specification, Working Group Note 26 February 2004.
042 *
043 * <p>See also the <a href='http://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226'>Document Object Model (DOM) Level 3 XPath Specification</a>.</p>
044 *
045 * </p>The evaluation of XPath expressions is provided by
046 * <code>XPathEvaluator</code>, which will provide evaluation of XPath 1.0
047 * expressions with no specialized extension functions or variables. It is
048 * expected that the <code>XPathEvaluator</code> interface will be
049 * implemented on the same object which implements the <code>Document</code>
050 * interface in an implementation which supports the XPath DOM module.
051 * <code>XPathEvaluator</code> implementations may be available from other
052 * sources that may provide support for special extension functions or
053 * variables which are not defined in this specification.</p>
054 *
055 * @see org.w3c.dom.xpath.XPathEvaluator
056 *
057 * @xsl.usage internal
058 */
059 public final class XPathEvaluatorImpl implements XPathEvaluator {
060
061 /**
062 * This prefix resolver is created whenever null is passed to the
063 * evaluate method. Its purpose is to satisfy the DOM L3 XPath API
064 * requirement that if a null prefix resolver is used, an exception
065 * should only be thrown when an attempt is made to resolve a prefix.
066 */
067 private static class DummyPrefixResolver implements PrefixResolver {
068
069 /**
070 * Constructor for DummyPrefixResolver.
071 */
072 DummyPrefixResolver() {}
073
074 /**
075 * @exception DOMException
076 * NAMESPACE_ERR: Always throws this exceptionn
077 *
078 * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String, Node)
079 */
080 public String getNamespaceForPrefix(String prefix, Node context) {
081 String fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_RESOLVER, null);
082 throw new DOMException(DOMException.NAMESPACE_ERR, fmsg); // Unable to resolve prefix with null prefix resolver.
083 }
084
085 /**
086 * @exception DOMException
087 * NAMESPACE_ERR: Always throws this exceptionn
088 *
089 * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String)
090 */
091 public String getNamespaceForPrefix(String prefix) {
092 return getNamespaceForPrefix(prefix,null);
093 }
094
095 /**
096 * @see org.apache.xml.utils.PrefixResolver#handlesNullPrefixes()
097 */
098 public boolean handlesNullPrefixes() {
099 return false;
100 }
101
102 /**
103 * @see org.apache.xml.utils.PrefixResolver#getBaseIdentifier()
104 */
105 public String getBaseIdentifier() {
106 return null;
107 }
108
109 }
110
111 /**
112 * The document to be searched to parallel the case where the XPathEvaluator
113 * is obtained by casting a Document.
114 */
115 private final Document m_doc;
116
117 /**
118 * Constructor for XPathEvaluatorImpl.
119 *
120 * @param doc The document to be searched, to parallel the case where''
121 * the XPathEvaluator is obtained by casting the document.
122 */
123 public XPathEvaluatorImpl(Document doc) {
124 m_doc = doc;
125 }
126
127 /**
128 * Constructor in the case that the XPath expression can be evaluated
129 * without needing an XML document at all.
130 *
131 */
132 public XPathEvaluatorImpl() {
133 m_doc = null;
134 }
135
136 /**
137 * Creates a parsed XPath expression with resolved namespaces. This is
138 * useful when an expression will be reused in an application since it
139 * makes it possible to compile the expression string into a more
140 * efficient internal form and preresolve all namespace prefixes which
141 * occur within the expression.
142 *
143 * @param expression The XPath expression string to be parsed.
144 * @param resolver The <code>resolver</code> permits translation of
145 * prefixes within the XPath expression into appropriate namespace URIs
146 * . If this is specified as <code>null</code>, any namespace prefix
147 * within the expression will result in <code>DOMException</code>
148 * being thrown with the code <code>NAMESPACE_ERR</code>.
149 * @return The compiled form of the XPath expression.
150 * @exception XPathException
151 * INVALID_EXPRESSION_ERR: Raised if the expression is not legal
152 * according to the rules of the <code>XPathEvaluator</code>i
153 * @exception DOMException
154 * NAMESPACE_ERR: Raised if the expression contains namespace prefixes
155 * which cannot be resolved by the specified
156 * <code>XPathNSResolver</code>.
157 *
158 * @see org.w3c.dom.xpath.XPathEvaluator#createExpression(String, XPathNSResolver)
159 */
160 public XPathExpression createExpression(
161 String expression,
162 XPathNSResolver resolver)
163 throws XPathException, DOMException {
164
165 try {
166
167 // If the resolver is null, create a dummy prefix resolver
168 XPath xpath = new XPath(expression,null,
169 ((null == resolver) ? new DummyPrefixResolver() : ((PrefixResolver)resolver)),
170 XPath.SELECT);
171
172 return new XPathExpressionImpl(xpath, m_doc);
173
174 } catch (TransformerException e) {
175 // Need to pass back exception code DOMException.NAMESPACE_ERR also.
176 // Error found in DOM Level 3 XPath Test Suite.
177 if(e instanceof XPathStylesheetDOM3Exception)
178 throw new DOMException(DOMException.NAMESPACE_ERR,e.getMessageAndLocation());
179 else
180 throw new XPathException(XPathException.INVALID_EXPRESSION_ERR,e.getMessageAndLocation());
181
182 }
183 }
184
185 /**
186 * Adapts any DOM node to resolve namespaces so that an XPath expression
187 * can be easily evaluated relative to the context of the node where it
188 * appeared within the document. This adapter works like the DOM Level 3
189 * method <code>lookupNamespaceURI</code> on nodes in resolving the
190 * namespaceURI from a given prefix using the current information available
191 * in the node's hierarchy at the time lookupNamespaceURI is called, also
192 * correctly resolving the implicit xml prefix.
193 *
194 * @param nodeResolver The node to be used as a context for namespace
195 * resolution.
196 * @return <code>XPathNSResolver</code> which resolves namespaces with
197 * respect to the definitions in scope for a specified node.
198 *
199 * @see org.w3c.dom.xpath.XPathEvaluator#createNSResolver(Node)
200 */
201 public XPathNSResolver createNSResolver(Node nodeResolver) {
202
203 return new XPathNSResolverImpl((nodeResolver.getNodeType() == Node.DOCUMENT_NODE)
204 ? ((Document) nodeResolver).getDocumentElement() : nodeResolver);
205 }
206
207 /**
208 * Evaluates an XPath expression string and returns a result of the
209 * specified type if possible.
210 *
211 * @param expression The XPath expression string to be parsed and
212 * evaluated.
213 * @param contextNode The <code>context</code> is context node for the
214 * evaluation of this XPath expression. If the XPathEvaluator was
215 * obtained by casting the <code>Document</code> then this must be
216 * owned by the same document and must be a <code>Document</code>,
217 * <code>Element</code>, <code>Attribute</code>, <code>Text</code>,
218 * <code>CDATASection</code>, <code>Comment</code>,
219 * <code>ProcessingInstruction</code>, or <code>XPathNamespace</code>
220 * node. If the context node is a <code>Text</code> or a
221 * <code>CDATASection</code>, then the context is interpreted as the
222 * whole logical text node as seen by XPath, unless the node is empty
223 * in which case it may not serve as the XPath context.
224 * @param resolver The <code>resolver</code> permits translation of
225 * prefixes within the XPath expression into appropriate namespace URIs
226 * . If this is specified as <code>null</code>, any namespace prefix
227 * within the expression will result in <code>DOMException</code>
228 * being thrown with the code <code>NAMESPACE_ERR</code>.
229 * @param type If a specific <code>type</code> is specified, then the
230 * result will be coerced to return the specified type relying on
231 * XPath type conversions and fail if the desired coercion is not
232 * possible. This must be one of the type codes of
233 * <code>XPathResult</code>.
234 * @param result The <code>result</code> specifies a specific result
235 * object which may be reused and returned by this method. If this is
236 * specified as <code>null</code>or the implementation does not reuse
237 * the specified result, a new result object will be constructed and
238 * returned.For XPath 1.0 results, this object will be of type
239 * <code>XPathResult</code>.
240 * @return The result of the evaluation of the XPath expression.For XPath
241 * 1.0 results, this object will be of type <code>XPathResult</code>.
242 * @exception XPathException
243 * INVALID_EXPRESSION_ERR: Raised if the expression is not legal
244 * according to the rules of the <code>XPathEvaluator</code>i
245 * <br>TYPE_ERR: Raised if the result cannot be converted to return the
246 * specified type.
247 * @exception DOMException
248 * NAMESPACE_ERR: Raised if the expression contains namespace prefixes
249 * which cannot be resolved by the specified
250 * <code>XPathNSResolver</code>.
251 * <br>WRONG_DOCUMENT_ERR: The Node is from a document that is not
252 * supported by this XPathEvaluator.
253 * <br>NOT_SUPPORTED_ERR: The Node is not a type permitted as an XPath
254 * context node.
255 *
256 * @see org.w3c.dom.xpath.XPathEvaluator#evaluate(String, Node, XPathNSResolver, short, XPathResult)
257 */
258 public Object evaluate(
259 String expression,
260 Node contextNode,
261 XPathNSResolver resolver,
262 short type,
263 Object result)
264 throws XPathException, DOMException {
265
266 XPathExpression xpathExpression = createExpression(expression, resolver);
267
268 return xpathExpression.evaluate(contextNode, type, result);
269 }
270
271 }