Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / XPath / Internal / PrecedingQuery.cs
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
31         public PrecedingQuery(Query qyInput, string name, string prefix, XPathNodeType typeTest) : base(qyInput, name, prefix, typeTest) {
32             ancestorStk = new StackNav();
33         }
34         private PrecedingQuery(PrecedingQuery other) : base(other) {
35             this.workIterator = Clone(other.workIterator);
36             this.ancestorStk = other.ancestorStk.Clone();
37         }
38
39         public override void Reset() {
40             workIterator = null;
41             ancestorStk.Clear();
42             base.Reset();
43         }
44         
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
91         public override XPathNodeIterator Clone() { return new PrecedingQuery(this); }
92         public override QueryProps Properties { get { return base.Properties | QueryProps.Reverse; } }
93     }
94 }
95