File: System\Xml\Xsl\Runtime\SetIterators.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.ComponentModel;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
 
namespace System.Xml.Xsl.Runtime
{
    /// <summary>
    /// Set iterators (Union, Intersection, Difference) that use containment to control two nested iterators return
    /// one of the following values from MoveNext().
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public enum SetIteratorResult
    {
        NoMoreNodes,                // Iteration is complete; there are no more nodes
        InitRightIterator,          // Initialize right nested iterator
        NeedLeftNode,               // The next node needs to be fetched from the left nested iterator
        NeedRightNode,              // The next node needs to be fetched from the right nested iterator
        HaveCurrentNode,            // This iterator's Current property is set to the next node in the iteration
    };
 
 
    /// <summary>
    /// This iterator manages two sets of nodes that are already in document order with no duplicates.
    /// Using a merge sort, this operator returns the union of these sets in document order with no duplicates.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public struct UnionIterator
    {
        private XmlQueryRuntime _runtime;
        private XPathNavigator _navCurr, _navOther;
        private IteratorState _state;
 
        private enum IteratorState
        {
            InitLeft = 0,
            NeedLeft,
            NeedRight,
            LeftIsCurrent,
            RightIsCurrent,
        };
 
        /// <summary>
        /// Create SetIterator.
        /// </summary>
        public void Create(XmlQueryRuntime runtime)
        {
            _runtime = runtime;
            _state = IteratorState.InitLeft;
        }
 
        /// <summary>
        /// Position this iterator to the next node in the union.
        /// </summary>
        public SetIteratorResult MoveNext(XPathNavigator nestedNavigator)
        {
            switch (_state)
            {
                case IteratorState.InitLeft:
                    // Fetched node from left iterator, now get initial node from right iterator
                    _navOther = nestedNavigator;
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.InitRightIterator;
 
                case IteratorState.NeedLeft:
                    _navCurr = nestedNavigator;
                    _state = IteratorState.LeftIsCurrent;
                    break;
 
                case IteratorState.NeedRight:
                    _navCurr = nestedNavigator;
                    _state = IteratorState.RightIsCurrent;
                    break;
 
                case IteratorState.LeftIsCurrent:
                    // Just returned left node as current, so get new left
                    _state = IteratorState.NeedLeft;
                    return SetIteratorResult.NeedLeftNode;
 
                case IteratorState.RightIsCurrent:
                    // Just returned right node as current, so get new right
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.NeedRightNode;
            }
 
            // Merge left and right nodes
            if (_navCurr == null)
            {
                // If both navCurr and navOther are null, then iteration is complete
                if (_navOther == null)
                    return SetIteratorResult.NoMoreNodes;
 
                Swap();
            }
            else if (_navOther != null)
            {
                int order = _runtime.ComparePosition(_navOther, _navCurr);
 
                // If navCurr is positioned to same node as navOther,
                if (order == 0)
                {
                    // Skip navCurr, since it is a duplicate
                    if (_state == IteratorState.LeftIsCurrent)
                    {
                        _state = IteratorState.NeedLeft;
                        return SetIteratorResult.NeedLeftNode;
                    }
 
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.NeedRightNode;
                }
 
                // If navOther is before navCurr in document order, then swap navCurr with navOther
                if (order < 0)
                    Swap();
            }
 
            // Return navCurr
            return SetIteratorResult.HaveCurrentNode;
        }
 
        /// <summary>
        /// Return the current result navigator.  This is only defined after MoveNext() has returned -1.
        /// </summary>
        public XPathNavigator Current
        {
            get { return _navCurr; }
        }
 
        /// <summary>
        /// Swap navCurr with navOther and invert state to reflect the change.
        /// </summary>
        private void Swap()
        {
            XPathNavigator navTemp = _navCurr;
            _navCurr = _navOther;
            _navOther = navTemp;
 
            if (_state == IteratorState.LeftIsCurrent)
                _state = IteratorState.RightIsCurrent;
            else
                _state = IteratorState.LeftIsCurrent;
        }
    }
 
 
    /// <summary>
    /// This iterator manages two sets of nodes that are already in document order with no duplicates.
    /// This iterator returns the intersection of these sets in document order with no duplicates.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public struct IntersectIterator
    {
        private XmlQueryRuntime _runtime;
        private XPathNavigator _navLeft, _navRight;
        private IteratorState _state;
 
        private enum IteratorState
        {
            InitLeft = 0,
            NeedLeft,
            NeedRight,
            NeedLeftAndRight,
            HaveCurrent,
        };
 
        /// <summary>
        /// Create IntersectIterator.
        /// </summary>
        public void Create(XmlQueryRuntime runtime)
        {
            _runtime = runtime;
            _state = IteratorState.InitLeft;
        }
 
        /// <summary>
        /// Position this iterator to the next node in the union.
        /// </summary>
        public SetIteratorResult MoveNext(XPathNavigator nestedNavigator)
        {
            int order;
 
            switch (_state)
            {
                case IteratorState.InitLeft:
                    // Fetched node from left iterator, now get initial node from right iterator
                    _navLeft = nestedNavigator;
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.InitRightIterator;
 
                case IteratorState.NeedLeft:
                    _navLeft = nestedNavigator;
                    break;
 
                case IteratorState.NeedRight:
                    _navRight = nestedNavigator;
                    break;
 
                case IteratorState.NeedLeftAndRight:
                    // After fetching left node, still need right node
                    _navLeft = nestedNavigator;
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.NeedRightNode;
 
                case IteratorState.HaveCurrent:
                    // Just returned left node as current, so fetch new left and right nodes
                    Debug.Assert(nestedNavigator == null, "null is passed to MoveNext after IteratorState.HaveCurrent has been returned.");
                    _state = IteratorState.NeedLeftAndRight;
                    return SetIteratorResult.NeedLeftNode;
            }
 
            if (_navLeft == null || _navRight == null)
            {
                // No more nodes from either left or right iterator (or both), so iteration is complete
                return SetIteratorResult.NoMoreNodes;
            }
 
            // Intersect left and right sets
            order = _runtime.ComparePosition(_navLeft, _navRight);
 
            if (order < 0)
            {
                // If navLeft is positioned to a node that is before navRight, skip left node
                _state = IteratorState.NeedLeft;
                return SetIteratorResult.NeedLeftNode;
            }
            else if (order > 0)
            {
                // If navLeft is positioned to a node that is after navRight, so skip right node
                _state = IteratorState.NeedRight;
                return SetIteratorResult.NeedRightNode;
            }
 
            // Otherwise, navLeft is positioned to the same node as navRight, so found one item in the intersection
            _state = IteratorState.HaveCurrent;
            return SetIteratorResult.HaveCurrentNode;
        }
 
        /// <summary>
        /// Return the current result navigator.  This is only defined after MoveNext() has returned -1.
        /// </summary>
        public XPathNavigator Current
        {
            get { return _navLeft; }
        }
    }
 
 
    /// <summary>
    /// This iterator manages two sets of nodes that are already in document order with no duplicates.
    /// This iterator returns the difference of these sets (Left - Right) in document order with no duplicates.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public struct DifferenceIterator
    {
        private XmlQueryRuntime _runtime;
        private XPathNavigator _navLeft, _navRight;
        private IteratorState _state;
 
        private enum IteratorState
        {
            InitLeft = 0,
            NeedLeft,
            NeedRight,
            NeedLeftAndRight,
            HaveCurrent,
        };
 
        /// <summary>
        /// Create DifferenceIterator.
        /// </summary>
        public void Create(XmlQueryRuntime runtime)
        {
            _runtime = runtime;
            _state = IteratorState.InitLeft;
        }
 
        /// <summary>
        /// Position this iterator to the next node in the union.
        /// </summary>
        public SetIteratorResult MoveNext(XPathNavigator nestedNavigator)
        {
            switch (_state)
            {
                case IteratorState.InitLeft:
                    // Fetched node from left iterator, now get initial node from right iterator
                    _navLeft = nestedNavigator;
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.InitRightIterator;
 
                case IteratorState.NeedLeft:
                    _navLeft = nestedNavigator;
                    break;
 
                case IteratorState.NeedRight:
                    _navRight = nestedNavigator;
                    break;
 
                case IteratorState.NeedLeftAndRight:
                    // After fetching left node, still need right node
                    _navLeft = nestedNavigator;
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.NeedRightNode;
 
                case IteratorState.HaveCurrent:
                    // Just returned left node as current, so fetch new left node
                    Debug.Assert(nestedNavigator == null, "null is passed to MoveNext after IteratorState.HaveCurrent has been returned.");
                    _state = IteratorState.NeedLeft;
                    return SetIteratorResult.NeedLeftNode;
            }
 
            if (_navLeft == null)
            {
                // If navLeft is null, then difference operation is complete
                return SetIteratorResult.NoMoreNodes;
            }
            else if (_navRight != null)
            {
                int order = _runtime.ComparePosition(_navLeft, _navRight);
 
                // If navLeft is positioned to same node as navRight,
                if (order == 0)
                {
                    // Skip navLeft and navRight
                    _state = IteratorState.NeedLeftAndRight;
                    return SetIteratorResult.NeedLeftNode;
                }
 
                // If navLeft is after navRight in document order, then skip navRight
                if (order > 0)
                {
                    _state = IteratorState.NeedRight;
                    return SetIteratorResult.NeedRightNode;
                }
            }
 
            // Return navLeft
            _state = IteratorState.HaveCurrent;
            return SetIteratorResult.HaveCurrentNode;
        }
 
        /// <summary>
        /// Return the current result navigator.  This is only defined after MoveNext() has returned -1.
        /// </summary>
        public XPathNavigator Current
        {
            get { return _navLeft; }
        }
    }
}