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