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

net.sf.saxon.style.XSLTemplate Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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.style;

import net.sf.saxon.expr.*;
import net.sf.saxon.expr.instruct.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.lib.Logger;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.trace.LocationKind;
import net.sf.saxon.trans.*;
import net.sf.saxon.tree.iter.AxisIterator;
import net.sf.saxon.tree.linked.NodeImpl;
import net.sf.saxon.type.*;
import net.sf.saxon.value.DecimalValue;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.Whitespace;

import java.util.*;

/**
 * An xsl:template element in the style sheet.
 */

public final class XSLTemplate extends StyleElement implements StylesheetComponent {

    private String matchAtt = null;
    private String modeAtt = null;
    private String nameAtt = null;
    private String priorityAtt = null;
    private String asAtt = null;
    private String visibilityAtt = null;

    private StructuredQName[] modeNames;
    private String diagnosticId;
    private Pattern match;
    private boolean prioritySpecified;
    private double priority;
    private SlotManager stackFrameMap;
    // A compiled named template exists if the template has a name
    private NamedTemplate compiledNamedTemplate = new NamedTemplate();
    // A set of compiled template rules exists if the template has a match pattern: one TemplateRule for each mode
    private Map compiledTemplateRules = new HashMap();
    private SequenceType requiredType = null;
    private boolean hasRequiredParams = false;
    private boolean isTailRecursive = false;
    private Visibility visibility = Visibility.PRIVATE;
    private ItemType requiredContextItemType = AnyItemType.getInstance();
    private boolean mayOmitContextItem = true;
    private boolean maySupplyContextItem = true;
    private Expression body;

    /**
     * Get the corresponding Procedure object that results from the compilation of this
     * StylesheetProcedure
     */
    public NamedTemplate getCompiledProcedure() {
        return compiledNamedTemplate;
    }

    @Override
    public void setCompilation(Compilation compilation) {
        super.setCompilation(compilation);
        //compiledNamedTemplate.setPackageData(compilation.getPackageData());
    }

    /**
     * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
     * (including xsl:include and xsl:import).
     *
     * @return true for this element
     */

    @Override
    public boolean isDeclaration() {
        return true;
    }

    /**
     * Determine whether this type of element is allowed to contain a template-body
     *
     * @return true: yes, it may contain a template-body
     */

    public boolean mayContainSequenceConstructor() {
        return true;
    }

    protected boolean mayContainParam(String attName) {
        return true;
    }

    protected boolean isWithinDeclaredStreamableConstruct() {
        try {
            for (Mode m : getApplicableModes()) {
                if (m.isDeclaredStreamable()) {
                    return true;
                }
            }
        } catch (XPathException e) {
            return false;
        }
        return false;
    }

    /**
     * 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;
    }

    /**
     * Specify that xsl:param and xsl:context-item are permitted children
     */

    protected boolean isPermittedChild(StyleElement child) {
        return child instanceof XSLLocalParam || child.getFingerprint() == StandardNames.XSL_CONTEXT_ITEM;
    }

    /**
     * Return the name of this template. Note that this may
     * be called before prepareAttributes has been called.
     *
     * @return the name of the template as a Structured QName.
     */

    /*@Nullable*/
    public StructuredQName getTemplateName() {

        //We use null to mean "not yet evaluated"

        try {
            if (getObjectName() == null) {
                // allow for forwards references
                String nameAtt = getAttributeValue("", "name");
                if (nameAtt != null) {
                    setObjectName(makeQName(nameAtt));
                }
            }
            return getObjectName();
        } catch (NamespaceException err) {
            return null;          // the errors will be picked up later
        } catch (XPathException err) {
            return null;
        }
    }

    public SymbolicName getSymbolicName() {
        if (getTemplateName() == null) {
            return null;
        } else {
            return new SymbolicName(StandardNames.XSL_TEMPLATE, getTemplateName());
        }
    }

    public ItemType getRequiredContextItemType() {
        return requiredContextItemType;
    }

