package client.net.sf.saxon.ce.expr.instruct; import client.net.sf.saxon.ce.Controller; import client.net.sf.saxon.ce.event.PipelineConfiguration; import client.net.sf.saxon.ce.event.SequenceOutputter; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.om.SequenceIterator; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.SourceLocator; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.type.TypeHierarchy; /** * Abstract superclass for all instructions in the compiled stylesheet. * This represents a compiled instruction, and as such, the minimum information is * retained from the original stylesheet. <br> * Note: this class implements SourceLocator: that is, it can identify where in the stylesheet * the source instruction was located. */ public abstract class Instruction extends Expression implements TailCallReturner { /** * Constructor */ public Instruction() {} /** * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process(). * This method indicates which of these methods is prefered. For instructions this is the process() method. */ public int getImplementationMethod() { return Expression.PROCESS_METHOD; } /** * Get the namecode of the instruction for use in diagnostics * @return a code identifying the instruction: typically but not always * the fingerprint of a name in the XSLT namespace */ public int getInstructionNameCode() { return -1; } /** * Get the item type of the items returned by evaluating this instruction * @return the static item type of the instruction * @param th the type hierarchy cache */ public ItemType getItemType(TypeHierarchy th) { return Type.ITEM_TYPE; } /** * Get the cardinality of the sequence returned by evaluating this instruction * @return the static cardinality */ public int computeCardinality() { return StaticProperty.ALLOWS_ZERO_OR_MORE; } /** * ProcessLeavingTail: called to do the real work of this instruction. This method * must be implemented in each subclass. The results of the instruction are written * to the current Receiver, which can be obtained via the Controller. * @param context The dynamic context of the transformation, giving access to the current node, * the current variables, etc. * @return null if the instruction has completed execution; or a TailCall indicating * a function call or template call that is delegated to the caller, to be made after the stack has * been unwound so as to save stack space. */ public abstract TailCall processLeavingTail(XPathContext context) throws XPathException; /** * Process the instruction, without returning any tail calls * @param context The dynamic context, giving access to the current node, * the current variables, etc. */ public void process(XPathContext context) throws XPathException { try { TailCall tc = processLeavingTail(context); while (tc != null) { tc = tc.processLeavingTail(); } } catch (XPathException err) { err.maybeSetLocation(getSourceLocator()); throw err; } } /** * Construct an exception with diagnostic information. Note that this method * returns the exception, it does not throw it: that is up to the caller. * @param loc the location of the error * @param error The exception containing information about the error * @param context The controller of the transformation * @return an exception based on the supplied exception, but with location information * added relating to this instruction */ protected static XPathException dynamicError(SourceLocator loc, XPathException error, XPathContext context) { if (error instanceof TerminationException) { return error; } error.maybeSetLocation(loc); error.maybeSetContext(context); return error; } /** * Assemble a ParameterSet. Method used by instructions that have xsl:with-param * children. This method is used for the non-tunnel parameters. * @param context the XPath dynamic context * @param actualParams the set of with-param parameters that specify tunnel="no" * @return a ParameterSet */ public static ParameterSet assembleParams(XPathContext context, WithParam[] actualParams) throws XPathException { if (actualParams == null || actualParams.length == 0) { return null; } ParameterSet params = new ParameterSet(actualParams.length); for (int i=0; i<actualParams.length; i++) { params.put(actualParams[i].getParameterId(), actualParams[i].getSelectValue(context), actualParams[i].isTypeChecked()); } return params; } /** * Assemble a ParameterSet. Method used by instructions that have xsl:with-param * children. This method is used for the tunnel parameters. * @param context the XPath dynamic context * @param actualParams the set of with-param parameters that specify tunnel="yes" * @return a ParameterSet */ public static ParameterSet assembleTunnelParams(XPathContext context, WithParam[] actualParams) throws XPathException { ParameterSet existingParams = context.getTunnelParameters(); if (existingParams == null) { return assembleParams(context, actualParams); } ParameterSet newParams = new ParameterSet(existingParams, (actualParams==null ? 0 : actualParams.length)); if (actualParams == null || actualParams.length == 0) { return newParams; } for (int i=0; i<actualParams.length; i++) { newParams.put(actualParams[i].getParameterId(), actualParams[i].getSelectValue(context), false); } return newParams; } /** * Simplify an expression. This performs any static optimization (by rewriting the expression * as a different expression). The default implementation does nothing. * * @exception client.net.sf.saxon.ce.trans.XPathException if an error is discovered during expression * rewriting * @return the simplified expression * @param visitor an expression visitor */ public abstract Expression simplify(ExpressionVisitor visitor) throws XPathException; /** * Get the static properties of this expression (other than its type). The result is * bit-signficant. These properties are used for optimizations. In general, if * property bit is set, it is true, but if it is unset, the value is unknown. * * @return a set of flags indicating static properties of this expression */ public int computeSpecialProperties() { int p = super.computeSpecialProperties(); if (createsNewNodes()) { return p; } else { return p | StaticProperty.NON_CREATIVE; } } /** * Determine whether this instruction creates new nodes. * This implementation returns a default value of false * @return true if the instruction creates new nodes (or if it can't be proved that it doesn't) */ public boolean createsNewNodes() { return false; } /** * Handle promotion offers, that is, non-local tree rewrites. * @param offer The type of rewrite being offered * @throws XPathException */ protected void promoteInst(PromotionOffer offer) throws XPathException { } /** * Offer promotion for this subexpression. The offer will be accepted if the subexpression * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer. * This method is always called at compile time. * * @param offer details of the offer, for example the offer to move * expressions that don't depend on the context to an outer level in * the containing expression * @param parent * @exception client.net.sf.saxon.ce.trans.XPathException if any error is detected * @return if the offer is not accepted, return this expression unchanged. * Otherwise return the result of rewriting the expression to promote * this subexpression */ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException { Expression exp = offer.accept(parent, this); if (exp!=null) { return exp; } else { promoteInst(offer); return this; } } /** * Evaluate an expression as a single item. This always returns either a single Item or * null (denoting the empty sequence). No conversion is done. This method should not be * used unless the static type of the expression is a subtype of "item" or "item?": that is, * it should not be called if the expression may return a sequence. There is no guarantee that * this condition will be detected. * * @param context The context in which the expression is to be evaluated * @exception XPathException if any dynamic error occurs evaluating the * expression * @return the node or atomic value that results from evaluating the * expression; or null to indicate that the result is an empty * sequence */ public Item evaluateItem(XPathContext context) throws XPathException { int m = getImplementationMethod(); if ((m & EVALUATE_METHOD) != 0) { throw new AssertionError( "evaluateItem() is not implemented in the subclass " + getClass()); } else if ((m & ITERATE_METHOD) != 0) { return iterate(context).next(); } else { Controller controller = context.getController(); XPathContext c2 = context.newMinorContext(); SequenceOutputter seq = controller.allocateSequenceOutputter(1); PipelineConfiguration pipe = controller.makePipelineConfiguration(); seq.setPipelineConfiguration(pipe); c2.setTemporaryReceiver(seq); process(c2); seq.close(); Item result = seq.getFirstItem(); seq.reset(); return result; } } /** * Return an Iterator to iterate over the values of a sequence. The value of every * expression can be regarded as a sequence, so this method is supported for all * expressions. This default implementation handles iteration for expressions that * return singleton values: for non-singleton expressions, the subclass must * provide its own implementation. * * @exception XPathException if any dynamic error occurs evaluating the * expression * @param context supplies the context for evaluation * @return a SequenceIterator that can be used to iterate over the result * of the expression */ public SequenceIterator iterate(XPathContext context) throws XPathException { int m = getImplementationMethod(); if ((m & EVALUATE_METHOD) != 0) { return super.iterate(context); } else if ((m & ITERATE_METHOD) != 0) { throw new AssertionError("iterate"); } else { Controller controller = context.getController(); XPathContext c2 = context.newMinorContext(); SequenceOutputter seq = controller.allocateSequenceOutputter(20); PipelineConfiguration pipe = controller.makePipelineConfiguration(); seq.setPipelineConfiguration(pipe); c2.setTemporaryReceiver(seq); process(c2); seq.close(); return seq.iterate(); } } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.