File: System\Xml\Xsl\XsltOld\ActionFrame.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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Xml;
using System.Xml.XPath;
using MS.Internal.Xml.XPath;
 
namespace System.Xml.Xsl.XsltOld
{
    internal sealed class ActionFrame
    {
        private int _state;         // Action execution state
        private int _counter;       // Counter, for the use of particular action
        private object[]? _variables;     // Store for template local variable values
        private Hashtable? _withParams;
        private Action? _action;        // Action currently being executed
        private ActionFrame? _container;     // Frame of enclosing container action and index within it
        private int _currentAction;
        private XPathNodeIterator? _nodeSet;       // Current node set
        private XPathNodeIterator? _newNodeSet;    // Node set for processing children or other templates
 
        // Variables to store action data between states:
        private PrefixQName? _calculatedName; // Used in ElementAction and AttributeAction
        private string? _storedOutput;  // Used in NumberAction, CopyOfAction, ValueOfAction and ProcessingInstructionAction
 
        internal PrefixQName? CalculatedName
        {
            get { return _calculatedName; }
            set { _calculatedName = value; }
        }
 
        internal string? StoredOutput
        {
            get { return _storedOutput; }
            set { _storedOutput = value; }
        }
 
        internal int State
        {
            get { return _state; }
            set { _state = value; }
        }
 
        internal int Counter
        {
            get { return _counter; }
            set { _counter = value; }
        }
 
        internal ActionFrame? Container
        {
            get { return _container; }
        }
 
        internal XPathNavigator? Node
        {
            get
            {
                if (_nodeSet != null)
                {
                    return _nodeSet.Current;
                }
                return null;
            }
        }
 
        internal XPathNodeIterator? NodeSet
        {
            get { return _nodeSet; }
        }
 
        internal XPathNodeIterator? NewNodeSet
        {
            get { return _newNodeSet; }
        }
 
        internal int IncrementCounter()
        {
            return ++_counter;
        }
 
        internal void AllocateVariables(int count)
        {
            if (0 < count)
            {
                _variables = new object[count];
            }
            else
            {
                _variables = null;
            }
        }
 
        internal object GetVariable(int index)
        {
            Debug.Assert(_variables != null && index < _variables.Length);
            return _variables[index];
        }
 
        internal void SetVariable(int index, object value)
        {
            Debug.Assert(_variables != null && index < _variables.Length);
            _variables[index] = value;
        }
 
        internal void SetParameter(XmlQualifiedName name, object value)
        {
            _withParams ??= new Hashtable();
            Debug.Assert(!_withParams.Contains(name), "We should check duplicate params at compile time");
            _withParams[name] = value;
        }
 
        internal void ResetParams()
        {
            _withParams?.Clear();
        }
 
        internal object? GetParameter(XmlQualifiedName name)
        {
            return _withParams?[name];
        }
 
        internal void InitNodeSet(XPathNodeIterator nodeSet)
        {
            Debug.Assert(nodeSet != null);
            _nodeSet = nodeSet;
        }
 
        [MemberNotNull(nameof(_newNodeSet))]
        internal void InitNewNodeSet(XPathNodeIterator nodeSet)
        {
            Debug.Assert(nodeSet != null);
            _newNodeSet = nodeSet;
        }
 
        [MemberNotNull(nameof(_newNodeSet))]
        internal void SortNewNodeSet(Processor proc, ArrayList sortarray)
        {
            Debug.Assert(0 < sortarray.Count);
            int numSorts = sortarray.Count;
            XPathSortComparer comparer = new XPathSortComparer(numSorts);
            for (int i = 0; i < numSorts; i++)
            {
                Sort sort = (Sort)sortarray[i]!;
                Query expr = proc.GetCompiledQuery(sort.select);
 
                comparer.AddSort(expr, new XPathComparerHelper(sort.order, sort.caseOrder, sort.lang, sort.dataType));
            }
            List<SortKey> results = new List<SortKey>();
 
            Debug.Assert(proc.ActionStack.Peek() == this, "the trick we are doing with proc.Current will work only if this is topmost frame");
 
            while (NewNextNode(proc))
            {
                XPathNodeIterator? savedNodeset = _nodeSet;
                _nodeSet = _newNodeSet;              // trick proc.Current node
 
                SortKey key = new SortKey(numSorts, /*originalPosition:*/results.Count, _newNodeSet!.Current!.Clone());
 
                for (int j = 0; j < numSorts; j++)
                {
                    key[j] = comparer.Expression(j).Evaluate(_newNodeSet);
                }
                results.Add(key);
 
                _nodeSet = savedNodeset;                 // restore proc.Current node
            }
            results.Sort(comparer);
            _newNodeSet = new XPathSortArrayIterator(results);
        }
 
