KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > query > XQueryExpression


1 package net.sf.saxon.query;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.Controller;
5 import net.sf.saxon.event.*;
6 import net.sf.saxon.expr.*;
7 import net.sf.saxon.instruct.*;
8 import net.sf.saxon.om.*;
9 import net.sf.saxon.pull.PullFromIterator;
10 import net.sf.saxon.pull.PullNamespaceReducer;
11 import net.sf.saxon.pull.PullProvider;
12 import net.sf.saxon.pull.PullPushCopier;
13 import net.sf.saxon.trace.TraceListener;
14 import net.sf.saxon.trans.DynamicError;
15 import net.sf.saxon.trans.XPathException;
16 import net.sf.saxon.trans.UncheckedXPathException;
17 import net.sf.saxon.type.Type;
18 import net.sf.saxon.value.Value;
19 import net.sf.saxon.value.DateTimeValue;
20
21 import javax.xml.transform.ErrorListener JavaDoc;
22 import javax.xml.transform.Result JavaDoc;
23 import javax.xml.transform.TransformerException JavaDoc;
24 import javax.xml.transform.stream.StreamResult JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.util.*;
27
28 /**
29  * XQueryExpression represents a compiled query. This object is immutable and thread-safe,
30  * the same compiled query may be executed many times in series or in parallel. The object
31  * can be created only by using the compileQuery method of the QueryProcessor class.
32  * <p/>
33  * <p>Various methods are provided for evaluating the query, with different options for
34  * delivery of the results.</p>
35  */

