KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > xpath > XPathExpressionImpl


1 package net.sf.saxon.xpath;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.expr.Atomizer;
4 import net.sf.saxon.expr.Expression;
5 import net.sf.saxon.expr.ExpressionTool;
6 import net.sf.saxon.expr.XPathContextMajor;
7 import net.sf.saxon.functions.NumberFn;
8 import net.sf.saxon.instruct.SlotManager;
9 import net.sf.saxon.om.*;
10 import net.sf.saxon.pattern.NodeTest;
11 import net.sf.saxon.sort.FixedSortKeyDefinition;
12 import net.sf.saxon.sort.SortedIterator;
13 import net.sf.saxon.trans.XPathException;
14 import net.sf.saxon.value.*;
15 import org.xml.sax.InputSource JavaDoc;
16
17 import javax.xml.namespace.QName JavaDoc;
18 import javax.xml.transform.sax.SAXSource JavaDoc;
19 import javax.xml.xpath.XPathConstants JavaDoc;
20 import javax.xml.xpath.XPathExpression JavaDoc;
21 import javax.xml.xpath.XPathExpressionException JavaDoc;
22 import java.util.List JavaDoc;
23
24 /**
25   * <p>The JAXP XPathExpression interface represents a compiled XPath expression that can be repeatedly
26   * evaluated. This class is Saxon's implementation of that interface.</p>
27   *
28   * <p>The class also includes some methods retained from Saxon's original XPath API. When these methods
29   * are used, the object contains the context node and other state, so it is not thread-safe.</p>
30  *
31   * @author Michael H. Kay
32   */