    public boolean isMayOmitContextItem() {
        return mayOmitContextItem;
    }

    public boolean isMaySupplyContextItem() {
        return maySupplyContextItem;
    }


    public void checkCompatibility(Component component) throws XPathException {
        NamedTemplate other = (NamedTemplate) component.getCode();
        if (!getSymbolicName().equals(other.getSymbolicName())) {
            throw new IllegalArgumentException();
        }

        SequenceType req = requiredType == null ? SequenceType.ANY_SEQUENCE : requiredType;
        if (!req.equals(other.getRequiredType())) {
            compileError("The overriding template has a different required type from the overridden template", "XTSE3070");
        }

        if (!requiredContextItemType.equals(other.getRequiredContextItemType()) ||
                mayOmitContextItem != other.isMayOmitContextItem() ||
                maySupplyContextItem != other.isMaySupplyContextItem()) {
            compileError("The required context item for the overriding template differs from that of the overridden template", "XTSE3070");
        }

        List otherParams = other.getLocalParams();
        Set overriddenParams = new HashSet();
        for (LocalParam lp0 : otherParams) {
            XSLLocalParam lp1 = getParam(lp0.getVariableQName());
            if (lp1 == null) {
                compileError("The overridden template declares a parameter " +
                        lp0.getVariableQName().getDisplayName() + " which is not declared in the overriding template", "XTSE3070");
                return;
            }
            if (!lp1.getRequiredType().equals(lp0.getRequiredType())) {
                lp1.compileError("The parameter " +
                        lp0.getVariableQName().getDisplayName() + " has a different required type in the overridden template", "XTSE3070");
                return;
            }
            if (lp1.isRequiredParam() != lp0.isRequiredParam()) {
                lp1.compileError("The parameter " +
                        lp0.getVariableQName().getDisplayName() + " is " +
                        (lp1.isRequiredParam() ? "required" : "optional") +
                        " in the overriding template, but " +
                        (lp0.isRequiredParam() ? "required" : "optional") +
                        " in the overridden template", "XTSE3070");
                return;
            }
            if (lp1.isTunnelParam() != lp0.isTunnelParam()) {
                lp1.compileError("The parameter " +
                        lp0.getVariableQName().getDisplayName() + " is a " +
                        (lp1.isTunnelParam() ? "tunnel" : "non-tunnel") +
                        " parameter in the overriding template, but " +
                        (lp0.isTunnelParam() ? "tunnel" : "non-tunnel") +
                        " parameter in the overridden template", "XTSE3070");
                return;
            }
            overriddenParams.add(lp0.getVariableQName());
        }

        AxisIterator params = iterateAxis(AxisInfo.CHILD);
        NodeInfo param;
        while ((param = params.next()) != null) {
            if (param instanceof XSLLocalParam &&
                    !overriddenParams.contains(((XSLLocalParam) param).getObjectName()) &&
                    ((XSLLocalParam) param).isRequiredParam()) {
                ((XSLLocalParam) param).compileError(
                        "An overriding template cannot introduce a required parameter that is not declared in the overridden template", "XTSE3070");
            }
        }

    }

    public XSLLocalParam getParam(StructuredQName name) {
        AxisIterator params = iterateAxis(AxisInfo.CHILD);
        NodeInfo param;
        while ((param = params.next()) != null) {
            if (param instanceof XSLLocalParam && name.equals(((XSLLocalParam) param).getObjectName())) {
                return (XSLLocalParam) param;
            }
        }
        return null;
    }


