329 lines
13 KiB
C#
329 lines
13 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="SetIterators.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
using System;
|
||
|
using System.Xml;
|
||
|
using System.Xml.XPath;
|
||
|
using System.Xml.Schema;
|
||
|
using System.Diagnostics;
|
||
|
using System.Collections;
|
||
|
using System.ComponentModel;
|
||
|
|
||
|
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) {
|
||
|
this.runtime = runtime;
|
||
|
this.state = IteratorState.InitLeft;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Position this iterator to the next node in the union.
|
||
|
/// </summary>
|
||
|
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
|
||
|
switch (this.state) {
|
||
|
case IteratorState.InitLeft:
|
||
|
// Fetched node from left iterator, now get initial node from right iterator
|
||
|
this.navOther = nestedNavigator;
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.InitRightIterator;
|
||
|
|
||
|
case IteratorState.NeedLeft:
|
||
|
this.navCurr = nestedNavigator;
|
||
|
this.state = IteratorState.LeftIsCurrent;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.NeedRight:
|
||
|
this.navCurr = nestedNavigator;
|
||
|
this.state = IteratorState.RightIsCurrent;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.LeftIsCurrent:
|
||
|
// Just returned left node as current, so get new left
|
||
|
this.state = IteratorState.NeedLeft;
|
||
|
return SetIteratorResult.NeedLeftNode;
|
||
|
|
||
|
case IteratorState.RightIsCurrent:
|
||
|
// Just returned right node as current, so get new right
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.NeedRightNode;
|
||
|
}
|
||
|
|
||
|
// Merge left and right nodes
|
||
|
if (this.navCurr == null) {
|
||
|
// If both navCurr and navOther are null, then iteration is complete
|
||
|
if (this.navOther == null)
|
||
|
return SetIteratorResult.NoMoreNodes;
|
||
|
|
||
|
Swap();
|
||
|
}
|
||
|
else if (this.navOther != null) {
|
||
|
int order = this.runtime.ComparePosition(this.navOther, this.navCurr);
|
||
|
|
||
|
// If navCurr is positioned to same node as navOther,
|
||
|
if (order == 0) {
|
||
|
// Skip navCurr, since it is a duplicate
|
||
|
if (this.state == IteratorState.LeftIsCurrent) {
|
||
|
this.state = IteratorState.NeedLeft;
|
||
|
return SetIteratorResult.NeedLeftNode;
|
||
|
}
|
||
|
|
||
|
this.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 this.navCurr; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Swap navCurr with navOther and invert state to reflect the change.
|
||
|
/// </summary>
|
||
|
private void Swap() {
|
||
|
XPathNavigator navTemp = this.navCurr;
|
||
|
this.navCurr = this.navOther;
|
||
|
this.navOther = navTemp;
|
||
|
|
||
|
if (this.state == IteratorState.LeftIsCurrent)
|
||
|
this.state = IteratorState.RightIsCurrent;
|
||
|
else
|
||
|
this.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) {
|
||
|
this.runtime = runtime;
|
||
|
this.state = IteratorState.InitLeft;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Position this iterator to the next node in the union.
|
||
|
/// </summary>
|
||
|
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
|
||
|
int order;
|
||
|
|
||
|
switch (this.state) {
|
||
|
case IteratorState.InitLeft:
|
||
|
// Fetched node from left iterator, now get initial node from right iterator
|
||
|
this.navLeft = nestedNavigator;
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.InitRightIterator;
|
||
|
|
||
|
case IteratorState.NeedLeft:
|
||
|
this.navLeft = nestedNavigator;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.NeedRight:
|
||
|
this.navRight = nestedNavigator;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.NeedLeftAndRight:
|
||
|
// After fetching left node, still need right node
|
||
|
this.navLeft = nestedNavigator;
|
||
|
this.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.");
|
||
|
this.state = IteratorState.NeedLeftAndRight;
|
||
|
return SetIteratorResult.NeedLeftNode;
|
||
|
}
|
||
|
|
||
|
if (this.navLeft == null || this.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 = this.runtime.ComparePosition(this.navLeft, this.navRight);
|
||
|
|
||
|
if (order < 0) {
|
||
|
// If navLeft is positioned to a node that is before navRight, skip left node
|
||
|
this.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
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.NeedRightNode;
|
||
|
}
|
||
|
|
||
|
// Otherwise, navLeft is positioned to the same node as navRight, so found one item in the intersection
|
||
|
this.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 this.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) {
|
||
|
this.runtime = runtime;
|
||
|
this.state = IteratorState.InitLeft;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Position this iterator to the next node in the union.
|
||
|
/// </summary>
|
||
|
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
|
||
|
switch (this.state) {
|
||
|
case IteratorState.InitLeft:
|
||
|
// Fetched node from left iterator, now get initial node from right iterator
|
||
|
this.navLeft = nestedNavigator;
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.InitRightIterator;
|
||
|
|
||
|
case IteratorState.NeedLeft:
|
||
|
this.navLeft = nestedNavigator;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.NeedRight:
|
||
|
this.navRight = nestedNavigator;
|
||
|
break;
|
||
|
|
||
|
case IteratorState.NeedLeftAndRight:
|
||
|
// After fetching left node, still need right node
|
||
|
this.navLeft = nestedNavigator;
|
||
|
this.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.");
|
||
|
this.state = IteratorState.NeedLeft;
|
||
|
return SetIteratorResult.NeedLeftNode;
|
||
|
}
|
||
|
|
||
|
if (this.navLeft == null) {
|
||
|
// If navLeft is null, then difference operation is complete
|
||
|
return SetIteratorResult.NoMoreNodes;
|
||
|
}
|
||
|
else if (this.navRight != null) {
|
||
|
int order = this.runtime.ComparePosition(this.navLeft, this.navRight);
|
||
|
|
||
|
// If navLeft is positioned to same node as navRight,
|
||
|
if (order == 0) {
|
||
|
// Skip navLeft and navRight
|
||
|
this.state = IteratorState.NeedLeftAndRight;
|
||
|
return SetIteratorResult.NeedLeftNode;
|
||
|
}
|
||
|
|
||
|
// If navLeft is after navRight in document order, then skip navRight
|
||
|
if (order > 0) {
|
||
|
this.state = IteratorState.NeedRight;
|
||
|
return SetIteratorResult.NeedRightNode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return navLeft
|
||
|
this.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 this.navLeft; }
|
||
|
}
|
||
|
}
|
||
|
}
|