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