001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: CachedXPathAPI.java,v 1.7 2005/01/24 20:51:23 zongaro Exp $
018: */
019: package org.apache.xpath;
020:
021: import javax.xml.transform.TransformerException;
022:
023: import org.apache.xml.utils.PrefixResolver;
024: import org.apache.xml.utils.PrefixResolverDefault;
025: import org.apache.xpath.objects.XObject;
026:
027: import org.w3c.dom.Document;
028: import org.w3c.dom.Node;
029: import org.w3c.dom.NodeList;
030: import org.w3c.dom.traversal.NodeIterator;
031:
032: /**
033: * The methods in this class are convenience methods into the
034: * low-level XPath API.
035: *
036: * These functions tend to be a little slow, since a number of objects must be
037: * created for each evaluation. A faster way is to precompile the
038: * XPaths using the low-level API, and then just use the XPaths
039: * over and over.
040: *
041: * This is an alternative for the old XPathAPI class, which provided
042: * static methods for the purpose but had the drawback of
043: * instantiating a new XPathContext (and thus building a new DTMManager,
044: * and new DTMs) each time it was called. XPathAPIObject instead retains
045: * its context as long as the object persists, reusing the DTMs. This
046: * does have a downside: if you've changed your source document, you should
047: * obtain a new XPathAPIObject to continue searching it, since trying to use
048: * the old DTMs will probably yield bad results or malfunction outright... and
049: * the cached DTMs may consume memory until this object and its context are
050: * returned to the heap. Essentially, it's the caller's responsibility to
051: * decide when to discard the cache.
052: *
053: * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a>
054: * */
055: public class CachedXPathAPI {
056: /** XPathContext, and thus the document model system (DTMs), persists through multiple
057: calls to this object. This is set in the constructor.
058: */
059: protected XPathContext xpathSupport;
060:
061: /**
062: * <p>Default constructor. Establishes its own {@link XPathContext}, and hence
063: * its own {@link org.apache.xml.dtm.DTMManager}.
064: * Good choice for simple uses.</p>
065: * <p>Note that any particular instance of {@link CachedXPathAPI} must not be
066: * operated upon by multiple threads without synchronization; we do
067: * not currently support multithreaded access to a single
068: * {@link org.apache.xml.dtm.DTM}.</p>
069: */
070: public CachedXPathAPI() {
071: xpathSupport = new XPathContext();
072: }
073:
074: /**
075: * <p>This constructor shares its {@link XPathContext} with a pre-existing
076: * {@link CachedXPathAPI}. That allows sharing document models
077: * ({@link org.apache.xml.dtm.DTM}) and previously established location
078: * state.</p>
079: * <p>Note that the original {@link CachedXPathAPI} and the new one should
080: * not be operated upon concurrently; we do not support multithreaded access
081: * to a single {@link org.apache.xml.dtm.DTM} at this time. Similarly,
082: * any particular instance of {@link CachedXPathAPI} must not be operated
083: * upon by multiple threads without synchronization.</p>
084: * <p>%REVIEW% Should this instead do a clone-and-reset on the XPathSupport object?</p>
085: *
086: */
087: public CachedXPathAPI(CachedXPathAPI priorXPathAPI) {
088: xpathSupport = priorXPathAPI.xpathSupport;
089: }
090:
091: /** Returns the XPathSupport object used in this CachedXPathAPI
092: *
093: * %REVIEW% I'm somewhat concerned about the loss of encapsulation
094: * this causes, but the xml-security folks say they need it.
095: * */
096: public XPathContext getXPathContext() {
097: return this .xpathSupport;
098: }
099:
100: /**
101: * Use an XPath string to select a single node. XPath namespace
102: * prefixes are resolved from the context node, which may not
103: * be what you want (see the next method).
104: *
105: * @param contextNode The node to start searching from.
106: * @param str A valid XPath string.
107: * @return The first node found that matches the XPath, or null.
108: *
109: * @throws TransformerException
110: */
111: public Node selectSingleNode(Node contextNode, String str)
112: throws TransformerException {
113: return selectSingleNode(contextNode, str, contextNode);
114: }
115:
116: /**
117: * Use an XPath string to select a single node.
118: * XPath namespace prefixes are resolved from the namespaceNode.
119: *
120: * @param contextNode The node to start searching from.
121: * @param str A valid XPath string.
122: * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
123: * @return The first node found that matches the XPath, or null.
124: *
125: * @throws TransformerException
126: */
127: public Node selectSingleNode(Node contextNode, String str,
128: Node namespaceNode) throws TransformerException {
129:
130: // Have the XObject return its result as a NodeSetDTM.
131: NodeIterator nl = selectNodeIterator(contextNode, str,
132: namespaceNode);
133:
134: // Return the first node, or null
135: return nl.nextNode();
136: }
137:
138: /**
139: * Use an XPath string to select a nodelist.
140: * XPath namespace prefixes are resolved from the contextNode.
141: *
142: * @param contextNode The node to start searching from.
143: * @param str A valid XPath string.
144: * @return A NodeIterator, should never be null.
145: *
146: * @throws TransformerException
147: */
148: public NodeIterator selectNodeIterator(Node contextNode, String str)
149: throws TransformerException {
150: return selectNodeIterator(contextNode, str, contextNode);
151: }
152:
153: /**
154: * Use an XPath string to select a nodelist.
155: * XPath namespace prefixes are resolved from the namespaceNode.
156: *
157: * @param contextNode The node to start searching from.
158: * @param str A valid XPath string.
159: * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
160: * @return A NodeIterator, should never be null.
161: *
162: * @throws TransformerException
163: */
164: public NodeIterator selectNodeIterator(Node contextNode,
165: String str, Node namespaceNode) throws TransformerException {
166:
167: // Execute the XPath, and have it return the result
168: XObject list = eval(contextNode, str, namespaceNode);
169:
170: // Have the XObject return its result as a NodeSetDTM.
171: return list.nodeset();
172: }
173:
174: /**
175: * Use an XPath string to select a nodelist.
176: * XPath namespace prefixes are resolved from the contextNode.
177: *
178: * @param contextNode The node to start searching from.
179: * @param str A valid XPath string.
180: * @return A NodeIterator, should never be null.
181: *
182: * @throws TransformerException
183: */
184: public NodeList selectNodeList(Node contextNode, String str)
185: throws TransformerException {
186: return selectNodeList(contextNode, str, contextNode);
187: }
188:
189: /**
190: * Use an XPath string to select a nodelist.
191: * XPath namespace prefixes are resolved from the namespaceNode.
192: *
193: * @param contextNode The node to start searching from.
194: * @param str A valid XPath string.
195: * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
196: * @return A NodeIterator, should never be null.
197: *
198: * @throws TransformerException
199: */
200: public NodeList selectNodeList(Node contextNode, String str,
201: Node namespaceNode) throws TransformerException {
202:
203: // Execute the XPath, and have it return the result
204: XObject list = eval(contextNode, str, namespaceNode);
205:
206: // Return a NodeList.
207: return list.nodelist();
208: }
209:
210: /**
211: * Evaluate XPath string to an XObject. Using this method,
212: * XPath namespace prefixes will be resolved from the namespaceNode.
213: * @param contextNode The node to start searching from.
214: * @param str A valid XPath string.
215: * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
216: * @see org.apache.xpath.objects.XObject
217: * @see org.apache.xpath.objects.XNull
218: * @see org.apache.xpath.objects.XBoolean
219: * @see org.apache.xpath.objects.XNumber
220: * @see org.apache.xpath.objects.XString
221: * @see org.apache.xpath.objects.XRTreeFrag
222: *
223: * @throws TransformerException
224: */
225: public XObject eval(Node contextNode, String str)
226: throws TransformerException {
227: return eval(contextNode, str, contextNode);
228: }
229:
230: /**
231: * Evaluate XPath string to an XObject.
232: * XPath namespace prefixes are resolved from the namespaceNode.
233: * The implementation of this is a little slow, since it creates
234: * a number of objects each time it is called. This could be optimized
235: * to keep the same objects around, but then thread-safety issues would arise.
236: *
237: * @param contextNode The node to start searching from.
238: * @param str A valid XPath string.
239: * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
240: * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
241: * @see org.apache.xpath.objects.XObject
242: * @see org.apache.xpath.objects.XNull
243: * @see org.apache.xpath.objects.XBoolean
244: * @see org.apache.xpath.objects.XNumber
245: * @see org.apache.xpath.objects.XString
246: * @see org.apache.xpath.objects.XRTreeFrag
247: *
248: * @throws TransformerException
249: */
250: public XObject eval(Node contextNode, String str, Node namespaceNode)
251: throws TransformerException {
252:
253: // Since we don't have a XML Parser involved here, install some default support
254: // for things like namespaces, etc.
255: // (Changed from: XPathContext xpathSupport = new XPathContext();
256: // because XPathContext is weak in a number of areas... perhaps
257: // XPathContext should be done away with.)
258:
259: // Create an object to resolve namespace prefixes.
260: // XPath namespaces are resolved from the input context node's document element
261: // if it is a root node, or else the current context node (for lack of a better
262: // resolution space, given the simplicity of this sample code).
263: PrefixResolverDefault prefixResolver = new PrefixResolverDefault(
264: (namespaceNode.getNodeType() == Node.DOCUMENT_NODE) ? ((Document) namespaceNode)
265: .getDocumentElement()
266: : namespaceNode);
267:
268: // Create the XPath object.
269: XPath xpath = new XPath(str, null, prefixResolver,
270: XPath.SELECT, null);
271:
272: // Execute the XPath, and have it return the result
273: // return xpath.execute(xpathSupport, contextNode, prefixResolver);
274: int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
275:
276: return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
277: }
278:
279: /**
280: * Evaluate XPath string to an XObject.
281: * XPath namespace prefixes are resolved from the namespaceNode.
282: * The implementation of this is a little slow, since it creates
283: * a number of objects each time it is called. This could be optimized
284: * to keep the same objects around, but then thread-safety issues would arise.
285: *
286: * @param contextNode The node to start searching from.
287: * @param str A valid XPath string.
288: * @param prefixResolver Will be called if the parser encounters namespace
289: * prefixes, to resolve the prefixes to URLs.
290: * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
291: * @see org.apache.xpath.objects.XObject
292: * @see org.apache.xpath.objects.XNull
293: * @see org.apache.xpath.objects.XBoolean
294: * @see org.apache.xpath.objects.XNumber
295: * @see org.apache.xpath.objects.XString
296: * @see org.apache.xpath.objects.XRTreeFrag
297: *
298: * @throws TransformerException
299: */
300: public XObject eval(Node contextNode, String str,
301: PrefixResolver prefixResolver) throws TransformerException {
302:
303: // Since we don't have a XML Parser involved here, install some default support
304: // for things like namespaces, etc.
305: // (Changed from: XPathContext xpathSupport = new XPathContext();
306: // because XPathContext is weak in a number of areas... perhaps
307: // XPathContext should be done away with.)
308: // Create the XPath object.
309: XPath xpath = new XPath(str, null, prefixResolver,
310: XPath.SELECT, null);
311:
312: // Execute the XPath, and have it return the result
313: XPathContext xpathSupport = new XPathContext();
314: int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
315:
316: return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
317: }
318: }
|