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