001: package net.sf.saxon.query;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Controller;
005: import net.sf.saxon.event.*;
006: import net.sf.saxon.expr.*;
007: import net.sf.saxon.instruct.Bindery;
008: import net.sf.saxon.instruct.DocumentInstr;
009: import net.sf.saxon.instruct.Executable;
010: import net.sf.saxon.instruct.SlotManager;
011: import net.sf.saxon.om.*;
012: import net.sf.saxon.pull.PullFromIterator;
013: import net.sf.saxon.pull.PullNamespaceReducer;
014: import net.sf.saxon.pull.PullProvider;
015: import net.sf.saxon.pull.PullPushCopier;
016: import net.sf.saxon.trace.TraceListener;
017: import net.sf.saxon.trans.DynamicError;
018: import net.sf.saxon.trans.UncheckedXPathException;
019: import net.sf.saxon.trans.XPathException;
020: import net.sf.saxon.type.Type;
021: import net.sf.saxon.value.DateTimeValue;
022: import net.sf.saxon.value.Value;
023:
024: import javax.xml.transform.ErrorListener;
025: import javax.xml.transform.Result;
026: import javax.xml.transform.TransformerException;
027: import javax.xml.transform.stream.StreamResult;
028: import java.io.OutputStream;
029: import java.util.*;
030:
031: /**
032: * XQueryExpression represents a compiled query. This object is immutable and thread-safe,
033: * the same compiled query may be executed many times in series or in parallel. The object
034: * can be created only by using the compileQuery method of the QueryProcessor class.
035: * <p/>
036: * <p>Various methods are provided for evaluating the query, with different options for
037: * delivery of the results.</p>
038: */
039: public class XQueryExpression implements Container {
040:
041: private Expression expression;
042: private SlotManager stackFrameMap;
043: private Executable executable;
044: private StaticQueryContext staticContext;
045:
046: // The documentInstruction is a document{...} wrapper around the expression as written by the user
047: private DocumentInstr documentInstruction;
048:
049: /**
050: * The constructor is protected, to ensure that instances can only be
051: * created using the compileQuery() methods of StaticQueryContext
052: */
053:
054: protected XQueryExpression(Expression exp, Executable exec,
055: StaticQueryContext staticEnv, Configuration config)
056: throws XPathException {
057: stackFrameMap = staticEnv.getStackFrameMap();
058: executable = exec;
059: if (exp instanceof ComputedExpression) {
060: ((ComputedExpression) exp).setParentExpression(this );
061: }
062: ExpressionTool.makeParentReferences(exp);
063: try {
064: exp = exp.simplify(staticEnv);
065: exp = exp.typeCheck(staticEnv, Type.ITEM_TYPE);
066: exp = exp.optimize(exec.getConfiguration().getOptimizer(),
067: staticEnv, Type.ITEM_TYPE);
068: } catch (XPathException err) {
069: config.reportFatalError(err);
070: throw err;
071: }
072: ExpressionTool.allocateSlots(exp, 0, staticEnv
073: .getStackFrameMap());
074:
075: expression = exp;
076: executable.setConfiguration(config);
077: executable.setDefaultCollationName(staticEnv
078: .getDefaultCollationName());
079: executable.setCollationTable(staticEnv.getAllCollations());
080: staticContext = staticEnv;
081:
082: }
083:
084: /**
085: * Get the expression wrapped in this XQueryExpression object
086: *
087: * @return the underlying expression
088: */
089:
090: public Expression getExpression() {
091: return expression;
092: }
093:
094: /**
095: * Get the static context in which this expression was compiled. Note, this will be an internal
096: * copy of the original user-created StaticQueryContext object. The user-created object is not modified
097: * by Saxon, whereas the copy includes additional information found in the query prolog.
098: *
099: * @return the internal copy of the StaticQueryContext
100: */
101: public StaticQueryContext getStaticContext() {
102: return staticContext;
103: }
104:
105: protected void setDocumentInstruction(DocumentInstr doc) {
106: documentInstruction = doc;
107: doc.setParentExpression(this );
108: }
109:
110: /**
111: * Execute a the compiled Query, returning the results as a List.
112: *
113: * @param env Provides the dynamic query evaluation context
114: * @return The results of the expression, as a List. The List represents the sequence
115: * of items returned by the expression. Each item in the list will either be an
116: * object representing a node, or an object representing an atomic value.
117: * For the types of Java object that may be returned, see the description of the
118: * {@link net.sf.saxon.xpath.XPathEvaluator#evaluate evaluate} method
119: * of class XPathProcessor
120: */
121:
122: public List evaluate(DynamicQueryContext env) throws XPathException {
123: SequenceIterator iterator = iterator(env);
124: ArrayList list = new ArrayList(100);
125: while (true) {
126: Item item = iterator.next();
127: if (item == null) {
128: return list;
129: }
130: list.add(Value.convert(item));
131: }
132: }
133:
134: /**
135: * Execute the compiled Query, returning the first item in the result.
136: * This is useful where it is known that the expression will only return
137: * a singleton value (for example, a single node, or a boolean).
138: *
139: * @param env Provides the dynamic query evaluation context
140: * @return The first item in the sequence returned by the expression. If the expression
141: * returns an empty sequence, this method returns null. Otherwise, it returns the first
142: * item in the result sequence, represented as a Java object using the same mapping as for
143: * the {@link XQueryExpression#evaluate evaluate} method
144: */
145:
146: public Object evaluateSingle(DynamicQueryContext env)
147: throws XPathException {
148: SequenceIterator iterator = iterator(env);
149: Item item = iterator.next();
150: if (item == null) {
151: return null;
152: }
153: return Value.convert(item);
154: }
155:
156: /**
157: * Get an iterator over the results of the expression. This returns results without
158: * any conversion of the returned items to "native" Java classes. The iterator will
159: * deliver a sequence of Item objects, each item being either a NodeInfo (representing
160: * a node) or an AtomicValue (representing an atomic value).
161: * <p/>
162: * <p>To get the results of the query in the form of an XML document in which each
163: * item is wrapped by an element indicating its type, use:</p>
164: * <p/>
165: * <p><code>QueryResult.wrap(iterator(env))</code></p>
166: * <p/>
167: * <p>To serialize the results to a file, use the QueryResult.serialize() method.</p>
168: *
169: * @param env Provides the dynamic query evaluation context
170: * @return an iterator over the results of the query. The class SequenceIterator
171: * is modeled on the standard Java Iterator class, but has extra functionality
172: * and can throw exceptions when errors occur.
173: * @throws XPathException if a dynamic error occurs in evaluating the query. Some
174: * dynamic errors will not be reported by this method, but will only be reported
175: * when the individual items of the result are accessed using the returned iterator.
176: */
177:
178: public SequenceIterator iterator(DynamicQueryContext env)
179: throws XPathException {
180: Controller controller = newController();
181: initializeController(env, controller);
182:
183: try {
184: NodeInfo node = env.getContextNode();
185:
186: Bindery bindery = controller.getBindery();
187: //bindery.openStackFrame();
188: controller.defineGlobalParameters(bindery);
189: XPathContextMajor context = controller.newXPathContext();
190:
191: if (node != null) {
192: AxisIterator single = SingletonIterator
193: .makeIterator(node);
194: single.next();
195: context.setCurrentIterator(single);
196: controller.setPrincipalSourceDocument(node
197: .getDocumentRoot());
198: }
199:
200: // In tracing/debugging mode, evaluate all the global variables first
201: if (controller.getConfiguration().getTraceListener() != null) {
202: controller.preEvaluateGlobals(context);
203: }
204:
205: context.openStackFrame(stackFrameMap);
206:
207: SequenceIterator iterator = expression.iterate(context);
208: return new ErrorReportingIterator(iterator, controller
209: .getErrorListener());
210: } catch (XPathException err) {
211: TransformerException terr = err;
212: while (terr.getException() instanceof TransformerException) {
213: terr = (TransformerException) terr.getException();
214: }
215: DynamicError de = DynamicError.makeDynamicError(terr);
216: controller.reportFatalError(de);
217: throw de;
218: }
219: }
220:
221: private void initializeController(DynamicQueryContext env,
222: Controller controller) {
223: HashMap parameters = env.getParameters();
224: if (parameters != null) {
225: Iterator iter = parameters.keySet().iterator();
226: while (iter.hasNext()) {
227: String paramName = (String) iter.next();
228: Object paramValue = parameters.get(paramName);
229: controller.setParameter(paramName, paramValue);
230: }
231: }
232:
233: controller.setURIResolver(env.getURIResolver());
234: controller.setErrorListener(env.getErrorListener());
235: DateTimeValue currentDateTime = env.getCurrentDateTime();
236: if (currentDateTime != null) {
237: try {
238: controller.setCurrentDateTime(currentDateTime);
239: } catch (XPathException e) {
240: throw new AssertionError(e); // the value should already have been checked
241: }
242: }
243: }
244:
245: /**
246: * Run the query, sending the results directly to a JAXP Result object. This way of executing
247: * the query is most efficient in the case of queries that produce a single document (or parentless
248: * element) as their output, because it avoids constructing the result tree in memory: instead,
249: * it is piped straight to the serializer.
250: *
251: * @param env the dynamic query context
252: * @param result the destination for the results of the query. The query is effectively wrapped
253: * in a document{} constructor, so that the items in the result are concatenated to form a single
254: * document; this is then written to the requested Result destination, which may be (for example)
255: * a DOMResult, a SAXResult, or a StreamResult
256: * @param outputProperties Supplies serialization properties, in JAXP format, if the result is to
257: * be serialized. This parameter can be defaulted to null.
258: * @throws XPathException if the query fails.
259: */
260:
261: public void run(DynamicQueryContext env, Result result,
262: Properties outputProperties) throws XPathException {
263:
264: Controller controller = newController();
265: initializeController(env, controller);
266:
267: // Validate the serialization properties requested
268:
269: Properties baseProperties = controller.getOutputProperties();
270: if (outputProperties != null) {
271: Enumeration iter = outputProperties.propertyNames();
272: while (iter.hasMoreElements()) {
273: String key = (String) iter.nextElement();
274: String value = outputProperties.getProperty(key);
275: try {
276: SaxonOutputKeys.checkOutputProperty(key, value,
277: controller.getConfiguration()
278: .getNameChecker());
279: baseProperties.setProperty(key, value);
280: } catch (DynamicError dynamicError) {
281: try {
282: outputProperties.remove(key);
283: controller.getErrorListener().warning(
284: dynamicError);
285: } catch (TransformerException err2) {
286: throw DynamicError.makeDynamicError(err2);
287: }
288: }
289: }
290: }
291: if (baseProperties.getProperty("method") == null) {
292: // XQuery forces the default method to XML, unlike XSLT where it depends on the contents of the result tree
293: baseProperties.setProperty("method", "xml");
294: }
295:
296: NodeInfo node = env.getContextNode();
297:
298: Bindery bindery = controller.getBindery();
299: controller.defineGlobalParameters(bindery);
300:
301: XPathContextMajor context = controller.newXPathContext();
302:
303: if (node != null) {
304: AxisIterator single = SingletonIterator.makeIterator(node);
305: context.setCurrentIterator(single);
306: single.next();
307: controller.setPrincipalSourceDocument(node
308: .getDocumentRoot());
309: }
310:
311: // In tracing/debugging mode, evaluate all the global variables first
312: TraceListener tracer = controller.getConfiguration()
313: .getTraceListener();
314: if (tracer != null) {
315: controller.preEvaluateGlobals(context);
316: tracer.open();
317: }
318:
319: context.openStackFrame(stackFrameMap);
320:
321: boolean mustClose = (result instanceof StreamResult && ((StreamResult) result)
322: .getOutputStream() == null);
323: context.changeOutputDestination(baseProperties, result, true,
324: Validation.PRESERVE, null);
325:
326: // Run the query
327: try {
328: expression.process(context);
329: } catch (XPathException err) {
330: controller.reportFatalError(err);
331: throw err;
332: }
333:
334: if (tracer != null) {
335: tracer.close();
336: }
337:
338: context.getReceiver().close();
339: if (mustClose) {
340: OutputStream os = ((StreamResult) result).getOutputStream();
341: if (os != null) {
342: try {
343: os.close();
344: } catch (java.io.IOException err) {
345: throw new DynamicError(err);
346: }
347: }
348: }
349: }
350:
351: /**
352: * Run the query in pull mode.
353: * <p/>
354: * For maximum effect this method should be used when lazyConstructionMode has been set in the Configuration.
355: *
356: * @see Configuration#setLazyConstructionMode(boolean)
357: */
358:
359: public void pull(DynamicQueryContext dynamicEnv,
360: Result destination, Properties outputProperties)
361: throws XPathException {
362: try {
363: SequenceIterator iter = iterator(dynamicEnv);
364: PullProvider pull = new PullFromIterator(iter);
365: pull = new PullNamespaceReducer(pull);
366: pull.setPipelineConfiguration(executable.getConfiguration()
367: .makePipelineConfiguration());
368:
369: Receiver receiver = ResultWrapper.getReceiver(destination,
370: pull.getPipelineConfiguration(), outputProperties);
371: //pull = new PullTracer(pull);
372: if ("yes".equals(outputProperties
373: .getProperty(SaxonOutputKeys.WRAP))) {
374: NamespaceReducer reducer = new NamespaceReducer();
375: reducer.setPipelineConfiguration(pull
376: .getPipelineConfiguration());
377: reducer.setUnderlyingReceiver(receiver);
378: receiver = new SequenceWrapper(reducer);
379: }
380: if (!(receiver instanceof SequenceWrapper)) {
381: receiver = new TreeReceiver(receiver);
382: }
383: new PullPushCopier(pull, receiver).copy();
384: } catch (UncheckedXPathException e) {
385: throw e.getXPathException();
386: }
387: }
388:
389: /**
390: * Get a controller that can be used to execute functions in this compiled query.
391: * Functions in the query module can be found using {@link StaticQueryContext#getUserDefinedFunction}.
392: * They can then be called directly from the Java application using {@link net.sf.saxon.instruct.UserFunction#call}
393: * The same Controller can be used for a series of function calls.
394: * <p/>
395: * Note, this method is poorly named. It creates a new Controller each time it is called: which is useful
396: * when a query is to be executed repeatedly.
397: */
398:
399: public Controller newController() {
400: Controller controller = new Controller(executable
401: .getConfiguration(), executable);
402: executable.initialiseBindery(controller.getBindery());
403: return controller;
404: }
405:
406: /**
407: * Deprecated synonym for {@link #newController}
408: * @deprecated since 8.5.1 - use newController()
409: */
410:
411: public Controller getController() {
412: return newController();
413: }
414:
415: /**
416: * Diagnostic method: display a representation of the compiled query on the
417: * System.err output stream.
418: */
419:
420: public void explain(NamePool pool) {
421: System.err
422: .println("============ Compiled Expression ============");
423: expression.display(10, pool, System.err);
424: System.err
425: .println("=============================================");
426: }
427:
428: /**
429: * Get the Executable (representing a complete stylesheet or query) of which this Container forms part
430: */
431:
432: public Executable getExecutable() {
433: return executable;
434: }
435:
436: /**
437: * Get the LocationProvider allowing location identifiers to be resolved.
438: */
439:
440: public LocationProvider getLocationProvider() {
441: return executable.getLocationMap();
442: }
443:
444: /**
445: * Return the public identifier for the current document event.
446: * <p/>
447: * <p>The return value is the public identifier of the document
448: * entity or of the external parsed entity in which the markup that
449: * triggered the event appears.</p>
450: *
451: * @return A string containing the public identifier, or
452: * null if none is available.
453: * @see #getSystemId
454: */
455: public String getPublicId() {
456: return null;
457: }
458:
459: /**
460: * Return the system identifier for the current document event.
461: * <p/>
462: * <p>The return value is the system identifier of the document
463: * entity or of the external parsed entity in which the markup that
464: * triggered the event appears.</p>
465: * <p/>
466: * <p>If the system identifier is a URL, the parser must resolve it
467: * fully before passing it to the application.</p>
468: *
469: * @return A string containing the system identifier, or null
470: * if none is available.
471: * @see #getPublicId
472: */
473: public String getSystemId() {
474: return null;
475: }
476:
477: /**
478: * Return the line number where the current document event ends.
479: * <p/>
480: * <p><strong>Warning:</strong> The return value from the method
481: * is intended only as an approximation for the sake of error
482: * reporting; it is not intended to provide sufficient information
483: * to edit the character content of the original XML document.</p>
484: * <p/>
485: * <p>The return value is an approximation of the line number
486: * in the document entity or external parsed entity where the
487: * markup that triggered the event appears.</p>
488: *
489: * @return The line number, or -1 if none is available.
490: * @see #getColumnNumber
491: */
492: public int getLineNumber() {
493: return -1;
494: }
495:
496: /**
497: * Return the character position where the current document event ends.
498: * <p/>
499: * <p><strong>Warning:</strong> The return value from the method
500: * is intended only as an approximation for the sake of error
501: * reporting; it is not intended to provide sufficient information
502: * to edit the character content of the original XML document.</p>
503: * <p/>
504: * <p>The return value is an approximation of the column number
505: * in the document entity or external parsed entity where the
506: * markup that triggered the event appears.</p>
507: *
508: * @return The column number, or -1 if none is available.
509: * @see #getLineNumber
510: */
511: public int getColumnNumber() {
512: return -1;
513: }
514:
515: /**
516: * ErrorReportingIterator is an iterator that wraps a base iterator and reports
517: * any exceptions that are raised to the ErrorListener
518: */
519:
520: private class ErrorReportingIterator implements SequenceIterator {
521: private SequenceIterator base;
522: private ErrorListener listener;
523:
524: public ErrorReportingIterator(SequenceIterator base,
525: ErrorListener listener) {
526: this .base = base;
527: this .listener = listener;
528: }
529:
530: public Item next() throws XPathException {
531: try {
532: return base.next();
533: } catch (XPathException e1) {
534: if (e1.getLocator() == null) {
535: e1
536: .setLocator(ExpressionTool
537: .getLocator(expression));
538: }
539: try {
540: listener.fatalError(e1);
541: } catch (TransformerException e2) {
542: //
543: }
544: e1.setHasBeenReported();
545: throw e1;
546: }
547: }
548:
549: public Item current() {
550: return base.current();
551: }
552:
553: public int position() {
554: return base.position();
555: }
556:
557: public SequenceIterator getAnother() throws XPathException {
558: return new ErrorReportingIterator(base.getAnother(),
559: listener);
560: }
561:
562: /**
563: * Get properties of this iterator, as a bit-significant integer.
564: *
565: * @return the properties of this iterator. This will be some combination of
566: * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
567: * and {@link LOOKAHEAD}. It is always
568: * acceptable to return the value zero, indicating that there are no known special properties.
569: * It is acceptable for the properties of the iterator to change depending on its state.
570: */
571:
572: public int getProperties() {
573: return 0;
574: }
575: }
576:
577: }
578:
579: //
580: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
581: // you may not use this file except in compliance with the License. You may obtain a copy of the
582: // License at http://www.mozilla.org/MPL/
583: //
584: // Software distributed under the License is distributed on an "AS IS" basis,
585: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
586: // See the License for the specific language governing rights and limitations under the License.
587: //
588: // The Original Code is: all this file.
589: //
590: // The Initial Developer of the Original Code is Michael H. Kay
591: //
592: // Contributor(s):
593: //
|