36 public class XQueryExpression implements Container {
37
38     private Expression expression;
39     private SlotManager stackFrameMap;
40     private Executable executable;
41     private StaticQueryContext staticContext;
42
43     // The documentInstruction is a document{...} wrapper around the expression as written by the user
44
private DocumentInstr documentInstruction;
45
46     /**
47      * The constructor is protected, to ensure that instances can only be
48      * created using the compileQuery() methods of StaticQueryContext
49      */

50
51     protected XQueryExpression(Expression exp, Executable exec, StaticQueryContext staticEnv, Configuration config)
52             throws XPathException {
53         stackFrameMap = staticEnv.getStackFrameMap();
54         executable = exec;
55         if (exp instanceof ComputedExpression) {
56             ((ComputedExpression)exp).setParentExpression(this);
57         }
58         ExpressionTool.makeParentReferences(exp);
59         try {
60             exp = exp.simplify(staticEnv);
61             exp = exp.typeCheck(staticEnv, Type.ITEM_TYPE);
62             exp = exp.optimize(exec.getConfiguration().getOptimizer(), staticEnv, Type.ITEM_TYPE);
63         } catch (XPathException err) {
64             try {
65                 config.getErrorListener().fatalError(err);
66             } catch (TransformerException JavaDoc e2) {
67                 //
68
}
69             throw err;
70         }
71         ExpressionTool.allocateSlots(exp, 0, staticEnv.getStackFrameMap());
72
73         expression = exp;
74         executable.setConfiguration(config);
75         executable.setDefaultCollationName(staticEnv.getDefaultCollationName());
76         executable.setCollationTable(staticEnv.getAllCollations());
77         staticContext = staticEnv;
78
79     }
80
81     /**
82      * Get the expression wrapped in this XQueryExpression object
83      *
84      * @return the underlying expression
85      */

86
87     public Expression getExpression() {
88         return expression;
89     }
90
91     /**
92      * Get the static context in which this expression was compiled. Note, this will be an internal
93      * copy of the original user-created StaticQueryContext object. The user-created object is not modified
94      * by Saxon, whereas the copy includes additional information found in the query prolog.
95      *
96      * @return the internal copy of the StaticQueryContext
97      */

98     public StaticQueryContext getStaticContext() {
99         return staticContext;
100     }
101
102     protected void setDocumentInstruction(DocumentInstr doc) {
103         documentInstruction = doc;
104         doc.setParentExpression(this);
105     }
106
107     /**
108      * Execute a the compiled Query, returning the results as a List.
109      *
110      * @param env Provides the dynamic query evaluation context
111      * @return The results of the expression, as a List. The List represents the sequence
112      * of items returned by the expression. Each item in the list will either be an
113      * object representing a node, or an object representing an atomic value.
114      * For the types of Java object that may be returned, see the description of the
115      * {@link net.sf.saxon.xpath.XPathEvaluator#evaluate evaluate} method
116      * of class XPathProcessor
117      */

118
119     public List evaluate(DynamicQueryContext env) throws XPathException {
120         SequenceIterator iterator = iterator(env);
121         ArrayList list = new ArrayList(100);
122         while (true) {
123             Item item = iterator.next();
124             if (item == null) {
125                 return list;
126             }
127             list.add(Value.convert(item));
128         }
129     }
130
131     /**
132      * Execute the compiled Query, returning the first item in the result.
133      * This is useful where it is known that the expression will only return
134      * a singleton value (for example, a single node, or a boolean).
135      *
136      * @param env Provides the dynamic query evaluation context
137      * @return The first item in the sequence returned by the expression. If the expression
138      * returns an empty sequence, this method returns null. Otherwise, it returns the first
139      * item in the result sequence, represented as a Java object using the same mapping as for
140      * the {@link XQueryExpression#evaluate evaluate} method
141      */

142
143     public Object JavaDoc evaluateSingle(DynamicQueryContext env) throws XPathException {
144         SequenceIterator iterator = iterator(env);
145         Item item = iterator.next();
146         if (item == null) {
147             return null;
148         }
149         return Value.convert(item);
150     }
151
152     /**
153      * Get an iterator over the results of the expression. This returns results without
154      * any conversion of the returned items to "native" Java classes. The iterator will
155      * deliver a sequence of Item objects, each item being either a NodeInfo (representing
156      * a node) or an AtomicValue (representing an atomic value).
157      * <p/>
158      * <p>To get the results of the query in the form of an XML document in which each
159      * item is wrapped by an element indicating its type, use:</p>
160      * <p/>
161      * <p><code>QueryResult.wrap(iterator(env))</code></p>
162      * <p/>
163      * <p>To serialize the results to a file, use the QueryResult.serialize() method.</p>
164      *
165      * @param env Provides the dynamic query evaluation context
166      * @return an iterator over the results of the query. The class SequenceIterator
167      * is modeled on the standard Java Iterator class, but has extra functionality
168      * and can throw exceptions when errors occur.
169      * @throws XPathException if a dynamic error occurs in evaluating the query. Some
170      * dynamic errors will not be reported by this method, but will only be reported
171      * when the individual items of the result are accessed using the returned iterator.
172      */

173
174     public SequenceIterator iterator(DynamicQueryContext env) throws XPathException {
175         Controller controller = newController();
176         initializeController(env, controller);
177
178         try {
179             NodeInfo node = env.getContextNode();
180
181             Bindery bindery = controller.getBindery();
182             //bindery.openStackFrame();
183
controller.defineGlobalParameters(bindery);
184             XPathContextMajor context = controller.newXPathContext();
185             //context.setLazyConstructionMode(true);
186

187             // In tracing/debugging mode, evaluate all the global variables first
188
if (controller.getConfiguration().getTraceListener() != null) {
189                 controller.preEvaluateGlobals(context);
190             }
191
192             context.openStackFrame(stackFrameMap);
193             if (node != null) {
194                 AxisIterator single = SingletonIterator.makeIterator(node);
195                 single.next();
196                 context.setCurrentIterator(single);
197                 controller.setPrincipalSourceDocument(node.getDocumentRoot());
198             }
199             SequenceIterator iterator = expression.iterate(context);
200             return new ErrorReportingIterator(iterator, controller.getErrorListener());
201         } catch (XPathException err) {
202             TransformerException JavaDoc terr = err;
203             while (terr.getException() instanceof TransformerException JavaDoc) {
204                 terr = (TransformerException JavaDoc)terr.getException();
205             }
206             try {
207                 controller.getErrorListener().fatalError(terr);
208             } catch (TransformerException JavaDoc e) {
209                 //
210
}
211             throw DynamicError.makeDynamicError(terr);
212         }
213     }
214
215     private void initializeController(DynamicQueryContext env, Controller controller) {
216         HashMap parameters = env.getParameters();
217         if (parameters != null) {
218             Iterator iter = parameters.keySet().iterator();
219             while (iter.hasNext()) {
220                 String JavaDoc paramName = (String JavaDoc)iter.next();
221                 Object JavaDoc paramValue = parameters.get(paramName);
222                 controller.setParameter(paramName, paramValue);
223             }
224         }
225
226         controller.setURIResolver(env.getURIResolver());
227         controller.setErrorListener(env.getErrorListener());
228         DateTimeValue currentDateTime = env.getCurrentDateTime();
229         if (currentDateTime != null) {
230             try {
231                 controller.setCurrentDateTime(currentDateTime);
232             } catch (XPathException e) {
233                 throw new AssertionError JavaDoc(e); // the value should already have been checked
234
}
235         }
236     }
237
238     /**
239      * Run the query, sending the results directly to a JAXP Result object. This way of executing
240      * the query is most efficient in the case of queries that produce a single document (or parentless
241      * element) as their output, because it avoids constructing the result tree in memory: instead,
242      * it is piped straight to the serializer.
243      *
244      * @param env the dynamic query context
245      * @param result the destination for the results of the query. The query is effectively wrapped
246      * in a document{} constructor, so that the items in the result are concatenated to form a single
247      * document; this is then written to the requested Result destination, which may be (for example)
248      * a DOMResult, a SAXResult, or a StreamResult
249      * @param outputProperties Supplies serialization properties, in JAXP format, if the result is to
250      * be serialized. This parameter can be defaulted to null.
251      * @throws XPathException if the query fails.
252      */

253
254     public void run(DynamicQueryContext env, Result JavaDoc result, Properties outputProperties) throws XPathException {
255
256         Controller controller = newController();
257         initializeController(env, controller);
258
259         // Validate the serialization properties requested
260

261         Properties baseProperties = controller.getOutputProperties();
262         if (outputProperties != null) {
263             Enumeration iter = outputProperties.propertyNames();
264             while (iter.hasMoreElements()) {
265                 String JavaDoc key = (String JavaDoc)iter.nextElement();
266                 String JavaDoc value = outputProperties.getProperty(key);
267                 try {
268                     SaxonOutputKeys.checkOutputProperty(key, value);
269                     baseProperties.setProperty(key, value);
270                 } catch (DynamicError dynamicError) {
271                     try {
272                         controller.getErrorListener().fatalError(dynamicError);
273                         throw dynamicError;
274                         // TODO: could be a warning, but currently all warnings are fatal.
275
//outputProperties.remove(key);
276
} catch (TransformerException JavaDoc err2) {
277                         throw DynamicError.makeDynamicError(err2);
278                     }
279                 }
280             }
281         }
282         if (baseProperties.getProperty("method") == null) {
283             // XQuery forces the default method to XML, unlike XSLT where it depends on the contents of the result tree
284
baseProperties.setProperty("method", "xml");
285         }
286
287         NodeInfo node = env.getContextNode();
288
289         Bindery bindery = controller.getBindery();
290         controller.defineGlobalParameters(bindery);
291
292         XPathContextMajor context = controller.newXPathContext();
293
294         // In tracing/debugging mode, evaluate all the global variables first
295
TraceListener tracer = controller.getConfiguration().getTraceListener();
296         if (tracer != null) {
297             controller.preEvaluateGlobals(context);
298             tracer.open();
299         }
300
301         context.openStackFrame(stackFrameMap);
302         if (node != null) {
303             AxisIterator single = SingletonIterator.makeIterator(node);
304             context.setCurrentIterator(single);
305             single.next();
306             controller.setPrincipalSourceDocument(node.getDocumentRoot());
307         }
308
309         boolean mustClose = (result instanceof StreamResult JavaDoc &&
310                 ((StreamResult JavaDoc)result).getOutputStream() == null);
311         context.changeOutputDestination(baseProperties, result, true, Validation.PRESERVE, null);
312
313         // Run the query
314
try {
315             expression.process(context);
316             // This saves building a temporary tree for queries that construct a single element,
317
// it also makes line number information available to a query debugger. But it's rather ad-hoc.
318
// if (expression instanceof ElementCreator) {
319
// expression.process(context);
320
// } else {
321
// documentInstruction.process(context);
322
// }
323
} catch (XPathException err) {
324             try {
325                 controller.getErrorListener().fatalError(err);
326             } catch (TransformerException JavaDoc e) {
327                 //
328
}
329             throw err;
330         }
331
332         if (tracer != null) {
333             tracer.close();
334         }
335
336         context.getReceiver().close();
337         if (mustClose) {
338             OutputStream JavaDoc os = ((StreamResult JavaDoc)result).getOutputStream();
339             if (os != null) {
340                 try {
341                     os.close();
342                 } catch (java.io.IOException JavaDoc err) {
343                     throw new DynamicError(err);
344                 }
345             }
346         }
347     }
348
349     /**
350      * Run the query in pull mode.
351      * <p/>
352      * For maximum effect this method should be used when lazyConstructionMode has been set in the Configuration.
353      *
354      * @see Configuration#setLazyConstructionMode(boolean)
355      */

356
357     public void pull(DynamicQueryContext dynamicEnv, Result JavaDoc destination, Properties outputProperties) throws XPathException {
358         try {
359             SequenceIterator iter = iterator(dynamicEnv);
360             PullProvider pull = new PullFromIterator(iter);
361             pull = new PullNamespaceReducer(pull);
362             pull.setPipelineConfiguration(executable.getConfiguration().makePipelineConfiguration());
363
364             Receiver receiver =
365                     ResultWrapper.getReceiver(destination,
366                             pull.getPipelineConfiguration(),
367                             outputProperties);
368             //pull = new PullTracer(pull);
369
if ("yes".equals(outputProperties.getProperty(SaxonOutputKeys.WRAP))) {
370                 NamespaceReducer reducer = new NamespaceReducer();
371                 reducer.setPipelineConfiguration(pull.getPipelineConfiguration());
372                 reducer.setUnderlyingReceiver(receiver);
373                 receiver = new SequenceWrapper(reducer);
374             }
375             // TODO: following test is iffy. Need a better test as to when a TreeReceiver is needed
376
if (!(receiver instanceof SequenceWrapper)) {
377                 receiver = new TreeReceiver(receiver);
378             }
379             new PullPushCopier(pull, receiver).copy();
380         } catch (UncheckedXPathException e) {
381             throw e.getXPathException();
382         }
383     }
384
385     /**
386      * Get a controller that can be used to execute functions in this compiled query.
387      * Functions in the query module can be found using {@link StaticQueryContext#getUserDefinedFunction}.
388      * They can then be called directly from the Java application using {@link net.sf.saxon.instruct.UserFunction#call}
389      * The same Controller can be used for a series of function calls.
390      * <p/>
391      * Note, this method is poorly named. It creates a new Controller each time it is called: which is useful
392      * when a query is to be executed repeatedly.
393      */

394
395     public Controller newController() {
396         Controller controller = new Controller(executable.getConfiguration(), executable);
397         executable.initialiseBindery(controller.getBindery());
398         return controller;
399     }
400
401     /**
402      * Deprecated synonym for {@link #newController}
403      * @deprecated since 8.5.1 - use newController()
404      */

405
406     public Controller getController() {
407         return newController();
408     }
409
410     /**
411      * Diagnostic method: display a representation of the compiled query on the
412      * System.err output stream.
413      */

414
415     public void explain(NamePool pool) {
416         System.err.println("============ Compiled Expression ============");
417         expression.display(10, pool, System.err);
418         System.err.println("=============================================");
419     }
420
421     /**
422      * Get the Executable (representing a complete stylesheet or query) of which this Container forms part
423      */

424
425     public Executable getExecutable() {
426         return executable;
427     }
428
429     /**
430      * Get the LocationProvider allowing location identifiers to be resolved.
431      */

432
433     public LocationProvider getLocationProvider() {
434         return executable.getLocationMap();
435     }
436
437     /**
438      * Return the public identifier for the current document event.
439      * <p/>
440      * <p>The return value is the public identifier of the document
441      * entity or of the external parsed entity in which the markup that
442      * triggered the event appears.</p>
443      *
444      * @return A string containing the public identifier, or
445      * null if none is available.
446      * @see #getSystemId
447      */

448     public String JavaDoc getPublicId() {
449         return null;
450     }
451
452     /**
453      * Return the system identifier for the current document event.
454      * <p/>
455      * <p>The return value is the system identifier of the document
456      * entity or of the external parsed entity in which the markup that
457      * triggered the event appears.</p>
458      * <p/>
459      * <p>If the system identifier is a URL, the parser must resolve it
460      * fully before passing it to the application.</p>
461      *
462      * @return A string containing the system identifier, or null
463      * if none is available.
464      * @see #getPublicId
465      */

466     public String JavaDoc getSystemId() {
467         return null;
468     }
469
470     /**
471      * Return the line number where the current document event ends.
472      * <p/>
473      * <p><strong>Warning:</strong> The return value from the method
474      * is intended only as an approximation for the sake of error
475      * reporting; it is not intended to provide sufficient information
476      * to edit the character content of the original XML document.</p>
477      * <p/>
478      * <p>The return value is an approximation of the line number
479      * in the document entity or external parsed entity where the
480      * markup that triggered the event appears.</p>
481      *
482      * @return The line number, or -1 if none is available.
483      * @see #getColumnNumber
484      */

485     public int getLineNumber() {
486         return -1;
487     }
488
489     /**
490      * Return the character position where the current document event ends.
491      * <p/>
492      * <p><strong>Warning:</strong> The return value from the method
493      * is intended only as an approximation for the sake of error
494      * reporting; it is not intended to provide sufficient information
495      * to edit the character content of the original XML document.</p>
496      * <p/>
497      * <p>The return value is an approximation of the column number
498      * in the document entity or external parsed entity where the
499      * markup that triggered the event appears.</p>
500      *
501      * @return The column number, or -1 if none is available.
502      * @see #getLineNumber
503      */

504     public int getColumnNumber() {
505         return -1;
506     }
507
508     /**
509      * ErrorReportingIterator is an iterator that wraps a base iterator and reports
510      * any exceptions that are raised to the ErrorListener
511      */

512
513     private class ErrorReportingIterator implements SequenceIterator {
514         private SequenceIterator base;
515         private ErrorListener JavaDoc listener;
516
517         public ErrorReportingIterator(SequenceIterator base, ErrorListener JavaDoc listener) {
518             this.base = base;
519             this.listener = listener;
520         }
521
522         public Item next() throws XPathException {
523             try {
524                 return base.next();
525             } catch (XPathException e1) {
526                 if (e1.getLocator() == null) {
527                     e1.setLocator(ExpressionTool.getLocator(expression));
528                 }
529                 try {
530                     listener.fatalError(e1);
531                 } catch (TransformerException JavaDoc e2) {
532                 }
533                 throw e1;
534             }
535         }
536
537         public Item current() {
538             return base.current();
539         }
540
541         public int position() {
542             return base.position();
543         }
544
545         public SequenceIterator getAnother() throws XPathException {
546             return new ErrorReportingIterator(base.getAnother(), listener);
547         }
548
549         /**
550          * Get properties of this iterator, as a bit-significant integer.
551          *
552          * @return the properties of this iterator. This will be some combination of
553          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
554          * and {@link LOOKAHEAD}. It is always
555          * acceptable to return the value zero, indicating that there are no known special properties.
556          * It is acceptable for the properties of the iterator to change depending on its state.
557          */

558
559         public int getProperties() {
560             return 0;
561         }
562     }
563
564 }
565
566 //
567
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
568
// you may not use this file except in compliance with the License. You may obtain a copy of the
569
// License at http://www.mozilla.org/MPL/
570
//
571
// Software distributed under the License is distributed on an "AS IS" basis,
572
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
573
// See the License for the specific language governing rights and limitations under the License.
574
//
575
// The Original Code is: all this file.
576
//
577
// The Initial Developer of the Original Code is Michael H. Kay
578
//
579
// Contributor(s):
580
//
581

582
Popular Tags