1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: XPathAPI.java 524807 2007-04-02 15:51:43Z zongaro $
20 */
21package org.apache.xpath;
22
23import javax.xml.transform.TransformerException;
24
25import org.apache.xml.utils.PrefixResolver;
26import org.apache.xml.utils.PrefixResolverDefault;
27import org.apache.xpath.objects.XObject;
28
29import org.w3c.dom.Document;
30import org.w3c.dom.Node;
31import org.w3c.dom.NodeList;
32import org.w3c.dom.traversal.NodeIterator;
33
34/**
35 * The methods in this class are convenience methods into the
36 * low-level XPath API.
37 * These functions tend to be a little slow, since a number of objects must be
38 * created for each evaluation.  A faster way is to precompile the
39 * XPaths using the low-level API, and then just use the XPaths
40 * over and over.
41 *
42 * NOTE: In particular, each call to this method will create a new
43 * XPathContext, a new DTMManager... and thus a new DTM. That's very
44 * safe, since it guarantees that you're always processing against a
45 * fully up-to-date view of your document. But it's also portentially
46 * very expensive, since you're rebuilding the DTM every time. You should
47 * consider using an instance of CachedXPathAPI rather than these static
48 * methods.
49 *
50 * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a>
51 * */
52public class XPathAPI
53{
54
55  /**
56   * Use an XPath string to select a single node. XPath namespace
57   * prefixes are resolved from the context node, which may not
58   * be what you want (see the next method).
59   *
60   * @param contextNode The node to start searching from.
61   * @param str A valid XPath string.
62   * @return The first node found that matches the XPath, or null.
63   *
64   * @throws TransformerException
65   */
66  public static Node selectSingleNode(Node contextNode, String str)
67          throws TransformerException
68  {
69    return selectSingleNode(contextNode, str, contextNode);
70  }
71
72  /**
73   * Use an XPath string to select a single node.
74   * XPath namespace prefixes are resolved from the namespaceNode.
75   *
76   * @param contextNode The node to start searching from.
77   * @param str A valid XPath string.
78   * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
79   * @return The first node found that matches the XPath, or null.
80   *
81   * @throws TransformerException
82   */
83  public static Node selectSingleNode(
84          Node contextNode, String str, Node namespaceNode)
85            throws TransformerException
86  {
87
88    // Have the XObject return its result as a NodeSetDTM.
89    NodeIterator nl = selectNodeIterator(contextNode, str, namespaceNode);
90
91    // Return the first node, or null
92    return nl.nextNode();
93  }
94
95  /**
96   *  Use an XPath string to select a nodelist.
97   *  XPath namespace prefixes are resolved from the contextNode.
98   *
99   *  @param contextNode The node to start searching from.
100   *  @param str A valid XPath string.
101   *  @return A NodeIterator, should never be null.
102   *
103   * @throws TransformerException
104   */
105  public static NodeIterator selectNodeIterator(Node contextNode, String str)
106          throws TransformerException
107  {
108    return selectNodeIterator(contextNode, str, contextNode);
109  }
110
111  /**
112   *  Use an XPath string to select a nodelist.
113   *  XPath namespace prefixes are resolved from the namespaceNode.
114   *
115   *  @param contextNode The node to start searching from.
116   *  @param str A valid XPath string.
117   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
118   *  @return A NodeIterator, should never be null.
119   *
120   * @throws TransformerException
121   */
122  public static NodeIterator selectNodeIterator(
123          Node contextNode, String str, Node namespaceNode)
124            throws TransformerException
125  {
126
127    // Execute the XPath, and have it return the result
128    XObject list = eval(contextNode, str, namespaceNode);
129
130    // Have the XObject return its result as a NodeSetDTM.
131    return list.nodeset();
132  }
133
134  /**
135   *  Use an XPath string to select a nodelist.
136   *  XPath namespace prefixes are resolved from the contextNode.
137   *
138   *  @param contextNode The node to start searching from.
139   *  @param str A valid XPath string.
140   *  @return A NodeIterator, should never be null.
141   *
142   * @throws TransformerException
143   */
144  public static NodeList selectNodeList(Node contextNode, String str)
145          throws TransformerException
146  {
147    return selectNodeList(contextNode, str, contextNode);
148  }
149
150  /**
151   *  Use an XPath string to select a nodelist.
152   *  XPath namespace prefixes are resolved from the namespaceNode.
153   *
154   *  @param contextNode The node to start searching from.
155   *  @param str A valid XPath string.
156   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
157   *  @return A NodeIterator, should never be null.
158   *
159   * @throws TransformerException
160   */
161  public static NodeList selectNodeList(
162          Node contextNode, String str, Node namespaceNode)
163            throws TransformerException
164  {
165
166    // Execute the XPath, and have it return the result
167    XObject list = eval(contextNode, str, namespaceNode);
168
169    // Return a NodeList.
170    return list.nodelist();
171  }
172
173  /**
174   *  Evaluate XPath string to an XObject.  Using this method,
175   *  XPath namespace prefixes will be resolved from the namespaceNode.
176   *  @param contextNode The node to start searching from.
177   *  @param str A valid XPath string.
178   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
179   *  @see org.apache.xpath.objects.XObject
180   *  @see org.apache.xpath.objects.XNull
181   *  @see org.apache.xpath.objects.XBoolean
182   *  @see org.apache.xpath.objects.XNumber
183   *  @see org.apache.xpath.objects.XString
184   *  @see org.apache.xpath.objects.XRTreeFrag
185   *
186   * @throws TransformerException
187   */
188  public static XObject eval(Node contextNode, String str)
189          throws TransformerException
190  {
191    return eval(contextNode, str, contextNode);
192  }
193
194  /**
195   *  Evaluate XPath string to an XObject.
196   *  XPath namespace prefixes are resolved from the namespaceNode.
197   *  The implementation of this is a little slow, since it creates
198   *  a number of objects each time it is called.  This could be optimized
199   *  to keep the same objects around, but then thread-safety issues would arise.
200   *
201   *  @param contextNode The node to start searching from.
202   *  @param str A valid XPath string.
203   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
204   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
205   *  @see org.apache.xpath.objects.XObject
206   *  @see org.apache.xpath.objects.XNull
207   *  @see org.apache.xpath.objects.XBoolean
208   *  @see org.apache.xpath.objects.XNumber
209   *  @see org.apache.xpath.objects.XString
210   *  @see org.apache.xpath.objects.XRTreeFrag
211   *
212   * @throws TransformerException
213   */
214  public static XObject eval(Node contextNode, String str, Node namespaceNode)
215          throws TransformerException
216  {
217
218    // Since we don't have a XML Parser involved here, install some default support
219    // for things like namespaces, etc.
220    // (Changed from: XPathContext xpathSupport = new XPathContext();
221    //    because XPathContext is weak in a number of areas... perhaps
222    //    XPathContext should be done away with.)
223    // Create an XPathContext that doesn't support pushing and popping of
224    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
225    XPathContext xpathSupport = new XPathContext(false);
226
227    // Create an object to resolve namespace prefixes.
228    // XPath namespaces are resolved from the input context node's document element
229    // if it is a root node, or else the current context node (for lack of a better
230    // resolution space, given the simplicity of this sample code).
231    PrefixResolverDefault prefixResolver = new PrefixResolverDefault(
232      (namespaceNode.getNodeType() == Node.DOCUMENT_NODE)
233      ? ((Document) namespaceNode).getDocumentElement() : namespaceNode);
234
235    // Create the XPath object.
236    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
237
238    // Execute the XPath, and have it return the result
239    // return xpath.execute(xpathSupport, contextNode, prefixResolver);
240    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
241
242    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
243  }
244
245  /**
246   *   Evaluate XPath string to an XObject.
247   *   XPath namespace prefixes are resolved from the namespaceNode.
248   *   The implementation of this is a little slow, since it creates
249   *   a number of objects each time it is called.  This could be optimized
250   *   to keep the same objects around, but then thread-safety issues would arise.
251   *
252   *   @param contextNode The node to start searching from.
253   *   @param str A valid XPath string.
254   *   @param prefixResolver Will be called if the parser encounters namespace
255   *                         prefixes, to resolve the prefixes to URLs.
256   *   @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
257   *   @see org.apache.xpath.objects.XObject
258   *   @see org.apache.xpath.objects.XNull
259   *   @see org.apache.xpath.objects.XBoolean
260   *   @see org.apache.xpath.objects.XNumber
261   *   @see org.apache.xpath.objects.XString
262   *   @see org.apache.xpath.objects.XRTreeFrag
263   *
264   * @throws TransformerException
265   */
266  public static XObject eval(
267          Node contextNode, String str, PrefixResolver prefixResolver)
268            throws TransformerException
269  {
270
271    // Since we don't have a XML Parser involved here, install some default support
272    // for things like namespaces, etc.
273    // (Changed from: XPathContext xpathSupport = new XPathContext();
274    //    because XPathContext is weak in a number of areas... perhaps
275    //    XPathContext should be done away with.)
276    // Create the XPath object.
277    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
278
279    // Create an XPathContext that doesn't support pushing and popping of
280    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
281    XPathContext xpathSupport = new XPathContext(false);
282
283    // Execute the XPath, and have it return the result
284    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
285
286    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
287  }
288}
289