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