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 // $Id: XPathExpressionImpl.java 1225277 2011-12-28 18:50:56Z mrglavas $ 019 020 package org.apache.xpath.jaxp; 021 022 import javax.xml.namespace.QName; 023 import javax.xml.parsers.DocumentBuilder; 024 import javax.xml.parsers.DocumentBuilderFactory; 025 import javax.xml.xpath.XPathConstants; 026 import javax.xml.xpath.XPathExpressionException; 027 import javax.xml.xpath.XPathFunctionResolver; 028 import javax.xml.xpath.XPathVariableResolver; 029 030 import org.apache.xalan.res.XSLMessages; 031 import org.apache.xpath.objects.XObject; 032 import org.apache.xpath.res.XPATHErrorResources; 033 import org.w3c.dom.DOMImplementation; 034 import org.w3c.dom.Document; 035 import org.w3c.dom.Node; 036 import org.w3c.dom.traversal.NodeIterator; 037 import org.xml.sax.InputSource; 038 039 /** 040 * The XPathExpression interface encapsulates a (compiled) XPath expression. 041 * 042 * @version $Revision: 1225277 $ 043 * @author Ramesh Mandava 044 */ 045 public class XPathExpressionImpl implements javax.xml.xpath.XPathExpression{ 046 047 private XPathFunctionResolver functionResolver; 048 private XPathVariableResolver variableResolver; 049 private JAXPPrefixResolver prefixResolver; 050 private org.apache.xpath.XPath xpath; 051 052 // By default Extension Functions are allowed in XPath Expressions. If 053 // Secure Processing Feature is set on XPathFactory then the invocation of 054 // extensions function need to throw XPathFunctionException 055 private boolean featureSecureProcessing = false; 056 057 /** Protected constructor to prevent direct instantiation; use compile() 058 * from the context. 059 */ 060 protected XPathExpressionImpl() { }; 061 062 protected XPathExpressionImpl(org.apache.xpath.XPath xpath, 063 JAXPPrefixResolver prefixResolver, 064 XPathFunctionResolver functionResolver, 065 XPathVariableResolver variableResolver ) { 066 this.xpath = xpath; 067 this.prefixResolver = prefixResolver; 068 this.functionResolver = functionResolver; 069 this.variableResolver = variableResolver; 070 this.featureSecureProcessing = false; 071 }; 072 073 protected XPathExpressionImpl(org.apache.xpath.XPath xpath, 074 JAXPPrefixResolver prefixResolver, 075 XPathFunctionResolver functionResolver, 076 XPathVariableResolver variableResolver, 077 boolean featureSecureProcessing ) { 078 this.xpath = xpath; 079 this.prefixResolver = prefixResolver; 080 this.functionResolver = functionResolver; 081 this.variableResolver = variableResolver; 082 this.featureSecureProcessing = featureSecureProcessing; 083 }; 084 085 public void setXPath (org.apache.xpath.XPath xpath ) { 086 this.xpath = xpath; 087 } 088 089 public Object eval(Object item, QName returnType) 090 throws javax.xml.transform.TransformerException { 091 XObject resultObject = eval ( item ); 092 return getResultAsType( resultObject, returnType ); 093 } 094 095 private XObject eval ( Object contextItem ) 096 throws javax.xml.transform.TransformerException { 097 org.apache.xpath.XPathContext xpathSupport = null; 098 099 // Create an XPathContext that doesn't support pushing and popping of 100 // variable resolution scopes. Sufficient for simple XPath 1.0 101 // expressions. 102 if ( functionResolver != null ) { 103 JAXPExtensionsProvider jep = new JAXPExtensionsProvider( 104 functionResolver, featureSecureProcessing ); 105 xpathSupport = new org.apache.xpath.XPathContext(jep, false); 106 } else { 107 xpathSupport = new org.apache.xpath.XPathContext(false); 108 } 109 110 xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); 111 XObject xobj = null; 112 113 Node contextNode = (Node)contextItem; 114 // We always need to have a ContextNode with Xalan XPath implementation 115 // To allow simple expression evaluation like 1+1 we are setting 116 // dummy Document as Context Node 117 if ( contextNode == null ) { 118 contextNode = getDummyDocument(); 119 } 120 121 xobj = xpath.execute(xpathSupport, contextNode, prefixResolver ); 122 return xobj; 123 } 124 125 126 /** 127 * <p>Evaluate the compiled XPath expression in the specified context and 128 * return the result as the specified type.</p> 129 * 130 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 131 * for context item evaluation, 132 * variable, function and QName resolution and return type conversion.</p> 133 * 134 * <p>If <code>returnType</code> is not one of the types defined 135 * in {@link XPathConstants}, 136 * then an <code>IllegalArgumentException</code> is thrown.</p> 137 * 138 * <p>If a <code>null</code> value is provided for 139 * <code>item</code>, an empty document will be used for the 140 * context. 141 * If <code>returnType</code> is <code>null</code>, then a 142 * <code>NullPointerException</code> is thrown.</p> 143 * 144 * @param item The starting context (node or node list, for example). 145 * @param returnType The desired return type. 146 * 147 * @return The <code>Object</code> that is the result of evaluating the 148 * expression and converting the result to 149 * <code>returnType</code>. 150 * 151 * @throws XPathExpressionException If the expression cannot be evaluated. 152 * @throws IllegalArgumentException If <code>returnType</code> is not one 153 * of the types defined in {@link XPathConstants}. 154 * @throws NullPointerException If <code>returnType</code> is 155 * <code>null</code>. 156 */ 157 public Object evaluate(Object item, QName returnType) 158 throws XPathExpressionException { 159 //Validating parameters to enforce constraints defined by JAXP spec 160 if ( returnType == null ) { 161 //Throwing NullPointerException as defined in spec 162 String fmsg = XSLMessages.createXPATHMessage( 163 XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, 164 new Object[] {"returnType"} ); 165 throw new NullPointerException( fmsg ); 166 } 167 // Checking if requested returnType is supported. returnType need to be 168 // defined in XPathConstants 169 if ( !isSupported ( returnType ) ) { 170 String fmsg = XSLMessages.createXPATHMessage( 171 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 172 new Object[] { returnType.toString() } ); 173 throw new IllegalArgumentException ( fmsg ); 174 } 175 try { 176 return eval( item, returnType); 177 } catch ( java.lang.NullPointerException npe ) { 178 // If VariableResolver returns null Or if we get 179 // NullPointerException at this stage for some other reason 180 // then we have to reurn XPathException 181 throw new XPathExpressionException ( npe ); 182 } catch ( javax.xml.transform.TransformerException te ) { 183 Throwable nestedException = te.getException(); 184 if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { 185 throw (javax.xml.xpath.XPathFunctionException)nestedException; 186 } else { 187 // For any other exceptions we need to throw 188 // XPathExpressionException ( as per spec ) 189 throw new XPathExpressionException( te); 190 } 191 } 192 193 } 194 195 /** 196 * <p>Evaluate the compiled XPath expression in the specified context and 197 * return the result as a <code>String</code>.</p> 198 * 199 * <p>This method calls {@link #evaluate(Object item, QName returnType)} 200 * with a <code>returnType</code> of 201 * {@link XPathConstants#STRING}.</p> 202 * 203 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 204 * for context item evaluation, 205 * variable, function and QName resolution and return type conversion.</p> 206 * 207 * <p>If a <code>null</code> value is provided for 208 * <code>item</code>, an empty document will be used for the 209 * context. 210 * 211 * @param item The starting context (node or node list, for example). 212 * 213 * @return The <code>String</code> that is the result of evaluating the 214 * expression and converting the result to a 215 * <code>String</code>. 216 * 217 * @throws XPathExpressionException If the expression cannot be evaluated. 218 */ 219 public String evaluate(Object item) 220 throws XPathExpressionException { 221 return (String)this.evaluate( item, XPathConstants.STRING ); 222 } 223 224 225 226 static DocumentBuilderFactory dbf = null; 227 static DocumentBuilder db = null; 228 static Document d = null; 229 230 /** 231 * <p>Evaluate the compiled XPath expression in the context of the 232 * specified <code>InputSource</code> and return the result as the 233 * specified type.</p> 234 * 235 * <p>This method builds a data model for the {@link InputSource} and calls 236 * {@link #evaluate(Object item, QName returnType)} on the resulting 237 * document object.</p> 238 * 239 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 240 * for context item evaluation, 241 * variable, function and QName resolution and return type conversion.</p> 242 * 243 * <p>If <code>returnType</code> is not one of the types defined in 244 * {@link XPathConstants}, 245 * then an <code>IllegalArgumentException</code> is thrown.</p> 246 * 247 *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>, 248 * then a <code>NullPointerException</code> is thrown.</p> 249 * 250 * @param source The <code>InputSource</code> of the document to evaluate 251 * over. 252 * @param returnType The desired return type. 253 * 254 * @return The <code>Object</code> that is the result of evaluating the 255 * expression and converting the result to 256 * <code>returnType</code>. 257 * 258 * @throws XPathExpressionException If the expression cannot be evaluated. 259 * @throws IllegalArgumentException If <code>returnType</code> is not one 260 * of the types defined in {@link XPathConstants}. 261 * @throws NullPointerException If <code>source</code> or 262 * <code>returnType</code> is <code>null</code>. 263 */ 264 public Object evaluate(InputSource source, QName returnType) 265 throws XPathExpressionException { 266 if ( ( source == null ) || ( returnType == null ) ) { 267 String fmsg = XSLMessages.createXPATHMessage( 268 XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL, 269 null ); 270 throw new NullPointerException ( fmsg ); 271 } 272 // Checking if requested returnType is supported. returnType need to be 273 // defined in XPathConstants 274 if ( !isSupported ( returnType ) ) { 275 String fmsg = XSLMessages.createXPATHMessage( 276 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 277 new Object[] { returnType.toString() } ); 278 throw new IllegalArgumentException ( fmsg ); 279 } 280 try { 281 if ( dbf == null ) { 282 dbf = DocumentBuilderFactory.newInstance(); 283 dbf.setNamespaceAware( true ); 284 dbf.setValidating( false ); 285 } 286 db = dbf.newDocumentBuilder(); 287 Document document = db.parse( source ); 288 return eval( document, returnType ); 289 } catch ( Exception e ) { 290 throw new XPathExpressionException ( e ); 291 } 292 } 293 294 /** 295 * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a 296 * <code>String</code>.</p> 297 * 298 * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of 299 * {@link XPathConstants#STRING}.</p> 300 * 301 * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 302 * for context item evaluation, 303 * variable, function and QName resolution and return type conversion.</p> 304 * 305 * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p> 306 * 307 * @param source The <code>InputSource</code> of the document to evaluate over. 308 * 309 * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a 310 * <code>String</code>. 311 * 312 * @throws XPathExpressionException If the expression cannot be evaluated. 313 * @throws NullPointerException If <code>source</code> is <code>null</code>. 314 */ 315 public String evaluate(InputSource source) 316 throws XPathExpressionException { 317 return (String)this.evaluate( source, XPathConstants.STRING ); 318 } 319 320 private boolean isSupported( QName returnType ) { 321 // XPathConstants.STRING 322 if ( ( returnType.equals( XPathConstants.STRING ) ) || 323 ( returnType.equals( XPathConstants.NUMBER ) ) || 324 ( returnType.equals( XPathConstants.BOOLEAN ) ) || 325 ( returnType.equals( XPathConstants.NODE ) ) || 326 ( returnType.equals( XPathConstants.NODESET ) ) ) { 327 328 return true; 329 } 330 return false; 331 } 332 333 private Object getResultAsType( XObject resultObject, QName returnType ) 334 throws javax.xml.transform.TransformerException { 335 // XPathConstants.STRING 336 if ( returnType.equals( XPathConstants.STRING ) ) { 337 return resultObject.str(); 338 } 339 // XPathConstants.NUMBER 340 if ( returnType.equals( XPathConstants.NUMBER ) ) { 341 return new Double ( resultObject.num()); 342 } 343 // XPathConstants.BOOLEAN 344 if ( returnType.equals( XPathConstants.BOOLEAN ) ) { 345 return resultObject.bool() ? Boolean.TRUE : Boolean.FALSE; 346 } 347 // XPathConstants.NODESET ---ORdered, UNOrdered??? 348 if ( returnType.equals( XPathConstants.NODESET ) ) { 349 return resultObject.nodelist(); 350 } 351 // XPathConstants.NODE 352 if ( returnType.equals( XPathConstants.NODE ) ) { 353 NodeIterator ni = resultObject.nodeset(); 354 //Return the first node, or null 355 return ni.nextNode(); 356 } 357 // If isSupported check is already done then the execution path 358 // shouldn't come here. Being defensive 359 String fmsg = XSLMessages.createXPATHMessage( 360 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 361 new Object[] { returnType.toString()}); 362 throw new IllegalArgumentException ( fmsg ); 363 } 364 365 366 private static Document getDummyDocument( ) { 367 try { 368 if ( dbf == null ) { 369 dbf = DocumentBuilderFactory.newInstance(); 370 dbf.setNamespaceAware( true ); 371 dbf.setValidating( false ); 372 } 373 db = dbf.newDocumentBuilder(); 374 375 DOMImplementation dim = db.getDOMImplementation(); 376 d = dim.createDocument("http://java.sun.com/jaxp/xpath", 377 "dummyroot", null); 378 return d; 379 } catch ( Exception e ) { 380 e.printStackTrace(); 381 } 382 return null; 383 } 384 385 386 387 388 }