From 7ab06fdb94119ece4e40a8467404d789f5a3d102 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 9 Apr 2018 15:24:22 -0400 Subject: [PATCH 01/33] feat: shim for platform independent delegate creation --- ICD.Common.Utils/ReflectionUtils.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index 94a9542..8e661b9 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -387,5 +387,23 @@ namespace ICD.Common.Utils return Convert.ChangeType(value, type, null); } + + /// + /// Platform independant delegate instantiation. + /// + /// + /// + /// + /// + public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method) + { + return +#if SIMPLSHARP + CDelegate +#else + Delegate +#endif + .CreateDelegate(type, firstArgument, method); + } } } From 0fd8d91850e3c26b51526e496c459cd91a5a2002 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 9 Apr 2018 16:22:48 -0400 Subject: [PATCH 02/33] docs: Better exception messages when failing to convert type --- ICD.Common.Utils/ReflectionUtils.cs | 63 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index 8e661b9..1260bb6 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -195,6 +195,24 @@ namespace ICD.Common.Utils != null; } + /// + /// Platform independant delegate instantiation. + /// + /// + /// + /// + /// + public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method) + { + return +#if SIMPLSHARP + CDelegate +#else + Delegate +#endif + .CreateDelegate(type, firstArgument, method); + } + /// /// Creates an instance of the given type, calling the default constructor. /// @@ -368,42 +386,33 @@ namespace ICD.Common.Utils if (type.CanBeNull()) return null; - throw new InvalidCastException(); + throw new InvalidCastException(string.Format("Unable to convert NULL to type {0}", type.Name)); } Type valueType = value.GetType(); if (valueType.IsAssignableTo(type)) return value; - // Handle enum - if (type.IsEnum) + try { - if (valueType.IsIntegerNumeric()) - return Enum.ToObject(type, value); + // Handle enum + if (type.IsEnum) + { + if (valueType.IsIntegerNumeric()) + return Enum.ToObject(type, value); - if (value is string) - return Enum.Parse(type, value as string, false); + if (value is string) + return Enum.Parse(type, value as string, false); + } + + return Convert.ChangeType(value, type, null); + } + catch (Exception e) + { + string valueString = valueType.ToString(); + string message = string.Format("Failed to convert {0} to type {1} - {2}", valueString, type, e.Message); + throw new InvalidCastException(message, e); } - - return Convert.ChangeType(value, type, null); - } - - /// - /// Platform independant delegate instantiation. - /// - /// - /// - /// - /// - public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method) - { - return -#if SIMPLSHARP - CDelegate -#else - Delegate -#endif - .CreateDelegate(type, firstArgument, method); } } } From 5d0d491659e23ce546f2035a171314acc973a3b2 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 9 Apr 2018 16:47:54 -0400 Subject: [PATCH 03/33] feat: Shim for JSON formatting a serializable instance --- ICD.Common.Utils/Json/JsonUtils.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils/Json/JsonUtils.cs b/ICD.Common.Utils/Json/JsonUtils.cs index 8bb7a9c..6449cd7 100644 --- a/ICD.Common.Utils/Json/JsonUtils.cs +++ b/ICD.Common.Utils/Json/JsonUtils.cs @@ -107,9 +107,21 @@ namespace ICD.Common.Utils.Json /// [PublicAPI] public static void Print(object value) + { + string formatted = Format(value); + IcdConsole.PrintLine(formatted); + } + + /// + /// Serializes the given item and formats the JSON into a human-readable form. + /// + /// + /// + [PublicAPI] + public static string Format(object value) { string serial = JsonConvert.SerializeObject(value); - Print(serial); + return Format(serial); } /// From 5cd947c0611eab37ec63c28bba9a7841f60081c8 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Tue, 10 Apr 2018 11:06:21 -0400 Subject: [PATCH 04/33] fix casting issue --- ICD.Common.Utils/EnumUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICD.Common.Utils/EnumUtils.cs b/ICD.Common.Utils/EnumUtils.cs index 03b6b90..af3f1e9 100644 --- a/ICD.Common.Utils/EnumUtils.cs +++ b/ICD.Common.Utils/EnumUtils.cs @@ -321,7 +321,7 @@ namespace ICD.Common.Utils throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value"); int maxEnumValue = (GetValues().Max(v => (int)(object)v) * 2) -1 ; - return Enumerable.Range(1, maxEnumValue).Cast().Where(v => HasFlags(value, v)); + return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v)); } /// From 29eb28b1f12aff2e82a6f9bcbe74fadc303c8296 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 10 Apr 2018 12:00:42 -0400 Subject: [PATCH 05/33] feat: New delegate for appending items to the ReprBuilder --- ICD.Common.Utils/ReprBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICD.Common.Utils/ReprBuilder.cs b/ICD.Common.Utils/ReprBuilder.cs index 6204d52..481feae 100644 --- a/ICD.Common.Utils/ReprBuilder.cs +++ b/ICD.Common.Utils/ReprBuilder.cs @@ -3,6 +3,8 @@ using System.Text; namespace ICD.Common.Utils { + public delegate void AddReprPropertyDelegate(string name, object value); + /// /// Simple class for building a string representation of an object. /// From 8d753e1cf9c70cd12f6db610cfe7ad830c3f35c9 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 10 Apr 2018 12:08:29 -0400 Subject: [PATCH 06/33] feat: Adding IGenericEventArgs interface for convenience --- ICD.Common.Utils/EventArguments/GenericEventArgs.cs | 5 ++++- ICD.Common.Utils/EventArguments/IGenericEventArgs.cs | 10 ++++++++++ ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 ICD.Common.Utils/EventArguments/IGenericEventArgs.cs diff --git a/ICD.Common.Utils/EventArguments/GenericEventArgs.cs b/ICD.Common.Utils/EventArguments/GenericEventArgs.cs index 895a067..9ed83a6 100644 --- a/ICD.Common.Utils/EventArguments/GenericEventArgs.cs +++ b/ICD.Common.Utils/EventArguments/GenericEventArgs.cs @@ -2,8 +2,11 @@ namespace ICD.Common.Utils.EventArguments { - public class GenericEventArgs : EventArgs + public class GenericEventArgs : EventArgs, IGenericEventArgs { + /// + /// Gets the wrapped data associated with the event. + /// public T Data { get; private set; } /// diff --git a/ICD.Common.Utils/EventArguments/IGenericEventArgs.cs b/ICD.Common.Utils/EventArguments/IGenericEventArgs.cs new file mode 100644 index 0000000..626a292 --- /dev/null +++ b/ICD.Common.Utils/EventArguments/IGenericEventArgs.cs @@ -0,0 +1,10 @@ +namespace ICD.Common.Utils.EventArguments +{ + public interface IGenericEventArgs + { + /// + /// Gets the wrapped data associated with the event. + /// + T Data { get; } + } +} diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 63af157..8038e0b 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -81,6 +81,7 @@ + From b24c93df09836d2a866bb45113f93eb1ef765787 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 10 Apr 2018 14:02:13 -0400 Subject: [PATCH 07/33] docs: Resolving closure warning --- ICD.Common.Utils/Timers/IcdStopwatch.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICD.Common.Utils/Timers/IcdStopwatch.cs b/ICD.Common.Utils/Timers/IcdStopwatch.cs index 1f9f398..c930671 100644 --- a/ICD.Common.Utils/Timers/IcdStopwatch.cs +++ b/ICD.Common.Utils/Timers/IcdStopwatch.cs @@ -140,6 +140,7 @@ namespace ICD.Common.Utils.Timers { using (IEnumerator enumerator = enumerable.GetEnumerator()) { +// ReSharper disable once AccessToDisposedClosure while (Profile(() => enumerator.MoveNext(), name)) yield return enumerator.Current; } From bd8f7f15a0551aff2a3cd84b7447217cf76cd396 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 10 Apr 2018 17:10:39 -0400 Subject: [PATCH 08/33] refactor: validation --- ICD.Common.Utils/Extensions/EventHandlerExtensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs b/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs index 958cf38..cb812b7 100644 --- a/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs +++ b/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs @@ -14,6 +14,9 @@ namespace ICD.Common.Utils.Extensions /// public static void Raise(this EventHandler extends, object sender) { + if (sender == null) + throw new ArgumentNullException("sender"); + if (extends != null) extends(sender, EventArgs.Empty); } @@ -28,6 +31,12 @@ namespace ICD.Common.Utils.Extensions public static void Raise(this EventHandler extends, object sender, T args) where T : EventArgs { + if (sender == null) + throw new ArgumentNullException("sender"); + + if (args == null) + throw new ArgumentNullException("args"); + if (extends != null) extends(sender, args); } From 990f4e0f8293db22df22d647b9b1a546f39d8019 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 12:14:06 -0400 Subject: [PATCH 09/33] feat: Initial commit of WeakKeyDictionary for keeping caches of items that can not invalidate themselves --- .../Collections/WeakKeyDictionaryTest.cs | 158 ++++++++++ .../Collections/WeakKeyDictionary.cs | 283 ++++++++++++++++++ .../ICD.Common.Utils_SimplSharp.csproj | 1 + 3 files changed, 442 insertions(+) create mode 100644 ICD.Common.Utils.Tests/Collections/WeakKeyDictionaryTest.cs create mode 100644 ICD.Common.Utils/Collections/WeakKeyDictionary.cs diff --git a/ICD.Common.Utils.Tests/Collections/WeakKeyDictionaryTest.cs b/ICD.Common.Utils.Tests/Collections/WeakKeyDictionaryTest.cs new file mode 100644 index 0000000..21c7874 --- /dev/null +++ b/ICD.Common.Utils.Tests/Collections/WeakKeyDictionaryTest.cs @@ -0,0 +1,158 @@ +using System; +using ICD.Common.Utils.Collections; +using NUnit.Framework; + +namespace ICD.Common.Utils.Tests.Collections +{ + [TestFixture] + public sealed class WeakKeyDictionaryTest + { + private sealed class TestClass + { + } + + #region Properties + + [Test] + public void CountTest() + { +#if DEBUG + Assert.Inconclusive(); + return; +#endif + WeakKeyDictionary dict = new WeakKeyDictionary(); + Assert.AreEqual(0, dict.Count); + + TestClass instance = new TestClass(); + + dict.Add(instance, 0); + + Assert.AreEqual(1, dict.Count, "Expected the added item to increase the dictionary size"); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.AreEqual(1, dict.Count, "Expected the dictionary to have one uncollected item"); + + // Need to actually USE the instance at some point AFTER the collection, otherwise it gets optimized out + // and is collected prematurely. + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + instance.ToString(); + + // Now clear the reference to make sure the instance gets collected. + // ReSharper disable once RedundantAssignment + instance = null; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.AreEqual(0, dict.Count, "Expected the dictionary to be empty after collecting"); + } + + [Test] + public void KeysTest() + { + Assert.Inconclusive(); + } + + [Test] + public void ValuesTest() + { + Assert.Inconclusive(); + } + + [Test] + public void IndexerTest() + { + Assert.Inconclusive(); + } + +#endregion + +#region Methods + + [Test] + public void AddTest() + { + Assert.Inconclusive(); + } + + [Test] + public void ContainsKeyTest() + { + Assert.Inconclusive(); + } + + [Test] + public void RemoveTest() + { + Assert.Inconclusive(); + } + + [Test] + public void TryGetValueTest() + { + Assert.Inconclusive(); + } + + [Test] + public void ClearTest() + { + Assert.Inconclusive(); + } + + [Test] + public void RemoveCollectedEntries() + { +#if DEBUG + Assert.Inconclusive(); + return; +#endif + WeakKeyDictionary dict = new WeakKeyDictionary(); + + dict.RemoveCollectedEntries(); + Assert.AreEqual(0, dict.Count, "Expected new dictionary to start empty"); + + TestClass instance = new TestClass(); + + dict.Add(instance, 0); + dict.RemoveCollectedEntries(); + + Assert.AreEqual(1, dict.Count, "Expected instance to add to the dictionary count"); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + dict.RemoveCollectedEntries(); + + Assert.AreEqual(1, dict.Count, "Expected instance to remain uncollected and stay in the dictionary"); + + // Need to actually USE the instance at some point AFTER the collection, otherwise it gets optimized out + // and is collected prematurely. + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + instance.ToString(); + + // Now clear the reference to make sure the instance gets collected. + // ReSharper disable once RedundantAssignment + instance = null; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + dict.RemoveCollectedEntries(); + + Assert.AreEqual(0, dict.Count, "Expected instance to be collected and removed from the dictionary"); + } + + [Test] + public void GetEnumeratorTest() + { + Assert.Inconclusive(); + } + +#endregion + } +} diff --git a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs new file mode 100644 index 0000000..fbc541a --- /dev/null +++ b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using ICD.Common.Properties; +using ICD.Common.Utils.Extensions; + +namespace ICD.Common.Utils.Collections +{ + public sealed class WeakKeyReference : WeakReference + { + private readonly int m_HashCode; + + public new T Target { get { return (T)base.Target; } } + + public int HashCode { get { return m_HashCode; } } + + /// + /// Constructor. + /// + /// + public WeakKeyReference(T key) + : this(key, EqualityComparer.Default) + { + } + + /// + /// Constructor. + /// + /// + /// + public WeakKeyReference(T key, IEqualityComparer comparer) + : base(key) + { + if (comparer == null) + throw new ArgumentNullException("comparer"); + + // Retain the object's hash code immediately so that even + // if the target is GC'ed we will be able to find and + // remove the dead weak reference. + m_HashCode = comparer.GetHashCode(key); + } + } + + public sealed class WeakKeyComparer : IEqualityComparer> + { + private readonly IEqualityComparer m_Comparer; + + /// + /// Constructor. + /// + /// + public WeakKeyComparer(IEqualityComparer comparer) + { + if (comparer == null) + comparer = EqualityComparer.Default; + + m_Comparer = comparer; + } + + public int GetHashCode(WeakKeyReference weakKey) + { + return weakKey.HashCode; + } + + // Note: There are actually 9 cases to handle here. + // + // Let Wa = Alive Weak Reference + // Let Wd = Dead Weak Reference + // Let S = Strong Reference + // + // x | y | Equals(x,y) + // ------------------------------------------------- + // Wa | Wa | comparer.Equals(x.Target, y.Target) + // Wa | Wd | false + // Wa | S | comparer.Equals(x.Target, y) + // Wd | Wa | false + // Wd | Wd | x == y + // Wd | S | false + // S | Wa | comparer.Equals(x, y.Target) + // S | Wd | false + // S | S | comparer.Equals(x, y) + // ------------------------------------------------- + public bool Equals(WeakKeyReference x, WeakKeyReference y) + { + bool xIsDead; + bool yIsDead; + + T first = GetTarget(x, out xIsDead); + T second = GetTarget(y, out yIsDead); + + if (xIsDead) + return yIsDead && x == y; + + return !yIsDead && m_Comparer.Equals(first, second); + } + + private static T GetTarget(WeakKeyReference obj, out bool isDead) + { + T target = obj.Target; + isDead = !obj.IsAlive; + return target; + } + } + + + /// + /// WeakDictionary keeps weak references to keys and drops key/value pairs once the key is garbage collected. + /// + /// + /// + public sealed class WeakKeyDictionary : IDictionary + { + private readonly Dictionary, TValue> m_Dictionary; + private readonly IEqualityComparer m_Comparer; + + #region Properties + + public int Count + { + get + { + RemoveCollectedEntries(); + return m_Dictionary.Count; + } + } + + public bool IsReadOnly { get { return false; } } + + public ICollection Keys { get { throw new NotImplementedException(); } } + + public ICollection Values { get { throw new NotImplementedException(); } } + + public TValue this[TKey key] + { + get + { + TValue output; + if (TryGetValue(key, out output)) + return output; + + throw new KeyNotFoundException(); + } + set + { + if (key == null) + throw new ArgumentNullException("key"); + + WeakKeyReference weakKey = new WeakKeyReference(key, m_Comparer); + m_Dictionary[weakKey] = value; + } + } + + #endregion + + public WeakKeyDictionary() + : this(0) + { + } + + public WeakKeyDictionary(int capacity) + : this(capacity, EqualityComparer.Default) + { + } + + public WeakKeyDictionary(IEqualityComparer comparer) + : this(0, comparer) + { + } + + public WeakKeyDictionary(int capacity, IEqualityComparer comparer) + { + m_Comparer = comparer; + + WeakKeyComparer keyComparer = new WeakKeyComparer(m_Comparer); + m_Dictionary = new Dictionary, TValue>(capacity, keyComparer); + } + + #region Methods + + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void Add(TKey key, TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + + WeakKeyReference weakKey = new WeakKeyReference(key, m_Comparer); + IcdConsole.PrintLine("Adding key {0} with hash code {1}", weakKey.Target, weakKey.HashCode); + + m_Dictionary.Add(weakKey, value); + + IcdConsole.PrintLine("Internal dict count is now {0}", m_Dictionary.Count); + } + + public bool ContainsKey(TKey key) + { + TValue unused; + return TryGetValue(key, out unused); + } + + public bool Remove(TKey key) + { + return m_Dictionary.Remove(new WeakKeyReference(key, m_Comparer)); + } + + public bool TryGetValue(TKey key, out TValue value) + { + return m_Dictionary.TryGetValue(new WeakKeyReference(key, m_Comparer), out value); + } + + public void Clear() + { + m_Dictionary.Clear(); + } + + /// + /// Removes the left-over weak references for entries in the dictionary + /// whose key or value has already been reclaimed by the garbage + /// collector. This will reduce the dictionary's Count by the number + /// of dead key-value pairs that were eliminated. + /// + [PublicAPI] + public void RemoveCollectedEntries() + { + IEnumerable> toRemove = + m_Dictionary.Select(pair => pair.Key) + .Where(weakKey => !weakKey.IsAlive) + .ToArray(); + + IcdConsole.PrintLine("-------------------"); + foreach (var item in toRemove) + IcdConsole.PrintLine("{0} - {1}", item, item.Target); + IcdConsole.PrintLine("-------------------"); + + m_Dictionary.RemoveAll(toRemove); + } + + #region ICollection + + void ICollection>.Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + bool ICollection>.Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerator + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair, TValue> kvp in m_Dictionary) + { + WeakKeyReference weakKey = kvp.Key; + TKey key = weakKey.Target; + if (weakKey.IsAlive) + yield return new KeyValuePair(key, kvp.Value); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #endregion + } +} diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 8038e0b..f69e8d2 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -74,6 +74,7 @@ + From 416bf6414e1aaa8db86db0ab8eb44fd8247b7bb7 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 13:05:13 -0400 Subject: [PATCH 10/33] feature: Extension method for copying an enumerable into an array --- .../Extensions/EnumerableExtensions.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ICD.Common.Utils/Extensions/EnumerableExtensions.cs b/ICD.Common.Utils/Extensions/EnumerableExtensions.cs index 74e69ba..865d2ee 100644 --- a/ICD.Common.Utils/Extensions/EnumerableExtensions.cs +++ b/ICD.Common.Utils/Extensions/EnumerableExtensions.cs @@ -578,6 +578,23 @@ namespace ICD.Common.Utils.Extensions return extends.Where(i => !comparer.Equals(item, i)); } + /// + /// Copies all the elements of the current one-dimensional array to the specified one-dimensional array + /// starting at the specified destination array index. The index is specified as a 32-bit integer. + /// + /// + /// + /// + /// + public static void CopyTo(this IEnumerable extends, T[] array, int index) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + ICollection collection = extends as ICollection ?? extends.ToArray(); + collection.CopyTo(array, index); + } + /// /// Returns the sequence as a IcdHashSet. /// From 647b67fbfc9f2117f1eef21c5f79c57b7a453985 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 13:09:03 -0400 Subject: [PATCH 11/33] refactor: Removing debug lines --- ICD.Common.Utils/Collections/WeakKeyDictionary.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs index fbc541a..eac0a83 100644 --- a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs +++ b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs @@ -231,11 +231,6 @@ namespace ICD.Common.Utils.Collections .Where(weakKey => !weakKey.IsAlive) .ToArray(); - IcdConsole.PrintLine("-------------------"); - foreach (var item in toRemove) - IcdConsole.PrintLine("{0} - {1}", item, item.Target); - IcdConsole.PrintLine("-------------------"); - m_Dictionary.RemoveAll(toRemove); } From 3e780a2cba114704d5407f3636eae73bff2623e3 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 13:09:20 -0400 Subject: [PATCH 12/33] feature: Implementing remaining WeakKeyDictionary members --- .../Collections/WeakKeyDictionary.cs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs index eac0a83..8801c2a 100644 --- a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs +++ b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs @@ -116,20 +116,13 @@ namespace ICD.Common.Utils.Collections #region Properties - public int Count - { - get - { - RemoveCollectedEntries(); - return m_Dictionary.Count; - } - } + public int Count { get { return GetAliveKvps().Count(); } } public bool IsReadOnly { get { return false; } } - public ICollection Keys { get { throw new NotImplementedException(); } } + public ICollection Keys { get { return GetAliveKvps().Select(kvp => kvp.Key).ToArray(); } } - public ICollection Values { get { throw new NotImplementedException(); } } + public ICollection Values { get { return GetAliveKvps().Select(kvp => kvp.Value).ToArray(); } } public TValue this[TKey key] { @@ -178,22 +171,13 @@ namespace ICD.Common.Utils.Collections #region Methods - bool ICollection>.Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - public void Add(TKey key, TValue value) { if (key == null) throw new ArgumentNullException("key"); WeakKeyReference weakKey = new WeakKeyReference(key, m_Comparer); - IcdConsole.PrintLine("Adding key {0} with hash code {1}", weakKey.Target, weakKey.HashCode); - m_Dictionary.Add(weakKey, value); - - IcdConsole.PrintLine("Internal dict count is now {0}", m_Dictionary.Count); } public bool ContainsKey(TKey key) @@ -241,21 +225,22 @@ namespace ICD.Common.Utils.Collections Add(item.Key, item.Value); } + bool ICollection>.Remove(KeyValuePair item) + { + return Remove(item.Key); + } + bool ICollection>.Contains(KeyValuePair item) { - throw new NotImplementedException(); + return ContainsKey(item.Key); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { - throw new NotImplementedException(); + GetAliveKvps().CopyTo(array, arrayIndex); } - #endregion - - #region IEnumerator - - public IEnumerator> GetEnumerator() + private IEnumerable> GetAliveKvps() { foreach (KeyValuePair, TValue> kvp in m_Dictionary) { @@ -266,6 +251,15 @@ namespace ICD.Common.Utils.Collections } } + #endregion + + #region IEnumerator + + public IEnumerator> GetEnumerator() + { + return GetAliveKvps().GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); From 374aa65af5b2fdff461e828c26601343e7f5b8c8 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 13:10:19 -0400 Subject: [PATCH 13/33] refactor: whitespace --- ICD.Common.Utils/Collections/WeakKeyDictionary.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs index 8801c2a..d8fc681 100644 --- a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs +++ b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs @@ -103,7 +103,6 @@ namespace ICD.Common.Utils.Collections } } - /// /// WeakDictionary keeps weak references to keys and drops key/value pairs once the key is garbage collected. /// From e70e9b21b0fc55f50db7d7a1c84930bf7064d1ed Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 13:58:33 -0400 Subject: [PATCH 14/33] chore: renaming CHANGELOG.txt to CHANGELOG.md --- CHANGELOG.txt => CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CHANGELOG.txt => CHANGELOG.md (100%) diff --git a/CHANGELOG.txt b/CHANGELOG.md similarity index 100% rename from CHANGELOG.txt rename to CHANGELOG.md From 27e3e55af4adb953747a145ea00ba8260fd2c669 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 14:33:11 -0400 Subject: [PATCH 15/33] fix: Removing bad validation --- ICD.Common.Utils/Extensions/EventHandlerExtensions.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs b/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs index cb812b7..6e222db 100644 --- a/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs +++ b/ICD.Common.Utils/Extensions/EventHandlerExtensions.cs @@ -14,9 +14,6 @@ namespace ICD.Common.Utils.Extensions /// public static void Raise(this EventHandler extends, object sender) { - if (sender == null) - throw new ArgumentNullException("sender"); - if (extends != null) extends(sender, EventArgs.Empty); } @@ -31,9 +28,6 @@ namespace ICD.Common.Utils.Extensions public static void Raise(this EventHandler extends, object sender, T args) where T : EventArgs { - if (sender == null) - throw new ArgumentNullException("sender"); - if (args == null) throw new ArgumentNullException("args"); From 0eba82e3ef800e0aebe15175106366a16fc3ceb0 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 11 Apr 2018 15:32:04 -0400 Subject: [PATCH 16/33] feat: Extension method for reading JSON value as a UInt --- ICD.Common.Utils/Extensions/JsonExtensions.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ICD.Common.Utils/Extensions/JsonExtensions.cs b/ICD.Common.Utils/Extensions/JsonExtensions.cs index 77a0eb0..ac56fa2 100644 --- a/ICD.Common.Utils/Extensions/JsonExtensions.cs +++ b/ICD.Common.Utils/Extensions/JsonExtensions.cs @@ -75,6 +75,24 @@ namespace ICD.Common.Utils.Extensions return Type.GetType(value); } + /// + /// Gets the current value as an unsigned integer. + /// + /// + /// + [PublicAPI] + public static uint GetValueAsUInt(this JsonReader extends) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + if (extends.TokenType == JsonToken.Integer) + return (uint)(long)extends.Value; + + string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer); + throw new InvalidCastException(message); + } + /// /// Gets the current value as an integer. /// From 2b49354fd6b91c72a9599b2a2f78b2e72481b71a Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 12 Apr 2018 13:34:32 -0400 Subject: [PATCH 17/33] feat: Reflection util for subscribing to an EventInfo --- ICD.Common.Utils/ReflectionUtils.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index 1260bb6..d21b9c9 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -414,5 +414,26 @@ namespace ICD.Common.Utils throw new InvalidCastException(message, e); } } + + /// + /// Subscribes to the event on the given instance using the handler and callback method. + /// + /// + /// + /// + /// + /// + public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback) + { + if (eventInfo == null) + throw new ArgumentNullException("eventInfo"); + + if (callback == null) + throw new ArgumentNullException("callback"); + + Delegate output = CreateDelegate(eventInfo.EventHandlerType, handler, callback); + eventInfo.AddEventHandler(instance, output); + return output; + } } } From bc1ecf8b8e278e5ac9b8e443e99e668d402b5539 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 12 Apr 2018 14:16:53 -0400 Subject: [PATCH 18/33] feat: Reflection method for unsubscribing from an event. --- ICD.Common.Utils/ReflectionUtils.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index d21b9c9..534ca34 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -418,10 +418,10 @@ namespace ICD.Common.Utils /// /// Subscribes to the event on the given instance using the handler and callback method. /// - /// - /// - /// - /// + /// The instance with the event. Null for static types. + /// The EventInfo for the event. + /// The instance with the callback MethodInfo. Null for static types. + /// The MethodInfo for the callback method. /// public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback) { @@ -435,5 +435,22 @@ namespace ICD.Common.Utils eventInfo.AddEventHandler(instance, output); return output; } + + /// + /// Unsubscribes from the event on the given instance. + /// + /// The instance with the event. Null for static types. + /// The EventInfo for the event. + /// The Delegate to be removed from the event. + public static void UnsubscribeEvent(object instance, EventInfo eventInfo, Delegate callback) + { + if (eventInfo == null) + throw new ArgumentNullException("eventInfo"); + + if (callback == null) + throw new ArgumentNullException("callback"); + + eventInfo.RemoveEventHandler(instance, callback); + } } } From 0859e7433fae4cfa4daf18d60514a1fd9b037ca6 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Fri, 13 Apr 2018 14:03:13 -0400 Subject: [PATCH 19/33] feat: Shims for adding a sequence of items to a sorted list --- ICD.Common.Utils/Extensions/ListExtensions.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ICD.Common.Utils/Extensions/ListExtensions.cs b/ICD.Common.Utils/Extensions/ListExtensions.cs index 9e72772..e5394a8 100644 --- a/ICD.Common.Utils/Extensions/ListExtensions.cs +++ b/ICD.Common.Utils/Extensions/ListExtensions.cs @@ -10,6 +10,46 @@ namespace ICD.Common.Utils.Extensions /// public static class ListExtensions { + /// + /// Adds the items into a sorted list. + /// + /// + /// + /// + [PublicAPI] + public static void AddSorted(this List extends, IEnumerable items) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + if (items == null) + throw new ArgumentNullException("items"); + + extends.AddSorted(items, Comparer.Default); + } + + /// + /// Adds the item into a sorted list. + /// + /// + /// + /// + /// + [PublicAPI] + public static void AddSorted(this List extends, IEnumerable items, IComparer comparer) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + if (items == null) + throw new ArgumentNullException("items"); + + if (comparer == null) + throw new ArgumentNullException("comparer"); + + items.ForEach(i => extends.AddSorted(i, comparer)); + } + /// /// Adds the item into a sorted list. /// From 7065b0c567b6ad4abf22e5cf631b804012107ce2 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 16 Apr 2018 16:54:03 -0400 Subject: [PATCH 20/33] feat: Adding util methods for path comparisons --- ICD.Common.Utils/IO/IcdPath.cs | 4 ++ ICD.Common.Utils/PathUtils.cs | 18 +++++++++ ICD.Common.Utils/StringUtils.cs | 72 +++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/ICD.Common.Utils/IO/IcdPath.cs b/ICD.Common.Utils/IO/IcdPath.cs index 37d0bad..6ee4a52 100644 --- a/ICD.Common.Utils/IO/IcdPath.cs +++ b/ICD.Common.Utils/IO/IcdPath.cs @@ -10,6 +10,10 @@ namespace ICD.Common.Utils.IO { public static class IcdPath { + public static char DirectorySeparatorChar { get { return Path.DirectorySeparatorChar; } } + + public static char AltDirectorySeparatorChar { get { return Path.AltDirectorySeparatorChar; } } + public static string GetFileName(string path) { if (path == null) diff --git a/ICD.Common.Utils/PathUtils.cs b/ICD.Common.Utils/PathUtils.cs index 7a691e1..3d473a6 100644 --- a/ICD.Common.Utils/PathUtils.cs +++ b/ICD.Common.Utils/PathUtils.cs @@ -185,6 +185,24 @@ namespace ICD.Common.Utils return IcdFile.Exists(path) || IcdDirectory.Exists(path); } + /// + /// Returns the path if the given path is already a directory or has a trailing slash. + /// Otherwise returns the parent directory name. + /// + /// + /// + [PublicAPI] + public static string GetDirectoryNameFromPath(string path) + { + if (IcdDirectory.Exists(path)) + return path; + + if (path.EndsWith(IcdPath.DirectorySeparatorChar) || path.EndsWith(IcdPath.AltDirectorySeparatorChar)) + return path; + + return IcdPath.GetDirectoryName(path); + } + #endregion } } diff --git a/ICD.Common.Utils/StringUtils.cs b/ICD.Common.Utils/StringUtils.cs index 36008e7..3235b7b 100644 --- a/ICD.Common.Utils/StringUtils.cs +++ b/ICD.Common.Utils/StringUtils.cs @@ -610,5 +610,77 @@ namespace ICD.Common.Utils { return value == null ? null : value.ToUpper(); } + + /// + /// Compares the given chars for equality. + /// + /// + /// + /// + /// + [PublicAPI] + public static bool Compare(char a, char b, bool ignoreCase) + { + if (ignoreCase) + { + a = char.ToUpper(a, CultureInfo.InvariantCulture); + b = char.ToUpper(b, CultureInfo.InvariantCulture); + } + + return a == b; + } + + /// + /// Find the longest common string between the matches. + /// E.g. + /// + /// C:\\Workspace + /// C:\\Workshop + /// + /// Results in + /// + /// C:\\Work + /// + /// + /// + /// + [PublicAPI] + public static string GetLongestCommonIntersectionFromStart(IEnumerable items, bool ignoreCase) + { + if (items == null) + throw new ArgumentNullException("items"); + + string output = null; + + foreach (string item in items) + { + // If there is a null in the sequence that's the best match we can make + if (string.IsNullOrEmpty(item)) + return null; + + // Seed our first item + if (output == null) + { + output = item; + continue; + } + + // Find the common substring + for (int index = 0; index < output.Length; index++) + { + if (index >= item.Length || !Compare(output[index], item[index], ignoreCase)) + { + output = output.Substring(0, index); + break; + } + } + + // Abandon the search if there is no common substring + if (string.IsNullOrEmpty(output)) + break; + } + + return output; + } } } From 23047c1c2c4a6e816bdb93e8b2bda2ca95f562e4 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 17 Apr 2018 14:05:11 -0400 Subject: [PATCH 21/33] docs: Fixing comment --- ICD.Common.Utils/Extensions/ListExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICD.Common.Utils/Extensions/ListExtensions.cs b/ICD.Common.Utils/Extensions/ListExtensions.cs index e5394a8..68642f9 100644 --- a/ICD.Common.Utils/Extensions/ListExtensions.cs +++ b/ICD.Common.Utils/Extensions/ListExtensions.cs @@ -29,7 +29,7 @@ namespace ICD.Common.Utils.Extensions } /// - /// Adds the item into a sorted list. + /// Adds the items into a sorted list. /// /// /// From 9e715d37904dd9cf8d7d6b14a522120767e9c518 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 17 Apr 2018 14:05:32 -0400 Subject: [PATCH 22/33] feat: Util methods for inserting items into a sorted list by a predicate --- ICD.Common.Utils/Extensions/ListExtensions.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/ICD.Common.Utils/Extensions/ListExtensions.cs b/ICD.Common.Utils/Extensions/ListExtensions.cs index 68642f9..50c28c7 100644 --- a/ICD.Common.Utils/Extensions/ListExtensions.cs +++ b/ICD.Common.Utils/Extensions/ListExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using ICD.Common.Properties; +using ICD.Common.Utils.Comparers; namespace ICD.Common.Utils.Extensions { @@ -50,6 +51,30 @@ namespace ICD.Common.Utils.Extensions items.ForEach(i => extends.AddSorted(i, comparer)); } + /// + /// Adds the items into a sorted list. + /// + /// + /// + /// + /// + /// + [PublicAPI] + public static void AddSorted(this List extends, IEnumerable items, Func predicate) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + if (items == null) + throw new ArgumentNullException("items"); + + if (predicate == null) + throw new ArgumentNullException("predicate"); + + PredicateComparer comparer = new PredicateComparer(predicate); + extends.AddSorted(items, comparer); + } + /// /// Adds the item into a sorted list. /// @@ -106,6 +131,27 @@ namespace ICD.Common.Utils.Extensions extends.Insert(index, item); } + /// + /// Adds the item into a sorted list. + /// + /// + /// + /// + /// + /// + [PublicAPI] + public static void AddSorted(this List extends, T item, Func predicate) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + if (predicate == null) + throw new ArgumentNullException("predicate"); + + PredicateComparer comparer = new PredicateComparer(predicate); + extends.AddSorted(item, comparer); + } + /// /// Pads the list to the given total length. /// From d587b794cbe7ee3fadf02a6457f177c6f62316a3 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 18 Apr 2018 14:12:21 -0400 Subject: [PATCH 23/33] feat: Adding extension method for getting assembly informational version --- CHANGELOG.md | 5 +++++ ICD.Common.Utils/Extensions/AssemblyExtensions.cs | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825f45d..3a11e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added + - Adding extension method for getting Informational Version from an Assembly + \ No newline at end of file diff --git a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs index 3a67c37..0cb0615 100644 --- a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs +++ b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs @@ -57,5 +57,18 @@ namespace ICD.Common.Utils.Extensions string path = extends.GetPath(); return path == null ? DateTime.MinValue : IcdFile.GetCreationTime(path); } + + /// + /// Gets the informational version for the given assembly. + /// + /// + /// + public static string GetInformationalVersion(this Assembly extends) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + return extends.GetCustomAttribute().InformationalVersion; + } } } From 2bd018b7f1522260ff0bbe98214a94aaeb32b9f1 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 19 Apr 2018 15:54:27 -0400 Subject: [PATCH 24/33] docs: Tidying comments --- ICD.Common.Utils/Extensions/AssemblyExtensions.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs index 0cb0615..373d344 100644 --- a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs +++ b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs @@ -12,7 +12,7 @@ namespace ICD.Common.Utils.Extensions public static class AssemblyExtensions { /// - /// Gets the path for the given assembly. Returns null if the assembly can not be found on disk. + /// Gets the path for the assembly. Returns null if the assembly can not be found on disk. /// /// /// @@ -45,10 +45,11 @@ namespace ICD.Common.Utils.Extensions } /// - /// Gets the creation date of the given assembly. + /// Gets the creation date of the assembly. /// /// /// + [PublicAPI] public static DateTime GetCreationTime(this Assembly extends) { if (extends == null) @@ -59,10 +60,11 @@ namespace ICD.Common.Utils.Extensions } /// - /// Gets the informational version for the given assembly. + /// Gets the informational version for the assembly. /// /// /// + [PublicAPI] public static string GetInformationalVersion(this Assembly extends) { if (extends == null) From f8d189564ad8f6ac873b8ebde267e3a21fc5bc0a Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 19 Apr 2018 15:55:01 -0400 Subject: [PATCH 25/33] feat: TryGetInformationalVersion extension method --- .../Extensions/AssemblyExtensions.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs index 373d344..fafd5a9 100644 --- a/ICD.Common.Utils/Extensions/AssemblyExtensions.cs +++ b/ICD.Common.Utils/Extensions/AssemblyExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.IO; #if SIMPLSHARP @@ -70,7 +71,28 @@ namespace ICD.Common.Utils.Extensions if (extends == null) throw new ArgumentNullException("extends"); - return extends.GetCustomAttribute().InformationalVersion; + string version; + if (extends.TryGetInformationalVersion(out version)) + return version; + + throw new InvalidOperationException("Assembly has no informational version attribute."); + } + + /// + /// Tries to get the informational version for the assembly. + /// + /// + /// + /// + [PublicAPI] + public static bool TryGetInformationalVersion(this Assembly extends, out string version) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + return extends.GetCustomAttributes() + .Select(a => a.InformationalVersion) + .TryFirst(out version); } } } From 3876c0470fea56e5a7c7dafd972f950ea6dc6b40 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Fri, 20 Apr 2018 17:30:49 -0400 Subject: [PATCH 26/33] refactor: Reducing redundant code by moving json serialization/deserialization features into abstract converter --- CHANGELOG.md | 3 + .../Json/AbstractGenericJsonConverter.cs | 75 ++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a11e17..a320b05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,4 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - Adding extension method for getting Informational Version from an Assembly + +### Changed + - JSON serialization/deserialization features moved into base converter \ No newline at end of file diff --git a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs index 151a7ca..ced8956 100644 --- a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs +++ b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs @@ -6,6 +6,15 @@ namespace ICD.Common.Utils.Json { public abstract class AbstractGenericJsonConverter : JsonConverter { + /// + /// Creates a new instance of T. + /// + /// + protected virtual T Instantiate() + { + return ReflectionUtils.CreateInstance(); + } + /// /// Writes the JSON representation of the object. /// @@ -36,7 +45,24 @@ namespace ICD.Common.Utils.Json /// The value. /// The calling serializer. [PublicAPI] - public abstract void WriteJson(JsonWriter writer, T value, JsonSerializer serializer); + public virtual void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) + { + writer.WriteStartObject(); + { + WriteProperties(writer, value, serializer); + } + writer.WriteEndObject(); + } + + /// + /// Override to write properties to the writer. + /// + /// + /// + /// + protected virtual void WriteProperties(JsonWriter writer, T value, JsonSerializer serializer) + { + } /// /// Reads the JSON representation of the object. @@ -70,7 +96,52 @@ namespace ICD.Common.Utils.Json /// The object value. /// [PublicAPI] - public abstract T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer); + public virtual T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer) + { + T output = default(T); + bool instantiated = false; + + while (reader.Read()) + { + if (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.EndObject) + break; + + if (!instantiated) + { + instantiated = true; + output = Instantiate(); + } + + // Get the property + if (reader.TokenType != JsonToken.PropertyName) + continue; + string property = (string)reader.Value; + + // Read into the value + reader.Read(); + + switch (property) + { + default: + ReadProperty(property, reader, output, serializer); + break; + } + } + + return output; + } + + /// + /// Override to handle the current property value with the given name. + /// + /// + /// + /// + /// + protected virtual void ReadProperty(string property, JsonReader reader, T instance, JsonSerializer serializer) + { + reader.Skip(); + } /// /// Determines whether this instance can convert the specified object type. From 7b8ae8402e515d0a3f9cb0acb57c3aa56f5c37e1 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 23 Apr 2018 14:06:02 -0400 Subject: [PATCH 27/33] chore: Updating changelog and major version number --- CHANGELOG.md | 5 +++++ ICD.Common.Utils/Properties/AssemblyInfo.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a320b05..65ec3d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [3.0.0] - 2018-04-23 ### Added - Adding extension method for getting Informational Version from an Assembly + - Adding WeakKeyDictionary for caching + - Reflection util methods ### Changed - JSON serialization/deserialization features moved into base converter + - Removed suffix from assembly name \ No newline at end of file diff --git a/ICD.Common.Utils/Properties/AssemblyInfo.cs b/ICD.Common.Utils/Properties/AssemblyInfo.cs index 4a435e7..2303f51 100644 --- a/ICD.Common.Utils/Properties/AssemblyInfo.cs +++ b/ICD.Common.Utils/Properties/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System.Reflection; [assembly: AssemblyCompany("ICD Systems")] [assembly: AssemblyProduct("ICD.Common.Utils")] [assembly: AssemblyCopyright("Copyright © ICD Systems 2018")] -[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyVersion("3.0.0.0")] From 08f525eb3efc38bb21058a5e9ca71b108c816dcc Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 25 Apr 2018 14:09:09 -0400 Subject: [PATCH 28/33] feat: Clearer log message for failing to cache assembly --- ICD.Common.Utils/AttributeUtils.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ICD.Common.Utils/AttributeUtils.cs b/ICD.Common.Utils/AttributeUtils.cs index e9cf173..2cd9943 100644 --- a/ICD.Common.Utils/AttributeUtils.cs +++ b/ICD.Common.Utils/AttributeUtils.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; @@ -84,6 +85,14 @@ namespace ICD.Common.Utils { foreach (Exception inner in e.LoaderExceptions) { + if (inner is FileNotFoundException) + { + Logger.AddEntry(eSeverity.Error, + "{0} failed to cache assembly {1} - Could not find one or more dependencies by path", + typeof(AttributeUtils).Name, assembly.GetName().Name); + continue; + } + Logger.AddEntry(eSeverity.Error, inner, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name, assembly.GetName().Name); } From a5cc9e694afc29943075443354b67b106c88e9ab Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 26 Apr 2018 11:05:49 -0400 Subject: [PATCH 29/33] refactor: Tidying --- ICD.Common.Utils/AttributeUtils.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICD.Common.Utils/AttributeUtils.cs b/ICD.Common.Utils/AttributeUtils.cs index 2cd9943..637b8cc 100644 --- a/ICD.Common.Utils/AttributeUtils.cs +++ b/ICD.Common.Utils/AttributeUtils.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; @@ -85,7 +84,7 @@ namespace ICD.Common.Utils { foreach (Exception inner in e.LoaderExceptions) { - if (inner is FileNotFoundException) + if (inner is System.IO.FileNotFoundException) { Logger.AddEntry(eSeverity.Error, "{0} failed to cache assembly {1} - Could not find one or more dependencies by path", From 9b1ddf41d290483cc87f78d9f9514e9097640b45 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Thu, 3 May 2018 11:56:09 -0400 Subject: [PATCH 30/33] add bool and ushort extensions --- ICD.Common.Utils/Extensions/BoolExtensions.cs | 10 ++++++++++ ICD.Common.Utils/Extensions/UshortExtensions.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 ICD.Common.Utils/Extensions/BoolExtensions.cs create mode 100644 ICD.Common.Utils/Extensions/UshortExtensions.cs diff --git a/ICD.Common.Utils/Extensions/BoolExtensions.cs b/ICD.Common.Utils/Extensions/BoolExtensions.cs new file mode 100644 index 0000000..53c2ee7 --- /dev/null +++ b/ICD.Common.Utils/Extensions/BoolExtensions.cs @@ -0,0 +1,10 @@ +namespace ICD.Common.Utils.Extensions +{ + public static class BoolExtensions + { + public static ushort ToUshort(this bool b) + { + return b ? (ushort)1 : (ushort)0; + } + } +} \ No newline at end of file diff --git a/ICD.Common.Utils/Extensions/UshortExtensions.cs b/ICD.Common.Utils/Extensions/UshortExtensions.cs new file mode 100644 index 0000000..e31c463 --- /dev/null +++ b/ICD.Common.Utils/Extensions/UshortExtensions.cs @@ -0,0 +1,10 @@ +namespace ICD.Common.Utils.Extensions +{ + public static class UshortExtensions + { + public static bool ToBool(this ushort u) + { + return u != 0; + } + } +} \ No newline at end of file From d4f5413ce6a695539d88a4c465b5da139d6f6618 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Thu, 3 May 2018 12:10:16 -0400 Subject: [PATCH 31/33] proper casing --- ICD.Common.Utils/Extensions/BoolExtensions.cs | 2 +- ICD.Common.Utils/Extensions/UshortExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ICD.Common.Utils/Extensions/BoolExtensions.cs b/ICD.Common.Utils/Extensions/BoolExtensions.cs index 53c2ee7..b820422 100644 --- a/ICD.Common.Utils/Extensions/BoolExtensions.cs +++ b/ICD.Common.Utils/Extensions/BoolExtensions.cs @@ -2,7 +2,7 @@ { public static class BoolExtensions { - public static ushort ToUshort(this bool b) + public static ushort ToUShort(this bool b) { return b ? (ushort)1 : (ushort)0; } diff --git a/ICD.Common.Utils/Extensions/UshortExtensions.cs b/ICD.Common.Utils/Extensions/UshortExtensions.cs index e31c463..a4bae69 100644 --- a/ICD.Common.Utils/Extensions/UshortExtensions.cs +++ b/ICD.Common.Utils/Extensions/UshortExtensions.cs @@ -1,6 +1,6 @@ namespace ICD.Common.Utils.Extensions { - public static class UshortExtensions + public static class UShortExtensions { public static bool ToBool(this ushort u) { From 553a79bd171c54bac2051c8049e1f2b3be3023ae Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Thu, 3 May 2018 12:11:03 -0400 Subject: [PATCH 32/33] csproj changes --- ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index f69e8d2..231f2aa 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -88,9 +88,11 @@ + + From b41a236716f96cda15490bc772445b5d0e2c54fc Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Fri, 4 May 2018 15:08:13 -0400 Subject: [PATCH 33/33] refactor: Resolving warning --- ICD.Common.Utils/Collections/WeakKeyDictionary.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs index d8fc681..402718c 100644 --- a/ICD.Common.Utils/Collections/WeakKeyDictionary.cs +++ b/ICD.Common.Utils/Collections/WeakKeyDictionary.cs @@ -135,6 +135,7 @@ namespace ICD.Common.Utils.Collections } set { +// ReSharper disable once CompareNonConstrainedGenericWithNull if (key == null) throw new ArgumentNullException("key"); @@ -172,6 +173,7 @@ namespace ICD.Common.Utils.Collections public void Add(TKey key, TValue value) { +// ReSharper disable once CompareNonConstrainedGenericWithNull if (key == null) throw new ArgumentNullException("key");