001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Controller;
005: import net.sf.saxon.value.Value;
006: import net.sf.saxon.event.SequenceOutputter;
007: import net.sf.saxon.event.SequenceReceiver;
008: import net.sf.saxon.expr.*;
009: import net.sf.saxon.om.*;
010: import net.sf.saxon.trace.InstructionInfo;
011: import net.sf.saxon.trans.DynamicError;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.type.ItemType;
014: import net.sf.saxon.type.Type;
015: import net.sf.saxon.type.TypeHierarchy;
016:
017: import javax.xml.transform.SourceLocator;
018:
019: /**
020: * Abstract superclass for all instructions in the compiled stylesheet.
021: * This represents a compiled instruction, and as such, the minimum information is
022: * retained from the original stylesheet. <br>
023: * Note: this class implements SourceLocator: that is, it can identify where in the stylesheet
024: * the source instruction was located.
025: */
026:
027: public abstract class Instruction extends ComputedExpression implements
028: SourceLocator, TailCallReturner {
029:
030: /**
031: * Constructor
032: */
033:
034: public Instruction() {
035: }
036:
037: /**
038: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
039: * This method indicates which of these methods is prefered. For instructions this is the process() method.
040: */
041:
042: public int getImplementationMethod() {
043: return Expression.PROCESS_METHOD;
044: }
045:
046: /**
047: * Get the namecode of the instruction for use in diagnostics
048: */
049:
050: public int getInstructionNameCode() {
051: return -1;
052: };
053:
054: /**
055: * Get the item type of the items returned by evaluating this instruction
056: * @return the static item type of the instruction
057: * @param th
058: */
059:
060: public ItemType getItemType(TypeHierarchy th) {
061: return Type.ITEM_TYPE;
062: }
063:
064: /**
065: * Get the cardinality of the sequence returned by evaluating this instruction
066: * @return the static cardinality
067: */
068:
069: public int computeCardinality() {
070: return StaticProperty.ALLOWS_ZERO_OR_MORE;
071: }
072:
073: /**
074: * ProcessLeavingTail: called to do the real work of this instruction. This method
075: * must be implemented in each subclass. The results of the instruction are written
076: * to the current Receiver, which can be obtained via the Controller.
077: * @param context The dynamic context of the transformation, giving access to the current node,
078: * the current variables, etc.
079: * @return null if the instruction has completed execution; or a TailCall indicating
080: * a function call or template call that is delegated to the caller, to be made after the stack has
081: * been unwound so as to save stack space.
082: */
083:
084: public abstract TailCall processLeavingTail(XPathContext context)
085: throws XPathException;
086:
087: /**
088: * Process the instruction, without returning any tail calls
089: * @param context The dynamic context, giving access to the current node,
090: * the current variables, etc.
091: */
092:
093: public void process(XPathContext context) throws XPathException {
094: TailCall tc = processLeavingTail(context);
095: while (tc != null) {
096: tc = tc.processLeavingTail(context);
097: }
098: }
099:
100: /**
101: * Get a SourceLocator identifying the location of this instruction
102: */
103:
104: public SourceLocator getSourceLocator() {
105: return this ;
106: }
107:
108: /**
109: * Construct an exception with diagnostic information. Note that this method
110: * returns the exception, it does not throw it: that is up to the caller.
111: * @param error The exception containing information about the error
112: * @param context The controller of the transformation
113: * @return an exception based on the supplied exception, but with location information
114: * added relating to this instruction
115: */
116:
117: protected static XPathException dynamicError(SourceLocator loc,
118: XPathException error, XPathContext context) {
119: if (error instanceof TerminationException)
120: return error;
121: if (error.getLocator() == null
122: || (error.getLocator() instanceof ExpressionLocation && context
123: .getController().getExecutable()
124: .getHostLanguage() != Configuration.XQUERY)
125: || error.getLocator().getLineNumber() == -1) {
126: // If the exception has no location information, construct a new
127: // exception containing the required information
128: try {
129: DynamicError de = new DynamicError(error.getMessage(),
130: loc, error.getException());
131: if (error instanceof DynamicError) {
132: de.setErrorCode(error.getErrorCodeLocalPart());
133: if (((DynamicError) error).getXPathContext() == null) {
134: de.setXPathContext(context);
135: } else {
136: de.setXPathContext(((DynamicError) error)
137: .getXPathContext());
138: }
139: }
140: return de;
141: } catch (Exception secondaryError) {
142: // currently happens when running XQuery
143: return error;
144: }
145: }
146: return error;
147: }
148:
149: /**
150: * Assemble a ParameterSet. Method used by instructions that have xsl:with-param
151: * children. This method is used for the non-tunnel parameters.
152: */
153:
154: protected static ParameterSet assembleParams(XPathContext context,
155: WithParam[] actualParams) throws XPathException {
156: if (actualParams == null || actualParams.length == 0) {
157: return null;
158: }
159: ParameterSet params = new ParameterSet(actualParams.length);
160: for (int i = 0; i < actualParams.length; i++) {
161: params.put(actualParams[i].getVariableFingerprint(),
162: actualParams[i].getSelectValue(context));
163: }
164: return params;
165: }
166:
167: /**
168: * Assemble a ParameterSet. Method used by instructions that have xsl:with-param
169: * children. This method is used for the tunnel parameters.
170: */
171:
172: protected static ParameterSet assembleTunnelParams(
173: XPathContext context, WithParam[] actualParams)
174: throws XPathException {
175: ParameterSet existingParams = context.getTunnelParameters();
176: if (existingParams == null) {
177: return assembleParams(context, actualParams);
178: }
179: ParameterSet newParams = new ParameterSet(existingParams,
180: (actualParams == null ? 0 : actualParams.length));
181: if (actualParams == null || actualParams.length == 0) {
182: return newParams;
183: }
184: for (int i = 0; i < actualParams.length; i++) {
185: newParams.put(actualParams[i].getVariableFingerprint(),
186: actualParams[i].getSelectValue(context));
187: }
188: return newParams;
189: }
190:
191: /**
192: * Simplify an expression. This performs any static optimization (by rewriting the expression
193: * as a different expression). The default implementation does nothing.
194: *
195: * @exception net.sf.saxon.trans.XPathException if an error is discovered during expression
196: * rewriting
197: * @return the simplified expression
198: */
199:
200: public abstract Expression simplify(StaticContext env)
201: throws XPathException;
202:
203: /**
204: * Get the static properties of this expression (other than its type). The result is
205: * bit-signficant. These properties are used for optimizations. In general, if
206: * property bit is set, it is true, but if it is unset, the value is unknown.
207: *
208: * @return a set of flags indicating static properties of this expression
209: */
210:
211: public int computeSpecialProperties() {
212: int p = super .computeSpecialProperties();
213: if (createsNewNodes()) {
214: return p;
215: } else {
216: return p | StaticProperty.NON_CREATIVE;
217: }
218: }
219:
220: /**
221: * Determine whether this instruction creates new nodes.
222: * This implementation returns a default value of false
223: */
224:
225: public boolean createsNewNodes() {
226: return false;
227: }
228:
229: /**
230: * Handle promotion offers, that is, non-local tree rewrites.
231: * @param offer The type of rewrite being offered
232: * @throws XPathException
233: */
234:
235: protected void promoteInst(PromotionOffer offer)
236: throws XPathException {
237: }
238:
239: /**
240: * Offer promotion for this subexpression. The offer will be accepted if the subexpression
241: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
242: * This method is always called at compile time.
243: *
244: * @param offer details of the offer, for example the offer to move
245: * expressions that don't depend on the context to an outer level in
246: * the containing expression
247: * @exception net.sf.saxon.trans.XPathException if any error is detected
248: * @return if the offer is not accepted, return this expression unchanged.
249: * Otherwise return the result of rewriting the expression to promote
250: * this subexpression
251: */
252:
253: public Expression promote(PromotionOffer offer)
254: throws XPathException {
255: Expression exp = offer.accept(this );
256: if (exp != null) {
257: return exp;
258: } else {
259: promoteInst(offer);
260: return this ;
261: }
262: }
263:
264: /**
265: * Evaluate an expression as a single item. This always returns either a single Item or
266: * null (denoting the empty sequence). No conversion is done. This method should not be
267: * used unless the static type of the expression is a subtype of "item" or "item?": that is,
268: * it should not be called if the expression may return a sequence. There is no guarantee that
269: * this condition will be detected.
270: *
271: * @param context The context in which the expression is to be evaluated
272: * @exception XPathException if any dynamic error occurs evaluating the
273: * expression
274: * @return the node or atomic value that results from evaluating the
275: * expression; or null to indicate that the result is an empty
276: * sequence
277: */
278:
279: public Item evaluateItem(XPathContext context)
280: throws XPathException {
281: int m = getImplementationMethod();
282: if ((m & EVALUATE_METHOD) != 0) {
283: throw new AssertionError(
284: "evaluateItem() is not implemented in the subclass "
285: + this .getClass());
286: } else if ((m & ITERATE_METHOD) != 0) {
287: return iterate(context).next();
288: } else {
289: Controller controller = context.getController();
290: XPathContext c2 = context.newMinorContext();
291: c2.setOrigin(this );
292: SequenceOutputter seq = new SequenceOutputter(1);
293: seq.setPipelineConfiguration(controller
294: .makePipelineConfiguration());
295: c2.setTemporaryReceiver(seq);
296: process(c2);
297: seq.close();
298: return seq.getFirstItem();
299: }
300: }
301:
302: /**
303: * Return an Iterator to iterate over the values of a sequence. The value of every
304: * expression can be regarded as a sequence, so this method is supported for all
305: * expressions. This default implementation handles iteration for expressions that
306: * return singleton values: for non-singleton expressions, the subclass must
307: * provide its own implementation.
308: *
309: * @exception XPathException if any dynamic error occurs evaluating the
310: * expression
311: * @param context supplies the context for evaluation
312: * @return a SequenceIterator that can be used to iterate over the result
313: * of the expression
314: */
315:
316: public SequenceIterator iterate(XPathContext context)
317: throws XPathException {
318: int m = getImplementationMethod();
319: if ((m & EVALUATE_METHOD) != 0) {
320: Item item = evaluateItem(context);
321: if (item == null) {
322: return EmptyIterator.getInstance();
323: } else {
324: return SingletonIterator.makeIterator(item);
325: }
326: } else if ((m & ITERATE_METHOD) != 0) {
327: throw new AssertionError(
328: "iterate() is not implemented in the subclass "
329: + this .getClass());
330: } else {
331: Controller controller = context.getController();
332: XPathContext c2 = context.newMinorContext();
333: c2.setOrigin(this );
334: SequenceOutputter seq = new SequenceOutputter();
335: seq.setPipelineConfiguration(controller
336: .makePipelineConfiguration());
337: c2.setTemporaryReceiver(seq);
338: process(c2);
339: seq.close();
340: return seq.iterate();
341: }
342: }
343:
344: /**
345: * Evaluate an expression as a String. This function must only be called in contexts
346: * where it is known that the expression will return a single string (or where an empty sequence
347: * is to be treated as a zero-length string). Implementations should not attempt to convert
348: * the result to a string, other than converting () to "". This method is used mainly to
349: * evaluate expressions produced by compiling an attribute value template.
350: *
351: * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
352: * expression
353: * @exception java.lang.ClassCastException if the result type of the
354: * expression is not xs:string?
355: * @param context The context in which the expression is to be evaluated
356: * @return the value of the expression, evaluated in the current context.
357: * The expression must return a string or (); if the value of the
358: * expression is (), this method returns "".
359: */
360:
361: public final String evaluateAsString(XPathContext context)
362: throws XPathException {
363: Item item = evaluateItem(context);
364: if (item == null) {
365: return "";
366: } else {
367: return item.getStringValue();
368: }
369: }
370:
371: public InstructionInfo getInstructionInfo() {
372: InstructionDetails details = new InstructionDetails();
373: details.setSystemId(getSystemId());
374: details.setLineNumber(getLineNumber());
375: details.setConstructType(getInstructionNameCode());
376: return details;
377: }
378:
379: /**
380: * Static method to append an item that results from evaluating an expression to the current
381: * result receiver. The method checks to see whether the item returned from the expression is
382: * actually a function-call-package representing the result of a tail call optimization; if so,
383: * the tail function calls are complete and the final result is passed on.
384: * @param it the item: possibly a FunctionCallPackage
385: * @param out the Receiver
386: * @throws XPathException
387: */
388:
389: public static void appendItem(Item it, SequenceReceiver out,
390: int locationId) throws XPathException {
391: // If this call to xsl:sequence is in a template (rather than a function) it may
392: // be marked as a tail call; in this situation we need to evaluate the returned
393: // function call package. Doing so may return further function call packages, which also need
394: // to be processed. This has to be iterative rather than recursive to avoid consuming stack space.
395: while (true) {
396: if (it == null) {
397: return;
398: } else if (it instanceof UserFunctionCall.FunctionCallPackage) {
399: // the rather curious structure of this method came about by inlining the following
400: // commented-out line. It seems to work, but it's not clear that the nested loops are needed.
401: //it = (Item)((UserFunctionCall.FunctionCallPackage)it).appendTo(out);
402: UserFunctionCall.FunctionCallPackage fcp = (UserFunctionCall.FunctionCallPackage) it;
403: ValueRepresentation v = fcp.call();
404: SequenceIterator fv = Value.getIterator(v);
405: while (true) {
406: Item fvit = fv.next();
407: if (fvit == null) {
408: it = null;
409: break;
410: } else if (fvit instanceof UserFunctionCall.FunctionCallPackage) {
411: it = fvit;
412: break;
413: } else {
414: out.append(fvit, locationId,
415: NodeInfo.ALL_NAMESPACES);
416: }
417: }
418: // continue round the loop to unwind all recursive calls
419: } else {
420: out.append(it, locationId, NodeInfo.ALL_NAMESPACES);
421: return;
422: }
423: }
424: }
425:
426: /**
427: * Establish whether this is an XSLT instruction or an XQuery instruction
428: * (used to produce appropriate diagnostics)
429: * @param context
430: * @return true for XSLT, false for XQuery
431: */
432: public boolean isXSLT(XPathContext context) {
433: return context.getController().getExecutable()
434: .getHostLanguage() == Configuration.XSLT;
435: }
436: }
437:
438: //
439: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
440: // you may not use this file except in compliance with the License. You may obtain a copy of the
441: // License at http://www.mozilla.org/MPL/
442: //
443: // Software distributed under the License is distributed on an "AS IS" basis,
444: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
445: // See the License for the specific language governing rights and limitations under the License.
446: //
447: // The Original Code is: all this file.
448: //
449: // The Initial Developer of the Original Code is Michael H. Kay
450: //
451: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
452: //
453: // Contributor(s): none
454: //
|