// 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();
}
}
}