All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.expr.instruct.TemplateRule Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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.ContextItemStaticInfo;
import net.sf.saxon.expr.parser.Location;
import net.sf.saxon.expr.parser.RebindingMap;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.LocationKind;
import net.sf.saxon.trans.Rule;
import net.sf.saxon.trans.RuleTarget;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.SequenceType;

import java.util.ArrayList;
import java.util.List;

/**
 * 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.

*/ public class TemplateRule implements RuleTarget, Location { // The body of the template is represented by an expression, // which is responsible for any type checking that's needed. private Expression body; private Pattern matchPattern; private boolean hasRequiredParams; private boolean bodyIsTailCallReturner; private SequenceType requiredType; private boolean declaredStreamable; private ItemType requiredContextItemType = AnyItemType.getInstance(); private boolean mayOmitContextItem = true; private boolean maySupplyContextItem = true; private SlotManager stackFrameMap; private PackageData packageData; private String systemId; private int lineNumber; private List rules = new ArrayList(); private List slaveCopies = 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; } public Expression getBody() { return body; } /** * Set the required context item type. Used when there is an xsl:context-item child element * * @param type the required context item type * @param mayBeOmitted true if the context item may be absent * @param mayBeSupplied true if the context item may be supplied */ public void setContextItemRequirements(ItemType type, boolean mayBeOmitted, boolean mayBeSupplied) { requiredContextItemType = type; mayOmitContextItem = mayBeOmitted; maySupplyContextItem = mayBeSupplied; } 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 */ public void setBody(Expression body) { this.body = body; bodyIsTailCallReturner = (body instanceof TailCallReturner); } public void setStackFrameMap(SlotManager map) { stackFrameMap = map; } public SlotManager getStackFrameMap() { return stackFrameMap; } // @Override // public void allocateAllBindingSlots(StylesheetPackage pack) { // super.allocateAllBindingSlots(pack); // if (matchPattern != null) { // allocateBindingSlotsRecursive(pack, this, matchPattern); // } // } /** * Set whether this template has one or more required parameters * * @param has true if the template has at least one required parameter */ public void setHasRequiredParams(boolean has) { hasRequiredParams = has; } /** * Ask whether this template has one or more required parameters * * @return true if this template has at least one required parameter */ public boolean hasRequiredParams() { return hasRequiredParams; } /** * 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 */ 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; } /** * 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) { this.declaredStreamable = streamable; } /** * Ask whether or not this template is declared as streamable * * @return true if the template belongs to a streamable mode; 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 boolean isDeclaredStreamable() { return declaredStreamable; } public int getContainerGranularity() { return 0; } public PackageData getPackageData() { return packageData; } public void setPackageData(PackageData data) { this.packageData = data; } public String getPublicId() { return null; } public String getSystemId() { return systemId; } public void setSystemId(String id) { this.systemId = id; } public int getLineNumber() { return lineNumber; } public void setLineNumber(int line) { this.lineNumber = line; } public int getColumnNumber() { return -1; } /** * 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. */ public Location saveLocation() { return this; } public ItemType getRequiredContextItemType() { return requiredContextItemType; } public boolean isMayOmitContextItem() { return mayOmitContextItem; } public boolean isMaySupplyContextItem() { return maySupplyContextItem; } public List getLocalParams() { List result = new ArrayList(); gatherLocalParams(getInterpretedBody(), result); return result; } private static void gatherLocalParams(Expression exp, List result) { if (exp instanceof LocalParamSetter) { result.add(((LocalParamSetter) exp).getBinding()); } else { for (Operand o : exp.operands()) { gatherLocalParams(o.getChildExpression(), result); } } } /** * Process the template, without returning any tail calls. This path is used by * xsl:apply-imports and xsl:next-match * * @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(XPathContextMajor context) throws XPathException { TailCall tc = applyLeavingTail(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 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(XPathContext context) throws XPathException { if (requiredContextItemType != AnyItemType.getInstance() && !requiredContextItemType.matches(context.getContextItem(), context.getConfiguration().getTypeHierarchy())) { XPathException err = new XPathException("The template requires a context item of type " + requiredContextItemType + ", but the supplied context item has type " + Type.getItemType(context.getContextItem(), context.getConfiguration().getTypeHierarchy()), "XTTE0590"); err.setLocation(this); err.setIsTypeError(true); throw err; } if (!maySupplyContextItem) { context = context.newMinorContext(); context.setCurrentIterator(null); } try { if (bodyIsTailCallReturner) { return ((TailCallReturner) body).processLeavingTail(context); } else { body.process(context); return null; } } catch (XPathException e) { e.maybeSetLocation(this); e.maybeSetContext(context); throw e; } catch (Exception e2) { String message = "Internal error evaluating template rule " + (getLineNumber() > 0 ? " at line " + getLineNumber() : "") + (getSystemId() != null ? " in module " + getSystemId() : ""); throw new RuntimeException(message, e2); } } /** * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name * (values in {@link StandardNames}: all less than 1024) * or it will be a constant in class {@link LocationKind}. */ public int getConstructType() { return LocationKind.TEMPLATE; } /** * Output diagnostic explanation to an ExpressionPresenter */ public void export(ExpressionPresenter presenter) throws XPathException{ presenter.startElement("template"); explainProperties(presenter); presenter.emitAttribute("slots", "" + getStackFrameMap().getNumberOfVariables()); //presenter.emitAttribute("binds", "" + getDeclaringComponent().getComponentBindings().size()); if (matchPattern != null) { presenter.setChildRole("match"); matchPattern.export(presenter); } if (getBody() != null) { presenter.setChildRole("body"); getBody().export(presenter); } presenter.endElement(); } public void explainProperties(ExpressionPresenter presenter) { if (getRequiredContextItemType() != AnyItemType.getInstance()) { presenter.emitAttribute("cxt", getRequiredContextItemType().toString()); } String flags = ""; if (mayOmitContextItem) { flags = "o"; } if (maySupplyContextItem) { flags += "s"; } presenter.emitAttribute("flags", flags); if (getRequiredType() != SequenceType.ANY_SEQUENCE) { presenter.emitAttribute("as", getRequiredType().toString()); } presenter.emitAttribute("line", getLineNumber() + ""); presenter.emitAttribute("module", getSystemId()); if (isDeclaredStreamable()) { presenter.emitAttribute("streamable", "1"); } } public Expression getInterpretedBody() { Expression original = body; return original; } /** * Create a copy of a template rule. This is needed when copying a rule from the "omniMode" (mode=#all) * to a specific mode. Because we want the rules to be chained in the right order within the mode object, * we create the copy as soon as we know it is needed. The problem is that at this stage many of the properties * of the template rule are still uninitialised. So we mark the new copy as a slave of the original, and at * the end of the compilation process we update all the slave copies to match the properties of the original. */ public TemplateRule copy() { TemplateRule tr = new TemplateRule(); if (body == null || matchPattern == null) { slaveCopies.add(tr); } else { copyTo(tr); } return tr; } /** * Update the properties of template rules that have been marked as slave copies of this one (typically the same * template, but in a different mode). */ public void updateSlaveCopies() { for (TemplateRule tr : slaveCopies) { copyTo(tr); } } private void copyTo(TemplateRule tr) { if (body != null) { tr.body = body.copy(new RebindingMap()); } if (matchPattern != null) { tr.matchPattern = matchPattern.copy(new RebindingMap()); } tr.hasRequiredParams = hasRequiredParams; tr.bodyIsTailCallReturner = bodyIsTailCallReturner; tr.requiredType = requiredType; tr.declaredStreamable = declaredStreamable; // ? this can vary from one mode to another tr.requiredContextItemType = requiredContextItemType; tr.mayOmitContextItem = mayOmitContextItem; tr.maySupplyContextItem = maySupplyContextItem; tr.stackFrameMap = stackFrameMap; tr.packageData = packageData; tr.systemId = systemId; tr.lineNumber = lineNumber; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy