1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xpath;
  58. import javax.xml.transform.TransformerException;
  59. import org.w3c.dom.Node;
  60. import org.w3c.dom.Document;
  61. import org.w3c.dom.traversal.NodeIterator;
  62. import org.w3c.dom.NodeList;
  63. import org.apache.xpath.XPathContext;
  64. import org.apache.xpath.XPath;
  65. import org.apache.xpath.compiler.XPathParser;
  66. import org.apache.xpath.XPathContext;
  67. import org.apache.xml.utils.PrefixResolverDefault;
  68. import org.apache.xml.utils.PrefixResolver;
  69. import org.apache.xpath.objects.XObject;
  70. import org.apache.xml.dtm.DTM;
  71. import org.apache.xml.dtm.ref.DTMNodeIterator;
  72. import org.apache.xml.dtm.ref.DTMNodeList;
  73. import org.apache.xml.dtm.ref.DTMManagerDefault;
  74. /**
  75. * The methods in this class are convenience methods into the
  76. * low-level XPath API.
  77. *
  78. * These functions tend to be a little slow, since a number of objects must be
  79. * created for each evaluation. A faster way is to precompile the
  80. * XPaths using the low-level API, and then just use the XPaths
  81. * over and over.
  82. *
  83. * This is an alternative for the old XPathAPI class, which provided
  84. * static methods for the purpose but had the drawback of
  85. * instantiating a new XPathContext (and thus building a new DTMManager,
  86. * and new DTMs) each time it was called. XPathAPIObject instead retains
  87. * its context as long as the object persists, reusing the DTMs. This
  88. * does have a downside: if you've changed your source document, you should
  89. * obtain a new XPathAPIObject to continue searching it, since trying to use
  90. * the old DTMs will probably yield bad results or malfunction outright... and
  91. * the cached DTMs may consume memory until this object and its context are
  92. * returned to the heap. Essentially, it's the caller's responsibility to
  93. * decide when to discard the cache.
  94. *
  95. * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a>
  96. * */
  97. public class CachedXPathAPI
  98. {
  99. /** XPathContext, and thus the document model system (DTMs), persists through multiple
  100. calls to this object. This is set in the constructor.
  101. */
  102. protected XPathContext xpathSupport;
  103. /** Default constructor. Establishes its own XPathContext, and hence
  104. * its own DTMManager. Good choice for simple uses.
  105. * */
  106. public CachedXPathAPI()
  107. {
  108. xpathSupport = new XPathContext();
  109. }
  110. /** This constructor shares its XPathContext with a pre-existing
  111. * CachedXPathAPI. That allows sharing document models (DTMs) and
  112. * previously established location state.
  113. *
  114. * Note that the original CachedXPathAPI and the new one should not
  115. * be operated concurrently; we do not support multithreaded access
  116. * to a single DTM at this time.
  117. *
  118. * %REVIEW% Should this instead do a clone-and-reset on the XPathSupport object?
  119. * */
  120. public CachedXPathAPI(CachedXPathAPI priorXPathAPI)
  121. {
  122. xpathSupport = priorXPathAPI.xpathSupport;
  123. }
  124. /** Returns the XPathSupport object used in this CachedXPathAPI
  125. *
  126. * %REVIEW% I'm somewhat concerned about the loss of encapsulation
  127. * this causes, but the xml-security folks say they need it.
  128. * */
  129. public XPathContext getXPathContext()
  130. {
  131. return this.xpathSupport;
  132. }
  133. /**
  134. * Use an XPath string to select a single node. XPath namespace
  135. * prefixes are resolved from the context node, which may not
  136. * be what you want (see the next method).
  137. *
  138. * @param contextNode The node to start searching from.
  139. * @param str A valid XPath string.
  140. * @return The first node found that matches the XPath, or null.
  141. *
  142. * @throws TransformerException
  143. */
  144. public Node selectSingleNode(Node contextNode, String str)
  145. throws TransformerException
  146. {
  147. return selectSingleNode(contextNode, str, contextNode);
  148. }
  149. /**
  150. * Use an XPath string to select a single node.
  151. * XPath namespace prefixes are resolved from the namespaceNode.
  152. *
  153. * @param contextNode The node to start searching from.
  154. * @param str A valid XPath string.
  155. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  156. * @return The first node found that matches the XPath, or null.
  157. *
  158. * @throws TransformerException
  159. */
  160. public Node selectSingleNode(
  161. Node contextNode, String str, Node namespaceNode)
  162. throws TransformerException
  163. {
  164. // Have the XObject return its result as a NodeSetDTM.
  165. NodeIterator nl = selectNodeIterator(contextNode, str, namespaceNode);
  166. // Return the first node, or null
  167. return nl.nextNode();
  168. }
  169. /**
  170. * Use an XPath string to select a nodelist.
  171. * XPath namespace prefixes are resolved from the contextNode.
  172. *
  173. * @param contextNode The node to start searching from.
  174. * @param str A valid XPath string.
  175. * @return A NodeIterator, should never be null.
  176. *
  177. * @throws TransformerException
  178. */
  179. public NodeIterator selectNodeIterator(Node contextNode, String str)
  180. throws TransformerException
  181. {
  182. return selectNodeIterator(contextNode, str, contextNode);
  183. }
  184. /**
  185. * Use an XPath string to select a nodelist.
  186. * XPath namespace prefixes are resolved from the namespaceNode.
  187. *
  188. * @param contextNode The node to start searching from.
  189. * @param str A valid XPath string.
  190. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  191. * @return A NodeIterator, should never be null.
  192. *
  193. * @throws TransformerException
  194. */
  195. public NodeIterator selectNodeIterator(
  196. Node contextNode, String str, Node namespaceNode)
  197. throws TransformerException
  198. {
  199. // Execute the XPath, and have it return the result
  200. XObject list = eval(contextNode, str, namespaceNode);
  201. // Have the XObject return its result as a NodeSetDTM.
  202. return list.nodeset();
  203. }
  204. /**
  205. * Use an XPath string to select a nodelist.
  206. * XPath namespace prefixes are resolved from the contextNode.
  207. *
  208. * @param contextNode The node to start searching from.
  209. * @param str A valid XPath string.
  210. * @return A NodeIterator, should never be null.
  211. *
  212. * @throws TransformerException
  213. */
  214. public NodeList selectNodeList(Node contextNode, String str)
  215. throws TransformerException
  216. {
  217. return selectNodeList(contextNode, str, contextNode);
  218. }
  219. /**
  220. * Use an XPath string to select a nodelist.
  221. * XPath namespace prefixes are resolved from the namespaceNode.
  222. *
  223. * @param contextNode The node to start searching from.
  224. * @param str A valid XPath string.
  225. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  226. * @return A NodeIterator, should never be null.
  227. *
  228. * @throws TransformerException
  229. */
  230. public NodeList selectNodeList(
  231. Node contextNode, String str, Node namespaceNode)
  232. throws TransformerException
  233. {
  234. // Execute the XPath, and have it return the result
  235. XObject list = eval(contextNode, str, namespaceNode);
  236. // Return a NodeList.
  237. return list.nodelist();
  238. }
  239. /**
  240. * Evaluate XPath string to an XObject. Using this method,
  241. * XPath namespace prefixes will be resolved from the namespaceNode.
  242. * @param contextNode The node to start searching from.
  243. * @param str A valid XPath string.
  244. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  245. * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
  246. * @see org.apache.xpath.objects.XObject
  247. * @see org.apache.xpath.objects.XNull
  248. * @see org.apache.xpath.objects.XBoolean
  249. * @see org.apache.xpath.objects.XNumber
  250. * @see org.apache.xpath.objects.XString
  251. * @see org.apache.xpath.objects.XRTreeFrag
  252. *
  253. * @throws TransformerException
  254. */
  255. public XObject eval(Node contextNode, String str)
  256. throws TransformerException
  257. {
  258. return eval(contextNode, str, contextNode);
  259. }
  260. /**
  261. * Evaluate XPath string to an XObject.
  262. * XPath namespace prefixes are resolved from the namespaceNode.
  263. * The implementation of this is a little slow, since it creates
  264. * a number of objects each time it is called. This could be optimized
  265. * to keep the same objects around, but then thread-safety issues would arise.
  266. *
  267. * @param contextNode The node to start searching from.
  268. * @param str A valid XPath string.
  269. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  270. * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
  271. * @see org.apache.xpath.objects.XObject
  272. * @see org.apache.xpath.objects.XNull
  273. * @see org.apache.xpath.objects.XBoolean
  274. * @see org.apache.xpath.objects.XNumber
  275. * @see org.apache.xpath.objects.XString
  276. * @see org.apache.xpath.objects.XRTreeFrag
  277. *
  278. * @throws TransformerException
  279. */
  280. public XObject eval(Node contextNode, String str, Node namespaceNode)
  281. throws TransformerException
  282. {
  283. // Since we don't have a XML Parser involved here, install some default support
  284. // for things like namespaces, etc.
  285. // (Changed from: XPathContext xpathSupport = new XPathContext();
  286. // because XPathContext is weak in a number of areas... perhaps
  287. // XPathContext should be done away with.)
  288. // Create an object to resolve namespace prefixes.
  289. // XPath namespaces are resolved from the input context node's document element
  290. // if it is a root node, or else the current context node (for lack of a better
  291. // resolution space, given the simplicity of this sample code).
  292. PrefixResolverDefault prefixResolver = new PrefixResolverDefault(
  293. (namespaceNode.getNodeType() == Node.DOCUMENT_NODE)
  294. ? ((Document) namespaceNode).getDocumentElement() : namespaceNode);
  295. // Create the XPath object.
  296. XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
  297. // Execute the XPath, and have it return the result
  298. // return xpath.execute(xpathSupport, contextNode, prefixResolver);
  299. int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
  300. return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
  301. }
  302. /**
  303. * Evaluate XPath string to an XObject.
  304. * XPath namespace prefixes are resolved from the namespaceNode.
  305. * The implementation of this is a little slow, since it creates
  306. * a number of objects each time it is called. This could be optimized
  307. * to keep the same objects around, but then thread-safety issues would arise.
  308. *
  309. * @param contextNode The node to start searching from.
  310. * @param str A valid XPath string.
  311. * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  312. * @param prefixResolver Will be called if the parser encounters namespace
  313. * prefixes, to resolve the prefixes to URLs.
  314. * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
  315. * @see org.apache.xpath.objects.XObject
  316. * @see org.apache.xpath.objects.XNull
  317. * @see org.apache.xpath.objects.XBoolean
  318. * @see org.apache.xpath.objects.XNumber
  319. * @see org.apache.xpath.objects.XString
  320. * @see org.apache.xpath.objects.XRTreeFrag
  321. *
  322. * @throws TransformerException
  323. */
  324. public XObject eval(
  325. Node contextNode, String str, PrefixResolver prefixResolver)
  326. throws TransformerException
  327. {
  328. // Since we don't have a XML Parser involved here, install some default support
  329. // for things like namespaces, etc.
  330. // (Changed from: XPathContext xpathSupport = new XPathContext();
  331. // because XPathContext is weak in a number of areas... perhaps
  332. // XPathContext should be done away with.)
  333. // Create the XPath object.
  334. XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
  335. // Execute the XPath, and have it return the result
  336. XPathContext xpathSupport = new XPathContext();
  337. int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
  338. return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
  339. }
  340. }