|
//------------------------------------------------------------------------------
// <copyright file="precedingquery.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
namespace MS.Internal.Xml.XPath {
using System;
using System.Xml;
using System.Xml.XPath;
using System.Diagnostics;
using System.Collections.Generic;
using StackNav = ClonableStack<System.Xml.XPath.XPathNavigator>;
// Algorithm:
// Input assumption: qyInput is in DocOrder.
// Preceding of a sequence of nodes will be preceding of last node in DocOrder in that sequence.
// Because qyInput is in DO last input is last node in DO. -- "last"
// If last node is attribute or namespace move last to it element.
// Push this last node and all its ancestors into the ancestorStk. The root node will be the top-most element on the stack.
// Create descendent iterator from the root. -- "workIterator"
// Advancing workIterator we meet all nodes from the ancestorStk in stack order. Nodes in ancestorStk do no belong to the
// the 'preceding' axis and must be ignored.
// Last node in ancestorStk is a centinel node; when we pop it from ancestorStk, we should stop iterations.
internal sealed class PrecedingQuery : BaseAxisQuery {
private XPathNodeIterator workIterator;
private StackNav ancestorStk;
public PrecedingQuery(Query qyInput, string name, string prefix, XPathNodeType typeTest) : base(qyInput, name, prefix, typeTest) {
ancestorStk = new StackNav();
}
private PrecedingQuery(PrecedingQuery other) : base(other) {
this.workIterator = Clone(other.workIterator);
this.ancestorStk = other.ancestorStk.Clone();
}
public override void Reset() {
workIterator = null;
ancestorStk.Clear();
base.Reset();
}
public override XPathNavigator Advance() {
if (workIterator == null) {
XPathNavigator last; {
XPathNavigator input = qyInput.Advance();
if (input == null) {
return null;
}
last = input.Clone();
do {
last.MoveTo(input);
} while ((input = qyInput.Advance()) != null);
if (last.NodeType == XPathNodeType.Attribute || last.NodeType == XPathNodeType.Namespace) {
last.MoveToParent();
}
}
// Fill ancestorStk :
do {
ancestorStk.Push(last.Clone());
} while (last.MoveToParent());
// Create workIterator :
// last.MoveToRoot(); We are on root already
workIterator = last.SelectDescendants(XPathNodeType.All, true);
}
while (workIterator.MoveNext()) {
currentNode = workIterator.Current;
if (currentNode.IsSamePosition(ancestorStk.Peek())) {
ancestorStk.Pop();
if (ancestorStk.Count == 0) {
currentNode = null;
workIterator = null;
Debug.Assert(qyInput.Advance() == null, "we read all qyInput.Advance() already");
return null;
}
continue;
}
if (matches(currentNode)) {
position++;
return currentNode;
}
}
Debug.Fail("Algorithm error: we missed the centinel node");
return null;
}
public override XPathNodeIterator Clone() { return new PrecedingQuery(this); }
public override QueryProps Properties { get { return base.Properties | QueryProps.Reverse; } }
}
}
|