
net.sf.saxon.expr.instruct.TemplateRule 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) 2018-2023 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.event.Outputter;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.elab.PushEvaluator;
import net.sf.saxon.expr.parser.RebindingMap;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.s9api.Location;
import net.sf.saxon.style.Compilation;
import net.sf.saxon.style.ComponentDeclaration;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.TraceableComponent;
import net.sf.saxon.trans.Mode;
import net.sf.saxon.trans.UncheckedXPathException;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.trans.rules.Rule;
import net.sf.saxon.trans.rules.RuleTarget;
import net.sf.saxon.transpile.CSharpReplaceBody;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.SequenceType;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
/**
* The runtime object corresponding to an xsl:template element in the stylesheet.
* Note that the Template object no longer has precedence information associated with it; this is now
* only in the Rule object that references this Template. This allows two rules to share the same template,
* with different precedences. This occurs when a stylesheet module is imported more than once, from different
* places, with different import precedences.
* From Saxon 9.7, the old Template class is split into NamedTemplate and TemplateRule.
* From Saxon 9.8, a subclass is used in Saxon-EE
*/
public class TemplateRule implements RuleTarget, Location, ExpressionOwner, TraceableComponent {
// The body of the template is represented by an expression,
// which is responsible for any type checking that's needed.
protected Mode mode;
protected Expression body;
protected PushEvaluator bodyEvaluator;
protected Pattern matchPattern;
private SequenceType requiredType;
private boolean declaredStreamable;
private ItemType requiredContextItemType = AnyItemType.getInstance();
private boolean absentFocus;
private SlotManager stackFrameMap;
private PackageData packageData;
private String systemId;
private int lineNumber;
private int columnNumber;
private final List rules = new ArrayList<>();
/**
* Create a template
*/
public TemplateRule() {
}
/**
* Set the match pattern used with this template
*
* @param pattern the match pattern (may be null for a named template)
*/
public void setMatchPattern(Pattern pattern) {
if (matchPattern != pattern) {
for (Rule r : rules) {
r.setPattern(pattern);
}
}
matchPattern = pattern;
}
/**
* Set the mode used by this template rule
* @param m the mode
*/
public void setMode(Mode m) {
this.mode = m;
}
/**
* Get the mode used by this template rule
* @return the mode
*/
public Mode getMode() {
return mode;
}
@Override
public Expression getBody() {
return body;
}
@Override
public Expression getChildExpression() {
return body;
}
@Override
public Location getLocation() {
return this;
}
/**
* Get the properties of this object to be included in trace messages, by supplying
* the property values to a supplied consumer function
*
* @param consumer the function to which the properties should be supplied, as (property name,
* value) pairs.
*/
@Override
public void gatherProperties(BiConsumer consumer) {
consumer.accept("match", getMatchPattern().toShortString());
}
/**
* Set the required context item type. Used when there is an xsl:context-item child element
*
* @param type the required context item type
* @param absentFocus true if the context item is treated as absent even if supplied (use="absent")
*/
public void setContextItemRequirements(ItemType type, boolean absentFocus) {
requiredContextItemType = type;
this.absentFocus = absentFocus;
}
public int getComponentKind() {
return StandardNames.XSL_TEMPLATE;
}
/**
* Get the match pattern used with this template
*
* @return the match pattern, or null if this is a named template with no match pattern. In the case of
* a template rule whose pattern is a union pattern, this will be the original union pattern; the individual
* Rule objects contain the branches of the union pattern.
*/
public Pattern getMatchPattern() {
return matchPattern;
}
/**
* Set the expression that forms the body of the template
*
* @param body the body of the template
*/
@Override
public void setBody(Expression body) {
this.body = body;
}
public void setStackFrameMap(SlotManager map) {
stackFrameMap = map;
}
public SlotManager getStackFrameMap() {
return stackFrameMap;
}
/**
* Set the required type to be returned by this template
*
* @param type the required type as defined in the "as" attribute on the xsl:template element
*/
public void setRequiredType(SequenceType type) {
requiredType = type;
}
/**
* Get the required type to be returned by this template
*
* @return the required type as defined in the "as" attribute on the xsl:template element
*/
public SequenceType getRequiredType() {
if (requiredType == null) {
return SequenceType.ANY_SEQUENCE;
} else {
return requiredType;
}
}
/**
* Register a rule for which this is the target
*
* @param rule a rule in which this is the target
*/
@Override
public void registerRule(Rule rule) {
rules.add(rule);
}
/**
* Get the rules that use this template. For a template with no match pattern, this will be
* an empty list. For a union pattern, there will be one rule for each branch of the union.
*
* @return the rules corresponding to this template.
*/
public List getRules() {
return rules;
}
public int getContainerGranularity() {
return 0;
}
public PackageData getPackageData() {
return packageData;
}
public void setPackageData(PackageData data) {
this.packageData = data;
}
@Override
public String getPublicId() {
return null;
}
@Override
public String getSystemId() {
return systemId;
}
public void setSystemId(String id) {
this.systemId = id;
}
@Override
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int line) {
this.lineNumber = line;
}
public void setColumnNumber(int col) {
this.columnNumber = col;
}
@Override
public int getColumnNumber() {
return columnNumber;
}
/**
* Get an immutable copy of this Location object. By default Location objects may be mutable, so they
* should not be saved for later use. The result of this operation holds the same location information,
* but in an immutable form.
*/
@Override
public Location saveLocation() {
return this;
}
public ItemType getRequiredContextItemType() {
return requiredContextItemType;
}
public boolean isAbsentFocus() {
return absentFocus;
}
public List getLocalParams() {
List result = new ArrayList<>();
gatherLocalParams(getBody(), result);
return result;
}
private static void gatherLocalParams(Expression exp, List result) {
if (exp instanceof LocalParam) {
result.add((LocalParam) exp);
} else {
for (Operand o : exp.operands()) {
gatherLocalParams(o.getChildExpression(), result);
}
}
}
/**
* Prepare for JIT compilation.
*
* @param compilation the XSLT compilation
* @param decl the component declaration of this template rule
*/
public void prepareInitializer(Compilation compilation, ComponentDeclaration decl) {
// No action in Saxon-HE
}
/**
* Ensure that any first-time initialization has been done. Used in Saxon-EE
* to do JIT compilation
*
* @throws XPathException if any error occurs
*/
public void initialize() throws XPathException {
// No action needed in Saxon-HE
}
/**
* Process the template, without returning any tail calls. This path is used by
* xsl:apply-imports and xsl:next-match
*
*
* @param output the destination for the result
* @param context The dynamic context, giving access to the current node,
* @throws XPathException if a dynamic error occurs while evaluating
* the template
*/
public void apply(Outputter output, XPathContextMajor context) throws XPathException {
TailCall tc = applyLeavingTail(output, context);
while (tc != null) {
tc = tc.processLeavingTail();
}
}
/**
* Process this template, with the possibility of returning a tail call package if the template
* contains any tail calls that are to be performed by the caller.
*
* @param output the destination for the result
* @param context the XPath dynamic context
* @return null if the template exited normally; but if it was a tail call, details of the call
* that hasn't been made yet and needs to be made by the caller
* @throws XPathException if a dynamic error occurs while evaluating
* the template
*/
public TailCall applyLeavingTail(Outputter output, XPathContext context) throws XPathException {
//initialize();
TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
if (requiredContextItemType != AnyItemType.getInstance() &&
!requiredContextItemType.matches(context.getContextItem(), th)) {
RoleDiagnostic role = new RoleDiagnostic(
RoleDiagnostic.MISC, "context item for the template rule", 0);
String message = role.composeErrorMessage(requiredContextItemType, context.getContextItem(), th);
throw new XPathException(message, "XTTE0590").withLocation(this).asTypeError();
}
if (absentFocus) {
context = context.newMinorContext();
context.setCurrentIterator(null);
}
try {
ensureBodyEvaluatorExists();
return bodyEvaluator.processLeavingTail(output, context);
} catch (UncheckedXPathException e) {
throw e.getXPathException().maybeWithLocation(this).maybeWithContext(context);
} catch (XPathException e) {
throw e.maybeWithLocation(this).maybeWithContext(context);
} catch (Exception e2) {
String message = "Internal error evaluating template rule "
+ (getLineNumber() > 0 ? " at line " + getLineNumber() : "")
+ (getSystemId() != null ? " in module " + getSystemId() : "");
e2.printStackTrace();
throw new RuntimeException(message, e2);
}
}
@CSharpReplaceBody(code="ensureBodyEvaluatorExistsCS();")
private void ensureBodyEvaluatorExists() {
if (bodyEvaluator == null) {
bodyEvaluator = atomicBodyEvaluator.updateAndGet(bodyEvaluator -> bodyEvaluator == null ? body.makeElaborator().elaborateForPush() : bodyEvaluator);
}
}
private AtomicReference atomicBodyEvaluator = new AtomicReference<>(null);
/**
* Output diagnostic explanation to an ExpressionPresenter
*/
@Override
public void export(ExpressionPresenter presenter) throws XPathException {
// NOT USED - see Rule.export
throw new UnsupportedOperationException();
}
/**
* Say whether or not this template is declared as streamable
*
* @param streamable true if the template belongs to a streamable mode; set to false if it does not belong
* to a streamable mode, or if it is found that the template is not actually streamable, and fallback
* to non-streaming has been requested.
*/
public void setDeclaredStreamable(boolean streamable) {
// Overridden in Saxon-EE
}
public boolean isDeclaredStreamable() {
// Overridden in Saxon-EE
return false;
}
public void explainProperties(ExpressionPresenter presenter) throws XPathException {
if (getRequiredContextItemType() != AnyItemType.getInstance()) {
SequenceType st = SequenceType.makeSequenceType(getRequiredContextItemType(), StaticProperty.EXACTLY_ONE);
presenter.emitAttribute("cxt", st.toAlphaCode());
}
String flags = "";
if (!absentFocus) {
flags += "s";
}
presenter.emitAttribute("flags", flags);
if (getRequiredType() != SequenceType.ANY_SEQUENCE) {
presenter.emitAttribute("as", getRequiredType().toAlphaCode());
}
presenter.emitAttribute("line", getLineNumber() + "");
presenter.emitAttribute("module", getSystemId());
if (isDeclaredStreamable()) {
presenter.emitAttribute("streamable", "1");
}
}
protected void copyTo(TemplateRule tr) {
if (body != null) {
tr.body = body.copy(new RebindingMap());
}
if (matchPattern != null) {
tr.matchPattern = matchPattern.copy(new RebindingMap());
}
tr.requiredType = requiredType;
tr.declaredStreamable = declaredStreamable; // ? this can vary from one mode to another
tr.requiredContextItemType = requiredContextItemType;
tr.absentFocus = absentFocus;
tr.stackFrameMap = stackFrameMap;
tr.packageData = packageData;
tr.systemId = systemId;
tr.lineNumber = lineNumber;
}
@Override
public void setChildExpression(Expression expr) {
setBody(expr);
}
@Override
public StructuredQName getObjectName() {
return null;
}
@Override
public String getTracingTag() {
return "xsl:template";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy