001: package net.sf.saxon.xpath;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.event.Builder;
005: import net.sf.saxon.event.Stripper;
006: import net.sf.saxon.expr.Expression;
007: import net.sf.saxon.expr.ExpressionTool;
008: import net.sf.saxon.expr.XPathContextMajor;
009: import net.sf.saxon.instruct.SlotManager;
010: import net.sf.saxon.om.AllElementStripper;
011: import net.sf.saxon.om.Item;
012: import net.sf.saxon.om.NodeInfo;
013: import net.sf.saxon.om.SequenceIterator;
014: import net.sf.saxon.type.SchemaException;
015: import net.sf.saxon.type.Type;
016: import net.sf.saxon.value.Value;
017: import org.xml.sax.InputSource;
018:
019: import javax.xml.namespace.NamespaceContext;
020: import javax.xml.namespace.QName;
021: import javax.xml.transform.Source;
022: import javax.xml.transform.stream.StreamSource;
023: import javax.xml.xpath.*;
024: import java.util.ArrayList;
025: import java.util.List;
026: import java.io.File;
027:
028: /**
029: * <p>XPathEvaluator provides a simple API for standalone XPath processing (that is,
030: * executing XPath expressions in the absence of an XSLT stylesheet). It is an implementation
031: * of the JAXP 1.3 XPath interface, with additional methods provided (a) for backwards
032: * compatibility (b) to give extra control over the XPath evaluation, and (c) to support
033: * XPath 2.0.</p>
034: *
035: * @author Michael H. Kay
036: */
037:
038: public class XPathEvaluator implements XPath {
039:
040: private Configuration config;
041: private NodeInfo contextNode = null;
042: private StandaloneContext staticContext;
043: private boolean stripSpace = false;
044:
045: /**
046: * Default constructor. Creates an XPathEvaluator with a default configuration and name pool.
047: * The default Configuration is not schema-aware.
048: */
049:
050: public XPathEvaluator() {
051: this (new Configuration());
052: }
053:
054: /**
055: * Construct an XPathEvaluator with a specified configuration.
056: * @param config the configuration to be used. If schema-aware XPath expressions are to be used,
057: * this must be a SchemaAwareConfiguration.
058: */
059: public XPathEvaluator(Configuration config) {
060: this .config = config;
061: staticContext = new StandaloneContext(config);
062: }
063:
064: /**
065: * Construct an XPathEvaluator to process a particular source document. This is equivalent to
066: * using the default constructor and immediately calling setSource().
067: * @param source The source document (or a specific node within it).
068: */
069:
070: public XPathEvaluator(Source source)
071: throws net.sf.saxon.trans.XPathException {
072: super ();
073: if (source instanceof NodeInfo) {
074: config = ((NodeInfo) source).getDocumentRoot()
075: .getConfiguration();
076: } else {
077: config = new Configuration();
078: }
079: staticContext = new StandaloneContext(config);
080: setSource(source);
081: }
082:
083: /**
084: * Indicate whether all whitespace text nodes in the source document are to be
085: * removed. This option has no effect unless it is called before the call on setSource(),
086: * and unless the Source supplied to setSource() is a SAXSource or StreamSource.
087: * @param strip True if all whitespace text nodes are to be stripped from the source document,
088: * false otherwise. The default if the method is not called is false.
089: */
090:
091: public void setStripSpace(boolean strip) {
092: stripSpace = strip;
093: }
094:
095: /**
096: * Supply the document against which XPath expressions are to be executed. This
097: * method must be called before preparing or executing an XPath expression.
098: * Setting a new source document clears all the namespaces that have been declared.
099: * @param source Any javax.xml.transform.Source object representing the document against
100: * which XPath expressions will be executed. Note that a Saxon {@link net.sf.saxon.om.DocumentInfo DocumentInfo}
101: * (indeed any {@link net.sf.saxon.om.NodeInfo NodeInfo})
102: * can be used as a Source. To use a third-party DOM Document as a source, create an instance of
103: * {@link javax.xml.transform.dom.DOMSource DOMSource} to wrap it.
104: * <p>The Source object supplied also determines the initial setting
105: * of the context item. In most cases the context node will be the root of the supplied document;
106: * however, if a NodeInfo or DOMSource is supplied it can be any node in the document. </p>
107: * @return the NodeInfo of the start node in the resulting document object.
108: */
109:
110: public NodeInfo setSource(Source source)
111: throws net.sf.saxon.trans.XPathException {
112: Stripper stripper = null;
113: if (stripSpace) {
114: stripper = AllElementStripper.getInstance();
115: }
116: contextNode = Builder.build(source, stripper, config);
117: return contextNode;
118: }
119:
120: /**
121: * Set the static context for compiling XPath expressions. This provides control over the
122: * environment in which the expression is compiled, for example it allows namespace prefixes to
123: * be declared, variables to be bound and functions to be defined. For most purposes, the static
124: * context can be defined by providing and tailoring an instance of the StandaloneContext class.
125: * Until this method is called, a default static context is used, in which no namespaces are defined
126: * other than the standard ones (xml, xslt, and saxon), and no variables or functions (other than the
127: * core XPath functions) are available.
128: */
129:
130: public void setStaticContext(StandaloneContext context) {
131: staticContext = context;
132: }
133:
134: /**
135: * Get the current static context
136: */
137:
138: public StandaloneContext getStaticContext() {
139: return staticContext;
140: }
141:
142: /**
143: * Prepare an XPath expression for subsequent evaluation.
144: * @param expression The XPath expression to be evaluated, supplied as a string.
145: * @return an XPathExpression object representing the prepared expression
146: * @throws net.sf.saxon.trans.XPathException if the syntax of the expression is wrong, or if it references namespaces,
147: * variables, or functions that have not been declared.
148: */
149:
150: public XPathExpressionImpl createExpression(String expression)
151: throws net.sf.saxon.trans.XPathException {
152: Expression exp = ExpressionTool.make(expression, staticContext,
153: 0, -1, 1);
154: exp = exp.typeCheck(staticContext, Type.ITEM_TYPE);
155: SlotManager map = staticContext.getConfiguration()
156: .makeSlotManager();
157: ExpressionTool.allocateSlots(exp, 0, map);
158: XPathExpressionImpl xpe = new XPathExpressionImpl(exp, config);
159: xpe.setStackFrameMap(map);
160: if (contextNode != null) {
161: xpe.setContextNode(contextNode);
162: }
163: return xpe;
164: }
165:
166: /**
167: * Set the context node. This provides the context node for any expressions executed after this
168: * method is called, including expressions that were prepared before it was called.
169: * @param node The node to be used as the context node. This must
170: * be a node within the context document (the document supplied using the setSource() method).
171: * @throws NullPointerException if the argument is null
172: * @throws IllegalArgumentException if the supplied node is not a node in the context document
173: */
174:
175: public void setContextNode(NodeInfo node) {
176: if (node == null) {
177: throw new NullPointerException(
178: "Context node cannot be null");
179: }
180: contextNode = node;
181: }
182:
183: /**
184: * Prepare and execute an XPath expression, supplied as a string, and returning the results
185: * as a List.
186: * @param expression The XPath expression to be evaluated, supplied as a string.
187: * @return The results of the expression, as a List. The List represents the sequence
188: * of items returned by the expression. Each item in the list will either be an object
189: * representing a node, or a Java object representing an atomic value.
190: * The types of Java object that may be included in the list, and the XML Schema data types that they
191: * correspond to, are as follows:<p>
192: * <ul>
193: * <li>Boolean (xs:boolean)</li>
194: * <li>String (xs:string)</li>
195: * <li>BigDecimal (xs:decimal)</li>
196: * <li>Long (xs:integer and its derived types)</li>
197: * <li>Double (xs:double)</li>
198: * <li>Float (xs:float)</li>
199: * <li>Date (xs:date, xs:dateTime)</li>
200: * </ul>
201: */
202:
203: public List evaluate(String expression)
204: throws net.sf.saxon.trans.XPathException {
205: Expression exp = ExpressionTool.make(expression, staticContext,
206: 0, -1, 1);
207: exp = exp.typeCheck(staticContext, Type.ITEM_TYPE);
208: SlotManager map = staticContext.getConfiguration()
209: .makeSlotManager();
210: ExpressionTool.allocateSlots(exp, 0, map);
211: XPathContextMajor context = new XPathContextMajor(contextNode,
212: staticContext.getConfiguration());
213: context.openStackFrame(map);
214: SequenceIterator iterator = exp.iterate(context);
215: ArrayList list = new ArrayList(20);
216: while (true) {
217: Item item = iterator.next();
218: if (item == null) {
219: return list;
220: }
221: list.add(Value.convert(item));
222: }
223: }
224:
225: public void reset() {
226: config = null;
227: contextNode = null;
228: stripSpace = false;
229: staticContext = new StandaloneContext(config);
230: }
231:
232: /**
233: * Set XPath 1.0 compatibility mode on or off (by default, it is false)
234: * @param compatible true if XPath 1.0 compatibility mode is to be set to true, false
235: * if it is to be set to false.
236: */
237:
238: public void setBackwardsCompatible(boolean compatible) {
239: staticContext.setBackwardsCompatibilityMode(true);
240: }
241:
242: /**
243: * Get the value of XPath 1.0 compatibility mode
244: * @return true if XPath 1.0 compatibility mode is set
245: */
246:
247: public boolean isBackwardsCompatible() {
248: return staticContext.isInBackwardsCompatibleMode();
249: }
250:
251: /**
252: * Set the resolver for XPath variables
253: * @param xPathVariableResolver
254: */
255:
256: public void setXPathVariableResolver(
257: XPathVariableResolver xPathVariableResolver) {
258: staticContext.setXPathVariableResolver(xPathVariableResolver);
259: }
260:
261: /**
262: * Get the resolver for XPath variables
263: * @return the resolver, if one has been set
264: */
265: public XPathVariableResolver getXPathVariableResolver() {
266: return staticContext.getXPathVariableResolver();
267: }
268:
269: /**
270: * Set the resolver for XPath functions
271: * @param xPathFunctionResolver
272: */
273:
274: public void setXPathFunctionResolver(
275: XPathFunctionResolver xPathFunctionResolver) {
276: staticContext.setXPathFunctionResolver(xPathFunctionResolver);
277: }
278:
279: /**
280: * Get the resolver for XPath functions
281: * @return the resolver, if one has been set
282: */
283:
284: public XPathFunctionResolver getXPathFunctionResolver() {
285: return staticContext.getXPathFunctionResolver();
286: }
287:
288: /**
289: * Set the namespace context to be used. This supplements any namespaces declared directly
290: * using declareNamespace on the staticContext object
291: * @param namespaceContext The namespace context
292: */
293:
294: public void setNamespaceContext(NamespaceContext namespaceContext) {
295: staticContext.setNamespaceContext(namespaceContext);
296: }
297:
298: /**
299: * Get the namespace context, if one has been set using {@link #setNamespaceContext}
300: * @return the namespace context if set, or null otherwise
301: */
302:
303: public NamespaceContext getNamespaceContext() {
304: return staticContext.getNamespaceContext();
305: }
306:
307: /**
308: * Import a schema. This is possible only if the schema-aware version of Saxon is being used,
309: * and if the Configuration is a SchemaAwareConfiguration. Having imported a schema, the types
310: * defined in that schema become part of the static context.
311: * @param source A Source object identifying the schema document to be loaded
312: * @throws net.sf.saxon.type.SchemaException if the schema contained in this document is invalid
313: * @throws UnsupportedOperationException if the configuration is not schema-aware
314: */
315:
316: public void importSchema(Source source) throws SchemaException {
317: staticContext.importSchema(source);
318: }
319:
320: /**
321: * Compile an XPath 2.0 expression
322: * @param expr the XPath 2.0 expression to be compiled, as a string
323: * @return the compiled form of the expression
324: * @throws XPathExpressionException if there are any static errors in the expression.
325: * Note that references to undeclared variables are not treated as static errors, because
326: * variables are not pre-declared using this API.
327: */
328: public XPathExpression compile(String expr)
329: throws XPathExpressionException {
330: try {
331: return createExpression(expr);
332: } catch (net.sf.saxon.trans.XPathException e) {
333: throw new XPathExpressionException(e);
334: }
335: }
336:
337: /**
338: * Single-shot method to compile and execute an XPath 2.0 expression.
339: * @param expr The XPath 2.0 expression to be compiled and executed
340: * @param node The context node for evaluation of the expression
341: * @param qName The type of result required. For details, see
342: * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
343: * @return the result of evaluating the expression, returned as described in
344: * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
345: * @throws XPathExpressionException if any static or dynamic error occurs
346: * in evaluating the expression.
347: */
348:
349: public Object evaluate(String expr, Object node, QName qName)
350: throws XPathExpressionException {
351: XPathExpression exp = compile(expr);
352: return exp.evaluate(node, qName);
353: }
354:
355: /**
356: * Single-shot method to compile an execute an XPath 2.0 expression, returning
357: * the result as a string.
358: * @param expr The XPath 2.0 expression to be compiled and executed
359: * @param node The context node for evaluation of the expression
360: * @return the result of evaluating the expression, converted to a string as if
361: * by calling the XPath string() function
362: * @throws XPathExpressionException if any static or dynamic error occurs
363: * in evaluating the expression.
364: */
365:
366: public String evaluate(String expr, Object node)
367: throws XPathExpressionException {
368: XPathExpression exp = compile(expr);
369: return exp.evaluate(node);
370: }
371:
372: /**
373: * Single-shot method to parse and build a source document, and
374: * compile an execute an XPath 2.0 expression, against that document
375: * @param expr The XPath 2.0 expression to be compiled and executed
376: * @param inputSource The source document: this will be parsed and built into a tree,
377: * and the XPath expression will be executed with the root node of the tree as the
378: * context node
379: * @param qName The type of result required. For details, see
380: * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
381: * @return the result of evaluating the expression, returned as described in
382: * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
383: * @throws XPathExpressionException if any static or dynamic error occurs
384: * in evaluating the expression.
385: */
386:
387: public Object evaluate(String expr, InputSource inputSource,
388: QName qName) throws XPathExpressionException {
389: XPathExpression exp = compile(expr);
390: return exp.evaluate(inputSource, qName);
391: }
392:
393: /**
394: * Single-shot method to parse and build a source document, and
395: * compile an execute an XPath 2.0 expression, against that document,
396: * returning the result as a string
397: * @param expr The XPath 2.0 expression to be compiled and executed
398: * @param inputSource The source document: this will be parsed and built into a tree,
399: * and the XPath expression will be executed with the root node of the tree as the
400: * context node
401: * @return the result of evaluating the expression, converted to a string as
402: * if by calling the XPath string() function
403: * @throws XPathExpressionException if any static or dynamic error occurs
404: * in evaluating the expression.
405: */
406:
407: public String evaluate(String expr, InputSource inputSource)
408: throws XPathExpressionException {
409: XPathExpression exp = compile(expr);
410: return exp.evaluate(inputSource);
411: }
412:
413: /**
414: * Prepare and execute an XPath expression, supplied as a string, and returning the first
415: * item in the result. This is useful where it is known that the expression will only return
416: * a singleton value (for example, a single node, or a boolean).
417: * @param expression The XPath expression to be evaluated, supplied as a string.
418: * @return The first item in the sequence returned by the expression. If the expression
419: * returns an empty sequence, this method returns null. Otherwise, it returns the first
420: * item in the result sequence, represented as a Java object using the same mapping as for
421: * the evaluate() method
422: */
423:
424: public Object evaluateSingle(String expression)
425: throws net.sf.saxon.trans.XPathException {
426: Expression exp = ExpressionTool.make(expression, staticContext,
427: 0, -1, 1);
428: exp = exp.typeCheck(staticContext, Type.ITEM_TYPE);
429: SlotManager map = staticContext.getConfiguration()
430: .makeSlotManager();
431: ExpressionTool.allocateSlots(exp, 0, map);
432: XPathContextMajor context = new XPathContextMajor(contextNode,
433: staticContext.getConfiguration());
434: context.openStackFrame(map);
435: SequenceIterator iterator = exp.iterate(context);
436: Item item = iterator.next();
437: if (item == null) {
438: return null;
439: } else {
440: return Value.convert(item);
441: }
442: }
443:
444: /**
445: * A simple command-line interface for the XPathEvaluator (not documented).
446: * First parameter is the filename containing the source document, second
447: * parameter is the XPath expression.
448: */
449:
450: public static void main(String[] args) throws Exception {
451: if (args.length != 2) {
452: System.err
453: .println("format: java XPathEvaluator source.xml \"expression\"");
454: return;
455: }
456: XPathEvaluator xpe = new XPathEvaluator(new StreamSource(
457: new File(args[0])));
458: List results = xpe.evaluate(args[1]);
459: for (int i = 0; i < results.size(); i++) {
460: Object o = results.get(i);
461: System.err.println(o);
462: }
463: // Configuration config = new Configuration();
464: // config.setLineNumbering(true);
465: // XPathEvaluator xpath = new XPathEvaluator(config);
466: // NodeInfo doca = xpath.setSource(new StreamSource((new File(args[0]))));
467: // List list = xpath.evaluate("//*");
468: // for (int i=0; i<list.size(); i++) {
469: // NodeInfo element = (NodeInfo)list.get(i);
470: // System.out.println("Element " + element.getDisplayName() + " at line " + element.getLineNumber());
471: // }
472:
473: }
474:
475: }
476:
477: //
478: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
479: // you may not use this file except in compliance with the License. You may obtain a copy of the
480: // License at http://www.mozilla.org/MPL/
481: //
482: // Software distributed under the License is distributed on an "AS IS" basis,
483: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
484: // See the License for the specific language governing rights and limitations under the License.
485: //
486: // The Original Code is: all this file.
487: //
488: // The Initial Developer of the Original Code is Michael H. Kay
489: //
490: // Contributor(s):
491: //
|