        // Finished
        internal void Finished()
        {
            State = Action.Finished;
        }
 
        internal void Inherit(ActionFrame parent)
        {
            Debug.Assert(parent != null);
            _variables = parent._variables;
        }
 
        private void Init(Action? action, ActionFrame? container, XPathNodeIterator? nodeSet)
        {
            _state = Action.Initialized;
            _action = action;
            _container = container;
            _currentAction = 0;
            _nodeSet = nodeSet;
            _newNodeSet = null;
        }
 
        internal void Init(Action action, XPathNodeIterator? nodeSet)
        {
            Init(action, null, nodeSet);
        }
 
        internal void Init(ActionFrame containerFrame, XPathNodeIterator? nodeSet)
        {
            Init(containerFrame.GetAction(0), containerFrame, nodeSet);
        }
 
        internal void SetAction(Action action)
        {
            SetAction(action, Action.Initialized);
        }
 
        internal void SetAction(Action action, int state)
        {
            _action = action;
            _state = state;
        }
 
        private Action? GetAction(int actionIndex)
        {
            Debug.Assert(_action is ContainerAction);
            return ((ContainerAction)_action).GetAction(actionIndex);
        }
 
        internal void Exit()
        {
            Finished();
            _container = null;
        }
 
        /*
         * Execute
         *  return values: true - pop, false - nothing
         */
        [MemberNotNullWhen(false, nameof(_action))]
        internal bool Execute(Processor processor)
        {
            if (_action == null)
            {
                return true;
            }
 
            // Execute the action
            _action.Execute(processor, this);
 
            // Process results
            if (State == Action.Finished)
            {
                // Advanced to next action
                if (_container != null)
                {
                    _currentAction++;
                    _action = _container.GetAction(_currentAction);
                    State = Action.Initialized;
                }
                else
                {
                    _action = null;
                }
                return _action == null;
            }
 
            return false;                       // Do not pop, unless specified otherwise
        }
 
        internal bool NextNode(Processor proc)
        {
            bool next = _nodeSet!.MoveNext();
            if (next && proc.Stylesheet.Whitespace)
            {
                XPathNodeType type = _nodeSet.Current!.NodeType;
                if (type == XPathNodeType.Whitespace)
                {
                    XPathNavigator nav = _nodeSet.Current.Clone();
                    bool flag;
                    do
                    {
                        nav.MoveTo(_nodeSet.Current);
                        nav.MoveToParent();
                        flag = !proc.Stylesheet.PreserveWhiteSpace(proc, nav) && (next = _nodeSet.MoveNext());
                        type = _nodeSet.Current.NodeType;
                    }
                    while (flag && (type == XPathNodeType.Whitespace));
                }
            }
            return next;
        }
 
        internal bool NewNextNode(Processor proc)
        {
            bool next = _newNodeSet!.MoveNext();
            if (next && proc.Stylesheet.Whitespace)
            {
                XPathNodeType type = _newNodeSet.Current!.NodeType;
                if (type == XPathNodeType.Whitespace)
                {
                    XPathNavigator nav = _newNodeSet.Current.Clone();
                    bool flag;
                    do
                    {
                        nav.MoveTo(_newNodeSet.Current);
                        nav.MoveToParent();
                        flag = !proc.Stylesheet.PreserveWhiteSpace(proc, nav) && (next = _newNodeSet.MoveNext());
                        type = _newNodeSet.Current.NodeType;
                    }
                    while (flag && (type == XPathNodeType.Whitespace));
                }
            }
            return next;
        }
 
        // special array iterator that iterates over ArrayList of SortKey
        private sealed class XPathSortArrayIterator : XPathArrayIterator
        {
            public XPathSortArrayIterator(List<SortKey> list) : base(list) { }
            public XPathSortArrayIterator(XPathSortArrayIterator it) : base(it) { }
 
            public override XPathNodeIterator Clone()
            {
                return new XPathSortArrayIterator(this);
            }
 
            public override XPathNavigator Current
            {
                get
                {
                    Debug.Assert(index > 0, "MoveNext() wasn't called");
                    return ((SortKey)this.list[this.index - 1]!).Node;
                }
            }
        }
    }
}