    public void prepareAttributes() throws XPathException {

        AttributeCollection atts = getAttributeList();

        for (int a = 0; a < atts.getLength(); a++) {
            String f = atts.getQName(a);
            if (f.equals("mode")) {
                modeAtt = Whitespace.trim(atts.getValue(a));
            } else if (f.equals("name")) {
                nameAtt = Whitespace.trim(atts.getValue(a));
            } else if (f.equals("match")) {
                matchAtt = atts.getValue(a);
            } else if (f.equals("priority")) {
                priorityAtt = Whitespace.trim(atts.getValue(a));
            } else if (f.equals("as")) {
                asAtt = atts.getValue(a);
            } else if (f.equals("visibility")) {
                visibilityAtt = Whitespace.trim(atts.getValue(a));
            } else {
                checkUnknownAttribute(atts.getNodeName(a));
            }
        }
        try {
            if (modeAtt == null) {
                if (matchAtt != null) {
                    // XSLT 3.0 allows the default mode to be specified on any element
                    StructuredQName defaultMode = getDefaultMode();
                    if (defaultMode == null) {
                        defaultMode = Mode.UNNAMED_MODE_NAME;
                    }
                    modeNames = new StructuredQName[1];
                    modeNames[0] = defaultMode;
                }
            } else {
                if (matchAtt == null) {
                    compileError("The mode attribute must be absent if the match attribute is absent", "XTSE0500");
                }
                getModeNames();
            }
        } catch (XPathException err) {
            err.maybeSetErrorCode("XTSE0280");
            if (err.getErrorCodeLocalPart().equals("XTSE0020")) {
                err.setErrorCode("XTSE0550");
            }
            err.setIsStaticError(true);
            compileError(err);
        }

        try {
            if (nameAtt != null) {
                StructuredQName qName = makeQName(nameAtt);
                setObjectName(qName);
                compiledNamedTemplate.setTemplateName(qName);
                diagnosticId = nameAtt;
            }
        } catch (NamespaceException err) {
            compileError(err.getMessage(), "XTSE0280");
        } catch (XPathException err) {
            err.maybeSetErrorCode("XTSE0280");
            err.setIsStaticError(true);
            compileError(err);
        }

        prioritySpecified = priorityAtt != null;
        if (prioritySpecified) {
            if (matchAtt == null) {
                compileError("The priority attribute must be absent if the match attribute is absent", "XTSE0500");
            }
            try {
                // it's got to be a valid decimal, but we want it as a double, so parse it twice
                if (!DecimalValue.castableAsDecimal(priorityAtt)) {
                    compileError("Invalid numeric value for priority (" + priority + ')', "XTSE0530");
                }
                priority = Double.parseDouble(priorityAtt);
            } catch (NumberFormatException err) {
                // shouldn't happen
                compileError("Invalid numeric value for priority (" + priority + ')', "XTSE0530");
            }
        }

        if (matchAtt != null) {
            match = makePattern(matchAtt, "match");
            if (diagnosticId == null) {
                diagnosticId = "match=\"" + matchAtt + '\"';
                if (modeAtt != null) {
                    diagnosticId += " mode=\"" + modeAtt + '\"';
                }
            }
        }

        if (match == null && nameAtt == null) {
            compileError("xsl:template must have a name or match attribute (or both)", "XTSE0500");
        }
        if (asAtt != null) {
            requiredType = makeSequenceType(asAtt);
        }

        if (visibilityAtt != null) {
            check30attribute("visibility");
            visibility = interpretVisibilityValue(visibilityAtt, "");
            if (nameAtt == null) {
                compileError("xsl:template/@visibility can be specified only if the template has a @name attribute", "XTSE0020");
            }
            compiledNamedTemplate.setDeclaredVisibility(visibility);
        }
    }

    /**
     * Return the list of mode names to which this template rule is applicable.
     * @return the list of mode names. If the mode attribute is absent, #default is assumed.
     * If #default is present explicitly or implicitly, it is replaced by the default mode, taken
     * from the in-scope default-modes attribute, which defaults to #unnamed. The unnamed mode
     * is represented by {@link Mode#UNNAMED_MODE_NAME}. The token #all translates to
     * {@link Mode#OMNI_MODE}.
     * @throws XPathException if the attribute is invalid.
     */

