net.sf.saxon.expr.instruct.CallTemplate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr.instruct;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.SymbolicName;
import net.sf.saxon.trans.Visibility;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.value.SequenceType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/**
* Instruction representing an xsl:call-template element in the stylesheet.
*/
public class CallTemplate extends Instruction implements ITemplateCall, ComponentInvocation {
private NamedTemplate template = null; // Null only for saxon:call-template
private StructuredQName calledTemplateName; // the name of the called template
private WithParam[] actualParams = WithParam.EMPTY_ARRAY;
private WithParam[] tunnelParams = WithParam.EMPTY_ARRAY;
private boolean useTailRecursion = false;
private int bindingSlot;
private boolean isWithinDeclaredStreamableConstruct;
/**
* Construct a CallTemplate instruction.
* @param template the template to be called. This is provisional, and applies only if the template
* is not overridden in another package; the template supplied will be the one with
* matching name in the same package. Null in the case of saxon:call-template, and also
* in the case where the supplied name is xsl:original
* @param calledTemplateName the name of the template to be called; null in the case of saxon:call-template
* where this is established dynamically
* @param useTailRecursion true if the call is potentially tail recursive
* where the name of the called template is to be calculated dynamically
*/
public CallTemplate(NamedTemplate template, StructuredQName calledTemplateName, boolean useTailRecursion, boolean inStreamable) {
this.template = template;
this.calledTemplateName = calledTemplateName;
this.useTailRecursion = useTailRecursion;
this.isWithinDeclaredStreamableConstruct = inStreamable;
}
/**
* Set the actual parameters on the call
*
* @param actualParams the parameters that are not tunnel parameters
* @param tunnelParams the tunnel parameters
*/
public void setActualParameters(
/*@NotNull*/ WithParam[] actualParams,
/*@NotNull*/ WithParam[] tunnelParams) {
this.actualParams = actualParams;
this.tunnelParams = tunnelParams;
for (WithParam actualParam : actualParams) {
adoptChildExpression(actualParam.getSelectExpression());
}
for (WithParam tunnelParam : tunnelParams) {
adoptChildExpression(tunnelParam.getSelectExpression());
}
}
/**
* Get the symbolic name of the template being called. This is essentially the component kind (template)
* plus the QName of the target template
*
* @return the symbolic name of the target template, or null in the case of saxon:call-template where
* the name is defined dynamically
*/
public SymbolicName getSymbolicName() {
return calledTemplateName == null ? null : new SymbolicName(StandardNames.XSL_TEMPLATE, calledTemplateName);
}
public Component getTarget() {
return template.getDeclaringComponent();
}
public Component getFixedTarget() {
Component c = getTarget();
Visibility v = c.getVisibility();
if (v == Visibility.PRIVATE || v == Visibility.FINAL) {
return c;
} else {
return null;
}
}
/**
* Get the actual parameters passed to the called template
*
* @return the non-tunnel parameters
*/
/*@NotNull*/
public WithParam[] getActualParams() {
return actualParams;
}
/**
* Get the tunnel parameters passed to the called template
*
* @return the tunnel parameters
*/
/*@NotNull*/
public WithParam[] getTunnelParams() {
return tunnelParams;
}
/**
* Set the target template
* @param target the target template
*/
public void setTargetTemplate(NamedTemplate target) {
this.template = target;
};
/**
* Get the target template
*
* @return the target template
*/
public NamedTemplate getTargetTemplate() {
return template;
}
/**
* Ask whether this is a tail call
*
* @return true if this is a tail call
*/
public boolean usesTailRecursion() {
return useTailRecursion;
}
/**
* Return the name of this instruction.
*/
public int getInstructionNameCode() {
return StandardNames.XSL_CALL_TEMPLATE;
}
/**
* Set the binding slot to be used. This is the offset within the binding vector of the containing
* component where the actual target template is to be found. The target template is not held directly
* in the CallTemplate instruction itself because it can be overridden in a using package.
*
* @param slot the offset in the binding vector of the containing package where the target template
* can be found.
*/
public void setBindingSlot(int slot) {
bindingSlot = slot;
}
/**
* Get the binding slot to be used. This is the offset within the binding vector of the containing
* component where the actual target template is to be found.
*
* @return the offset in the binding vector of the containing package where the target template
* can be found.
*/
public int getBindingSlot() {
return bindingSlot;
}
/**
* Simplify an expression. This performs any static optimization (by rewriting the expression
* as a different expression).
*
* @return the simplified expression
* @throws XPathException if an error is discovered during expression
* rewriting
*/
/*@NotNull*/
public Expression simplify() throws XPathException {
WithParam.simplify(actualParams);
WithParam.simplify(tunnelParams);
return this;
}
/*@NotNull*/
public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
WithParam.typeCheck(actualParams, visitor, contextInfo);
WithParam.typeCheck(tunnelParams, visitor, contextInfo);
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(wp.getVariableQName());
if (lp != null) {
SequenceType req = lp.getRequiredType();
RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.PARAM, wp.getVariableQName().getDisplayName(), p);
Expression select = TypeChecker.staticTypeCheck(
wp.getSelectExpression(), req, backwards, role, visitor);
wp.setSelectExpression(this, select);
wp.setTypeChecked(true);
}
}
}
return this;
}
/*@NotNull*/
public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo 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
*
* @return the static item type of the instruction
*/
/*@NotNull*/
public ItemType getItemType() {
if (template == null) {
return AnyItemType.getInstance();
} else {
return template.getRequiredType().getPrimaryType();
}
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
* @param rebindings
*/
/*@NotNull*/
public Expression copy(RebindingMap rebindings) {
CallTemplate ct = new CallTemplate(template, calledTemplateName, useTailRecursion, isWithinDeclaredStreamableConstruct);
ExpressionTool.copyLocationInfo(this, ct);
ct.actualParams = WithParam.copy(ct, actualParams, rebindings);
ct.tunnelParams = WithParam.copy(ct, tunnelParams, rebindings);
return ct;
}
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;
}
@Override
public Iterable operands() {
ArrayList list = new ArrayList(10);
WithParam.gatherOperands(this, actualParams, list);
WithParam.gatherOperands(this, tunnelParams, list);
return list;
}
/**
* Handle promotion offers, that is, non-local tree rewrites.
*
* @param offer The type of rewrite being offered
* @throws net.sf.saxon.trans.XPathException
*
*/
protected void promoteChildren(PromotionOffer offer) throws XPathException {
WithParam.promoteParams(actualParams, offer);
WithParam.promoteParams(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 {
NamedTemplate t;
Component target = getFixedTarget();
if (bindingSlot >= 0) {
target = context.getTargetComponent(bindingSlot);
if (target.getVisibility() == Visibility.ABSENT) {
XPathException err = new XPathException("Cannot call a template declared with visibility='ABSENT'", "XTDE3052");
err.setLocation(getLocation());
throw err;
}
}
t = (NamedTemplate) target.getCode();
XPathContextMajor c2 = context.newContext();
c2.setCurrentComponent(target);
c2.setOrigin(this);
c2.openStackFrame(t.getStackFrameMap());
c2.setLocalParameters(assembleParams(context, actualParams));
c2.setTunnelParameters(assembleTunnelParams(context, tunnelParams));
if (isWithinDeclaredStreamableConstruct) {
c2.setCurrentGroupIterator(null);
}
try {
TailCall tc = t.expand(c2);
while (tc != null) {
tc = tc.processLeavingTail();
}
} catch (StackOverflowError e) {
XPathException err = new XPathException("Too many nested template or function calls. The stylesheet may be looping.");
err.setLocation(getLocation());
err.setXPathContext(context);
throw err;
}
}
/**
* 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) {
Component targetComponent;
if (bindingSlot >= 0) {
targetComponent = context.getTargetComponent(bindingSlot);
} else {
targetComponent = getFixedTarget();
}
if (targetComponent == null) {
throw new XPathException("Internal Saxon error: No binding available for call-template instruction", SaxonErrorCode.SXPK0001, this.getLocation());
}
if (targetComponent.getVisibility() == Visibility.ABSENT) {
throw new XPathException("Cannot call a template declared with visibility=absent", "XTDE3052", this.getLocation());
}
// 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(targetComponent, params, tunnels, this, context);
} else {
process(context);
return null;
}
}
public StructuredQName getObjectName() {
return template == null ? null : template.getTemplateName();
}
/**
* Export of expression structure. The abstract expression tree
* is written to the supplied output destination.
*/
public void export(ExpressionPresenter out) throws XPathException {
out.startElement("callT", this);
if (template != null && template.getTemplateName() != null) {
out.emitAttribute("name", template.getTemplateName());
}
out.emitAttribute("bSlot", ""+getBindingSlot());
if (isWithinDeclaredStreamableConstruct) {
out.emitAttribute("flags", "d");
}
if (actualParams.length > 0) {
WithParam.exportParameters(actualParams, out, false);
}
if (tunnelParams.length > 0) {
WithParam.exportParameters(tunnelParams, out, true);
}
out.endElement();
}
/**
* 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 Component targetComponent;
private ParameterSet params;
private ParameterSet tunnelParams;
private CallTemplate instruction;
private XPathContext evaluationContext;
/**
* Construct a CallTemplatePackage that contains information about a call.
*
* @param targetComponent 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 instruction the xsl:call-template instruction
* @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(Component targetComponent,
ParameterSet params,
ParameterSet tunnelParams,
CallTemplate instruction,
XPathContext evaluationContext) {
this.targetComponent = targetComponent;
if (!(targetComponent.getCode() instanceof NamedTemplate)) {
throw new ClassCastException("Target of call-template must be a named template");
}
this.params = params;
this.tunnelParams = tunnelParams;
this.instruction = instruction;
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.
NamedTemplate template = (NamedTemplate)targetComponent.getCode();
XPathContextMajor c2 = evaluationContext.newContext();
c2.setCurrentComponent(targetComponent);
c2.setOrigin(instruction);
c2.setLocalParameters(params);
c2.setTunnelParameters(tunnelParams);
c2.openStackFrame(template.getStackFrameMap());
// System.err.println("Tail call on template");
return template.expand(c2);
}
}
@Override
public String toString() {
// fallback implementation
FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.C64);
buff.append("CallTemplate#");
buff.append(template.getObjectName().getDisplayName());
boolean first = true;
for (WithParam p : getActualParams()) {
buff.append(first ? "(" : ", ");
buff.append(p.getVariableQName().getDisplayName());
buff.append("=");
buff.append(p.getSelectExpression().toString());
first = false;
}
if (!first) {
buff.append(")");
}
return buff.toString();
}
@Override
public String toShortString() {
// fallback implementation
FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.C64);
buff.append("CallTemplate#");
buff.append(template.getObjectName().getDisplayName());
return buff.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy