main
cs 299 lines 9.58 KB
Raw
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 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 internal sealed class RowIndexedList<T> : IList<T> where T : Row
15 {
16 private readonly Dictionary<string, T> index;
17 private readonly List<T> rows;
18 private readonly List<T> duplicates;
19
20 /// <summary>
21 /// Creates an empty <see cref="RowIndexedList{T}"/>.
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="RowIndexedList{T}"/> 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 (var row in rows)
38 {
39 this.Add(row);
40 }
41 }
42
43 /// <summary>
44 /// Creates and populates a <see cref="RowIndexedList{T}"/> 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 return this.TryGet(key, out var 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 var row = this.rows[index];
172
173 this.rows.RemoveAt(index);
174
175 if (this.index.TryGetValue(row.GetKey(), out var indexRow) && indexRow == row)
176 {
177 this.index.Remove(row.GetKey());
178 }
179 else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe).
180 {
181 this.duplicates.Remove(row);
182 }
183 }
184
185 /// <summary>
186 /// Gets or sets a row at the specified index.
187 /// </summary>
188 /// <param name="index">Index to get the row.</param>
189 /// <returns>Row at specified index.</returns>
190 public T this[int index]
191 {
192 get
193 {
194 return this.rows[index];
195 }
196 set
197 {
198 this.rows[index] = value;
199 try
200 {
201 this.index.Add(value.GetKey(), value);
202 }
203 catch (ArgumentException) // if the key already exists, we have a duplicate.
204 {
205 this.duplicates.Add(value);
206 }
207 }
208 }
209
210 /// <summary>
211 /// Empties the list and it's index.
212 /// </summary>
213 public void Clear()
214 {
215 this.index.Clear();
216 this.rows.Clear();
217 this.duplicates.Clear();
218 }
219
220 /// <summary>
221 /// Searches the list for a row without using the index.
222 /// </summary>
223 /// <param name="row">Row to look for in the list.</param>
224 /// <returns>True if the row is in the list, otherwise false.</returns>
225 public bool Contains(T row)
226 {
227 return this.rows.Contains(row);
228 }
229
230 /// <summary>
231 /// Copies the rows of the list to an array.
232 /// </summary>
233 /// <param name="array">Array to copy the list into.</param>
234 /// <param name="arrayIndex">Index to start copying at.</param>
235 public void CopyTo(T[] array, int arrayIndex)
236 {
237 this.rows.CopyTo(array, arrayIndex);
238 }
239
240 /// <summary>
241 /// Number of rows in the list.
242 /// </summary>
243 public int Count
244 {
245 get { return this.rows.Count; }
246 }
247
248 /// <summary>
249 /// Indicates whether the list is read-only. Always false.
250 /// </summary>
251 public bool IsReadOnly
252 {
253 get { return false; }
254 }
255
256 /// <summary>
257 /// Removes a row from the list. Indexed rows will be removed but the colleciton will NOT
258 /// promote duplicates to the index automatically. The duplicate would also need to be removed
259 /// and re-added to be indexed.
260 /// </summary>
261 /// <param name="row"></param>
262 /// <returns></returns>
263 public bool Remove(T row)
264 {
265 var removed = this.rows.Remove(row);
266 if (removed)
267 {
268 if (this.index.TryGetValue(row.GetKey(), out var indexRow) && indexRow == row)
269 {
270 this.index.Remove(row.GetKey());
271 }
272 else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe).
273 {
274 this.duplicates.Remove(row);
275 }
276 }
277
278 return removed;
279 }
280
281 /// <summary>
282 /// Gets an enumerator over the whole list.
283 /// </summary>
284 /// <returns>List enumerator.</returns>
285 public IEnumerator<T> GetEnumerator()
286 {
287 return this.rows.GetEnumerator();
288 }
289
290 /// <summary>
291 /// Gets an untyped enumerator over the whole list.
292 /// </summary>
293 /// <returns>Untyped list enumerator.</returns>
294 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
295 {
296 return this.rows.GetEnumerator();
297 }
298 }
299 }