    public StructuredQName[] getModeNames() throws XPathException {
        if (modeNames == null) {
            // modeAtt is a space-separated list of mode names, or "#default", or "#all"
            if (modeAtt == null) {
                modeAtt = getAttributeValue("mode");
                if (modeAtt == null) {
                    modeAtt = "#default";
                }
            }

            int count = 0;
            boolean allModes = false;
            StringTokenizer st = new StringTokenizer(modeAtt, " \t\n\r", false);
            while (st.hasMoreTokens()) {
                st.nextToken();
                count++;
            }

            if (count == 0) {
                compileError("The mode attribute must not be empty", "XTSE0550");
            }

            modeNames = new StructuredQName[count];
            count = 0;
            st = new StringTokenizer(modeAtt, " \t\n\r", false);
            while (st.hasMoreTokens()) {
                String s = st.nextToken();
                StructuredQName mname;
                if ("#default".equals(s)) {
                    mname = getDefaultMode();
                    if (mname == null) {
                        mname = Mode.UNNAMED_MODE_NAME;
                    }
                } else if ("#unnamed".equals(s) && isXslt30Processor()) {
                    mname = Mode.UNNAMED_MODE_NAME;
                } else if ("#all".equals(s)) {
                    allModes = true;
                    mname = Mode.OMNI_MODE;
                } else {
                    try {
                        mname = makeQName(s);
                    } catch (NamespaceException e) {
                        compileError(e.getMessage(), "XTSE0280");
                        mname = Mode.UNNAMED_MODE_NAME;
                    }
                }
                for (int e = 0; e < count; e++) {
                    if (modeNames[e].equals(mname)) {
                        compileError("In the list of modes, the value " + s + " is duplicated", "XTSE0550");
                    }
                }
                modeNames[count++] = mname;
            }
            if (allModes && (count > 1)) {
                compileError("mode='#all' cannot be combined with other modes", "XTSE0550");
            }
        }
        return modeNames;
    }

    public Set getApplicableModes() throws XPathException {
        StructuredQName[] names = getModeNames();
        Set modes = new HashSet(names.length);
        RuleManager mgr = getPrincipalStylesheetModule().getRuleManager();
        for (StructuredQName name : names) {
            if (name.equals(Mode.OMNI_MODE)) {
                modes.addAll(mgr.getAllNamedModes());
            } else {
                Mode mode = mgr.obtainMode(name, false);
                if (mode != null) {
                    modes.add(mode);
                }
            }
        }
        return modes;
    }

    public void validate(ComponentDeclaration decl) throws XPathException {
        stackFrameMap = getConfiguration().makeSlotManager();
        checkTopLevel("XTSE0010", true);

        // the check for duplicates is now done in the buildIndexes() method of XSLStylesheet
        if (match != null) {
            match = typeCheck("match", match);
            if (match.getItemType() instanceof ErrorType) {
                issueWarning(new XPathException("Pattern will never match anything", SaxonErrorCode.SXWN9015, this));
            }
            if (getPrincipalStylesheetModule().isDeclaredModes()) {
                RuleManager manager = getPrincipalStylesheetModule().getRuleManager();
                if (modeNames != null) {
                    for (StructuredQName name : modeNames) {
                        if (name.equals(Mode.UNNAMED_MODE_NAME) && !manager.isUnnamedModeExplicit()) {
                            compileError("The unnamed mode has not been declared in an xsl:mode declaration", "XTSE3085");
                        }
                        if (manager.obtainMode(name, false) == null) {
                            compileError("Mode name " + name.getDisplayName() + " has not been declared in an xsl:mode declaration", "XTSE3085");
                        }
                    }
                } else {
                    if (!manager.isUnnamedModeExplicit()) {
                        compileError("The unnamed mode has not been declared in an xsl:mode declaration", "XTSE3085");
                    }
                }
            }
            if (visibility == Visibility.ABSTRACT) {
                compileError("An abstract template must have no match attribute");
            }
        }

        // See if there are any required parameters.
        AxisIterator kids = iterateAxis(AxisInfo.CHILD);
        boolean hasContent = false;
        NodeImpl param;
        while ((param = (NodeImpl)kids.next()) != null) {
            if (param instanceof StyleElement) {
                if (param.getFingerprint() == StandardNames.XSL_CONTEXT_ITEM) {
                    // no action
                } else if (param instanceof XSLLocalParam) {
                    if (((XSLLocalParam) param).isRequiredParam()) {
                        hasRequiredParams = true;
                    }
                } else {
                    hasContent = true;
                }
            }
        }

        if (visibility == Visibility.ABSTRACT && hasContent) {
            compileError("A template with visibility='abstract' must have no body");
        }

    }


