1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 // $Id: XPathExpressionImpl.java 524813 2007-04-02 15:52:07Z zongaro $ 19 20 package org.apache.xpath.jaxp; 21 22 import org.apache.xpath.*; 23 import javax.xml.transform.TransformerException; 24 25 import org.apache.xpath.objects.XObject; 26 import org.apache.xml.utils.PrefixResolver; 27 import org.apache.xpath.res.XPATHErrorResources; 28 import org.apache.xalan.res.XSLMessages; 29 30 import javax.xml.namespace.NamespaceContext; 31 import javax.xml.namespace.QName; 32 import javax.xml.xpath.XPathExpressionException; 33 import javax.xml.xpath.XPathConstants; 34 import javax.xml.xpath.XPathFunctionResolver; 35 import javax.xml.xpath.XPathVariableResolver; 36 import javax.xml.xpath.XPathConstants; 37 38 import org.w3c.dom.Node; 39 import org.w3c.dom.Document; 40 import org.w3c.dom.DOMImplementation; 41 import org.w3c.dom.traversal.NodeIterator; 42 import javax.xml.parsers.DocumentBuilderFactory; 43 import javax.xml.parsers.DocumentBuilder; 44 45 import org.xml.sax.InputSource; 46 47 /** 48 * The XPathExpression interface encapsulates a (compiled) XPath expression. 49 * 50 * @version $Revision: 524813 $ 51 * @author Ramesh Mandava 52 */ 53 public class XPathExpressionImpl implements javax.xml.xpath.XPathExpression{ 54 55 private XPathFunctionResolver functionResolver; 56 private XPathVariableResolver variableResolver; 57 private JAXPPrefixResolver prefixResolver; 58 private org.apache.xpath.XPath xpath; 59 60 // By default Extension Functions are allowed in XPath Expressions. If 61 // Secure Processing Feature is set on XPathFactory then the invocation of 62 // extensions function need to throw XPathFunctionException 63 private boolean featureSecureProcessing = false; 64 65 /** Protected constructor to prevent direct instantiation; use compile() 66 * from the context. 67 */ XPathExpressionImpl()68 protected XPathExpressionImpl() { }; 69 XPathExpressionImpl(org.apache.xpath.XPath xpath, JAXPPrefixResolver prefixResolver, XPathFunctionResolver functionResolver, XPathVariableResolver variableResolver )70 protected XPathExpressionImpl(org.apache.xpath.XPath xpath, 71 JAXPPrefixResolver prefixResolver, 72 XPathFunctionResolver functionResolver, 73 XPathVariableResolver variableResolver ) { 74 this.xpath = xpath; 75 this.prefixResolver = prefixResolver; 76 this.functionResolver = functionResolver; 77 this.variableResolver = variableResolver; 78 this.featureSecureProcessing = false; 79 }; 80 XPathExpressionImpl(org.apache.xpath.XPath xpath, JAXPPrefixResolver prefixResolver, XPathFunctionResolver functionResolver, XPathVariableResolver variableResolver, boolean featureSecureProcessing )81 protected XPathExpressionImpl(org.apache.xpath.XPath xpath, 82 JAXPPrefixResolver prefixResolver, 83 XPathFunctionResolver functionResolver, 84 XPathVariableResolver variableResolver, 85 boolean featureSecureProcessing ) { 86 this.xpath = xpath; 87 this.prefixResolver = prefixResolver; 88 this.functionResolver = functionResolver; 89 this.variableResolver = variableResolver; 90 this.featureSecureProcessing = featureSecureProcessing; 91 }; 92 setXPath(org.apache.xpath.XPath xpath )93 public void setXPath (org.apache.xpath.XPath xpath ) { 94 this.xpath = xpath; 95 } 96 eval(Object item, QName returnType)97 public Object eval(Object item, QName returnType) 98 throws javax.xml.transform.TransformerException { 99 XObject resultObject = eval ( item ); 100 return getResultAsType( resultObject, returnType ); 101 } 102 eval( Object contextItem )103 private XObject eval ( Object contextItem ) 104 throws javax.xml.transform.TransformerException { 105 org.apache.xpath.XPathContext xpathSupport = null; 106 107 // Create an XPathContext that doesn't support pushing and popping of 108 // variable resolution scopes. Sufficient for simple XPath 1.0 109 // expressions. 110 if ( functionResolver != null ) { 111 JAXPExtensionsProvider jep = new JAXPExtensionsProvider( 112 functionResolver, featureSecureProcessing ); 113 xpathSupport = new org.apache.xpath.XPathContext(jep, false); 114 } else { 115 xpathSupport = new org.apache.xpath.XPathContext(false); 116 } 117 118 xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); 119 XObject xobj = null; 120 121 Node contextNode = (Node)contextItem; 122 // We always need to have a ContextNode with Xalan XPath implementation 123 // To allow simple expression evaluation like 1+1 we are setting 124 // dummy Document as Context Node 125 if ( contextNode == null ) { 126 contextNode = getDummyDocument(); 127 } 128 129 xobj = xpath.execute(xpathSupport, contextNode, prefixResolver ); 130 return xobj; 131 } 132 133 134 /** 135 * <p>Evaluate the compiled XPath expression in the specified context and 136 * return the result as the specified type.</p> 137 * 138 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 139 * for context item evaluation, 140 * variable, function and QName resolution and return type conversion.</p> 141 * 142 * <p>If <code>returnType</code> is not one of the types defined 143 * in {@link XPathConstants}, 144 * then an <code>IllegalArgumentException</code> is thrown.</p> 145 * 146 * <p>If a <code>null</code> value is provided for 147 * <code>item</code>, an empty document will be used for the 148 * context. 149 * If <code>returnType</code> is <code>null</code>, then a 150 * <code>NullPointerException</code> is thrown.</p> 151 * 152 * @param item The starting context (node or node list, for example). 153 * @param returnType The desired return type. 154 * 155 * @return The <code>Object</code> that is the result of evaluating the 156 * expression and converting the result to 157 * <code>returnType</code>. 158 * 159 * @throws XPathExpressionException If the expression cannot be evaluated. 160 * @throws IllegalArgumentException If <code>returnType</code> is not one 161 * of the types defined in {@link XPathConstants}. 162 * @throws NullPointerException If <code>returnType</code> is 163 * <code>null</code>. 164 */ evaluate(Object item, QName returnType)165 public Object evaluate(Object item, QName returnType) 166 throws XPathExpressionException { 167 //Validating parameters to enforce constraints defined by JAXP spec 168 if ( returnType == null ) { 169 //Throwing NullPointerException as defined in spec 170 String fmsg = XSLMessages.createXPATHMessage( 171 XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, 172 new Object[] {"returnType"} ); 173 throw new NullPointerException( fmsg ); 174 } 175 // Checking if requested returnType is supported. returnType need to be 176 // defined in XPathConstants 177 if ( !isSupported ( returnType ) ) { 178 String fmsg = XSLMessages.createXPATHMessage( 179 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 180 new Object[] { returnType.toString() } ); 181 throw new IllegalArgumentException ( fmsg ); 182 } 183 try { 184 return eval( item, returnType); 185 } catch ( java.lang.NullPointerException npe ) { 186 // If VariableResolver returns null Or if we get 187 // NullPointerException at this stage for some other reason 188 // then we have to reurn XPathException 189 throw new XPathExpressionException ( npe ); 190 } catch ( javax.xml.transform.TransformerException te ) { 191 Throwable nestedException = te.getException(); 192 if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { 193 throw (javax.xml.xpath.XPathFunctionException)nestedException; 194 } else { 195 // For any other exceptions we need to throw 196 // XPathExpressionException ( as per spec ) 197 throw new XPathExpressionException( te); 198 } 199 } 200 201 } 202 203 /** 204 * <p>Evaluate the compiled XPath expression in the specified context and 205 * return the result as a <code>String</code>.</p> 206 * 207 * <p>This method calls {@link #evaluate(Object item, QName returnType)} 208 * with a <code>returnType</code> of 209 * {@link XPathConstants#STRING}.</p> 210 * 211 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 212 * for context item evaluation, 213 * variable, function and QName resolution and return type conversion.</p> 214 * 215 * <p>If a <code>null</code> value is provided for 216 * <code>item</code>, an empty document will be used for the 217 * context. 218 * 219 * @param item The starting context (node or node list, for example). 220 * 221 * @return The <code>String</code> that is the result of evaluating the 222 * expression and converting the result to a 223 * <code>String</code>. 224 * 225 * @throws XPathExpressionException If the expression cannot be evaluated. 226 */ evaluate(Object item)227 public String evaluate(Object item) 228 throws XPathExpressionException { 229 return (String)this.evaluate( item, XPathConstants.STRING ); 230 } 231 232 233 234 static DocumentBuilderFactory dbf = null; 235 static DocumentBuilder db = null; 236 static Document d = null; 237 238 /** 239 * <p>Evaluate the compiled XPath expression in the context of the 240 * specified <code>InputSource</code> and return the result as the 241 * specified type.</p> 242 * 243 * <p>This method builds a data model for the {@link InputSource} and calls 244 * {@link #evaluate(Object item, QName returnType)} on the resulting 245 * document object.</p> 246 * 247 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 248 * for context item evaluation, 249 * variable, function and QName resolution and return type conversion.</p> 250 * 251 * <p>If <code>returnType</code> is not one of the types defined in 252 * {@link XPathConstants}, 253 * then an <code>IllegalArgumentException</code> is thrown.</p> 254 * 255 *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>, 256 * then a <code>NullPointerException</code> is thrown.</p> 257 * 258 * @param source The <code>InputSource</code> of the document to evaluate 259 * over. 260 * @param returnType The desired return type. 261 * 262 * @return The <code>Object</code> that is the result of evaluating the 263 * expression and converting the result to 264 * <code>returnType</code>. 265 * 266 * @throws XPathExpressionException If the expression cannot be evaluated. 267 * @throws IllegalArgumentException If <code>returnType</code> is not one 268 * of the types defined in {@link XPathConstants}. 269 * @throws NullPointerException If <code>source</code> or 270 * <code>returnType</code> is <code>null</code>. 271 */ evaluate(InputSource source, QName returnType)272 public Object evaluate(InputSource source, QName returnType) 273 throws XPathExpressionException { 274 if ( ( source == null ) || ( returnType == null ) ) { 275 String fmsg = XSLMessages.createXPATHMessage( 276 XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL, 277 null ); 278 throw new NullPointerException ( fmsg ); 279 } 280 // Checking if requested returnType is supported. returnType need to be 281 // defined in XPathConstants 282 if ( !isSupported ( returnType ) ) { 283 String fmsg = XSLMessages.createXPATHMessage( 284 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 285 new Object[] { returnType.toString() } ); 286 throw new IllegalArgumentException ( fmsg ); 287 } 288 try { 289 if ( dbf == null ) { 290 dbf = DocumentBuilderFactory.newInstance(); 291 dbf.setNamespaceAware( true ); 292 dbf.setValidating( false ); 293 } 294 db = dbf.newDocumentBuilder(); 295 Document document = db.parse( source ); 296 return eval( document, returnType ); 297 } catch ( Exception e ) { 298 throw new XPathExpressionException ( e ); 299 } 300 } 301 302 /** 303 * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a 304 * <code>String</code>.</p> 305 * 306 * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of 307 * {@link XPathConstants#STRING}.</p> 308 * 309 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 310 * for context item evaluation, 311 * variable, function and QName resolution and return type conversion.</p> 312 * 313 * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p> 314 * 315 * @param source The <code>InputSource</code> of the document to evaluate over. 316 * 317 * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a 318 * <code>String</code>. 319 * 320 * @throws XPathExpressionException If the expression cannot be evaluated. 321 * @throws NullPointerException If <code>source</code> is <code>null</code>. 322 */ evaluate(InputSource source)323 public String evaluate(InputSource source) 324 throws XPathExpressionException { 325 return (String)this.evaluate( source, XPathConstants.STRING ); 326 } 327 isSupported( QName returnType )328 private boolean isSupported( QName returnType ) { 329 // XPathConstants.STRING 330 if ( ( returnType.equals( XPathConstants.STRING ) ) || 331 ( returnType.equals( XPathConstants.NUMBER ) ) || 332 ( returnType.equals( XPathConstants.BOOLEAN ) ) || 333 ( returnType.equals( XPathConstants.NODE ) ) || 334 ( returnType.equals( XPathConstants.NODESET ) ) ) { 335 336 return true; 337 } 338 return false; 339 } 340 getResultAsType( XObject resultObject, QName returnType )341 private Object getResultAsType( XObject resultObject, QName returnType ) 342 throws javax.xml.transform.TransformerException { 343 // XPathConstants.STRING 344 if ( returnType.equals( XPathConstants.STRING ) ) { 345 return resultObject.str(); 346 } 347 // XPathConstants.NUMBER 348 if ( returnType.equals( XPathConstants.NUMBER ) ) { 349 return new Double ( resultObject.num()); 350 } 351 // XPathConstants.BOOLEAN 352 if ( returnType.equals( XPathConstants.BOOLEAN ) ) { 353 return new Boolean( resultObject.bool()); 354 } 355 // XPathConstants.NODESET ---ORdered, UNOrdered??? 356 if ( returnType.equals( XPathConstants.NODESET ) ) { 357 return resultObject.nodelist(); 358 } 359 // XPathConstants.NODE 360 if ( returnType.equals( XPathConstants.NODE ) ) { 361 NodeIterator ni = resultObject.nodeset(); 362 //Return the first node, or null 363 return ni.nextNode(); 364 } 365 // If isSupported check is already done then the execution path 366 // shouldn't come here. Being defensive 367 String fmsg = XSLMessages.createXPATHMessage( 368 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 369 new Object[] { returnType.toString()}); 370 throw new IllegalArgumentException ( fmsg ); 371 } 372 373 getDummyDocument( )374 private static Document getDummyDocument( ) { 375 try { 376 if ( dbf == null ) { 377 dbf = DocumentBuilderFactory.newInstance(); 378 dbf.setNamespaceAware( true ); 379 dbf.setValidating( false ); 380 } 381 db = dbf.newDocumentBuilder(); 382 383 DOMImplementation dim = db.getDOMImplementation(); 384 d = dim.createDocument("http://java.sun.com/jaxp/xpath", 385 "dummyroot", null); 386 return d; 387 } catch ( Exception e ) { 388 e.printStackTrace(); 389 } 390 return null; 391 } 392 393 394 395 396 } 397