package client.net.sf.saxon.ce.expr.instruct;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.AnyItemType;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.SequenceType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* Instruction representing an xsl:call-template element in the stylesheet.
*/
public class CallTemplate extends Instruction {
private Template template = null;
private WithParam[] actualParams = null;
private WithParam[] tunnelParams = null;
private boolean useTailRecursion = false;
/**
* Construct a CallTemplate instruction.
* @param template the Template object identifying the template to be called, in the normal
* case where this is known statically
* @param useTailRecursion true if the call is potentially tail recursive
*/
public CallTemplate(Template template,
boolean useTailRecursion) {
this.template = template;
this.useTailRecursion = useTailRecursion;
}
public void setUseTailRecursion(boolean useIt) {
useTailRecursion = useIt;
}
/**
* Set the actual parameters on the call
* @param actualParams the parameters that are not tunnel parameters
* @param tunnelParams the tunnel parameters
*/
public void setActualParameters(
WithParam[] actualParams,
WithParam[] tunnelParams ) {
this.actualParams = actualParams;
this.tunnelParams = tunnelParams;
for (int i=0; i<actualParams.length; i++) {
adoptChildExpression(actualParams[i]);
}
for (int i=0; i<tunnelParams.length; i++) {
adoptChildExpression(tunnelParams[i]);
}
}
/**
* Return the name of this instruction.
*/
public int getInstructionNameCode() {
return StandardNames.XSL_CALL_TEMPLATE;
}
/**
* Simplify an expression. This performs any static optimization (by rewriting the expression
* as a different expression).
*
* @exception XPathException if an error is discovered during expression
* rewriting
* @return the simplified expression
* @param visitor an expression visitor
*/
public Expression simplify(ExpressionVisitor visitor) throws XPathException {
WithParam.simplify(actualParams, visitor);
WithParam.simplify(tunnelParams, visitor);
return this;
}
public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
WithParam.typeCheck(actualParams, visitor, contextItemType);
WithParam.typeCheck(tunnelParams, visitor, contextItemType);
if (template.getBody() != null) {
// For non-tunnel parameters, see if the supplied value is type-safe against the declared
// type of the value, and if so, avoid the dynamic type check
// Can't do this check unless the target template has been compiled.
boolean backwards = visitor.getStaticContext().isInBackwardsCompatibleMode();
for (int p=0; p<actualParams.length; p++) {
WithParam wp = actualParams[p];
int id = wp.getParameterId();
LocalParam lp = template.getLocalParam(id);
if (lp != null) {
SequenceType req = lp.getRequiredType();
RoleLocator role = new RoleLocator(RoleLocator.PARAM, wp.getVariableQName().getDisplayName(), p);
Expression select = TypeChecker.staticTypeCheck(
wp.getSelectExpression(), req, backwards, role, visitor);
wp.setSelectExpression(select);
wp.setTypeChecked(true);
}
}
}
return this;
}
public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
WithParam.optimize(visitor, actualParams, contextItemType);
WithParam.optimize(visitor, tunnelParams, contextItemType);
return this;
}
/**
* Get the cardinality of the sequence returned by evaluating this instruction
*
* @return the static cardinality
*/
public int computeCardinality() {
if (template == null) {
return StaticProperty.ALLOWS_ZERO_OR_MORE;
} else {
return template.getRequiredType().getCardinality();
}
}
/**
* Get the item type of the items returned by evaluating this instruction
*
* @param th the type hierarchy cache
* @return the static item type of the instruction
*/
public ItemType getItemType(TypeHierarchy th) {
if (template == null) {
return AnyItemType.getInstance();
} else {
return template.getRequiredType().getPrimaryType();
}
}
public int getIntrinsicDependencies() {
// we could go to the called template and find which parts of the context it depends on, but this
// would create the risk of infinite recursion. So we just assume that the dependencies exist
return StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
StaticProperty.DEPENDS_ON_FOCUS;
}
/**
* Determine whether this instruction creates new nodes.
* This implementation currently returns true unconditionally.
*/
public final boolean createsNewNodes() {
return true;
}
/**
* Get all the XPath expressions associated with this instruction
* (in XSLT terms, the expression present on attributes of the instruction,
* as distinct from the child instructions in a sequence construction)
*/
public Iterator<Expression> iterateSubExpressions() {
ArrayList list = new ArrayList(10);
WithParam.getXPathExpressions(actualParams, list);
WithParam.getXPathExpressions(tunnelParams, list);
return list.iterator();
}
/**
* Replace one subexpression by a replacement subexpression
* @param original the original subexpression
* @param replacement the replacement subexpression
* @return true if the original subexpression is found
*/
public boolean replaceSubExpression(Expression original, Expression replacement) {
boolean found = false;
if (WithParam.replaceXPathExpression(actualParams, original, replacement)) {
found = true;
}
if (WithParam.replaceXPathExpression(tunnelParams, original, replacement)) {
found = true;
}
return found;
}
/**
* Handle promotion offers, that is, non-local tree rewrites.
* @param offer The type of rewrite being offered
* @throws client.net.sf.saxon.ce.trans.XPathException
*/
protected void promoteInst(PromotionOffer offer) throws XPathException {
WithParam.promoteParams(this, actualParams, offer);
WithParam.promoteParams(this, tunnelParams, offer);
}
/**
* Process this instruction, without leaving any tail calls.
* @param context the dynamic context for this transformation
* @throws XPathException if a dynamic error occurs
*/
public void process(XPathContext context) throws XPathException {
Template t = getTargetTemplate();
XPathContextMajor c2 = context.newContext();
c2.openStackFrame(t.getStackFrameMap());
c2.setLocalParameters(assembleParams(context, actualParams));
c2.setTunnelParameters(assembleTunnelParams(context, tunnelParams));
TailCall tc = t.expand(c2);
while (tc != null) {
tc = tc.processLeavingTail();
}
}
/**
* Process this instruction. If the called template contains a tail call (which may be
* an xsl:call-template of xsl:apply-templates instruction) then the tail call will not
* actually be evaluated, but will be returned in a TailCall object for the caller to execute.
* @param context the dynamic context for this transformation
* @return an object containing information about the tail call to be executed by the
* caller. Returns null if there is no tail call.
*/
public TailCall processLeavingTail(XPathContext context) throws XPathException
{
if (!useTailRecursion) {
process(context);
return null;
}
// if name is determined dynamically, determine it now
Template target = getTargetTemplate();
// handle parameters if any
ParameterSet params = assembleParams(context, actualParams);
ParameterSet tunnels = assembleTunnelParams(context, tunnelParams);
// Call the named template. Actually, don't call it; rather construct a call package
// and return it to the caller, who will then process this package.
//System.err.println("Call template using tail recursion");
if (params==null) { // bug 490967
params = ParameterSet.EMPTY_PARAMETER_SET;
}
// clear all the local variables: they are no longer needed
Arrays.fill(context.getStackFrame().getStackFrameValues(), null);
return new CallTemplatePackage(target, params, tunnels, context);
}
/**
* Get the template, in the case where it is specified dynamically.
* @return The template to be called
* @throws XPathException if a dynamic error occurs: specifically, if the
* template name is computed at run-time (Saxon extension) and the name is invalid
* or does not reference a known template
*/
public Template getTargetTemplate() throws XPathException {
return template;
}
public StructuredQName getObjectName() {
return (template==null ? null : template.getTemplateName());
}
/**
* A CallTemplatePackage is an object that encapsulates the name of a template to be called,
* the parameters to be supplied, and the execution context. This object can be returned as a tail
* call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
* template to execute in a finite stack size
*/
public static class CallTemplatePackage implements TailCall {
private Template target;
private ParameterSet params;
private ParameterSet tunnelParams;
private XPathContext evaluationContext;
/**
* Construct a CallTemplatePackage that contains information about a call.
* @param template the Template to be called
* @param params the parameters to be supplied to the called template
* @param tunnelParams the tunnel parameter supplied to the called template
* @param evaluationContext saved context information from the Controller (current mode, etc)
* which must be reset to ensure that the template is called with all the context information
* intact
*/
public CallTemplatePackage(Template template,
ParameterSet params,
ParameterSet tunnelParams,
XPathContext evaluationContext) {
target = template;
this.params = params;
this.tunnelParams = tunnelParams;
this.evaluationContext = evaluationContext;
}
/**
* Process the template call encapsulated by this package.
* @return another TailCall. This will never be the original call, but it may be the next
* recursive call. For example, if A calls B which calls C which calls D, then B may return
* a TailCall to A representing the call from B to C; when this is processed, the result may be
* a TailCall representing the call from C to D.
* @throws XPathException if a dynamic error occurs
*/
public TailCall processLeavingTail() throws XPathException {
// TODO: the idea of tail call optimization is to reuse the caller's stack frame rather than
// creating a new one. We're doing this for the Java stack, but not for the context stack where
// local variables are held. It should be possible to avoid creating a new context, and instead
// to update the existing one in situ.
XPathContextMajor c2 = evaluationContext.newContext();
c2.setLocalParameters(params);
c2.setTunnelParameters(tunnelParams);
c2.openStackFrame(target.getStackFrameMap());
// System.err.println("Tail call on template");
return target.expand(c2);
}
}
}
// 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.