aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.Burn/RowIndexedList.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.Burn/RowIndexedList.cs')
-rw-r--r--src/WixToolset.Core.Burn/RowIndexedList.cs302
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
3namespace 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}