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