Skip to content

Commit

Permalink
Add Null checks/logging, make public, Discard new value instead of bo…
Browse files Browse the repository at this point in the history
…th on .Add
  • Loading branch information
MrPurple6411 committed Jan 26, 2023
1 parent 6703854 commit 2a81af4
Showing 1 changed file with 105 additions and 21 deletions.
126 changes: 105 additions & 21 deletions SMLHelper/Utility/SelfCheckingDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,33 @@
/// This dictionary strtucture automatically checks for duplicate keys as they are being added to the collection.
/// Duplicate entires are logged and removed from the final collection.
/// </summary>
/// <typeparam name="K"></typeparam>
/// <typeparam name="V"></typeparam>
internal class SelfCheckingDictionary<K, V> : IDictionary<K, V>
/// <typeparam name="K">The Key Type</typeparam>
/// <typeparam name="V">The Value Type</typeparam>
public class SelfCheckingDictionary<K, V> : IDictionary<K, V>
{
/// <summary>
/// Maintains a collection of the keys that have encountered duplicates and how many of them were discarded.
/// </summary>
internal readonly Dictionary<K, int> DuplicatesDiscarded;
private readonly Dictionary<K, int> DuplicatesDiscarded;

/// <summary>
/// Maintains the final collection of only unique keys.
/// </summary>
internal readonly Dictionary<K, V> UniqueEntries;
internal readonly string CollectionName;
private readonly Dictionary<K, V> UniqueEntries;
private readonly string CollectionName;

internal readonly Func<K, string> ToLogString;
private readonly Func<K, string> ToLogString;

private SelfCheckingDictionary(Func<K, string> toLog)
{
ToLogString = toLog ?? ((k) => k.ToString());
}

/// <summary>
/// Creates a <see cref="SelfCheckingDictionary{K, V}"/> with an optional ToString function.
/// </summary>
/// <param name="collectionName"></param>
/// <param name="toLog"></param>
public SelfCheckingDictionary(string collectionName, Func<K, string> toLog = null)
: this(toLog)
{
Expand All @@ -39,6 +44,12 @@ public SelfCheckingDictionary(string collectionName, Func<K, string> toLog = nul
DuplicatesDiscarded = new Dictionary<K, int>();
}

/// <summary>
/// Creates a <see cref="SelfCheckingDictionary{K, V}"/> with an EqualityComparer and an optional ToString function.
/// </summary>
/// <param name="collectionName"></param>
/// <param name="equalityComparer"></param>
/// <param name="toLog"></param>
public SelfCheckingDictionary(string collectionName, IEqualityComparer<K> equalityComparer, Func<K, string> toLog = null)
: this(toLog)
{
Expand All @@ -55,12 +66,27 @@ public SelfCheckingDictionary(string collectionName, IEqualityComparer<K> equali
/// <returns>The value corresponding to the key.</returns>
public V this[K key]
{
get => UniqueEntries[key];
get
{
if(key == null)
{
LogNullKeyError();
return default;
}
return UniqueEntries[key];
}

set
{
if (UniqueEntries.ContainsKey(key))
if(key == null)
{
LogNullKeyError();
return;
}

if(UniqueEntries.ContainsKey(key))
{
if (DuplicatesDiscarded.ContainsKey(key))
if(DuplicatesDiscarded.ContainsKey(key))
{
DuplicatesDiscarded[key]++;
}
Expand All @@ -76,32 +102,51 @@ public V this[K key]
}
}

/// <summary>
/// Gets a collection containing the keys in the <see cref="SelfCheckingDictionary{K, V}"/>
/// </summary>
public ICollection<K> Keys => UniqueEntries.Keys;

/// <summary>
/// Gets a collection containing the values in the <see cref="SelfCheckingDictionary{K, V}"/>
/// </summary>
public ICollection<V> Values => UniqueEntries.Values;

/// <summary>
/// Gets the number of unique entries in the <see cref="SelfCheckingDictionary{K, V}"/>
/// </summary>
public int Count => UniqueEntries.Count;

/// <summary>
/// Defaults to false.
/// </summary>
public bool IsReadOnly { get; } = false;

/// <summary>
/// Add a new entry the collection.
/// If a duplicate key is found, all entries with that key will be excluded from the final collection.
/// If a duplicate key is found, the new value will be discarded.
/// </summary>
/// <param name="key">The unique key.</param>
/// <param name="value">The value.</param>
public void Add(K key, V value)
{
if(key == null)
{
LogNullKeyError();
return;
}

if (DuplicatesDiscarded.ContainsKey(key))
{
DuplicatesDiscarded[key]++;
DupFoundAllDiscardedLog(key);
DupFoundNewDiscardedLog(key);
return;
}

if (UniqueEntries.ContainsKey(key))
{
UniqueEntries.Remove(key);
DuplicatesDiscarded.Add(key, 2); // Original is discarded and new entry is not added.

DupFoundAllDiscardedLog(key);
DuplicatesDiscarded.Add(key, 1);
DupFoundNewDiscardedLog(key);
return;
}

Expand All @@ -110,14 +155,16 @@ public void Add(K key, V value)

/// <summary>
/// Add a new entry the collection.
/// If a duplicate key is found, all entries with that key will be excluded from the final collection.
/// If a duplicate key is found, the new value will be discarded.
/// </summary>
/// <param name="item">The key value pair.</param>
public void Add(KeyValuePair<K, V> item)
{
Add(item.Key, item.Value);
}


#pragma warning disable 1591
public void Clear()
{
UniqueEntries.Clear();
Expand All @@ -126,12 +173,25 @@ public void Clear()

public bool Contains(KeyValuePair<K, V> item)
{

if(item.Key == null)
{
LogNullKeyError();
return false;
}

return UniqueEntries.TryGetValue(item.Key, out V value) && value.Equals(item.Value);
}

public bool ContainsKey(K key)
{
return UniqueEntries.ContainsKey(key) | DuplicatesDiscarded.ContainsKey(key);
if(key == null)
{
LogNullKeyError();
return false;
}

return UniqueEntries.ContainsKey(key);
}

public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
Expand All @@ -149,16 +209,33 @@ public IEnumerator<KeyValuePair<K, V>> GetEnumerator()

public bool Remove(K key)
{
if(key == null)
{
LogNullKeyError();
return false;
}

return UniqueEntries.Remove(key) | DuplicatesDiscarded.Remove(key);
}

public bool Remove(KeyValuePair<K, V> item)
{
if(item.Key == null)
{
LogNullKeyError();
return false;
}
return UniqueEntries.Remove(item.Key) | DuplicatesDiscarded.Remove(item.Key);
}

public bool TryGetValue(K key, out V value)
{
if(key == null)
{
LogNullKeyError();
value= default(V);
return false;
}
return UniqueEntries.TryGetValue(key, out value);
}

Expand All @@ -167,15 +244,22 @@ IEnumerator IEnumerable.GetEnumerator()
return UniqueEntries.GetEnumerator();
}

#pragma warning restore 1591

private void LogNullKeyError()
{
InternalLogger.Error($"Tried to Add to or Access {CollectionName} Dictionary with a Null Key!");
}

/// <summary>
/// Informs the user that all entries for the specified key have been discarded.
/// Informs the user that the new entry for the specified key has been discarded.
/// </summary>
/// <param name="key">The no longer unique key.</param>
private void DupFoundAllDiscardedLog(K key)
private void DupFoundNewDiscardedLog(K key)
{
string keyLogString = ToLogString(key);
InternalLogger.Warn($"{CollectionName} already exists for '{keyLogString}'.{Environment.NewLine}" +
$"All entries will be removed so conflict can be noted and resolved.{Environment.NewLine}" +
$"New value has been rejected. {Environment.NewLine}" +
$"So far we have discarded or overwritten {DuplicatesDiscarded[key]} entries for '{keyLogString}'.");
}

Expand Down

0 comments on commit 2a81af4

Please sign in to comment.