File: System\Xml\Xsl\XsltOld\TemplateAction.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Xml;
using System.Xml.XPath;
using MS.Internal.Xml.XPath;
 
namespace System.Xml.Xsl.XsltOld
{
    internal sealed class TemplateAction : TemplateBaseAction
    {
        private int _matchKey = Compiler.InvalidQueryKey;
        private XmlQualifiedName? _name;
        private double _priority = double.NaN;
        private XmlQualifiedName? _mode;
        private int _templateId;
        private bool _replaceNSAliasesDone;
 
        internal int MatchKey
        {
            get { return _matchKey; }
        }
 
        internal XmlQualifiedName? Name
        {
            get { return _name; }
        }
 
        internal double Priority
        {
            get { return _priority; }
        }
 
        internal XmlQualifiedName? Mode
        {
            get { return _mode; }
        }
 
        internal int TemplateId
        {
            get { return _templateId; }
            set
            {
                Debug.Assert(_templateId == 0);
                _templateId = value;
            }
        }
 
        internal override void Compile(Compiler compiler)
        {
            CompileAttributes(compiler);
            if (_matchKey == Compiler.InvalidQueryKey)
            {
                if (_name == null)
                {
                    throw XsltException.Create(SR.Xslt_TemplateNoAttrib);
                }
                if (_mode != null)
                {
                    throw XsltException.Create(SR.Xslt_InvalidModeAttribute);
                }
            }
            compiler.BeginTemplate(this);
 
            if (compiler.Recurse())
            {
                CompileParameters(compiler);
                CompileTemplate(compiler);
 
                compiler.ToParent();
            }
 
            compiler.EndTemplate();
            AnalyzePriority(compiler);
        }
 
        internal void CompileSingle(Compiler compiler)
        {
            _matchKey = compiler.AddQuery("/", /*allowVars:*/false, /*allowKey:*/true, /*pattern*/true);
            _priority = Compiler.RootPriority;
 
            CompileOnceTemplate(compiler);
        }
 
        internal override bool CompileAttribute(Compiler compiler)
        {
            string name = compiler.Input.LocalName;
            string value = compiler.Input.Value;
 
            if (Ref.Equal(name, compiler.Atoms.Match))
            {
                Debug.Assert(_matchKey == Compiler.InvalidQueryKey);
                _matchKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey:*/true, /*pattern*/true);
            }
            else if (Ref.Equal(name, compiler.Atoms.Name))
            {
                Debug.Assert(_name == null);
                _name = compiler.CreateXPathQName(value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Priority))
            {
                Debug.Assert(double.IsNaN(_priority));
                _priority = XmlConvert.ToXPathDouble(value);
                if (double.IsNaN(_priority) && !compiler.ForwardCompatibility)
                {
                    throw XsltException.Create(SR.Xslt_InvalidAttrValue, "priority", value);
                }
            }
            else if (Ref.Equal(name, compiler.Atoms.Mode))
            {
                Debug.Assert(_mode == null);
                _mode = compiler.CreateXPathQName(value);
            }
            else
            {
                return false;
            }
 
            return true;
        }
 
        private void AnalyzePriority(Compiler compiler)
        {
            if (!double.IsNaN(_priority) || _matchKey == Compiler.InvalidQueryKey)
            {
                return;
            }
            // Split Unions:
            TheQuery theQuery = (TheQuery)compiler.QueryStore![this.MatchKey]!;
            CompiledXpathExpr expr = (CompiledXpathExpr)theQuery.CompiledQuery;
            Query query = expr.QueryTree;
            UnionExpr? union;
            while ((union = query as UnionExpr) != null)
            {
                Debug.Assert(!(union.qy2 is UnionExpr), "only qy1 can be union");
                TemplateAction copy = this.CloneWithoutName();
                compiler.QueryStore.Add(new TheQuery(
                    new CompiledXpathExpr(union.qy2, expr.Expression, false),
                    theQuery._ScopeManager
                ));
                copy._matchKey = compiler.QueryStore.Count - 1;
                copy._priority = union.qy2.XsltDefaultPriority;
                compiler.AddTemplate(copy);
 
                query = union.qy1;
            }
            if (expr.QueryTree != query)
            {
                // query was split and we need create new TheQuery for this template
                compiler.QueryStore[this.MatchKey] = new TheQuery(
                    new CompiledXpathExpr(query, expr.Expression, false),
                    theQuery._ScopeManager
                );
            }
            _priority = query.XsltDefaultPriority;
        }
 
        private void CompileParameters(Compiler compiler)
        {
            NavigatorInput input = compiler.Input;
            do
            {
                switch (input.NodeType)
                {
                    case XPathNodeType.Element:
                        if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl) &&
                            Ref.Equal(input.LocalName, input.Atoms.Param))
                        {
                            compiler.PushNamespaceScope();
                            AddAction(compiler.CreateVariableAction(VariableType.LocalParameter));
                            compiler.PopScope();
                            continue;
                        }
                        else
                        {
                            return;
                        }
                    case XPathNodeType.Text:
                        return;
                    case XPathNodeType.SignificantWhitespace:
                        this.AddEvent(compiler.CreateTextEvent());
                        continue;
                    default:
                        continue;
                }
            }
            while (input.Advance());
        }
 
        //
        // Priority calculation plus template splitting
        //
 
        private TemplateAction CloneWithoutName()
        {
            TemplateAction clone = new TemplateAction();
            {
                clone.containedActions = this.containedActions;
                clone._mode = _mode;
                clone.variableCount = this.variableCount;
                clone._replaceNSAliasesDone = true; // We shouldn't replace NS in clones.
            }
            return clone;
        }
 
        internal override void ReplaceNamespaceAlias(Compiler compiler)
        {
            // if template has both name and match it will be twice caled by stylesheet to replace NS aliases.
            if (!_replaceNSAliasesDone)
            {
                base.ReplaceNamespaceAlias(compiler);
                _replaceNSAliasesDone = true;
            }
        }
        //
        // Execution
        //
 
        internal override void Execute(Processor processor, ActionFrame frame)
        {
            Debug.Assert(processor != null && frame != null);
 
            switch (frame.State)
            {
                case Initialized:
                    if (this.variableCount > 0)
                    {
                        frame.AllocateVariables(this.variableCount);
                    }
                    if (this.containedActions != null && this.containedActions.Count > 0)
                    {
                        processor.PushActionFrame(frame);
                        frame.State = ProcessingChildren;
                    }
                    else
                    {
                        frame.Finished();
                    }
                    break;                              // Allow children to run
                case ProcessingChildren:
                    frame.Finished();
                    break;
                default:
                    Debug.Fail("Invalid Container action execution state");
                    break;
            }
        }
    }
}