From cbeedd45a34cf9857a7ea861513611b54b49c142 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 3 Apr 2018 11:58:08 -0400 Subject: [PATCH 01/18] Removing suffix from assembly name --- ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 52ac708..63af157 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -8,7 +8,7 @@ Library Properties ICD.Common.Utils - ICD.Common.Utils_SimplSharp + ICD.Common.Utils {0B4745B0-194B-4BB6-8E21-E9057CA92500};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} WindowsCE E2BECB1F-8C8C-41ba-B736-9BE7D946A398 From 03940d3075c173a79e275072ea54ecfeeeb00d83 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Tue, 3 Apr 2018 13:46:41 -0400 Subject: [PATCH 02/18] refactor: Setting assembly name --- .../ICD.Common.Utils.Tests_NetStandard.csproj | 1 + ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj b/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj index 50c6390..d92f88e 100644 --- a/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj +++ b/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj @@ -4,6 +4,7 @@ Library netcoreapp2.0 ICD.Common.Utils.Tests + ICD.Common.Utils.Tests false true diff --git a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj index 9477e03..15e36c8 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj @@ -5,8 +5,8 @@ netstandard2.0 ICD.Common.Utils $(AssemblyName) - false - true + false + true Chris Cameron, Jeff Thompson ICD.Common.Utils From 778f28222e7b7f0a5231b93da0d62368cb48a149 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 4 Apr 2018 16:46:09 -0400 Subject: [PATCH 03/18] feat: util method for looking up a property on a class via another property info --- ICD.Common.Utils/ReflectionUtils.cs | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index 76b182b..cdf400a 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -335,5 +335,38 @@ namespace ICD.Common.Utils : AssemblyLoadContext.Default.LoadFromAssemblyPath(path); #endif } + + /// + /// Finds the corresponding property info on the given type. + /// + /// + /// + /// + public static PropertyInfo GetImplementation(Type type, PropertyInfo property) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (property == null) + throw new ArgumentNullException("property"); + + if (type.IsInterface) + throw new InvalidOperationException("Type must not be an interface"); + + property = type +#if SIMPLSHARP + .GetCType() +#else + .GetTypeInfo() +#endif + .GetProperty(property.Name, property.PropertyType); + + if (property == null) + return null; + + return property.DeclaringType == type + ? property + : GetImplementation(property.DeclaringType, property); + } } } From e6d11bd3fae1b46247a7f08545e8a6b63d3a24d8 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 09:31:11 -0400 Subject: [PATCH 04/18] fix: Catching case where MapRange would generate a NaN value --- ICD.Common.Utils/MathUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ICD.Common.Utils/MathUtils.cs b/ICD.Common.Utils/MathUtils.cs index 26b9258..7d718fd 100644 --- a/ICD.Common.Utils/MathUtils.cs +++ b/ICD.Common.Utils/MathUtils.cs @@ -71,6 +71,9 @@ namespace ICD.Common.Utils /// The newly mapped value public static double MapRange(double inputStart, double inputEnd, double outputStart, double outputEnd, double value) { + if (inputStart.Equals(inputEnd)) + throw new DivideByZeroException(); + double slope = (outputEnd - outputStart) / (inputEnd - inputStart); return outputStart + slope * (value - inputStart); } From 3239307f66f7466b5adb1afd8bcf67da51e1f8e3 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 11:24:25 -0400 Subject: [PATCH 05/18] docs: adding missing comments --- ICD.Common.Utils/Extensions/TypeExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ICD.Common.Utils/Extensions/TypeExtensions.cs b/ICD.Common.Utils/Extensions/TypeExtensions.cs index ad0a70d..ae79c64 100644 --- a/ICD.Common.Utils/Extensions/TypeExtensions.cs +++ b/ICD.Common.Utils/Extensions/TypeExtensions.cs @@ -100,6 +100,11 @@ namespace ICD.Common.Utils.Extensions return s_DecimalNumericTypes.Contains(extends); } + /// + /// Gets the Assembly containing the type. + /// + /// + /// public static Assembly GetAssembly(this Type extends) { if (extends == null) @@ -114,6 +119,12 @@ namespace ICD.Common.Utils.Extensions .Assembly; } + /// + /// Returns true if the type is assignable to the given type. + /// + /// + /// + /// public static bool IsAssignableTo(this Type from, Type to) { if (from == null) From 007663a193ae7aaca00161dba6416c7f489aab95 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 11:26:11 -0400 Subject: [PATCH 06/18] feat: Extension methods for determining if a type represents an integer number, or if a type can represent a null value --- .../Extensions/TypeExtensionsTest.cs | 25 ++++++++++++ ICD.Common.Utils/Extensions/TypeExtensions.cs | 38 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs b/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs index 1f8fb59..f6450bb 100644 --- a/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs +++ b/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs @@ -10,6 +10,14 @@ namespace ICD.Common.Utils.Tests.Extensions [TestFixture] public sealed class TypeExtensionsTest { + [TestCase(typeof(byte), false)] + [TestCase(typeof(byte?), true)] + [TestCase(typeof(string), true)] + public void CanBeNullTest(Type value, bool expected) + { + Assert.AreEqual(expected, value.CanBeNull()); + } + [TestCase(typeof(byte), true)] [TestCase(typeof(decimal), true)] [TestCase(typeof(double), true)] @@ -61,6 +69,23 @@ namespace ICD.Common.Utils.Tests.Extensions Assert.AreEqual(expected, value.IsDecimalNumeric()); } + [TestCase(typeof(byte), true)] + [TestCase(typeof(decimal), false)] + [TestCase(typeof(double), false)] + [TestCase(typeof(float), false)] + [TestCase(typeof(int), true)] + [TestCase(typeof(long), true)] + [TestCase(typeof(sbyte), true)] + [TestCase(typeof(short), true)] + [TestCase(typeof(uint), true)] + [TestCase(typeof(ulong), true)] + [TestCase(typeof(ushort), true)] + [TestCase(typeof(string), false)] + public void IsIntegerNumericTest(Type value, bool expected) + { + Assert.AreEqual(expected, value.IsIntegerNumeric()); + } + [TestCase(typeof(string), typeof(object), true)] [TestCase(typeof(object), typeof(string), false)] public void IsAssignableToTest(Type a, Type b, bool expected) diff --git a/ICD.Common.Utils/Extensions/TypeExtensions.cs b/ICD.Common.Utils/Extensions/TypeExtensions.cs index ae79c64..34829aa 100644 --- a/ICD.Common.Utils/Extensions/TypeExtensions.cs +++ b/ICD.Common.Utils/Extensions/TypeExtensions.cs @@ -45,6 +45,18 @@ namespace ICD.Common.Utils.Extensions typeof(float), }; + private static readonly IcdHashSet s_IntegerNumericTypes = new IcdHashSet + { + typeof(byte), + typeof(int), + typeof(long), + typeof(sbyte), + typeof(short), + typeof(uint), + typeof(ulong), + typeof(ushort) + }; + private static readonly Dictionary s_TypeAllTypes; private static readonly Dictionary s_TypeBaseTypes; private static readonly Dictionary s_TypeImmediateInterfaces; @@ -61,6 +73,19 @@ namespace ICD.Common.Utils.Extensions s_TypeMinimalInterfaces = new Dictionary(); } + /// + /// Returns true if the given type can represent a null value. + /// + /// + /// + public static bool CanBeNull(this Type extends) + { + if (extends == null) + throw new ArgumentException("extends"); + + return !extends.IsValueType || Nullable.GetUnderlyingType(extends) != null; + } + /// /// Returns true if the given type is a numeric type. /// @@ -100,6 +125,19 @@ namespace ICD.Common.Utils.Extensions return s_DecimalNumericTypes.Contains(extends); } + /// + /// Returns true if the given type is an integer numeric type. + /// + /// + /// + public static bool IsIntegerNumeric(this Type extends) + { + if (extends == null) + throw new ArgumentException("extends"); + + return s_IntegerNumericTypes.Contains(extends); + } + /// /// Gets the Assembly containing the type. /// From 26107887f1c7ccff5a119f134931909d62d15757 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 11:27:27 -0400 Subject: [PATCH 07/18] feat: ChangeType util method for converting a value to a given type --- ICD.Common.Utils.Tests/ReflectionUtilsTest.cs | 26 ++++++++++++- ICD.Common.Utils/ReflectionUtils.cs | 37 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs index 81b34d9..21adc45 100644 --- a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs @@ -121,9 +121,33 @@ namespace ICD.Common.Utils.Tests } [Test] - public void LoadAssemblyFromPath() + public void LoadAssemblyFromPathTest() { Assert.Inconclusive(); } + + [Test] + public void GetImplementationTest() + { + Assert.Inconclusive(); + } + + [Test] + public void ChangeTypeTest() + { + // Same type + Assert.AreEqual(10, ReflectionUtils.ChangeType(10, typeof(int))); + + // Null + Assert.AreEqual(null, ReflectionUtils.ChangeType(null, typeof(string))); + + // Enums + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType((int)(object)BindingFlags.GetProperty, typeof(BindingFlags))); + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType(BindingFlags.GetProperty.ToString(), typeof(BindingFlags))); + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType(((int)(object)BindingFlags.GetProperty).ToString(), typeof(BindingFlags))); + + // Everything else + Assert.AreEqual(10, ReflectionUtils.ChangeType("10", typeof(int))); + } } } diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index cdf400a..2a4733d 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -368,5 +368,42 @@ namespace ICD.Common.Utils ? property : GetImplementation(property.DeclaringType, property); } + + /// + /// Changes the given value to the given type. + /// + /// + /// + /// + public static object ChangeType(object value, Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + // Handle null value + if (value == null) + { + if (type.CanBeNull()) + return null; + + throw new InvalidCastException(); + } + + Type valueType = value.GetType(); + if (valueType.IsAssignableTo(type)) + return 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); + } + + return Convert.ChangeType(value, type, null); + } } } From 9d034872cba209e5f90155195e0f3b670b83ded7 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 11:49:46 -0400 Subject: [PATCH 08/18] Tidying --- .../Extensions/EnumerableExtensions.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ICD.Common.Utils/Extensions/EnumerableExtensions.cs b/ICD.Common.Utils/Extensions/EnumerableExtensions.cs index 3d928ae..74e69ba 100644 --- a/ICD.Common.Utils/Extensions/EnumerableExtensions.cs +++ b/ICD.Common.Utils/Extensions/EnumerableExtensions.cs @@ -1062,12 +1062,14 @@ namespace ICD.Common.Utils.Extensions { public readonly T value; public readonly bool isParsed; + public TryParseStruct(T value, bool isParsed) { this.value = value; this.isParsed = isParsed; } } + // since Func<...,T> can't specify `out` parameters public delegate bool TryParseDelegate(string input, out T output); @@ -1079,7 +1081,8 @@ namespace ICD.Common.Utils.Extensions /// enumerable of strings to parse /// TryParse function for given type /// enumerable of successfully parsed values - public static IEnumerable TryParseSkipFailures(this IEnumerable extends, TryParseDelegate tryParseFunc) + public static IEnumerable TryParseSkipFailures(this IEnumerable extends, + TryParseDelegate tryParseFunc) { if (extends == null) throw new ArgumentNullException("extends"); @@ -1088,13 +1091,13 @@ namespace ICD.Common.Utils.Extensions throw new ArgumentNullException("tryParseFunc"); return extends.Select(str => - { - T value = default(T); - bool isParsed = tryParseFunc(str, out value); - return new TryParseStruct(value, isParsed); - }) - .Where(v => v.isParsed == true) - .Select(v => v.value); + { + T value; + bool isParsed = tryParseFunc(str, out value); + return new TryParseStruct(value, isParsed); + }) + .Where(v => v.isParsed) + .Select(v => v.value); } #if SIMPLSHARP From 6a7f5a74af211889c7123593fe94c73fd6333c39 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 13:42:08 -0400 Subject: [PATCH 09/18] refactor: reducing code duplication, CreateInstance takes parameters --- ICD.Common.Utils.Tests/ReflectionUtilsTest.cs | 24 +++---- ICD.Common.Utils/ReflectionUtils.cs | 72 +++++++------------ 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs index 21adc45..1ba8e18 100644 --- a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs @@ -34,23 +34,12 @@ namespace ICD.Common.Utils.Tests } } - [TestCase("test", 10)] - [TestCase(null, 0)] - public void InstantiateTest(string param1, int param2) - { - TestClass result = ReflectionUtils.Instantiate(typeof(TestClass), param1, param2) as TestClass; - - Assert.NotNull(result); - Assert.AreEqual(param1, result.Param1); - Assert.AreEqual(param2, result.Param2); - } - [Test] public void MatchesConstructorParametersTest() { Assert.Throws(() => ReflectionUtils.MatchesConstructorParameters(null, new object[] { "test", 10 })); - ConstructorInfo constructor = typeof(TestClass).GetConstructor(new Type[] {typeof(string), typeof(int)}); + ConstructorInfo constructor = typeof(TestClass).GetConstructor(new[] {typeof(string), typeof(int)}); Assert.IsTrue(ReflectionUtils.MatchesConstructorParameters(constructor, new object[] {"test", 10})); Assert.IsTrue(ReflectionUtils.MatchesConstructorParameters(constructor, new object[] {null, 10})); @@ -114,6 +103,17 @@ namespace ICD.Common.Utils.Tests Assert.NotNull(output); } + [TestCase("test", 10)] + [TestCase(null, 0)] + public void CreateInstanceTest(string param1, int param2) + { + TestClass result = ReflectionUtils.CreateInstance(typeof(TestClass), param1, param2) as TestClass; + + Assert.NotNull(result); + Assert.AreEqual(param1, result.Param1); + Assert.AreEqual(param2, result.Param2); + } + [Test] public void GetCustomAttributesTest() { diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index 2a4733d..e42b2c5 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -1,60 +1,22 @@ using System; using System.Collections.Generic; using System.Linq; -using ICD.Common.Properties; using ICD.Common.Utils.Extensions; using ICD.Common.Utils.IO; #if SIMPLSHARP using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.Reflection; -using Activator = Crestron.SimplSharp.Reflection.Activator; #else using System.IO; using System.Reflection; using Microsoft.Extensions.DependencyModel; using System.Runtime.Loader; -using Activator = System.Activator; #endif namespace ICD.Common.Utils { public static class ReflectionUtils { - /// - /// Instantiates the given type using the constructor matching the given values. - /// - /// - /// - /// - [PublicAPI] - public static object Instantiate(Type type, params object[] values) - { - if (type == null) - throw new ArgumentNullException("type"); - - ConstructorInfo constructor = -#if SIMPLSHARP - ((CType)type) -#else - type -#endif - .GetConstructors() - .FirstOrDefault(c => MatchesConstructorParameters(c, values)); - - try - { - if (constructor != null) - return constructor.Invoke(values); - } - catch (TypeLoadException e) - { - throw new TypeLoadException(e.GetBaseException().Message); - } - - string message = string.Format("Unable to find constructor for {0}", type.Name); - throw new InvalidOperationException(message); - } - /// /// Returns true if the parameters match the constructor parameters. /// @@ -206,7 +168,7 @@ namespace ICD.Common.Utils .GetTypeInfo() #endif .IsValueType - ? Activator.CreateInstance(type) + ? CreateInstance(type) : null; } @@ -235,29 +197,47 @@ namespace ICD.Common.Utils /// /// /// - public static T CreateInstance() - where T : new() + public static T CreateInstance(params object[] parameters) { - return (T)CreateInstance(typeof(T)); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + return (T)CreateInstance(typeof(T), parameters); } /// /// Creates an instance of the given type, calling the default constructor. /// /// - public static object CreateInstance(Type type) + public static object CreateInstance(Type type, params object[] parameters) { if (type == null) throw new ArgumentNullException("type"); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + ConstructorInfo constructor = +#if SIMPLSHARP + type.GetCType() +#else + type +#endif + .GetConstructors() + .FirstOrDefault(c => MatchesConstructorParameters(c, parameters)); + try { - return Activator.CreateInstance(type); + if (constructor != null) + return constructor.Invoke(parameters); } - catch (TargetInvocationException e) + catch (TypeLoadException e) { - throw e.GetBaseException(); + throw new TypeLoadException(e.GetBaseException().Message); } + + string message = string.Format("Unable to find constructor for {0}", type.Name); + throw new InvalidOperationException(message); } /// From 6be6971cf780bd04fb2631098a8cb08fbdd1fa57 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 13:59:07 -0400 Subject: [PATCH 10/18] fix: Reverting dumb change that prevented default values from being instantiated --- ICD.Common.Utils/ReflectionUtils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index e42b2c5..db28c5d 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -6,11 +6,13 @@ using ICD.Common.Utils.IO; #if SIMPLSHARP using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.Reflection; +using Activator = Crestron.SimplSharp.Reflection.Activator; #else using System.IO; using System.Reflection; using Microsoft.Extensions.DependencyModel; using System.Runtime.Loader; +using Activator = System.Activator; #endif namespace ICD.Common.Utils @@ -168,7 +170,7 @@ namespace ICD.Common.Utils .GetTypeInfo() #endif .IsValueType - ? CreateInstance(type) + ? Activator.CreateInstance(type) : null; } From 861acc34c0b971e2e93720a416d4dcf35cb8e22f Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 14:18:14 -0400 Subject: [PATCH 11/18] Updating test framework version --- .../ICD.Common.Utils.Tests_NetStandard.csproj | 2 +- ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj b/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj index d92f88e..4410842 100644 --- a/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj +++ b/ICD.Common.Utils.Tests/ICD.Common.Utils.Tests_NetStandard.csproj @@ -19,7 +19,7 @@ - + diff --git a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj index 15e36c8..8837802 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_NetStandard.csproj @@ -44,7 +44,7 @@ - + From 14d83f102fcaa13ae6eccc50963073aae011045f Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 15:09:33 -0400 Subject: [PATCH 12/18] feature: JsonUtils.Print shim for serializing and printing a given object --- ICD.Common.Utils/Json/JsonUtils.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils/Json/JsonUtils.cs b/ICD.Common.Utils/Json/JsonUtils.cs index dcb456d..92dda6d 100644 --- a/ICD.Common.Utils/Json/JsonUtils.cs +++ b/ICD.Common.Utils/Json/JsonUtils.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.Linq; using System.Text; using ICD.Common.Properties; @@ -94,6 +93,17 @@ namespace ICD.Common.Utils.Json IcdConsole.PrintLine(Format(json)); } + /// + /// Serializes the given item and pretty-prints to JSON. + /// + /// + [PublicAPI] + public static void Print(object value) + { + string serial = JsonConvert.SerializeObject(value); + Print(serial); + } + /// /// Formats the JSON into a human-readable form. /// From a2bf1ace182896cf8207ab42e9dd5e5aad65e59d Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 15:13:58 -0400 Subject: [PATCH 13/18] fix: adding missing using directive --- ICD.Common.Utils/Json/JsonUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICD.Common.Utils/Json/JsonUtils.cs b/ICD.Common.Utils/Json/JsonUtils.cs index 92dda6d..d5eea96 100644 --- a/ICD.Common.Utils/Json/JsonUtils.cs +++ b/ICD.Common.Utils/Json/JsonUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Text; using ICD.Common.Properties; From 907697813d8b9608600e7d3919f67f7fd911efec Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 15:39:39 -0400 Subject: [PATCH 14/18] Tidying --- ICD.Common.Utils/ReflectionUtils.cs | 94 ++++++++++++++--------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index db28c5d..94a9542 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -34,12 +34,13 @@ namespace ICD.Common.Utils throw new ArgumentNullException("parameters"); #if SIMPLSHARP - IEnumerable methodTypes + IEnumerable #else - IEnumerable methodTypes + IEnumerable #endif - = constructor.GetParameters().Select(p => p.ParameterType); - return ParametersMatchTypes(methodTypes, parameters); + parameterTypes = constructor.GetParameters().Select(p => p.ParameterType); + + return ParametersMatchTypes(parameterTypes, parameters); } /// @@ -57,12 +58,13 @@ namespace ICD.Common.Utils throw new ArgumentNullException("parameters"); #if SIMPLSHARP - IEnumerable methodTypes + IEnumerable #else - IEnumerable methodTypes + IEnumerable #endif - = method.GetParameters().Select(p => p.ParameterType); - return ParametersMatchTypes(methodTypes, parameters); + parameterTypes = method.GetParameters().Select(p => p.ParameterType); + + return ParametersMatchTypes(parameterTypes, parameters); } /// @@ -77,11 +79,12 @@ namespace ICD.Common.Utils throw new ArgumentNullException("property"); #if SIMPLSHARP - CType propertyType + CType #else - Type propertyType + Type #endif - = property.PropertyType; + propertyType = property.PropertyType; + return ParametersMatchTypes(new[] {propertyType}, new[] {parameter}); } @@ -91,31 +94,41 @@ namespace ICD.Common.Utils /// /// /// + private static bool ParametersMatchTypes( #if SIMPLSHARP - private static bool ParametersMatchTypes(IEnumerable types, IEnumerable parameters) - { - if (types == null) - throw new ArgumentNullException("types"); - - CType[] typesArray = types as CType[] ?? types.ToArray(); -#else - private static bool ParametersMatchTypes(IEnumerable types, IEnumerable parameters) - { - if (types == null) - throw new ArgumentNullException("types"); - - Type[] typesArray = types as Type[] ?? types.ToArray(); + IEnumerable +#else + IEnumerable #endif + types, IEnumerable parameters) + { + if (types == null) + throw new ArgumentNullException("types"); + if (parameters == null) throw new ArgumentNullException("parameters"); +#if SIMPLSHARP + CType[] +#else + Type[] +#endif + typesArray = types as +#if SIMPLSHARP + CType[] +#else + Type[] +#endif + ?? types.ToArray(); + object[] parametersArray = parameters as object[] ?? parameters.ToArray(); if (parametersArray.Length != typesArray.Length) return false; // Compares each pair of items in the two arrays. - return !parametersArray.Where((t, index) => !ParameterMatchesType(typesArray[index], t)).Any(); + return !parametersArray.Where((t, index) => !ParameterMatchesType(typesArray[index], t)) + .Any(); } /// @@ -124,50 +137,38 @@ namespace ICD.Common.Utils /// /// /// -#if SIMPLSHARP - private static bool ParameterMatchesType(CType type, object parameter) + private static bool ParameterMatchesType(Type type, object parameter) { if (type == null) throw new ArgumentNullException("type"); +#if SIMPLSHARP // Can the parameter be assigned a null value? if (parameter == null) return (type.IsClass || !type.IsValueType || Nullable.GetUnderlyingType(type) != null); - return type.IsInstanceOfType(parameter); - } #else - private static bool ParameterMatchesType(Type type, object parameter) - { - if (type == null) - throw new ArgumentNullException("type"); - - TypeInfo info = type.GetTypeInfo(); + TypeInfo info = type.GetTypeInfo(); // Can the parameter be assigned a null value? if (parameter == null) return (info.IsClass || !info.IsValueType || Nullable.GetUnderlyingType(type) != null); - return info.IsInstanceOfType(parameter); - } #endif + } /// /// Same as doing default(Type). /// /// /// -#if SIMPLSHARP - public static object GetDefaultValue(CType type) -#else public static object GetDefaultValue(Type type) -#endif { if (type == null) throw new ArgumentNullException("type"); return type #if !SIMPLSHARP - .GetTypeInfo() + .GetTypeInfo() #endif .IsValueType ? Activator.CreateInstance(type) @@ -220,13 +221,12 @@ namespace ICD.Common.Utils throw new ArgumentNullException("parameters"); ConstructorInfo constructor = -#if SIMPLSHARP - type.GetCType() -#else type +#if SIMPLSHARP + .GetCType() #endif - .GetConstructors() - .FirstOrDefault(c => MatchesConstructorParameters(c, parameters)); + .GetConstructors() + .FirstOrDefault(c => MatchesConstructorParameters(c, parameters)); try { From 27f5fd0fe7db18644448a1899212f4839699f8b0 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 5 Apr 2018 17:03:24 -0400 Subject: [PATCH 15/18] feat: Additional validation --- .../Json/AbstractGenericJsonConverter.cs | 12 +++++++++ ICD.Common.Utils/Json/JsonUtils.cs | 27 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs index 31a2a6e..151a7ca 100644 --- a/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs +++ b/ICD.Common.Utils/Json/AbstractGenericJsonConverter.cs @@ -14,6 +14,12 @@ namespace ICD.Common.Utils.Json /// The calling serializer. public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { + if (writer == null) + throw new ArgumentNullException("writer"); + + if (serializer == null) + throw new ArgumentNullException("serializer"); + if (value == null) { writer.WriteNull(); @@ -45,6 +51,12 @@ namespace ICD.Common.Utils.Json public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { + if (reader == null) + throw new ArgumentNullException("reader"); + + if (serializer == null) + throw new ArgumentNullException("serializer"); + return ReadJson(reader, (T)existingValue, serializer); } diff --git a/ICD.Common.Utils/Json/JsonUtils.cs b/ICD.Common.Utils/Json/JsonUtils.cs index d5eea96..8bb7a9c 100644 --- a/ICD.Common.Utils/Json/JsonUtils.cs +++ b/ICD.Common.Utils/Json/JsonUtils.cs @@ -37,6 +37,9 @@ namespace ICD.Common.Utils.Json /// public static void CacheType(Type type) { + if (type == null) + throw new ArgumentNullException("type"); + string serialized = JsonConvert.SerializeObject(ReflectionUtils.CreateInstance(type)); JsonConvert.DeserializeObject(serialized, type); } @@ -91,7 +94,11 @@ namespace ICD.Common.Utils.Json [PublicAPI] public static void Print(string json) { - IcdConsole.PrintLine(Format(json)); + if (json == null) + throw new ArgumentNullException("json"); + + string formatted = Format(json); + IcdConsole.PrintLine(formatted); } /// @@ -113,6 +120,9 @@ namespace ICD.Common.Utils.Json [PublicAPI] public static string Format(string json) { + if (json == null) + throw new ArgumentNullException("json"); + int indent = 0; bool quoted = false; StringBuilder sb = new StringBuilder(); @@ -298,6 +308,12 @@ namespace ICD.Common.Utils.Json /// public static object Deserialize(Type type, JToken token) { + if (type == null) + throw new ArgumentNullException("type"); + + if (token == null) + throw new ArgumentNullException("token"); + return Deserialize(type, token, new JsonSerializer()); } @@ -310,6 +326,15 @@ namespace ICD.Common.Utils.Json /// public static object Deserialize(Type type, JToken token, JsonSerializer serializer) { + if (type == null) + throw new ArgumentNullException("type"); + + if (token == null) + throw new ArgumentNullException("token"); + + if (serializer == null) + throw new ArgumentNullException("serializer"); + using (JTokenReader jsonReader = new JTokenReader(token)) return serializer.Deserialize(jsonReader, type); } From 7ab06fdb94119ece4e40a8467404d789f5a3d102 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 9 Apr 2018 15:24:22 -0400 Subject: [PATCH 16/18] 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 17/18] 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 18/18] 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); } ///