// 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. namespace WixToolset.Core.Burn { using System; using System.Collections.Generic; using WixToolset.Data.WindowsInstaller; /// /// A list of rows indexed by their primary key. Unlike a /// this indexed list will track rows in their added order and will allow rows with /// duplicate keys to be added to the list, although only the first row will be indexed. /// public sealed class RowIndexedList : IList where T : Row { private Dictionary index; private List rows; private List duplicates; /// /// Creates an empty . /// public RowIndexedList() { this.index = new Dictionary(StringComparer.InvariantCulture); this.rows = new List(); this.duplicates = new List(); } /// /// Creates and populates a with the rows from the given enumerator. /// /// Rows to index. public RowIndexedList(IEnumerable rows) : this() { foreach (T row in rows) { this.Add(row); } } /// /// Creates and populates a with the rows from the given . /// /// The table to index. /// /// Rows added to the index are not automatically added to the given . /// public RowIndexedList(Table table) : this() { if (null != table) { foreach (T row in table.Rows) { this.Add(row); } } } /// /// Gets the duplicates in the list. /// public IEnumerable Duplicates { get { return this.duplicates; } } /// /// Gets the row by integer key. /// /// Integer key to look up. /// Row or null if key is not found. public T Get(int key) { return this.Get(key.ToString()); } /// /// Gets the row by string key. /// /// String key to look up. /// Row or null if key is not found. public T Get(string key) { T result; return this.TryGet(key, out result) ? result : null; } /// /// Gets the row by string key if it exists. /// /// Key of row to get. /// Row found. /// True if key was found otherwise false. public bool TryGet(string key, out T row) { return this.index.TryGetValue(key, out row); } /// /// Tries to add a row as long as it would not create a duplicate. /// /// Row to add. /// True if the row as added otherwise false. public bool TryAdd(T row) { try { this.index.Add(row.GetKey(), row); } catch (ArgumentException) // if the key already exists, bail. { return false; } this.rows.Add(row); return true; } /// /// Adds a row to the list. If a row with the same key is already index, the row is /// is not in the index but will still be part of the list and added to the duplicates /// list. /// /// public void Add(T row) { this.rows.Add(row); try { this.index.Add(row.GetKey(), row); } catch (ArgumentException) // if the key already exists, we have a duplicate. { this.duplicates.Add(row); } } /// /// Gets the index of a row. /// /// Iterates through the list of rows to find the index of a particular row. /// Index of row or -1 if not found. public int IndexOf(T row) { return this.rows.IndexOf(row); } /// /// Inserts a row at a particular index of the list. /// /// Index to insert the row after. /// Row to insert. public void Insert(int index, T row) { this.rows.Insert(index, row); try { this.index.Add(row.GetKey(), row); } catch (ArgumentException) // if the key already exists, we have a duplicate. { this.duplicates.Add(row); } } /// /// Removes a row from a particular index. /// /// Index to remove the row at. public void RemoveAt(int index) { T row = this.rows[index]; this.rows.RemoveAt(index); T indexRow; if (this.index.TryGetValue(row.GetKey(), out indexRow) && indexRow == row) { this.index.Remove(row.GetKey()); } else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe). { this.duplicates.Remove(row); } } /// /// Gets or sets a row at the specified index. /// /// Index to get the row. /// Row at specified index. public T this[int index] { get { return this.rows[index]; } set { this.rows[index] = value; try { this.index.Add(value.GetKey(), value); } catch (ArgumentException) // if the key already exists, we have a duplicate. { this.duplicates.Add(value); } } } /// /// Empties the list and it's index. /// public void Clear() { this.index.Clear(); this.rows.Clear(); this.duplicates.Clear(); } /// /// Searches the list for a row without using the index. /// /// Row to look for in the list. /// True if the row is in the list, otherwise false. public bool Contains(T row) { return this.rows.Contains(row); } /// /// Copies the rows of the list to an array. /// /// Array to copy the list into. /// Index to start copying at. public void CopyTo(T[] array, int arrayIndex) { this.rows.CopyTo(array, arrayIndex); } /// /// Number of rows in the list. /// public int Count { get { return this.rows.Count; } } /// /// Indicates whether the list is read-only. Always false. /// public bool IsReadOnly { get { return false; } } /// /// Removes a row from the list. Indexed rows will be removed but the colleciton will NOT /// promote duplicates to the index automatically. The duplicate would also need to be removed /// and re-added to be indexed. /// /// /// public bool Remove(T row) { bool removed = this.rows.Remove(row); if (removed) { T indexRow; if (this.index.TryGetValue(row.GetKey(), out indexRow) && indexRow == row) { this.index.Remove(row.GetKey()); } else // only try to remove from duplicates if the row was not indexed (if it was indexed, it wasn't a dupe). { this.duplicates.Remove(row); } } return removed; } /// /// Gets an enumerator over the whole list. /// /// List enumerator. public IEnumerator GetEnumerator() { return this.rows.GetEnumerator(); } /// /// Gets an untyped enumerator over the whole list. /// /// Untyped list enumerator. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.rows.GetEnumerator(); } } }