1 //------------------------------------------------------------------------------ 2 // <copyright file="precedingquery.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 //------------------------------------------------------------------------------ 7 8 namespace MS.Internal.Xml.XPath { 9 using System; 10 using System.Xml; 11 using System.Xml.XPath; 12 using System.Diagnostics; 13 using System.Collections.Generic; 14 using StackNav = ClonableStack<System.Xml.XPath.XPathNavigator>; 15 16 // Algorithm: 17 // Input assumption: qyInput is in DocOrder. 18 // Preceding of a sequence of nodes will be preceding of last node in DocOrder in that sequence. 19 // Because qyInput is in DO last input is last node in DO. -- "last" 20 // If last node is attribute or namespace move last to it element. 21 // Push this last node and all its ancestors into the ancestorStk. The root node will be the top-most element on the stack. 22 // Create descendent iterator from the root. -- "workIterator" 23 // Advancing workIterator we meet all nodes from the ancestorStk in stack order. Nodes in ancestorStk do no belong to the 24 // the 'preceding' axis and must be ignored. 25 // Last node in ancestorStk is a centinel node; when we pop it from ancestorStk, we should stop iterations. 26 27 internal sealed class PrecedingQuery : BaseAxisQuery { 28 private XPathNodeIterator workIterator; 29 private StackNav ancestorStk; 30 PrecedingQuery(Query qyInput, string name, string prefix, XPathNodeType typeTest)31 public PrecedingQuery(Query qyInput, string name, string prefix, XPathNodeType typeTest) : base(qyInput, name, prefix, typeTest) { 32 ancestorStk = new StackNav(); 33 } PrecedingQuery(PrecedingQuery other)34 private PrecedingQuery(PrecedingQuery other) : base(other) { 35 this.workIterator = Clone(other.workIterator); 36 this.ancestorStk = other.ancestorStk.Clone(); 37 } 38 Reset()39 public override void Reset() { 40 workIterator = null; 41 ancestorStk.Clear(); 42 base.Reset(); 43 } 44 Advance()45 public override XPathNavigator Advance() { 46 if (workIterator == null) { 47 XPathNavigator last; { 48 XPathNavigator input = qyInput.Advance(); 49 if (input == null) { 50 return null; 51 } 52 last = input.Clone(); 53 do { 54 last.MoveTo(input); 55 } while ((input = qyInput.Advance()) != null); 56 57 if (last.NodeType == XPathNodeType.Attribute || last.NodeType == XPathNodeType.Namespace) { 58 last.MoveToParent(); 59 } 60 } 61 // Fill ancestorStk : 62 do { 63 ancestorStk.Push(last.Clone()); 64 } while (last.MoveToParent()); 65 // Create workIterator : 66 // last.MoveToRoot(); We are on root already 67 workIterator = last.SelectDescendants(XPathNodeType.All, true); 68 } 69 70 while (workIterator.MoveNext()) { 71 currentNode = workIterator.Current; 72 if (currentNode.IsSamePosition(ancestorStk.Peek())) { 73 ancestorStk.Pop(); 74 if (ancestorStk.Count == 0) { 75 currentNode = null; 76 workIterator = null; 77 Debug.Assert(qyInput.Advance() == null, "we read all qyInput.Advance() already"); 78 return null; 79 } 80 continue; 81 } 82 if (matches(currentNode)) { 83 position++; 84 return currentNode; 85 } 86 } 87 Debug.Fail("Algorithm error: we missed the centinel node"); 88 return null; 89 } 90 Clone()91 public override XPathNodeIterator Clone() { return new PrecedingQuery(this); } 92 public override QueryProps Properties { get { return base.Properties | QueryProps.Reverse; } } 93 } 94 } 95 96