    public void postValidate() throws XPathException {
        isTailRecursive = markTailCalls();
    }

    public void index(ComponentDeclaration decl, PrincipalStylesheetModule top) throws XPathException {
        top.indexNamedTemplate(decl);
    }

    /**
     * Mark tail-recursive calls on templates and functions.
     */

    public boolean markTailCalls() {
        StyleElement last = getLastChildInstruction();
        return last != null && last.markTailCalls();
    }

    /**
     * Compile: creates the executable form of the template
     */

    public void compileDeclaration(Compilation compilation, ComponentDeclaration decl) throws XPathException {

        RetainedStaticContext rsc = makeRetainedStaticContext();
        body = compileSequenceConstructor(compilation, decl, true);
        //body.verifyParentPointers();
        if (body.getRetainedStaticContext() == null) {
            body.setRetainedStaticContext(rsc); // bug 2608
        }
        if (body == null) {
            body = Literal.makeEmptySequence();
        }

        if (getTemplateName() != null) {
            compiledNamedTemplate.setTemplateName(getObjectName());
            compiledNamedTemplate.setPackageData(rsc.getPackageData());
            compiledNamedTemplate.setBody(body);
            compiledNamedTemplate.setStackFrameMap(stackFrameMap);
            compiledNamedTemplate.setSystemId(getSystemId());
            compiledNamedTemplate.setLineNumber(getLineNumber());
            compiledNamedTemplate.setHasRequiredParams(hasRequiredParams);
            compiledNamedTemplate.setRequiredType(requiredType);
            compiledNamedTemplate.setContextItemRequirements(requiredContextItemType, mayOmitContextItem, maySupplyContextItem);
            compiledNamedTemplate.setRetainedStaticContext(rsc);
            compiledNamedTemplate.setDeclaredVisibility(getDeclaredVisibility());
            Component overridden = getOverriddenComponent();
            if (overridden != null) {
                checkCompatibility(overridden);
            }
        }

        if (match != null) {
            for (StructuredQName modeName : modeNames) {
                TemplateRule templateRule = compiledTemplateRules.get(modeName);
                if (templateRule == null) {
                    templateRule = new TemplateRule();
                    compiledTemplateRules.put(modeName, templateRule);
                }
                templateRule.setMatchPattern(match);
                templateRule.setBody(body);
                templateRule.setStackFrameMap(stackFrameMap);
                templateRule.setSystemId(getSystemId());
                templateRule.setLineNumber(getLineNumber());
                templateRule.setHasRequiredParams(hasRequiredParams);
                templateRule.setRequiredType(requiredType);
                templateRule.setContextItemRequirements(requiredContextItemType, mayOmitContextItem, maySupplyContextItem);
            }
            //compiledTemplateRule.setRetainedStaticContext(makeRetainedStaticContext());
        }
        try {
            body = body.simplify();
        } catch (XPathException e) {
            compileError(e);
        }

        if (visibility != Visibility.ABSTRACT) {
            try {
                if (requiredType != null) {
                    RoleDiagnostic role =
                            new RoleDiagnostic(RoleDiagnostic.TEMPLATE_RESULT, diagnosticId, 0);
                    //role.setSourceLocator(new ExpressionLocation(this));
                    role.setErrorCode("XTTE0505");
                    body = TypeChecker.staticTypeCheck(body, requiredType, false, role, makeExpressionVisitor());
                }
            } catch (XPathException err) {
                compileError(err);
            }
        }

        if (getConfiguration().isCompileWithTracing()) {
            // Add trace wrapper code if required
            body = makeTraceInstruction(this, body);
            if (body instanceof TraceExpression) {
                ((TraceExpression) body).setProperty("match", matchAtt);
                ((TraceExpression) body).setProperty("mode", modeAtt);
            }
        }

        if (getTemplateName() != null) {
            compiledNamedTemplate.setBody(body);
        }

        if (match != null) {
            for (TemplateRule rule : compiledTemplateRules.values()) {
                rule.setBody(body);
                rule.updateSlaveCopies();
            }
        }

        // following code needed only for diagnostics
        //body.verifyParentPointers();

    }

