File: System\Xml\Xsl\XsltOld\VariableAction.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.Diagnostics;
using System.Xml.XPath;
 
namespace System.Xml.Xsl.XsltOld
{
    internal enum VariableType
    {
        GlobalVariable,
        GlobalParameter,
        LocalVariable,
        LocalParameter,
        WithParameter,
    }
 
    internal class VariableAction : ContainerAction, IXsltContextVariable
    {
        public static readonly object BeingComputedMark = new object();
        private const int ValueCalculated = 2;
 
        protected XmlQualifiedName? name;
        protected string? nameStr;
        protected string? baseUri;
        protected int selectKey = Compiler.InvalidQueryKey;
        protected int stylesheetid;
        protected VariableType varType;
        private int _varKey;
 
        internal int Stylesheetid
        {
            get { return this.stylesheetid; }
        }
        internal XmlQualifiedName? Name
        {
            get { return this.name; }
        }
        internal string? NameStr
        {
            get { return this.nameStr; }
        }
        internal VariableType VarType
        {
            get { return this.varType; }
        }
        internal int VarKey
        {
            get { return _varKey; }
        }
        internal bool IsGlobal
        {
            get { return this.varType == VariableType.GlobalVariable || this.varType == VariableType.GlobalParameter; }
        }
 
        internal VariableAction(VariableType type)
        {
            this.varType = type;
        }
 
        internal override void Compile(Compiler compiler)
        {
            this.stylesheetid = compiler.Stylesheetid;
            this.baseUri = compiler.Input.BaseURI;
            CompileAttributes(compiler);
            CheckRequiredAttribute(this.name, "name");
 
 
            if (compiler.Recurse())
            {
                CompileTemplate(compiler);
                compiler.ToParent();
 
                if (this.selectKey != Compiler.InvalidQueryKey && this.containedActions != null)
                {
                    throw XsltException.Create(SR.Xslt_VariableCntSel2, this.nameStr);
                }
            }
            if (this.containedActions != null)
            {
                baseUri = $"{baseUri}#{compiler.GetUnicRtfId()}";
            }
            else
            {
                baseUri = null;
            }
 
            _varKey = compiler.InsertVariable(this);
        }
 
        internal override bool CompileAttribute(Compiler compiler)
        {
            string name = compiler.Input.LocalName;
            string value = compiler.Input.Value;
 
            if (Ref.Equal(name, compiler.Atoms.Name))
            {
                Debug.Assert(this.name == null && this.nameStr == null);
                this.nameStr = value;
                this.name = compiler.CreateXPathQName(this.nameStr);
            }
            else if (Ref.Equal(name, compiler.Atoms.Select))
            {
                this.selectKey = compiler.AddQuery(value);
            }
            else
            {
                return false;
            }
 
            return true;
        }
 
        internal override void Execute(Processor processor, ActionFrame frame)
        {
            Debug.Assert(processor != null && frame != null && frame.State != ValueCalculated);
            object? value = null;
 
            switch (frame.State)
            {
                case Initialized:
                    if (IsGlobal)
                    {
                        if (frame.GetVariable(_varKey) != null)
                        { // This var was calculated already
                            frame.Finished();
                            break;
                        }
                        // Mark that the variable is being computed to check for circular references
                        frame.SetVariable(_varKey, BeingComputedMark);
                    }
                    // If this is a parameter, check whether the caller has passed the value
                    if (this.varType == VariableType.GlobalParameter)
                    {
                        value = processor.GetGlobalParameter(this.name!);
                    }
                    else if (this.varType == VariableType.LocalParameter)
                    {
                        value = processor.GetParameter(this.name!);
                    }
                    if (value != null)
                    {
                        goto case ValueCalculated;
                    }
 
                    // If value was not passed, check the 'select' attribute
                    if (this.selectKey != Compiler.InvalidQueryKey)
                    {
                        value = processor.RunQuery(frame, this.selectKey);
                        goto case ValueCalculated;
                    }
 
                    // If there is no 'select' attribute and the content is empty, use the empty string
                    if (this.containedActions == null)
                    {
                        value = string.Empty;
                        goto case ValueCalculated;
                    }
 
                    // RTF case
                    NavigatorOutput output = new NavigatorOutput(this.baseUri!);
                    processor.PushOutput(output);
                    processor.PushActionFrame(frame);
                    frame.State = ProcessingChildren;
                    break;
 
                case ProcessingChildren:
                    IRecordOutput recOutput = processor.PopOutput();
                    Debug.Assert(recOutput is NavigatorOutput);
                    value = ((NavigatorOutput)recOutput).Navigator;
                    goto case ValueCalculated;
 
                case ValueCalculated:
                    Debug.Assert(value != null);
                    frame.SetVariable(_varKey, value);
                    frame.Finished();
                    break;
 
                default:
                    Debug.Fail("Invalid execution state inside VariableAction.Execute");
                    break;
            }
        }
 
        // ---------------------- IXsltContextVariable --------------------
 
        XPathResultType IXsltContextVariable.VariableType
        {
            get { return XPathResultType.Any; }
        }
        object IXsltContextVariable.Evaluate(XsltContext xsltContext)
        {
            return ((XsltCompileContext)xsltContext).EvaluateVariable(this);
        }
        bool IXsltContextVariable.IsLocal
        {
            get { return this.varType == VariableType.LocalVariable || this.varType == VariableType.LocalParameter; }
        }
        bool IXsltContextVariable.IsParam
        {
            get { return this.varType == VariableType.LocalParameter || this.varType == VariableType.GlobalParameter; }
        }
    }
}