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