diff options
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/RowIndexedList.cs')
-rw-r--r-- | src/WixToolset.Data.WindowsInstaller/RowIndexedList.cs | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/WixToolset.Data.WindowsInstaller/RowIndexedList.cs b/src/WixToolset.Data.WindowsInstaller/RowIndexedList.cs new file mode 100644 index 00000000..27c43a81 --- /dev/null +++ b/src/WixToolset.Data.WindowsInstaller/RowIndexedList.cs | |||
@@ -0,0 +1,301 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Data | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | |||
8 | /// <summary> | ||
9 | /// A list of rows indexed by their primary key. Unlike a <see cref="RowDictionary"/> | ||
10 | /// this indexed list will track rows in their added order and will allow rows with | ||
11 | /// duplicate keys to be added to the list, although only the first row will be indexed. | ||
12 | /// </summary> | ||
13 | public sealed class RowIndexedList<T> : IList<T> where T : Row | ||
14 | { | ||
15 | private Dictionary<string, T> index; | ||
16 | private List<T> rows; | ||
17 | private List<T> duplicates; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Creates an empty <see cref="RowIndexedList"/>. | ||
21 | /// </summary> | ||
22 | public RowIndexedList() | ||
23 | { | ||
24 | this.index = new Dictionary<string, T>(StringComparer.InvariantCulture); | ||
25 | this.rows = new List<T>(); | ||
26 | this.duplicates = new List<T>(); | ||
27 | } | ||
28 | |||
29 | /// <summary> | ||
30 | /// Creates and populates a <see cref="RowDictionary"/> with the rows from the given enumerator. | ||
31 | /// </summary> | ||
32 | /// <param name="rows">Rows to index.</param> | ||
33 | public RowIndexedList(IEnumerable<T> rows) | ||
34 | : this() | ||
35 | { | ||
36 | foreach (T row in rows) | ||
37 | { | ||
38 | this.Add(row); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | /// <summary> | ||
43 | /// Creates and populates a <see cref="RowDictionary"/> with the rows from the given <see cref="Table"/>. | ||
44 | /// </summary> | ||
45 | /// <param name="table">The table to index.</param> | ||
46 | /// <remarks> | ||
47 | /// Rows added to the index are not automatically added to the given <paramref name="table"/>. | ||
48 | /// </remarks> | ||
49 | public RowIndexedList(Table table) | ||
50 | : this() | ||
51 | { | ||
52 | if (null != table) | ||
53 | { | ||
54 | foreach (T row in table.Rows) | ||
55 | { | ||
56 | this.Add(row); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | /// <summary> | ||
62 | /// Gets the duplicates in the list. | ||
63 | /// </summary> | ||
64 | public IEnumerable<T> Duplicates { get { return this.duplicates; } } | ||
65 | |||
66 | /// <summary> | ||
67 | /// Gets the row by integer key. | ||
68 | /// </summary> | ||
69 | /// <param name="key">Integer key to look up.</param> | ||
70 | /// <returns>Row or null if key is not found.</returns> | ||
71 | public T Get(int key) | ||
72 | { | ||
73 | return this.Get(key.ToString()); | ||
74 | } | ||
75 | |||
76 | /// <summary> | ||
77 | /// Gets the row by string key. | ||
78 | /// </summary> | ||
79 | /// <param name="key">String key to look up.</param> | ||
80 | /// <returns>Row or null if key is not found.</returns> | ||
81 | public T Get(string key) | ||
82 | { | ||
83 | T result; | ||
84 | return this.TryGet(key, out result) ? result : null; | ||
85 | } | ||
86 | |||
87 | /// <summary> | ||
88 | /// Gets the row by string key if it exists. | ||
89 | /// </summary> | ||
90 | /// <param name="key">Key of row to get.</param> | ||
91 | /// <param name="row">Row found.</param> | ||
92 | /// <returns>True if key was found otherwise false.</returns> | ||
93 | public bool TryGet(string key, out T row) | ||
94 | { | ||
95 | return this.index.TryGetValue(key, out row); | ||
96 | } | ||
97 | |||
98 | /// <summary> | ||
99 | /// Tries to add a row as long as it would not create a duplicate. | ||
100 | /// </summary> | ||
101 | /// <param name="row">Row to add.</param> | ||
102 | /// <returns>True if the row as added otherwise false.</returns> | ||
103 | public bool TryAdd(T row) | ||
104 | { | ||
105 | try | ||
106 | { | ||
107 | this.index.Add(row.GetKey(), row); | ||
108 | } | ||
109 | catch (ArgumentException) // if the key already exists, bail. | ||
110 | { | ||
111 | return false; | ||
112 | } | ||
113 | |||
114 | this.rows.Add(row); | ||
115 | return true; | ||
116 | } | ||
117 | |||
118 | /// <summary> | ||
119 | /// Adds a row to the list. If a row with the same key is already index, the row is | ||
120 | /// is not in the index but will still be part of the list and added to the duplicates | ||
121 | /// list. | ||
122 | /// </summary> | ||
123 | /// <param name="row"></param> | ||
124 | public void Add(T row) | ||
125 | { | ||
126 | this.rows.Add(row); | ||
127 | try | ||
128 | { | ||
129 | this.index.Add(row.GetKey(), row); | ||
130 | } | ||
131 | catch (ArgumentException) // if the key already exists, we have a duplicate. | ||
132 | { | ||
133 | this.duplicates.Add(row); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Gets the index of a row. | ||
139 | /// </summary> | ||
140 | /// <param name="row">Iterates through the list of rows to find the index of a particular row.</param> | ||
141 | /// <returns>Index of row or -1 if not found.</returns> | ||
142 | public int IndexOf(T row) | ||
143 | { | ||
144 | return this.rows.IndexOf(row); | ||
145 | } | ||
146 | |||
147 | /// <summary> | ||
148 | /// Inserts a row at a particular index of the list. | ||
149 | /// </summary> | ||
150 | /// <param name="index">Index to insert the row after.</param> | ||
151 | /// <param name="row">Row to insert.</param> | ||
152 | public void Insert(int index, T row) | ||
153 | { | ||
154 | this.rows.Insert(index, row); | ||
155 | try | ||
156 | { | ||
157 | this.index.Add(row.GetKey(), row); | ||
158 | } | ||
159 | catch (ArgumentException) // if the key already exists, we have a duplicate. | ||
160 | { | ||
161 | this.duplicates.Add(row); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /// <summary> | ||
166 | /// Removes a row from a particular index. | ||
167 | /// </summary> | ||
168 | /// <param name="index">Index to remove the row at.</param> | ||
169 | public void RemoveAt(int index) | ||
170 | { | ||
171 | T row = this.rows[index]; | ||
172 | |||
173 | this.rows.RemoveAt(index); | ||
174 | |||
175 | T indexRow; | ||
176 | if (this.index.TryGetValue(row.GetKey(), out indexRow) && indexRow == row) | ||
177 | { | ||
178 | this.index.Remove(row.GetKey()); | ||
179 | } | ||
180 | else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe). | ||
181 | { | ||
182 | this.duplicates.Remove(row); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /// <summary> | ||
187 | /// Gets or sets a row at the specified index. | ||
188 | /// </summary> | ||
189 | /// <param name="index">Index to get the row.</param> | ||
190 | /// <returns>Row at specified index.</returns> | ||
191 | public T this[int index] | ||
192 | { | ||
193 | get | ||
194 | { | ||
195 | return this.rows[index]; | ||
196 | } | ||
197 | set | ||
198 | { | ||
199 | this.rows[index] = value; | ||
200 | try | ||
201 | { | ||
202 | this.index.Add(value.GetKey(), value); | ||
203 | } | ||
204 | catch (ArgumentException) // if the key already exists, we have a duplicate. | ||
205 | { | ||
206 | this.duplicates.Add(value); | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /// <summary> | ||
212 | /// Empties the list and it's index. | ||
213 | /// </summary> | ||
214 | public void Clear() | ||
215 | { | ||
216 | this.index.Clear(); | ||
217 | this.rows.Clear(); | ||
218 | this.duplicates.Clear(); | ||
219 | } | ||
220 | |||
221 | /// <summary> | ||
222 | /// Searches the list for a row without using the index. | ||
223 | /// </summary> | ||
224 | /// <param name="row">Row to look for in the list.</param> | ||
225 | /// <returns>True if the row is in the list, otherwise false.</returns> | ||
226 | public bool Contains(T row) | ||
227 | { | ||
228 | return this.rows.Contains(row); | ||
229 | } | ||
230 | |||
231 | /// <summary> | ||
232 | /// Copies the rows of the list to an array. | ||
233 | /// </summary> | ||
234 | /// <param name="array">Array to copy the list into.</param> | ||
235 | /// <param name="arrayIndex">Index to start copying at.</param> | ||
236 | public void CopyTo(T[] array, int arrayIndex) | ||
237 | { | ||
238 | this.rows.CopyTo(array, arrayIndex); | ||
239 | } | ||
240 | |||
241 | /// <summary> | ||
242 | /// Number of rows in the list. | ||
243 | /// </summary> | ||
244 | public int Count | ||
245 | { | ||
246 | get { return this.rows.Count; } | ||
247 | } | ||
248 | |||
249 | /// <summary> | ||
250 | /// Indicates whether the list is read-only. Always false. | ||
251 | /// </summary> | ||
252 | public bool IsReadOnly | ||
253 | { | ||
254 | get { return false; } | ||
255 | } | ||
256 | |||
257 | /// <summary> | ||
258 | /// Removes a row from the list. Indexed rows will be removed but the colleciton will NOT | ||
259 | /// promote duplicates to the index automatically. The duplicate would also need to be removed | ||
260 | /// and re-added to be indexed. | ||
261 | /// </summary> | ||
262 | /// <param name="row"></param> | ||
263 | /// <returns></returns> | ||
264 | public bool Remove(T row) | ||
265 | { | ||
266 | bool removed = this.rows.Remove(row); | ||
267 | if (removed) | ||
268 | { | ||
269 | T indexRow; | ||
270 | if (this.index.TryGetValue(row.GetKey(), out indexRow) && indexRow == row) | ||
271 | { | ||
272 | this.index.Remove(row.GetKey()); | ||
273 | } | ||
274 | else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe). | ||
275 | { | ||
276 | this.duplicates.Remove(row); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | return removed; | ||
281 | } | ||
282 | |||
283 | /// <summary> | ||
284 | /// Gets an enumerator over the whole list. | ||
285 | /// </summary> | ||
286 | /// <returns>List enumerator.</returns> | ||
287 | public IEnumerator<T> GetEnumerator() | ||
288 | { | ||
289 | return this.rows.GetEnumerator(); | ||
290 | } | ||
291 | |||
292 | /// <summary> | ||
293 | /// Gets an untyped enumerator over the whole list. | ||
294 | /// </summary> | ||
295 | /// <returns>Untyped list enumerator.</returns> | ||
296 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||
297 | { | ||
298 | return this.rows.GetEnumerator(); | ||
299 | } | ||
300 | } | ||
301 | } | ||