    /**
     * Registers the template rule with each Mode that it belongs to.
     *
     * @param declaration Associates this template with a stylesheet module (in principle an xsl:template
     *                    element can be in a document that is imported more than once; these are separate declarations)
     * @throws XPathException if a failure occurs
     */

    public void register(ComponentDeclaration declaration) throws XPathException {
        if (match != null) {
            StylesheetModule module = declaration.getModule();
            RuleManager mgr = getCompilation().getPrincipalStylesheetModule().getRuleManager();
            ExpressionVisitor visitor = ExpressionVisitor.make(getStaticContext());
            for (StructuredQName modeName : getModeNames()) {
                Mode mode = mgr.obtainMode(modeName, false);
                if (mode == null) {
                    if (mgr.existsOmniMode()) {
                        Mode omniMode = mgr.obtainMode(Mode.OMNI_MODE, true);
                        mode = mgr.obtainMode(modeName, true);
                        SimpleMode.copyRules(omniMode.getActivePart(), mode.getActivePart());
                    } else {
                        mode = mgr.obtainMode(modeName, true);
                    }
                } else {
                    boolean ok = getPrincipalStylesheetModule().checkAcceptableModeForPackage(this, mode);
                    if (!ok) {
                        return;
                    }
                }
                Pattern match2 = match;
                String typed = mode.getActivePart().getPropertyValue("typed");
                if ("strict".equals(typed) || "lax".equals(typed)) {
                    try {
                        match2 = match.convertToTypedPattern(typed);
                    } catch (XPathException e) {
                        e.maybeSetLocation(this);
                        throw e;
                    }
                    if (match2 != match) {
                        ContextItemStaticInfo info = new ContextItemStaticInfo(AnyItemType.getInstance(), false);
                        ExpressionTool.copyLocationInfo(match, match2);
                        //match2.setPackageData(match.getPackageData());
                        match2.setOriginalText(match.toString());
                        //match2.setSystemId(match.getSystemId());
                        //match2.setLineNumber(match.getLineNumber());
                        match2 = match2.typeCheck(visitor, info);
                    }
                    if (modeNames.length == 1) {
                        // If this is the only mode for the template, then we can use this enhanced match pattern
                        // for subsequent type-checking of the template body.
                        // TODO: we can now do this for all modes...
                        // TODO: but we need to take account of mode=#all, where modeNames.length==1
                        match = match2;
                    }
                }
                TemplateRule rule = compiledTemplateRules.get(modeName);
                if (rule == null) {
                    rule = new TemplateRule();
                    compiledTemplateRules.put(modeName, rule);
                }

                double prio = prioritySpecified ? priority : Double.NaN;
                mgr.setTemplateRule(match2, rule, mode, module, prio);

                if (mode.isDeclaredStreamable()) {
                    rule.setDeclaredStreamable(true);
                }

                // if adding a rule to the omniMode (mode='all') add it to all
                // the other modes as well. For all but the first, it needs to
                // be copied because the external component bindings might
                // differ from one mode to another.

                if (mode.getModeName().equals(Mode.OMNI_MODE)) {
                    mgr.setTemplateRule(match2, rule, mgr.getUnnamedMode(), module, prio);
                    for (Mode m : mgr.getAllNamedModes()) {
                        if (m instanceof SimpleMode) {
                            TemplateRule ruleCopy = rule.copy();
                            if (m.isDeclaredStreamable()) {
                                ruleCopy.setDeclaredStreamable(true);
                            }
                            compiledTemplateRules.put(m.getModeName(), ruleCopy);
                            mgr.setTemplateRule(match2.copy(new RebindingMap()), ruleCopy, m, module, prio);
                        }
                    }
                }
            }
        }
    }

