1 //------------------------------------------------------------------------------
2 // <copyright file="XPathDocumentIterator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7 using System;
8 using System.Xml;
9 using System.Xml.XPath;
10 using System.Diagnostics;
11 
12 namespace MS.Internal.Xml.Cache {
13 
14     /// <summary>
15     /// Base internal class of all XPathDocument XPathNodeIterator implementations.
16     /// </summary>
17     internal abstract class XPathDocumentBaseIterator : XPathNodeIterator {
18         protected XPathDocumentNavigator ctxt;
19         protected int pos;
20 
21         /// <summary>
22         /// Create a new iterator that is initially positioned on the "ctxt" node.
23         /// </summary>
XPathDocumentBaseIterator(XPathDocumentNavigator ctxt)24         protected XPathDocumentBaseIterator(XPathDocumentNavigator ctxt) {
25             this.ctxt = new XPathDocumentNavigator(ctxt);
26         }
27 
28         /// <summary>
29         /// Create a new iterator that is a copy of "iter".
30         /// </summary>
XPathDocumentBaseIterator(XPathDocumentBaseIterator iter)31         protected XPathDocumentBaseIterator(XPathDocumentBaseIterator iter) {
32             this.ctxt = new XPathDocumentNavigator(iter.ctxt);
33             this.pos = iter.pos;
34         }
35 
36         /// <summary>
37         /// Return the current navigator.
38         /// </summary>
39         public override XPathNavigator Current {
40             get { return this.ctxt; }
41         }
42 
43         /// <summary>
44         /// Return the iterator's current position.
45         /// </summary>
46         public override int CurrentPosition {
47             get { return this.pos; }
48         }
49     }
50 
51 
52     /// <summary>
53     /// Iterate over all element children with a particular QName.
54     /// </summary>
55     internal class XPathDocumentElementChildIterator : XPathDocumentBaseIterator {
56         private string localName, namespaceUri;
57 
58         /// <summary>
59         /// Create an iterator that ranges over all element children of "parent" having the specified QName.
60         /// </summary>
XPathDocumentElementChildIterator(XPathDocumentNavigator parent, string name, string namespaceURI)61         public XPathDocumentElementChildIterator(XPathDocumentNavigator parent, string name, string namespaceURI) : base(parent) {
62             if (namespaceURI == null) throw new ArgumentNullException("namespaceURI");
63 
64             this.localName = parent.NameTable.Get(name);
65             this.namespaceUri = namespaceURI;
66         }
67 
68         /// <summary>
69         /// Create a new iterator that is a copy of "iter".
70         /// </summary>
XPathDocumentElementChildIterator(XPathDocumentElementChildIterator iter)71         public XPathDocumentElementChildIterator(XPathDocumentElementChildIterator iter) : base(iter) {
72             this.localName = iter.localName;
73             this.namespaceUri = iter.namespaceUri;
74         }
75 
76         /// <summary>
77         /// Create a copy of this iterator.
78         /// </summary>
Clone()79         public override XPathNodeIterator Clone() {
80             return new XPathDocumentElementChildIterator(this);
81         }
82 
83         /// <summary>
84         /// Position the iterator to the next matching child.
85         /// </summary>
MoveNext()86         public override bool MoveNext() {
87             if (this.pos == 0) {
88                 if (!this.ctxt.MoveToChild(this.localName, this.namespaceUri))
89                     return false;
90             }
91             else {
92                 if (!this.ctxt.MoveToNext(this.localName, this.namespaceUri))
93                     return false;
94             }
95 
96             this.pos++;
97             return true;
98         }
99     }
100 
101 
102     /// <summary>
103     /// Iterate over all content children with a particular XPathNodeType.
104     /// </summary>
105     internal class XPathDocumentKindChildIterator : XPathDocumentBaseIterator {
106         private XPathNodeType typ;
107 
108         /// <summary>
109         /// Create an iterator that ranges over all content children of "parent" having the specified XPathNodeType.
110         /// </summary>
XPathDocumentKindChildIterator(XPathDocumentNavigator parent, XPathNodeType typ)111         public XPathDocumentKindChildIterator(XPathDocumentNavigator parent, XPathNodeType typ) : base(parent) {
112             this.typ = typ;
113         }
114 
115         /// <summary>
116         /// Create a new iterator that is a copy of "iter".
117         /// </summary>
XPathDocumentKindChildIterator(XPathDocumentKindChildIterator iter)118         public XPathDocumentKindChildIterator(XPathDocumentKindChildIterator iter) : base(iter) {
119             this.typ = iter.typ;
120         }
121 
122         /// <summary>
123         /// Create a copy of this iterator.
124         /// </summary>
Clone()125         public override XPathNodeIterator Clone() {
126             return new XPathDocumentKindChildIterator(this);
127         }
128 
129         /// <summary>
130         /// Position the iterator to the next descendant.
131         /// </summary>
MoveNext()132         public override bool MoveNext() {
133             if (this.pos == 0) {
134                 if (!this.ctxt.MoveToChild(this.typ))
135                     return false;
136             }
137             else {
138                 if (!this.ctxt.MoveToNext(this.typ))
139                     return false;
140             }
141 
142             this.pos++;
143             return true;
144         }
145     }
146 
147 
148     /// <summary>
149     /// Iterate over all element descendants with a particular QName.
150     /// </summary>
151     internal class XPathDocumentElementDescendantIterator : XPathDocumentBaseIterator {
152         private XPathDocumentNavigator end;
153         private string localName, namespaceUri;
154         private bool matchSelf;
155 
156         /// <summary>
157         /// Create an iterator that ranges over all element descendants of "root" having the specified QName.
158         /// </summary>
XPathDocumentElementDescendantIterator(XPathDocumentNavigator root, string name, string namespaceURI, bool matchSelf)159         public XPathDocumentElementDescendantIterator(XPathDocumentNavigator root, string name, string namespaceURI, bool matchSelf) : base(root) {
160             if (namespaceURI == null) throw new ArgumentNullException("namespaceURI");
161 
162             this.localName = root.NameTable.Get(name);
163             this.namespaceUri = namespaceURI;
164             this.matchSelf = matchSelf;
165 
166             // Find the next non-descendant node that follows "root" in document order
167             if (root.NodeType != XPathNodeType.Root) {
168                 this.end = new XPathDocumentNavigator(root);
169                 this.end.MoveToNonDescendant();
170             }
171         }
172 
173         /// <summary>
174         /// Create a new iterator that is a copy of "iter".
175         /// </summary>
XPathDocumentElementDescendantIterator(XPathDocumentElementDescendantIterator iter)176         public XPathDocumentElementDescendantIterator(XPathDocumentElementDescendantIterator iter) : base(iter) {
177             this.end = iter.end;
178             this.localName = iter.localName;
179             this.namespaceUri = iter.namespaceUri;
180             this.matchSelf = iter.matchSelf;
181         }
182 
183         /// <summary>
184         /// Create a copy of this iterator.
185         /// </summary>
Clone()186         public override XPathNodeIterator Clone() {
187             return new XPathDocumentElementDescendantIterator(this);
188         }
189 
190         /// <summary>
191         /// Position the iterator to the next descendant.
192         /// </summary>
MoveNext()193         public override bool MoveNext() {
194             if (this.matchSelf) {
195                 this.matchSelf = false;
196 
197                 if (this.ctxt.IsElementMatch(this.localName, this.namespaceUri)) {
198                     this.pos++;
199                     return true;
200                 }
201             }
202 
203             if (!this.ctxt.MoveToFollowing(this.localName, this.namespaceUri, this.end))
204                 return false;
205 
206             this.pos++;
207             return true;
208         }
209     }
210 
211 
212     /// <summary>
213     /// Iterate over all content descendants with a particular XPathNodeType.
214     /// </summary>
215     internal class XPathDocumentKindDescendantIterator : XPathDocumentBaseIterator {
216         private XPathDocumentNavigator end;
217         private XPathNodeType typ;
218         private bool matchSelf;
219 
220         /// <summary>
221         /// Create an iterator that ranges over all content descendants of "root" having the specified XPathNodeType.
222         /// </summary>
XPathDocumentKindDescendantIterator(XPathDocumentNavigator root, XPathNodeType typ, bool matchSelf)223         public XPathDocumentKindDescendantIterator(XPathDocumentNavigator root, XPathNodeType typ, bool matchSelf) : base(root) {
224             this.typ = typ;
225             this.matchSelf = matchSelf;
226 
227             // Find the next non-descendant node that follows "root" in document order
228             if (root.NodeType != XPathNodeType.Root) {
229                 this.end = new XPathDocumentNavigator(root);
230                 this.end.MoveToNonDescendant();
231             }
232         }
233 
234         /// <summary>
235         /// Create a new iterator that is a copy of "iter".
236         /// </summary>
XPathDocumentKindDescendantIterator(XPathDocumentKindDescendantIterator iter)237         public XPathDocumentKindDescendantIterator(XPathDocumentKindDescendantIterator iter) : base(iter) {
238             this.end = iter.end;
239             this.typ = iter.typ;
240             this.matchSelf = iter.matchSelf;
241         }
242 
243         /// <summary>
244         /// Create a copy of this iterator.
245         /// </summary>
Clone()246         public override XPathNodeIterator Clone() {
247             return new XPathDocumentKindDescendantIterator(this);
248         }
249 
250         /// <summary>
251         /// Position the iterator to the next descendant.
252         /// </summary>
MoveNext()253         public override bool MoveNext() {
254             if (this.matchSelf) {
255                 this.matchSelf = false;
256 
257                 if (this.ctxt.IsKindMatch(this.typ)) {
258                     this.pos++;
259                     return true;
260                 }
261             }
262 
263             if (!this.ctxt.MoveToFollowing(this.typ, this.end))
264                 return false;
265 
266             this.pos++;
267             return true;
268         }
269     }
270 }
271 
272