File: FrameworkFork\Microsoft.Xml\Xml\XPath\Internal\CacheChildrenQuery.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace MS.Internal.Xml.XPath
{
    using System;
    using Microsoft.Xml;
    using Microsoft.Xml.XPath;
    using System.Diagnostics;
    using System.Globalization;
    using System.Collections.Generic;
    using StackInt = ClonableStack<int>;
    using StackNav = ClonableStack<Microsoft.Xml.XPath.XPathNavigator>;
 
    // This class implements Children axis on Ancestor & Descendant imputs. (as well as id(), preciding, following)
    // The problem here is that is descenant::*/child::* and ancestor::*/child::* can produce duplicates nodes
    // The algorithm havily uses the fact that in our implementation of both AncestorQuery and DecsndantQuery return nodes in document order. 
    // As result first child is always before or equal of next input. 
    // So we don't need to call DecideNextNode() when needInput == true && stack is empty.
    internal sealed class CacheChildrenQuery : ChildrenQuery
    {
        private XPathNavigator _nextInput = null;
        private StackNav _elementStk;
        private StackInt _positionStk;
        private bool _needInput;
#if DEBUG
        private XPathNavigator _lastNode = null;
#endif
 
        public CacheChildrenQuery(Query qyInput, string name, string prefix, XPathNodeType type) : base(qyInput, name, prefix, type)
        {
            _elementStk = new StackNav();
            _positionStk = new StackInt();
            _needInput = true;
        }
        private CacheChildrenQuery(CacheChildrenQuery other) : base(other)
        {
            _nextInput = Clone(other._nextInput);
            _elementStk = other._elementStk.Clone();
            _positionStk = other._positionStk.Clone();
            _needInput = other._needInput;
#if DEBUG
            _lastNode = Clone(other._lastNode);
#endif
        }
 
        public override void Reset()
        {
            _nextInput = null;
            _elementStk.Clear();
            _positionStk.Clear();
            _needInput = true;
            base.Reset();
#if DEBUG
            _lastNode = null;
#endif
        }
 
        public override XPathNavigator Advance()
        {
            do
            {
                if (_needInput)
                {
                    if (_elementStk.Count == 0)
                    {
                        currentNode = GetNextInput();
                        if (currentNode == null)
                        {
                            return null;
                        }
                        if (!currentNode.MoveToFirstChild())
                        {
                            continue;
                        }
                        position = 0;
                    }
                    else
                    {
                        currentNode = _elementStk.Pop();
                        position = _positionStk.Pop();
                        if (!DecideNextNode())
                        {
                            continue;
                        }
                    }
                    _needInput = false;
                }
                else
                {
                    if (!currentNode.MoveToNext() || !DecideNextNode())
                    {
                        _needInput = true;
                        continue;
                    }
                }
#if DEBUG
                if (_lastNode != null)
                {
                    if (currentNode.GetType().ToString() == "Microsoft.VisualStudio.Modeling.StoreNavigator")
                    {
                        XmlNodeOrder order = CompareNodes(_lastNode, currentNode);
                        Debug.Assert(order == XmlNodeOrder.Before, "Algorith error. Nodes expected to be DocOrderDistinct");
                    }
                }
                _lastNode = currentNode.Clone();
#endif
                if (matches(currentNode))
                {
                    position++;
                    return currentNode;
                }
            } while (true);
        } // Advance
 
        private bool DecideNextNode()
        {
            _nextInput = GetNextInput();
            if (_nextInput != null)
            {
                if (CompareNodes(currentNode, _nextInput) == XmlNodeOrder.After)
                {
                    _elementStk.Push(currentNode);
                    _positionStk.Push(position);
                    currentNode = _nextInput;
                    _nextInput = null;
                    if (!currentNode.MoveToFirstChild())
                    {
                        return false;
                    }
                    position = 0;
                }
            }
            return true;
        }
 
        private XPathNavigator GetNextInput()
        {
            XPathNavigator result;
            if (_nextInput != null)
            {
                result = _nextInput;
                _nextInput = null;
            }
            else
            {
                result = qyInput.Advance();
                if (result != null)
                {
                    result = result.Clone();
                }
            }
            return result;
        }
 
        public override XPathNodeIterator Clone() { return new CacheChildrenQuery(this); }
    } // Children Query}
}