    /**
     * Allocate slot numbers to any local variables declared within a predicate within the match pattern
     *
     * @throws XPathException if a failure occurs
     */

    public void allocatePatternSlotNumbers() throws XPathException {
        if (match != null) {
            for (TemplateRule templateRule : compiledTemplateRules.values()) {
                for (Rule r : templateRule.getRules()) {
                    // In the case of a union pattern, allocate slots separately for each branch
                    Pattern match = r.getPattern();
                    // first slot in pattern is reserved for current()
                    int nextFree = 0;
                    if ((match.getDependencies() & StaticProperty.DEPENDS_ON_CURRENT_ITEM) != 0) {
                        nextFree = 1;
                    }
                    int slots = match.allocateSlots(getSlotManager(), nextFree);
                    if (slots > 0) {
                        RuleManager mgr = getCompilation().getPrincipalStylesheetModule().getRuleManager();
                        boolean appliesToAll = false;
                        for (StructuredQName nc : modeNames) {
                            if (nc.equals(Mode.OMNI_MODE)) {
                                appliesToAll = true;
                                break;
                            }
                            Mode mode = mgr.obtainMode(nc, true);
                            mode.getActivePart().allocatePatternSlots(slots);
                        }
                        if (appliesToAll) {
                            for (Mode m : mgr.getAllNamedModes()) {
                                m.getActivePart().allocatePatternSlots(slots);
                            }
                            mgr.getUnnamedMode().getActivePart().allocatePatternSlots(slots);
                        }
                    }

                }
            }
        }
    }



    /**
     * This method is a bit of a misnomer, because it does more than invoke optimization of the template body.
     * In particular, it also registers the template rule with each Mode that it belongs to.
     *
     * @param declaration Associates this template with a stylesheet module (in principle an xsl:template
     *                    element can be in a document that is imported more than once; these are separate declarations)
     * @throws XPathException
     */

