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