|
//------------------------------------------------------------------------------
// <copyright file="TreeIterators.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.XPath;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Iterate over all descendant content nodes according to XPath descendant axis rules.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DescendantIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent, navEnd;
private bool hasFirst;
/// <summary>
/// Initialize the DescendantIterator (no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator input, XmlNavigatorFilter filter, bool orSelf) {
// Save input node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
this.filter = filter;
// Position navEnd to the node at which the descendant scan should terminate
if (input.NodeType == XPathNodeType.Root) {
this.navEnd = null;
}
else {
this.navEnd = XmlQueryRuntime.SyncToNavigator(this.navEnd, input);
this.navEnd.MoveToNonDescendant();
}
// If self node matches, then return it first
this.hasFirst = (orSelf && !this.filter.IsFiltered(this.navCurrent));
}
/// <summary>
/// Position this iterator to the next descendant node. Return false if there are no more descendant nodes.
/// Return true if the Current property is set to the next node in the iteration.
/// </summary>
public bool MoveNext() {
if (this.hasFirst) {
this.hasFirst = false;
return true;
}
return (this.filter.MoveToFollowing(this.navCurrent, this.navEnd));
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all descendant content nodes according to XPath descendant axis rules. Eliminate duplicates by not
/// querying over nodes that are contained in the subtree of the previous node.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DescendantMergeIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent, navRoot, navEnd;
private IteratorState state;
private bool orSelf;
private enum IteratorState {
NoPrevious = 0,
NeedCurrent,
NeedDescendant,
}
/// <summary>
/// Initialize the DescendantIterator (merge multiple sets of descendant nodes in document order and remove duplicates).
/// </summary>
public void Create(XmlNavigatorFilter filter, bool orSelf) {
this.filter = filter;
this.state = IteratorState.NoPrevious;
this.orSelf = orSelf;
}
/// <summary>
/// Position this iterator to the next descendant node. Return IteratorResult.NoMoreNodes if there are no more
/// descendant nodes. Return IteratorResult.NeedInputNode if the next input node needs to be fetched.
/// Return IteratorResult.HaveCurrent if the Current property is set to the next node in the iteration.
/// </summary>
public IteratorResult MoveNext(XPathNavigator input) {
if (this.state != IteratorState.NeedDescendant) {
if (input == null)
return IteratorResult.NoMoreNodes;
// Descendants of the input node will be duplicates if the input node is in the subtree
// of the previous root.
if (this.state != IteratorState.NoPrevious && this.navRoot.IsDescendant(input))
return IteratorResult.NeedInputNode;
// Save input node as current node and end of input's tree in navEnd
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
this.navRoot = XmlQueryRuntime.SyncToNavigator(this.navRoot, input);
this.navEnd = XmlQueryRuntime.SyncToNavigator(this.navEnd, input);
this.navEnd.MoveToNonDescendant();
this.state = IteratorState.NeedDescendant;
// If self node matches, then return it
if (this.orSelf && !this.filter.IsFiltered(input))
return IteratorResult.HaveCurrentNode;
}
if (this.filter.MoveToFollowing(this.navCurrent, this.navEnd))
return IteratorResult.HaveCurrentNode;
// No more descendants, so transition to NeedCurrent state and get the next input node
this.state = IteratorState.NeedCurrent;
return IteratorResult.NeedInputNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true or IteratorResult.HaveCurrentNode.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over matching parent node according to XPath parent axis rules.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct ParentIterator {
private XPathNavigator navCurrent;
private bool haveCurrent;
/// <summary>
/// Initialize the ParentIterator.
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
// Save context node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
// Attempt to find a matching parent node
this.haveCurrent = (this.navCurrent.MoveToParent()) && (!filter.IsFiltered(this.navCurrent));
}
/// <summary>
/// Return true if a matching parent node exists and set Current property. Otherwise, return false
/// (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.haveCurrent) {
this.haveCurrent = false;
return true;
}
// Iteration is complete
return false;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all ancestor nodes according to XPath ancestor axis rules, returning nodes in reverse
/// document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct AncestorIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent;
private bool haveCurrent;
/// <summary>
/// Initialize the AncestorIterator.
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter, bool orSelf) {
this.filter = filter;
// Save context node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
// If self node matches, then next call to MoveNext() should return it
// Otherwise, MoveNext() will fetch next ancestor
this.haveCurrent = (orSelf && !this.filter.IsFiltered(this.navCurrent));
}
/// <summary>
/// Position the iterator on the next matching ancestor node. Return true if such a node exists and
/// set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.haveCurrent) {
this.haveCurrent = false;
return true;
}
while (this.navCurrent.MoveToParent()) {
if (!this.filter.IsFiltered(this.navCurrent))
return true;
}
// Iteration is complete
return false;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all ancestor nodes according to XPath ancestor axis rules, but return the nodes in document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct AncestorDocOrderIterator {
private XmlNavigatorStack stack;
private XPathNavigator navCurrent;
/// <summary>
/// Initialize the AncestorDocOrderIterator (return ancestor nodes in document order, no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter, bool orSelf) {
AncestorIterator wrapped = new AncestorIterator();
wrapped.Create(context, filter, orSelf);
this.stack.Reset();
// Fetch all ancestor nodes in reverse document order and push them onto the stack
while (wrapped.MoveNext())
this.stack.Push(wrapped.Current.Clone());
}
/// <summary>
/// Return true if the Current property is set to the next Ancestor node in document order.
/// </summary>
public bool MoveNext() {
if (this.stack.IsEmpty)
return false;
this.navCurrent = this.stack.Pop();
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all following nodes according to XPath following axis rules. These rules specify that
/// descendants are not included, even though they follow the starting node in document order. For the
/// "true" following axis, see FollowingIterator.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct XPathFollowingIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent;
bool needFirst;
/// <summary>
/// Initialize the XPathFollowingIterator (no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator input, XmlNavigatorFilter filter) {
// Save input node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
this.filter = filter;
this.needFirst = true;
}
/// <summary>
/// Position this iterator to the next following node. Return false if there are no more following nodes.
/// Return true if the Current property is set to the next node in the iteration.
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
if (!MoveFirst(this.filter, this.navCurrent))
return false;
this.needFirst = false;
return true;
}
return this.filter.MoveToFollowing(this.navCurrent, null);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
/// <summary>
/// Position "nav" to the matching node which follows it in document order but is not a descendant node.
/// Return false if this is no such matching node.
/// </summary>
internal static bool MoveFirst(XmlNavigatorFilter filter, XPathNavigator nav) {
// Attributes and namespace nodes include descendants of their owner element in the set of following nodes
if (nav.NodeType == XPathNodeType.Attribute || nav.NodeType == XPathNodeType.Namespace) {
if (!nav.MoveToParent()) {
// Floating attribute or namespace node that has no following nodes
return false;
}
if (!filter.MoveToFollowing(nav, null)) {
// No matching following nodes
return false;
}
}
else {
// XPath spec doesn't include descendants of the input node in the following axis
if (!nav.MoveToNonDescendant())
// No following nodes
return false;
// If the sibling does not match the node-test, find the next following node that does
if (filter.IsFiltered(nav)) {
if (!filter.MoveToFollowing(nav, null)) {
// No matching following nodes
return false;
}
}
}
// Success
return true;
}
}
/// <summary>
/// Iterate over all following nodes according to XPath following axis rules. Merge multiple sets of following nodes
/// in document order and remove duplicates.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct XPathFollowingMergeIterator {
private XmlNavigatorFilter filter;
private IteratorState state;
private XPathNavigator navCurrent, navNext;
private enum IteratorState {
NeedCandidateCurrent = 0,
HaveCandidateCurrent,
HaveCurrentNeedNext,
HaveCurrentHaveNext,
HaveCurrentNoNext,
};
/// <summary>
/// Initialize the XPathFollowingMergeIterator (merge multiple sets of following nodes in document order and remove duplicates).
/// </summary>
public void Create(XmlNavigatorFilter filter) {
this.filter = filter;
this.state = IteratorState.NeedCandidateCurrent;
}
/// <summary>
/// Position this iterator to the next following node. Prune by finding the first input node in
/// document order that has no other input nodes in its subtree. All other input nodes should be
/// discarded. Return IteratorResult.NeedInputNode if the next input node needs to be fetched
/// first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
/// iteration.
/// </summary>
public IteratorResult MoveNext(XPathNavigator input) {
switch (this.state) {
case IteratorState.NeedCandidateCurrent:
// If there are no more input nodes, then iteration is complete
if (input == null)
return IteratorResult.NoMoreNodes;
// Save input node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
// Still must check next input node to see if is a descendant of this one
this.state = IteratorState.HaveCandidateCurrent;
return IteratorResult.NeedInputNode;
case IteratorState.HaveCandidateCurrent:
// If there are no more input nodes,
if (input == null) {
// Then candidate node has been selected, and there are no further input nodes
this.state = IteratorState.HaveCurrentNoNext;
return MoveFirst();
}
// If input node is in the subtree of the candidate node, then use the input node instead
if (this.navCurrent.IsDescendant(input))
goto case IteratorState.NeedCandidateCurrent;
// Found node on which to perform following scan. Now skip past all input nodes in the same document.
this.state = IteratorState.HaveCurrentNeedNext;
goto case IteratorState.HaveCurrentNeedNext;
case IteratorState.HaveCurrentNeedNext:
// If there are no more input nodes,
if (input == null) {
// Then candidate node has been selected, and there are no further input nodes
this.state = IteratorState.HaveCurrentNoNext;
return MoveFirst();
}
// Skip input node unless it's in a different document than the node on which the following scan was performed
if (this.navCurrent.ComparePosition(input) != XmlNodeOrder.Unknown)
return IteratorResult.NeedInputNode;
// Next node is in a different document, so save it
this.navNext = XmlQueryRuntime.SyncToNavigator(this.navNext, input);
this.state = IteratorState.HaveCurrentHaveNext;
return MoveFirst();
}
if (!this.filter.MoveToFollowing(this.navCurrent, null))
return MoveFailed();
return IteratorResult.HaveCurrentNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true or IteratorResult.HaveCurrentNode.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
/// <summary>
/// Called when an attempt to move to a following node failed. If a Next node exists, then make that the new
/// candidate current node. Otherwise, iteration is complete.
/// </summary>
private IteratorResult MoveFailed() {
XPathNavigator navTemp;
Debug.Assert(this.state == IteratorState.HaveCurrentHaveNext || this.state == IteratorState.HaveCurrentNoNext);
if (this.state == IteratorState.HaveCurrentNoNext) {
// No more nodes, so iteration is complete
this.state = IteratorState.NeedCandidateCurrent;
return IteratorResult.NoMoreNodes;
}
// Make next node the new candidate node
this.state = IteratorState.HaveCandidateCurrent;
// Swap navigators in order to sometimes avoid creating clones
navTemp = this.navCurrent;
this.navCurrent = this.navNext;
this.navNext = navTemp;
return IteratorResult.NeedInputNode;
}
/// <summary>
/// Position this.navCurrent to the node which follows it in document order but is not a descendant node.
/// </summary>
private IteratorResult MoveFirst() {
Debug.Assert(this.state == IteratorState.HaveCurrentHaveNext || this.state == IteratorState.HaveCurrentNoNext);
if (!XPathFollowingIterator.MoveFirst(this.filter, this.navCurrent))
return MoveFailed();
return IteratorResult.HaveCurrentNode;
}
}
/// <summary>
/// Iterate over all content-typed nodes which precede the starting node in document order. Return nodes
/// in reverse document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct PrecedingIterator {
private XmlNavigatorStack stack;
private XPathNavigator navCurrent;
/// <summary>
/// Initialize the PrecedingIterator (no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
// Start at root, which is always first node in the document
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.navCurrent.MoveToRoot();
this.stack.Reset();
// If root node is not the ending node,
if (!this.navCurrent.IsSamePosition(context)) {
// Push root onto the stack if it is not filtered
if (!filter.IsFiltered(this.navCurrent))
this.stack.Push(this.navCurrent.Clone());
// Push all matching nodes onto stack
while (filter.MoveToFollowing(this.navCurrent, context))
this.stack.Push(this.navCurrent.Clone());
}
}
/// <summary>
/// Return true if the Current property is set to the next Preceding node in reverse document order.
/// </summary>
public bool MoveNext() {
if (this.stack.IsEmpty)
return false;
this.navCurrent = this.stack.Pop();
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all preceding nodes according to XPath preceding axis rules, returning nodes in reverse
/// document order. These rules specify that ancestors are not included, even though they precede the
/// starting node in document order. For the "true" preceding axis, see PrecedingIterator.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct XPathPrecedingIterator {
private XmlNavigatorStack stack;
private XPathNavigator navCurrent;
/// <summary>
/// Initialize the XPathPrecedingIterator (no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
XPathPrecedingDocOrderIterator wrapped = new XPathPrecedingDocOrderIterator();
wrapped.Create(context, filter);
this.stack.Reset();
// Fetch all preceding nodes in document order and push them onto the stack
while (wrapped.MoveNext())
this.stack.Push(wrapped.Current.Clone());
}
/// <summary>
/// Return true if the Current property is set to the next Preceding node in reverse document order.
/// </summary>
public bool MoveNext() {
if (this.stack.IsEmpty)
return false;
this.navCurrent = this.stack.Pop();
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all preceding nodes according to XPath preceding axis rules, returning nodes in document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct XPathPrecedingDocOrderIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent;
private XmlNavigatorStack navStack;
/// <summary>
/// Initialize the XPathPrecedingDocOrderIterator (return preceding nodes in document order, no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator input, XmlNavigatorFilter filter) {
// Save input node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
this.filter = filter;
PushAncestors();
}
/// <summary>
/// Position this iterator to the next preceding node. Return false if there are no more preceding nodes.
/// Return true if the Current property is set to the next node in the iteration.
/// </summary>
public bool MoveNext() {
if (!this.navStack.IsEmpty) {
while (true) {
// Move to the next matching node that is before the top node on the stack in document order
if (this.filter.MoveToFollowing(this.navCurrent, this.navStack.Peek()))
// Found match
return true;
// Do not include ancestor nodes as part of the preceding axis
this.navCurrent.MoveTo(this.navStack.Pop());
// No more preceding matches possible
if (this.navStack.IsEmpty)
break;
}
}
return false;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true or
/// IteratorResult.HaveCurrentNode.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
/// <summary>
/// Push all ancestors of this.navCurrent onto a stack. The set of preceding nodes should not contain any of these
/// ancestors.
/// </summary>
private void PushAncestors() {
this.navStack.Reset();
do {
this.navStack.Push(this.navCurrent.Clone());
}
while (this.navCurrent.MoveToParent());
// Pop the root of the tree, since MoveToFollowing calls will never return it
this.navStack.Pop();
}
}
/// <summary>
/// Iterate over all preceding nodes according to XPath preceding axis rules, except that nodes are always
/// returned in document order. Merge multiple sets of preceding nodes in document order and remove duplicates.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct XPathPrecedingMergeIterator {
private XmlNavigatorFilter filter;
private IteratorState state;
private XPathNavigator navCurrent, navNext;
private XmlNavigatorStack navStack;
private enum IteratorState {
NeedCandidateCurrent = 0,
HaveCandidateCurrent,
HaveCurrentHaveNext,
HaveCurrentNoNext,
}
/// <summary>
/// Initialize the XPathPrecedingMergeIterator (merge multiple sets of preceding nodes in document order and remove duplicates).
/// </summary>
public void Create(XmlNavigatorFilter filter) {
this.filter = filter;
this.state = IteratorState.NeedCandidateCurrent;
}
/// <summary>
/// Position this iterator to the next preceding node in document order. Discard all input nodes
/// that are followed by another input node in the same document. This leaves one node per document from
/// which the complete set of preceding nodes can be derived without possibility of duplicates.
/// Return IteratorResult.NeedInputNode if the next input node needs to be fetched first. Return
/// IteratorResult.HaveCurrent if the Current property is set to the next node in the iteration.
/// </summary>
public IteratorResult MoveNext(XPathNavigator input) {
switch (this.state) {
case IteratorState.NeedCandidateCurrent:
// If there are no more input nodes, then iteration is complete
if (input == null)
return IteratorResult.NoMoreNodes;
// Save input node as current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
// Scan for additional input nodes within the same document (since they are after navCurrent in docorder)
this.state = IteratorState.HaveCandidateCurrent;
return IteratorResult.NeedInputNode;
case IteratorState.HaveCandidateCurrent:
// If there are no more input nodes,
if (input == null) {
// Then candidate node has been selected, and there are no further input nodes
this.state = IteratorState.HaveCurrentNoNext;
}
else {
// If the input node is in the same document as the current node,
if (this.navCurrent.ComparePosition(input) != XmlNodeOrder.Unknown) {
// Then update the current node and get the next input node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
return IteratorResult.NeedInputNode;
}
// Save the input node as navNext
this.navNext = XmlQueryRuntime.SyncToNavigator(this.navNext, input);
this.state = IteratorState.HaveCurrentHaveNext;
}
PushAncestors();
break;
}
if (!this.navStack.IsEmpty) {
while (true) {
// Move to the next matching node that is before the top node on the stack in document order
if (this.filter.MoveToFollowing(this.navCurrent, this.navStack.Peek()))
// Found match
return IteratorResult.HaveCurrentNode;
// Do not include ancestor nodes as part of the preceding axis
this.navCurrent.MoveTo(this.navStack.Pop());
// No more preceding matches possible
if (this.navStack.IsEmpty)
break;
}
}
if (this.state == IteratorState.HaveCurrentNoNext) {
// No more nodes, so iteration is complete
this.state = IteratorState.NeedCandidateCurrent;
return IteratorResult.NoMoreNodes;
}
// Make next node the current node and start trying to find input node greatest in docorder
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, this.navNext);
this.state = IteratorState.HaveCandidateCurrent;
return IteratorResult.HaveCurrentNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true or
/// IteratorResult.HaveCurrentNode.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
/// <summary>
/// Push all ancestors of this.navCurrent onto a stack. The set of preceding nodes should not contain any of these
/// ancestors.
/// </summary>
private void PushAncestors() {
Debug.Assert(this.state == IteratorState.HaveCurrentHaveNext || this.state == IteratorState.HaveCurrentNoNext);
this.navStack.Reset();
do {
this.navStack.Push(this.navCurrent.Clone());
}
while (this.navCurrent.MoveToParent());
// Pop the root of the tree, since MoveToFollowing calls will never return it
this.navStack.Pop();
}
}
/// <summary>
/// Iterate over these nodes in document order (filtering out those that do not match the filter test):
/// 1. Starting node
/// 2. All content-typed nodes which follow the starting node until the ending node is reached
/// 3. Ending node
///
/// If the starting node is the same node as the ending node, iterate over the singleton node.
/// If the starting node is after the ending node, or is in a different document, iterate to the
/// end of the document.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct NodeRangeIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent, navEnd;
private IteratorState state;
private enum IteratorState {
HaveCurrent,
NeedCurrent,
HaveCurrentNoNext,
NoNext,
}
/// <summary>
/// Initialize the NodeRangeIterator (no possibility of duplicates).
/// </summary>
public void Create(XPathNavigator start, XmlNavigatorFilter filter, XPathNavigator end) {
// Save start node as current node and save ending node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, start);
this.navEnd = XmlQueryRuntime.SyncToNavigator(this.navEnd, end);
this.filter = filter;
if (start.IsSamePosition(end)) {
// Start is end, so only return node if it is not filtered
this.state = !filter.IsFiltered(start) ? IteratorState.HaveCurrentNoNext : IteratorState.NoNext;
}
else {
// Return nodes until end is reached
this.state = !filter.IsFiltered(start) ? IteratorState.HaveCurrent : IteratorState.NeedCurrent;
}
}
/// <summary>
/// Position this iterator to the next following node. Return false if there are no more following nodes,
/// or if the end node has been reached. Return true if the Current property is set to the next node in
/// the iteration.
/// </summary>
public bool MoveNext() {
switch (this.state) {
case IteratorState.HaveCurrent:
this.state = IteratorState.NeedCurrent;
return true;
case IteratorState.NeedCurrent:
// Move to next following node which matches
if (!this.filter.MoveToFollowing(this.navCurrent, this.navEnd)) {
// No more nodes unless ending node matches
if (filter.IsFiltered(this.navEnd)) {
this.state = IteratorState.NoNext;
return false;
}
this.navCurrent.MoveTo(this.navEnd);
this.state = IteratorState.NoNext;
}
return true;
case IteratorState.HaveCurrentNoNext:
this.state = IteratorState.NoNext;
return true;
}
Debug.Assert(this.state == IteratorState.NoNext, "Illegal state: " + this.state);
return false;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
}
|