    public void optimize(ComponentDeclaration declaration) throws XPathException {
        ItemType contextItemType = Type.ITEM_TYPE;
        ContextItemStaticInfo cit;
        if (getObjectName() == null) {
            // the template can't be called by name, so the context item must match the match pattern
            contextItemType = match.getItemType();
            if (contextItemType.equals(ErrorType.getInstance())) {
                // if the match pattern can't match anything, we produce a warning, not a hard error
                contextItemType = AnyItemType.getInstance();
            }
            cit = new ContextItemStaticInfo(contextItemType, false);
        } else {
            cit = new ContextItemStaticInfo(contextItemType, true);
        }
        // TODO: use the xsl:context-item declaration within the template

        ExpressionTool.resetPropertiesWithinSubtree(body);
        ExpressionVisitor visitor = makeExpressionVisitor();
//        visitor.setOptimizeForStreaming(compiledNamedTemplate.isDeclaredStreamable());
        Optimizer opt = getConfiguration().obtainOptimizer();
        try {
            // We've already done the typecheck of each XPath expression, but it's worth doing again at this
            // level because we have more information now.
            body = body.typeCheck(visitor, cit);
            ExpressionTool.resetPropertiesWithinSubtree(body);
            boolean needCopy = false;

            if (getTemplateName() != null) {
                Expression namedTemplateBody = ExpressionTool.optimizeComponentBody(body, getCompilation(), visitor, cit, true);
                needCopy = true;
                compiledNamedTemplate.setBody(namedTemplateBody);
                allocateLocalSlots(namedTemplateBody);
                if (isExplaining()) {
                    Logger err = getConfiguration().getLogger();
                    err.info("Optimized expression tree for named template at line " +
                        getLineNumber() + " in " + getSystemId() + ':');
                    namedTemplateBody.explain(err);
                }
                namedTemplateBody.restoreParentPointers();
            }
            for (TemplateRule compiledTemplateRule : compiledTemplateRules.values()) {
                Expression templateRuleBody = needCopy ? body.copy(new RebindingMap()) : body;
                templateRuleBody = ExpressionTool.optimizeComponentBody(templateRuleBody, getCompilation(), visitor, cit, true);
                compiledTemplateRule.setBody(templateRuleBody);
                opt.checkStreamability(this, compiledTemplateRule);
                allocateLocalSlots(templateRuleBody);
                for (Rule r : compiledTemplateRule.getRules()) {
                    Pattern match = r.getPattern();
                    ContextItemStaticInfo info = new ContextItemStaticInfo(match.getItemType(), false, true);
                    Pattern m2 = match.optimize(visitor, info);
                    if (needCopy) {
                        m2 = m2.copy(new RebindingMap());
                    }
                    if (m2 != match) {
                        r.setPattern(m2);
                    }
                }
                needCopy = true;
                if (isExplaining()) {
                    Logger err = getConfiguration().getLogger();
                    err.info("Optimized expression tree for template rule at line " +
                        getLineNumber() + " in " + getSystemId() + ':');
                    templateRuleBody.explain(err);
                }
            }
        } catch (XPathException e) {
            e.maybeSetLocation(this);
            compileError(e);
        }

    }

    /**
     * Generate byte code for the template (if appropriate)
     * @param opt the optimizer
     * @throws XPathException if byte code generation fails
     */

    public void generateByteCode(Optimizer opt) throws XPathException {
        // Generate byte code if appropriate

        if (getCompilation().getCompilerInfo().isGenerateByteCode() && !isTailRecursive) {
            if (getTemplateName() != null) {
                try {
                    Expression exp = compiledNamedTemplate.getBody();
                    Expression cbody = opt.compileToByteCode(exp, nameAtt, Expression.PROCESS_METHOD);
                    if (cbody != null) {
                        compiledNamedTemplate.setBody(cbody);
                    }
                } catch (Exception e) {
                    System.err.println("Failed while compiling named template " + nameAtt);
                    e.printStackTrace();
                    throw new XPathException(e);
                }
            }
            for (TemplateRule compiledTemplateRule : compiledTemplateRules.values()) {
                if (!compiledTemplateRule.isDeclaredStreamable()) {
                    try {
                        Expression exp = compiledTemplateRule.getBody();
                        Expression cbody = opt.compileToByteCode(exp, matchAtt, Expression.PROCESS_METHOD);
                        if (cbody != null) {
                            compiledTemplateRule.setBody(cbody);
                        }
                    } catch (Exception e) {
                        System.err.println("Failed while compiling template rule with match = '" + matchAtt + "'");
                        e.printStackTrace();
                        throw new XPathException(e);
                    }
                }
            }
        }
    }


    /**
     * Get associated Procedure (for details of stack frame)
     */

    public SlotManager getSlotManager() {
        return stackFrameMap;
    }


    /**
     * Get the compiled template
     *
     * @return the compiled template
     */

    public NamedTemplate getCompiledNamedTemplate() {
        return compiledNamedTemplate;
    }

    /**
     * Get the type of construct. This will be a constant in
     * class {@link LocationKind}. This method is part of the {@link net.sf.saxon.trace.InstructionInfo} interface
     */

    public int getConstructType() {
        return StandardNames.XSL_TEMPLATE;
    }


    //project:preconditions
    public Pattern getMatch() {
        return match;
    }

    public Map getTemplateRulesByMode() {
        return compiledTemplateRules;
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy