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;
6 using System.Diagnostics;
7 using System.Collections;
8 using System.Collections.Generic;
9 
10 namespace System.DirectoryServices.AccountManagement
11 {
12     internal class TrackedCollectionEnumerator<T> : IEnumerator, IEnumerator<T>
13     {
14         //
15         // Public properties
16         //
17 
18         public T Current
19         {
20             get
21             {
22                 CheckDisposed();
23 
24                 // Since MoveNext() saved off the current value for us, this is largely trivial.
25 
26                 if (_endReached == true || _enumerator == null)
27                 {
28                     // Either we're at the end or before the beginning
29                     GlobalDebug.WriteLineIf(GlobalDebug.Warn, "TrackedCollectionEnumerator", "Current: bad position, endReached={0}", _endReached);
30                     throw new InvalidOperationException(SR.TrackedCollectionEnumInvalidPos);
31                 }
32 
33                 return _current;
34             }
35         }
36 
37         object IEnumerator.Current
38         {
39             get
40             {
41                 return Current;
42             }
43         }
44 
45         //
46         // Public methods
47         //
48 
MoveNext()49         public bool MoveNext()
50         {
51             GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Entering MoveNext");
52 
53             CheckDisposed();
54             CheckChanged();
55 
56             if (_endReached)
57             {
58                 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: endReached");
59                 return false;
60             }
61 
62             if (_enumerator == null)
63             {
64                 // Must be at the very beginning
65                 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: at beginning");
66 
67                 _enumerator = ((IEnumerable)_combinedValues).GetEnumerator();
68                 Debug.Assert(_enumerator != null);
69             }
70 
71             bool gotNextValue = _enumerator.MoveNext();
72 
73             // If we got the next value,
74             // save it off so that Current can later return it.
75             if (gotNextValue)
76             {
77                 // Have to handle differently, since inserted values are just a T, while
78                 // original value are a Pair<T,T>, where Pair.Right is the current value
79                 TrackedCollection<T>.ValueEl el = (TrackedCollection<T>.ValueEl)_enumerator.Current;
80 
81                 if (el.isInserted)
82                 {
83                     GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: current ({0}) is inserted", _current);
84                     _current = el.insertedValue;
85                 }
86                 else
87                 {
88                     _current = el.originalValue.Right;
89                     GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: current ({0}) is original", _current);
90                 }
91             }
92             else
93             {
94                 // Nothing more to enumerate
95                 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: nothing more to enumerate");
96 
97                 _endReached = true;
98             }
99 
100             return gotNextValue;
101         }
102 
IEnumerator.MoveNext()103         bool IEnumerator.MoveNext()
104         {
105             return MoveNext();
106         }
107 
Reset()108         public void Reset()
109         {
110             GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Reset");
111 
112             CheckDisposed();
113             CheckChanged();
114 
115             _endReached = false;
116             _enumerator = null;
117         }
118 
IEnumerator.Reset()119         void IEnumerator.Reset()
120         {
121             Reset();
122         }
123 
Dispose()124         public void Dispose()
125         {
126             _disposed = true;
127         }
128 
129         //
130         // Internal constructors
131         //
TrackedCollectionEnumerator(string outerClassName, TrackedCollection<T> trackedCollection, List<TrackedCollection<T>.ValueEl> combinedValues)132         internal TrackedCollectionEnumerator(string outerClassName, TrackedCollection<T> trackedCollection, List<TrackedCollection<T>.ValueEl> combinedValues)
133         {
134             GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Ctor");
135 
136             _outerClassName = outerClassName;
137             _trackedCollection = trackedCollection;
138             _combinedValues = combinedValues;
139         }
140 
141         //
142         // Private implementation
143         //
144 
145         // Have we been disposed?
146         private bool _disposed = false;
147 
148         //  The name of our outer class. Used when throwing an ObjectDisposedException.
149         private string _outerClassName;
150 
151         private List<TrackedCollection<T>.ValueEl> _combinedValues = null;
152 
153         // The value we're currently positioned at
154         private T _current;
155 
156         // The enumerator for our inner list, combinedValues.
157         private IEnumerator _enumerator = null;
158 
159         // True when we reach the end of combinedValues (no more values to enumerate in the TrackedCollection)
160         private bool _endReached = false;
161 
162         // When this enumerator was constructed, to detect changes made to the TrackedCollection after it was constructed
163         private DateTime _creationTime = DateTime.UtcNow;
164         private TrackedCollection<T> _trackedCollection = null;
165 
CheckDisposed()166         private void CheckDisposed()
167         {
168             if (_disposed)
169             {
170                 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "TrackedCollectionEnumerator", "CheckDisposed: accessing disposed object");
171                 throw new ObjectDisposedException(_outerClassName);
172             }
173         }
174 
CheckChanged()175         private void CheckChanged()
176         {
177             // Make sure the app hasn't changed our underlying list
178             if (_trackedCollection.LastChange > _creationTime)
179             {
180                 GlobalDebug.WriteLineIf(
181                             GlobalDebug.Warn,
182                             "TrackedCollectionEnumerator",
183                             "CheckChanged: has changed (last change={0}, creation={1})",
184                             _trackedCollection.LastChange,
185                             _creationTime);
186 
187                 throw new InvalidOperationException(SR.TrackedCollectionEnumHasChanged);
188             }
189         }
190     }
191 }
192 
193