33
34
35 public class XPathExpressionImpl implements XPathExpression JavaDoc {
36
37     private Configuration config;
38     private Expression expression;
39     private NodeInfo contextNode;
40     private SlotManager stackFrameMap;
41     private XPathExpressionImpl sortKey = null;
42
43     /**
44     * The constructor is protected, to ensure that instances can only be
45     * created using the createExpression() method of XPathEvaluator
46     */

47
48     protected XPathExpressionImpl(Expression exp, Configuration config) {
49         expression = exp;
50         this.config = config;
51     }
52
53     /**
54      * Define the number of slots needed for local variables within the expression.
55      * This method is for internal use only.
56      */

57
58     protected void setStackFrameMap(SlotManager map) {
59         stackFrameMap = map;
60     }
61
62     /**
63     * Define the sort order for the results of the expression. If this method is called, then
64     * the list returned by a subsequent call on the evaluate() method will first be sorted.
65     * @param sortKey an XPathExpression, which will be applied to each item in the sequence;
66     * the result of this expression determines the ordering of the list returned by the evaluate()
67     * method. The sortKey can be null, to clear a previous sort key.
68     */

69
70     public void setSortKey(XPathExpressionImpl sortKey) {
71         this.sortKey = sortKey;
72     }
73
74     /**
75     * Set the context node for evaluating the expression. If this method is not called,
76     * the context node will be the root of the document to which the prepared expression is
77     * bound.
78     */

79
80     public void setContextNode(NodeInfo node) {
81         if (node==null) {
82             throw new NullPointerException JavaDoc("Context node cannot be null");
83         }
84         if (node.getNamePool() != config.getNamePool()) {
85             throw new IllegalArgumentException JavaDoc("Supplied node uses the wrong NamePool");
86         }
87         contextNode = node;
88     }
89
90
91     /**
92     * Execute a prepared XPath expression, returning the results as a List. The context
93      * node must have been set previously using {@link #setContextNode(net.sf.saxon.om.NodeInfo)}.
94     * @return The results of the expression, as a List. The List represents the sequence
95     * of items returned by the expression. Each item in the list will either be an instance
96     * of net.sf.saxon.om.NodeInfo, representing a node, or a Java object representing an atomic value.
97     * For the types of Java object that may be returned, see {@link #evaluate(Object, javax.xml.namespace.QName)}
98      * with the second argument set to NODESET.
99     */

100
101     public List JavaDoc evaluate() throws XPathException {
102         XPathContextMajor context = new XPathContextMajor(contextNode, config);
103         context.openStackFrame(stackFrameMap);
104         SequenceIterator iter = expression.iterate(context);
105         SequenceExtent extent = new SequenceExtent(iter);
106         return (List JavaDoc)extent.convertToJava(Object JavaDoc.class, context);
107     }
108
109     /**
110     * Execute a prepared XPath expression, returning the first item in the result.
111     * This is useful where it is known that the expression will only return
112     * a singleton value (for example, a single node, or a boolean). The context node
113      * must be set previously using {@link #setContextNode(net.sf.saxon.om.NodeInfo)}.
114     * @return The first item in the sequence returned by the expression. If the expression
115     * returns an empty sequence, this method returns null. Otherwise, it returns the first
116     * item in the result sequence, represented as a Java object using the same mapping as for
117     * the evaluate() method
118     */

119
120     public Object JavaDoc evaluateSingle() throws XPathException {
121         XPathContextMajor context = new XPathContextMajor(contextNode, config);
122         context.openStackFrame(stackFrameMap);
123         SequenceIterator iterator = expression.iterate(context);
124         Item item = iterator.next();
125         if (item == null) {
126             return null;
127         } else {
128             return Value.convert(item);
129         }
130     }
131
132     /**
133     * Get a raw iterator over the results of the expression. This returns results without
134     * any conversion of the returned items to "native" Java classes. This method is intended
135     * for use by applications that need to process the results of the expression using
136     * internal Saxon interfaces.
137     */

138
139     public SequenceIterator rawIterator() throws XPathException {
140         XPathContextMajor context = new XPathContextMajor(contextNode, config);
141         context.openStackFrame(stackFrameMap);
142         SequenceIterator iterator = expression.iterate(context);
143         if (sortKey != null) {
144             Expression key = sortKey.expression;
145             if (key.getItemType() instanceof NodeTest) {
146                 key = new Atomizer(key, config);
147             }
148
149             FixedSortKeyDefinition[] sk = new FixedSortKeyDefinition[1];
150
151             sk[0] = new FixedSortKeyDefinition();
152             sk[0].setSortKey(key);
153             sk[0].bindComparer(context);
154
155             iterator = new SortedIterator(context, iterator, sk);
156         }
157         return iterator;
158     }
159
160     /**
161      * JAXP 1.3 evaluate() method
162      * @param node The context node. This must use a representation of nodes that this implementation understands.
163      * This may be a Saxon NodeInfo, or a node in one of the external object models supported, for example
164      * DOM, JDOM, or XOM, provided the support module for that object model is loaded.
165      * @param qName Indicates the type of result required. This must be one of the constants defined in
166      * the JAXP {@link XPathConstants} class.
167      * Saxon will attempt to convert the actual result of the expression to the required type using the
168      * XPath 1.0 conversion rules.
169      * @return the result of the evaluation, as a Java object of the appropriate type. Saxon interprets the
170      * rules as follows:
171      * <table>
172      * <thead><tr><td>QName</td><td>Return Value</td></thead>
173      * <tbody>
174      * <tr><td>BOOLEAN</td>
175      * <td>The effective boolean value of the actual result,
176      * as a Java Boolean object</td></tr>
177      * <tr><td>STRING</td>
178      * <td>The result of applying the string() function to the actual result,
179      * as a Java String object</td></tr>
180      * <tr><td>NUMBER</td>
181      * <td>The result of applying the number() function to the actual result,
182      * as a Java Double object</td></tr>
183      * <tr><td>NODE</td>
184      * <td>A single node, in the native data model supplied as input. If the
185      * expression returns more than one node, the first is returned. If
186      * the expression returns an empty sequence, null is returned. If the
187      * expression returns an atomic value, or if the first item in the
188      * result sequence is an atomic value, an exception is thrown.</td></tr>
189      * <tr><td>NODESET</td>
190      * <td>This is interpreted as allowing any sequence, of nodes or atomic values.
191      * The result is returned as a Java List object, unless it is empty, in which
192      * case null is returned. The contents of the list may be node objects (in the
193      * native data model supplied as input, or Java objects representing the XPath
194      * atomic values in the actual result: String for an xs:string, Double for a xs:double,
195      * Long for an xs:integer, and so on. (For safety, cast the values to a type
196      * such as xs:string within the XPath expression). Note that the result is never
197      * returned as a DOM NodeList.</td></tr></table>
198      *
199      * @throws XPathExpressionException if evaluation of the expression fails or if the
200      * result cannot be converted to the requested type.
201      */

202     public Object JavaDoc evaluate(Object JavaDoc node, QName JavaDoc qName) throws XPathExpressionException JavaDoc {
203         ExternalObjectModel model = null;
204         if (node instanceof NodeInfo) {
205             setContextNode((NodeInfo)node);
206         } else {
207             model = config.findExternalObjectModel(node);
208             if (model == null) {
209                 throw new XPathExpressionException JavaDoc(
210                         "Cannot locate an object model implementation for nodes of class "
211                         + node.getClass().getName());
212             }
213             DocumentInfo doc = model.wrapDocument(node, "", config);
214             NodeInfo startNode = model.wrapNode(doc, node);
215             setContextNode(startNode);
216         }
217         XPathContextMajor context = new XPathContextMajor(contextNode, config);
218         context.openStackFrame(stackFrameMap);
219         try {
220             if (qName.equals(XPathConstants.BOOLEAN)) {
221                 return Boolean.valueOf(ExpressionTool.effectiveBooleanValue(expression.iterate(context)));
222             } else if (qName.equals(XPathConstants.STRING)) {
223                 SequenceIterator iter = expression.iterate(context);
224
225                 Item first = iter.next();
226                 if (first == null) {
227                     return "";
228                 }
229                 return first.getStringValue();
230
231             } else if (qName.equals(XPathConstants.NUMBER)) {
232                 SequenceIterator iter = new Atomizer(expression, config).iterate(context);
233
234                 Item first = iter.next();
235                 if (first == null) {
236                     return new Double JavaDoc(Double.NaN);
237                 }
238                 if (first instanceof NumericValue) {
239                     return new Double JavaDoc(((NumericValue)first).getDoubleValue());
240                 } else {
241                     DoubleValue v = NumberFn.convert((AtomicValue)first);
242                     return new Double JavaDoc(v.getDoubleValue());
243                 }
244
245             } else if (qName.equals(XPathConstants.NODE)) {
246                 SequenceIterator iter = expression.iterate(context);
247                 Item first = iter.next();
248                 if (first instanceof VirtualNode) {
249                     return ((VirtualNode)first).getUnderlyingNode();
250                 }
251                 if (first == null || first instanceof NodeInfo) {
252                     return first;
253                 }
254                 throw new XPathExpressionException JavaDoc("Expression result is not a node");
255             } else if (qName.equals(XPathConstants.NODESET)) {
256                 SequenceIterator iter = expression.iterate(context);
257                 SequenceExtent extent = new SequenceExtent(iter);
258                 if (model != null) {
259                     Object JavaDoc result = model.convertToNodeList(extent);
260                     if (result != null) {
261                         return result;
262                     }
263                 }
264                 return extent.convertToJava(List JavaDoc.class, context);
265             } else {
266                 throw new IllegalArgumentException JavaDoc("Unknown type for expected result");
267             }
268         } catch (XPathException e) {
269             throw new XPathExpressionException JavaDoc(e);
270         }
271     }
272
273     /**
274      * Evaluate the expression to return a string value
275      * @param node the initial context node. This must be an instance of NodeInfo
276      * @return the results of the expression, converted to a String
277      * @throws XPathExpressionException if evaluation fails
278      */

279
280     public String JavaDoc evaluate(Object JavaDoc node) throws XPathExpressionException JavaDoc {
281         return (String JavaDoc)evaluate(node, XPathConstants.STRING);
282     }
283
284     /**
285      * Evaluate the XPath expression against an input source to obtain a result of a specified type
286      * @param inputSource The input source document against which the expression is evaluated.
287      * (Note that there is no caching. This will be parsed, and the parsed result will be discarded.)
288      * @param qName The type required, identified by a constant in {@link XPathConstants}
289      * @return the result of the evaluation, as a Java object of the appropriate type:
290      * see {@link #evaluate(Object, javax.xml.namespace.QName)}
291      * @throws XPathExpressionException
292      */

293     public Object JavaDoc evaluate(InputSource JavaDoc inputSource, QName JavaDoc qName) throws XPathExpressionException JavaDoc {
294         try {
295             NodeInfo doc = new XPathEvaluator().setSource(new SAXSource JavaDoc(inputSource));
296             return evaluate(doc, qName);
297         } catch (XPathException e) {
298             throw new XPathExpressionException JavaDoc(e);
299         }
300     }
301
302     /**
303      * Evaluate the XPath expression against an input source to obtain a string result
304      * @param inputSource The input source document against which the expression is evaluated.
305      * (Note that there is no caching. This will be parsed, and the parsed result will be discarded.)
306      * @return the result of the evaluation, converted to a String
307      * @throws XPathExpressionException
308      */

309
310     public String JavaDoc evaluate(InputSource JavaDoc inputSource) throws XPathExpressionException JavaDoc {
311         try {
312             NodeInfo doc = new XPathEvaluator().setSource(new SAXSource JavaDoc(inputSource));
313             return (String JavaDoc)evaluate(doc, XPathConstants.STRING);
314         } catch (XPathException e) {
315             throw new XPathExpressionException JavaDoc(e);
316         }
317     }
318
319     /**
320      * Low-level method to get the internal Saxon expression object. This exposes a wide range of
321      * internal methods that may be needed by specialized applications, and allows greater control
322      * over the dynamic context for evaluating the expression.
323      * @return the underlying Saxon expression object.
324      */

325
326     public Expression getInternalExpression() {
327         return expression;
328     }
329
330 }
331
332 //
333
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
334
// you may not use this file except in compliance with the License. You may obtain a copy of the
335
// License at http://www.mozilla.org/MPL/
336
//
337
// Software distributed under the License is distributed on an "AS IS" basis,
338
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
339
// See the License for the specific language governing rights and limitations under the License.
340
//
341
// The Original Code is: all this file.
342

343 // The Initial Developer of the Original Code is
344
// Michael H. Kay.
345
//
346
// Contributor(s):
347
//
348
Popular Tags