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 }