Compare commits

..

46 Commits

Author SHA1 Message Date
Chris Cameron
c607d1254e chore: Updating changelog, incrementing minor version 2019-03-01 14:48:57 -05:00
Chris Cameron
e90914664f Merge remote-tracking branch 'origin/ConnectPro_v1.3' into ConnectPro_v1.2 2019-03-01 14:32:25 -05:00
Chris Cameron
fc855d407b Merge remote-tracking branch 'origin/fix/IcdConsole' into ConnectPro_v1.2 2019-03-01 14:32:13 -05:00
Chris Cameron
e3a4713b3b fix: Fixed bug preventing deserialization of XML lists 2019-03-01 14:29:46 -05:00
Drew Tingen
f85896e947 fix: PrintLine raises OnConoslePrint, Crestron Console Response uses PrintLine, Better environment checks 2019-02-24 16:19:03 -05:00
Chris Cameron
131e2f87f4 feat: Adding constructor to BiDictionary to build from an existing dict 2019-02-21 14:04:06 -05:00
Chris Cameron
4a330637a7 feat: Type IsAssignableTo extension shim 2019-02-19 14:32:32 -05:00
Chris Cameron
fa124a0afc refactor: Resolving warning 2019-02-11 12:13:56 -05:00
Chris Cameron
72fd823643 test: Fixing unit tests 2019-02-07 17:13:25 -05:00
Chris Cameron
644388edad chore: Updating changelog, incrementing minor version 2019-02-07 16:55:22 -05:00
Chris Cameron
cbb05c8b79 refactor: Removing unused code 2019-02-07 16:53:33 -05:00
Chris Cameron
066c9f93a7 chore: Updating AssemblyInfo 2019-02-07 15:32:49 -05:00
Chris Cameron
9aa7459b0f chore: Updating changelog 2019-02-07 14:42:22 -05:00
Chris Cameron
f8a813c97b perf: Removing redundant code, logging micro-optimization 2019-02-07 10:37:35 -05:00
Chris Cameron
be6a6de65a perf: ReprBuilder micro-optimizations 2019-02-06 17:09:37 -05:00
Chris Cameron
ffe3e67241 perf: Better JSON serialization of nullable types 2019-02-06 13:10:52 -05:00
Chris Cameron
141d911eb0 perf: Reduced size of JSON serialized nullable types 2019-02-06 11:31:14 -05:00
Chris Cameron
d6abf3fdf6 perf: Don't serialize namespace for builtin types 2019-02-04 13:22:59 -05:00
Chris Cameron
46a1ea09b6 refactor: Moving JSON type name util into TypeExtensions 2019-02-04 12:03:15 -05:00
Chris Cameron
2dc705d335 perf: Significantly reducing the size of JSON serialized types 2019-02-04 11:38:39 -05:00
Chris Cameron
0700a357dd feat: Adding ToStringJsonConverter 2019-02-01 14:52:33 -05:00
Chris Cameron
5fb7636ed4 feat: Extension method for reading JSON token as a Guid 2019-01-30 10:52:44 -05:00
Chris Cameron
196c2a535a feat: Added shims for ReflectionUtils.SubscribeEvent for known callbacks 2019-01-29 21:59:10 -05:00
Chris Cameron
db50ada952 refactor: Tidying 2019-01-29 17:49:23 -05:00
Chris Cameron
087b04fd62 chore: Updating changelog, incrementing major version 2019-01-29 14:52:36 -05:00
Chris Cameron
ea73351d39 refactor: Removing redundant critical section, support for chaining TableBuilder methods 2019-01-28 16:58:21 -05:00
Chris Cameron
ca1fae29b0 chore: Updating nuget packages 2019-01-28 14:19:38 -05:00
Chris Cameron
1916c2b750 refactor: Reducing duplicate code 2019-01-28 14:02:16 -05:00
Chris Cameron
3af2544e70 feat: ReprBuilder better supports method chaining 2019-01-28 13:32:35 -05:00
Chris Cameron
20e8aa93f2 Merge branch 'feat/ConsoleOnPrint' of Common/Utils into dev 2019-01-28 13:04:40 +00:00
Drew Tingen
3937902f38 Merge remote-tracking branch 'origin/dev' into feat/ConsoleOnPrint
# Conflicts:
#	CHANGELOG.md
2019-01-27 22:59:25 -08:00
Drew Tingen
021d5781a3 Merge branch 'feat/json' of Common/Utils into dev 2019-01-28 06:56:15 +00:00
Drew Tingen
b429e4bc53 feat: OnConsolePrint event and better VC-4 console support 2019-01-27 22:55:24 -08:00
Chris Cameron
4a203e2449 refactor: Reducing duplicate code 2019-01-27 17:55:47 -05:00
Chris Cameron
d41203b856 docs: Updating changelog 2019-01-25 17:11:08 -05:00
Chris Cameron
2f21379e52 Merge remote-tracking branch 'origin/dev' into feat/json 2019-01-25 17:10:03 -05:00
Chris Cameron
bd2ea606ae docs: Updating changelog 2019-01-25 16:52:09 -05:00
Chris Cameron
1f20c07e94 refactor: Splitting JSON extensions into Reader and Writer extensions, removing unused code 2019-01-25 14:06:51 -05:00
Chris Cameron
1be588b86a refactor: Simplifying JSON converters 2019-01-25 13:57:41 -05:00
Chris Cameron
488df297fc refactor: Removing unused code 2019-01-25 13:57:19 -05:00
Chris Cameron
80a4d94358 refactor: Removing unused code 2019-01-24 17:28:59 -05:00
Chris Cameron
6a40f3ce18 refactor: Tidying 2019-01-24 16:46:07 -05:00
Chris Cameron
d564a0a423 feat: Adding shorthand method for serializing an object into a JSON message 2019-01-24 16:21:29 -05:00
Chris Cameron
a9a544c433 feat: Simplifying generic JSON converters 2019-01-24 15:58:46 -05:00
Chris Cameron
2e0837f5c8 feat: Extension methods for reading current JSON token to the given type 2019-01-24 15:58:21 -05:00
Chris Cameron
77be0ec477 refactor: Removing unused code 2019-01-24 15:57:53 -05:00
27 changed files with 725 additions and 715 deletions

View File

@@ -6,6 +6,34 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [9.2.0] - 2019-03-01
### Added
- Added Type IsAssignableTo extension shim
- Added constructor to BiDictionary to instantiate from an existing dict
### Changed
- Fixed bug preventing deserialization of XML lists
- Crestron ConsoleResponse uses PrintLine instead of Print
- Use PrintLine instead of ConsoleResponse on Crestron server
## [9.1.0] - 2019-02-07
### Added
- Added SubscribeEvent shim for delegate callbacks
- Extension method for reading JSON token as a GUID
- Added ToStringJsonConverter
### Changed
- Significantly reduced size of JSON serialized Types
- Small logging optimizations
## [9.0.0] - 2019-01-29
### Added
- IcdConsole.OnConsolePrint event
### Changed
- Better VC-4 support for IcdConsole
- JSON refactoring for simpler deserialization
## [8.3.0] - 2019-01-25
### Added
- Added SimplSharpProMono to eRuntimeEnvironment enum

View File

@@ -1,90 +0,0 @@
using System.Collections.Generic;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.EventArguments;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class AsyncEventQueueTest
{
[Test]
public void ItemDequeuedFeedbackTest()
{
Assert.Inconclusive();
}
[Test]
public void CountTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
queue.OnItemDequeued +=
(sender, args) =>
{
ThreadingUtils.Sleep(200);
};
Assert.AreEqual(0, queue.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
Assert.AreEqual(5, queue.Count);
}
}
[Test]
public void EnqueueTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
List<GenericEventArgs<int>> eventArgs = new List<GenericEventArgs<int>>();
queue.OnItemDequeued +=
(sender, args) =>
{
eventArgs.Add(args);
};
Assert.AreEqual(0, eventArgs.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
ThreadingUtils.Sleep(500);
Assert.AreEqual(5, eventArgs.Count);
Assert.AreEqual(0, eventArgs[0].Data);
Assert.AreEqual(1, eventArgs[1].Data);
Assert.AreEqual(2, eventArgs[2].Data);
Assert.AreEqual(3, eventArgs[3].Data);
Assert.AreEqual(4, eventArgs[4].Data);
}
}
[Test]
public void ClearTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
queue.OnItemDequeued +=
(sender, args) =>
{
ThreadingUtils.Sleep(200);
};
Assert.AreEqual(0, queue.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
Assert.AreEqual(5, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Text;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
using Newtonsoft.Json;
using NUnit.Framework;
@@ -17,6 +18,36 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.Inconclusive();
}
[Test]
public void ReadObjectTest()
{
const string json =
"{\"name\":\"Test\",\"help\":\"Test test.\",\"type\":\"System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\",\"value\":\"Test\"}";
Dictionary<string, string> expected = new Dictionary<string, string>
{
{"name", "Test"},
{"help", "Test test."},
{"type", "System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"},
{"value", "Test"}
};
Dictionary<string, string> deserialized = new Dictionary<string, string>();
using (IcdStringReader textReader = new IcdStringReader(json))
{
using (JsonReader reader = new JsonTextReader(textReader.WrappedTextReader))
{
JsonSerializer serializer = new JsonSerializer();
reader.Read();
reader.ReadObject(serializer, (p, r, s) => deserialized.Add(p, (string)r.Value));
}
}
Assert.IsTrue(deserialized.DictionaryEqual(expected));
}
[Test]
public void GetValueAsIntTest()
{
@@ -78,53 +109,5 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.IsTrue(deserialized.SequenceEqual(new[] {1, 2, 3, 4}));
}
[Test]
public void SerializeDictionaryTest()
{
Dictionary<int, string> dict = new Dictionary<int, string>
{
{1, "Item 1"},
{10, "Item 2"},
{15, "Item 3"}
};
JsonSerializer serializer = new JsonSerializer();
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
{
using (JsonWriter writer = new JsonTextWriter(stringWriter))
{
serializer.SerializeDictionary(writer, dict);
}
}
string json = stringBuilder.ToString();
Assert.AreEqual("{\"1\":\"Item 1\",\"10\":\"Item 2\",\"15\":\"Item 3\"}", json);
}
[Test]
public void DeserializeDictionaryTest()
{
const string json = "{\"1\":\"Item 1\",\"10\":\"Item 2\",\"15\":\"Item 3\"}";
JsonSerializer serializer = new JsonSerializer();
Dictionary<int, string> deserialized;
using (StringReader stringReader = new StringReader(json))
{
using (JsonReader reader = new JsonTextReader(stringReader))
{
reader.Read();
deserialized = serializer.DeserializeDictionary<int, string>(reader).ToDictionary();
}
}
Assert.AreEqual(3, deserialized.Count);
Assert.AreEqual("Item 1", deserialized[1]);
Assert.AreEqual("Item 2", deserialized[10]);
Assert.AreEqual("Item 3", deserialized[15]);
}
}
}

View File

@@ -155,6 +155,22 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(expected, type.GetNameWithoutGenericArity());
}
[TestCase(typeof(int), "System.Int32")]
[TestCase(typeof(int?), "System.Nullable`1[[System.Int32]]")]
[TestCase(typeof(KeyValuePair<int, string>), "System.Collections.Generic.KeyValuePair`2[[System.Int32],[System.String]]")]
[TestCase(typeof(List<>), "System.Collections.Generic.List`1")]
public void GetMinimalNameTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetMinimalName());
}
[TestCase(typeof(int), "System.Int32, System.Private.CoreLib")]
[TestCase(typeof(int?), "System.Nullable`1[[System.Int32, System.Private.CoreLib]], System.Private.CoreLib")]
public void GetNameWithoutAssemblyDetailsTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetNameWithoutAssemblyDetails());
}
[TestCase(typeof(string), "string")]
[TestCase(typeof(int?), "int?")]
[TestCase(typeof(List<int?>), "List<int?>")]

View File

@@ -22,7 +22,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,6 @@
using ICD.Common.Utils.IO;
using ICD.Common.Utils.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Json
@@ -141,32 +140,6 @@ namespace ICD.Common.Utils.Tests.Json
Assert.AreEqual("test message", messageName);
}
[Test]
public void DeserializeTypeTest()
{
const string json = "{\"test\":10}";
JObject root = JObject.Parse(json);
JToken token = root["test"];
int value = (int)JsonUtils.Deserialize(typeof(int), token);
Assert.AreEqual(10, value);
}
[Test]
public void DeserializeTestSerializerTest()
{
const string json = "{\"test\":10}";
JObject root = JObject.Parse(json);
JToken token = root["test"];
int value = (int)JsonUtils.Deserialize(typeof(int), token, new JsonSerializer());
Assert.AreEqual(10, value);
}
public sealed class TestSerializable
{
private const string PROPERTY_NAME = "Test";

View File

@@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -149,5 +151,75 @@ namespace ICD.Common.Utils.Tests
// Everything else
Assert.AreEqual(10, ReflectionUtils.ChangeType("10", typeof(int)));
}
#region Subscription Tests
[Test]
public void SubscribeEventTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
Delegate del = ReflectionUtils.SubscribeEvent<IntEventArgs>(this, eventInfo, IncrementCount);
Assert.NotNull(del);
m_Count = 0;
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(10, m_Count);
OnIncrementCount = null;
}
[Test]
public void SubscribeEventMethodInfoTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
MethodInfo methodInfo =
GetType().GetMethod("IncrementCount", BindingFlags.Instance | BindingFlags.NonPublic);
Delegate del = ReflectionUtils.SubscribeEvent(this, eventInfo, this, methodInfo);
Assert.NotNull(del);
m_Count = 0;
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(10, m_Count);
OnIncrementCount = null;
}
[Test]
public void UnsubscribeEventTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
MethodInfo methodInfo =
GetType().GetMethod("IncrementCount", BindingFlags.Instance | BindingFlags.NonPublic);
Delegate del = ReflectionUtils.SubscribeEvent(this, eventInfo, this, methodInfo);
Assert.NotNull(del);
m_Count = 0;
ReflectionUtils.UnsubscribeEvent(this, eventInfo, del);
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(0, m_Count);
}
// Event has to be public
public event EventHandler<IntEventArgs> OnIncrementCount;
private int m_Count;
private void IncrementCount(object sender, IntEventArgs args)
{
m_Count += args.Data;
}
#endregion
}
}

View File

@@ -1,135 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// Provides a buffer for storing items to be raised in order in a new thread.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class AsyncEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
{
/// <summary>
/// Raised to handle to the next item in the queue.
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
private readonly Queue<T> m_Queue;
private readonly SafeCriticalSection m_QueueSection;
private readonly SafeCriticalSection m_ProcessSection;
#region Properties
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
bool ICollection.IsSynchronized { get { return true; } }
object ICollection.SyncRoot { get { return this; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public AsyncEventQueue()
{
m_Queue = new Queue<T>();
m_QueueSection = new SafeCriticalSection();
m_ProcessSection = new SafeCriticalSection();
}
#region Methods
/// <summary>
/// Release resources.
/// </summary>
public void Dispose()
{
OnItemDequeued = null;
Clear();
}
/// <summary>
/// Enqueues the given item and begins processing the queue.
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
m_QueueSection.Execute(() => m_Queue.Enqueue(item));
ThreadingUtils.SafeInvoke(ProcessQueue);
}
/// <summary>
/// Clears the queued items.
/// </summary>
public void Clear()
{
m_QueueSection.Execute(() => m_Queue.Clear());
}
#endregion
#region Private Methods
/// <summary>
/// Dequeues and raises each item in sequence.
/// </summary>
private void ProcessQueue()
{
if (!m_ProcessSection.TryEnter())
return;
try
{
T item;
while (m_Queue.Dequeue(out item))
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
}
finally
{
m_ProcessSection.Leave();
}
}
#endregion
#region IEnumerable/ICollection
public IEnumerator<T> GetEnumerator()
{
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
m_QueueSection.Enter();
try
{
foreach (T item in this)
{
array.SetValue(item, index);
index++;
}
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
}
}

View File

@@ -30,9 +30,24 @@ namespace ICD.Common.Utils.Collections
/// Constructor.
/// </summary>
public BiDictionary()
: this(null)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
public BiDictionary(Dictionary<TKey, TValue> dict)
{
m_KeyToValue = new Dictionary<TKey, TValue>();
m_ValueToKey = new Dictionary<TValue, TKey>();
if (dict == null)
return;
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
#region Methods

View File

@@ -56,7 +56,16 @@ namespace ICD.Common.Utils.Collections
{
OnItemDequeued = null;
m_DequeueTimer.Dispose();
m_QueueSection.Enter();
try
{
m_DequeueTimer.Dispose();
}
finally
{
m_QueueSection.Leave();
}
}
/// <summary>

View File

@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
@@ -16,8 +15,6 @@ namespace ICD.Common.Utils.Collections
private readonly LinkedList<TContents> m_Collection;
private int m_MaxSize;
private readonly SafeCriticalSection m_CollectionLock;
#region Properties
/// <summary>
@@ -41,13 +38,13 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count { get { return m_CollectionLock.Execute(() => m_Collection.Count); } }
public int Count { get { return m_Collection.Count; } }
/// <summary>
/// The IsSynchronized Boolean property returns True if the
/// collection is designed to be thread safe; otherwise, it returns False.
/// </summary>
public bool IsSynchronized { get { return true; } }
public bool IsSynchronized { get { return false; } }
/// <summary>
/// The SyncRoot property returns an object, which is used for synchronizing
@@ -64,7 +61,6 @@ namespace ICD.Common.Utils.Collections
/// <param name="maxSize"></param>
public ScrollQueue(int maxSize)
{
m_CollectionLock = new SafeCriticalSection();
m_Collection = new LinkedList<TContents>();
MaxSize = maxSize;
}
@@ -76,7 +72,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
public void Clear()
{
m_CollectionLock.Execute(() => m_Collection.Clear());
m_Collection.Clear();
}
/// <summary>
@@ -86,17 +82,8 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public void Enqueue(TContents item)
{
m_CollectionLock.Enter();
try
{
m_Collection.AddLast(item);
Trim();
}
finally
{
m_CollectionLock.Leave();
}
m_Collection.AddLast(item);
Trim();
}
/// <summary>
@@ -106,18 +93,9 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public TContents Dequeue()
{
m_CollectionLock.Enter();
try
{
TContents output = m_Collection.First.Value;
m_Collection.RemoveFirst();
return output;
}
finally
{
m_CollectionLock.Leave();
}
TContents output = Peek();
m_Collection.RemoveFirst();
return output;
}
/// <summary>
@@ -127,7 +105,7 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public TContents Peek()
{
return m_CollectionLock.Execute(() => m_Collection.First.Value);
return m_Collection.First.Value;
}
#endregion
@@ -141,24 +119,15 @@ namespace ICD.Common.Utils.Collections
public IEnumerator<TContents> GetEnumerator()
{
return m_CollectionLock.Execute(() => m_Collection.ToList(Count).GetEnumerator());
return m_Collection.GetEnumerator();
}
void ICollection.CopyTo(Array myArr, int index)
{
m_CollectionLock.Enter();
try
foreach (TContents item in m_Collection)
{
foreach (TContents item in m_Collection)
{
myArr.SetValue(item, index);
index++;
}
}
finally
{
m_CollectionLock.Leave();
myArr.SetValue(item, index);
index++;
}
}
@@ -171,17 +140,8 @@ namespace ICD.Common.Utils.Collections
/// </summary>
private void Trim()
{
m_CollectionLock.Enter();
try
{
while (Count > MaxSize)
m_Collection.RemoveFirst();
}
finally
{
m_CollectionLock.Leave();
}
while (Count > MaxSize)
m_Collection.RemoveFirst();
}
#endregion

View File

@@ -3,25 +3,37 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ICD.Common.Utils.Extensions
{
/// <summary>
/// Extension methods for working with JSON.
/// </summary>
public static class JsonExtensions
public static class JsonReaderExtensions
{
/// <summary>
/// Writes the object value.
/// Reads the current token in the reader and deserializes to the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
JsonSerializer serializer = new JsonSerializer();
return extends.ReadAsObject<T>(serializer);
}
/// <summary>
/// Reads the current token in the reader and deserializes to the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
/// <param name="converter"></param>
[PublicAPI]
public static void WriteObject(this JsonWriter extends, object value, JsonSerializer serializer,
JsonConverter converter)
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends, JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -29,36 +41,48 @@ namespace ICD.Common.Utils.Extensions
if (serializer == null)
throw new ArgumentNullException("serializer");
if (converter == null)
throw new ArgumentNullException("converter");
JObject jObject = JObject.FromObject(value, serializer);
jObject.WriteTo(extends, converter);
return serializer.Deserialize<T>(extends);
}
/// <summary>
/// Writes the type value.
/// Reads through the current object token and calls the callback for each property value.
/// </summary>
/// <param name="extends"></param>
/// <param name="type"></param>
[PublicAPI]
public static void WriteType(this JsonWriter extends, Type type)
/// <param name="serializer"></param>
/// <param name="readPropertyValue"></param>
public static void ReadObject(this JsonReader extends, JsonSerializer serializer,
Action<string, JsonReader, JsonSerializer> readPropertyValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (type == null)
{
extends.WriteNull();
if (serializer == null)
throw new ArgumentNullException("serializer");
if (readPropertyValue == null)
throw new ArgumentNullException("readPropertyValue");
if (extends.TokenType == JsonToken.Null)
return;
if (extends.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, extends.TokenType));
while (extends.Read())
{
if (extends.TokenType == JsonToken.EndObject)
break;
// Get the property
if (extends.TokenType != JsonToken.PropertyName)
continue;
string property = (string)extends.Value;
// Read into the value
extends.Read();
readPropertyValue(property, extends, serializer);
}
// Find the smallest possible name representation for the type that will still resolve
string name = Type.GetType(type.FullName) == null
? type.AssemblyQualifiedName
: type.FullName;
extends.WriteValue(name);
}
/// <summary>
@@ -167,55 +191,18 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// Gets the current value as a guid.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items)
/// <returns></returns>
[PublicAPI]
public static Guid GetValueAsGuid(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="write"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items,
Action<JsonSerializer, JsonWriter, TItem> write)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
if (write == null)
throw new ArgumentNullException("write");
writer.WriteStartArray();
{
foreach (TItem item in items)
write(extends, writer, item);
}
writer.WriteEndArray();
string stringValue = extends.GetValueAsString();
return new Guid(stringValue);
}
/// <summary>
@@ -285,90 +272,5 @@ namespace ICD.Common.Utils.Extensions
reader.Read();
}
}
/// <summary>
/// Serializes the given sequence of key-value-pairs to the writer.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeDictionary<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
writer.WriteStartObject();
{
foreach (KeyValuePair<TKey, TValue> kvp in items)
{
writer.WritePropertyName(kvp.Key.ToString());
extends.Serialize(writer, kvp.Value);
}
}
writer.WriteEndObject();
}
/// <summary>
/// Deserializes a dictionary of items from the reader's current value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends,
JsonReader reader)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
if (reader.TokenType == JsonToken.Null)
return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject, reader.TokenType));
return DeserializeDictionaryIterator<TKey, TValue>(extends, reader);
}
/// <summary>
/// Deserializes a dictionary of items from the reader's current value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="serializer"></param>
/// <param name="reader"></param>
private static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionaryIterator<TKey, TValue>(
JsonSerializer serializer, JsonReader reader)
{
// Step into the first key
reader.Read();
while (reader.TokenType != JsonToken.EndObject)
{
TKey key = (TKey)Convert.ChangeType(reader.Value, typeof(TKey), null);
// Step into the value
reader.Read();
TValue value = serializer.Deserialize<TValue>(reader);
yield return new KeyValuePair<TKey, TValue>(key, value);
// Read out of the last value
reader.Read();
}
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{
public static class JsonWriterExtensions
{
/// <summary>
/// Writes the type value.
/// </summary>
/// <param name="extends"></param>
/// <param name="type"></param>
[PublicAPI]
public static void WriteType(this JsonWriter extends, Type type)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (type == null)
{
extends.WriteNull();
return;
}
string name = type.GetMinimalName();
extends.WriteValue(name);
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="write"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items,
Action<JsonSerializer, JsonWriter, TItem> write)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
if (write == null)
throw new ArgumentNullException("write");
writer.WriteStartArray();
{
foreach (TItem item in items)
write(extends, writer, item);
}
writer.WriteEndArray();
}
}
}

View File

@@ -62,6 +62,8 @@ namespace ICD.Common.Utils.Extensions
private static readonly Dictionary<Type, Type[]> s_TypeBaseTypes;
private static readonly Dictionary<Type, Type[]> s_TypeImmediateInterfaces;
private static readonly Dictionary<Type, Type[]> s_TypeMinimalInterfaces;
private static readonly Dictionary<Type, string> s_TypeToMinimalName;
private static readonly Dictionary<Type, string> s_TypeToNameWithoutAssemblyDetails;
/// <summary>
/// Static constructor.
@@ -72,6 +74,8 @@ namespace ICD.Common.Utils.Extensions
s_TypeBaseTypes = new Dictionary<Type, Type[]>();
s_TypeImmediateInterfaces = new Dictionary<Type, Type[]>();
s_TypeMinimalInterfaces = new Dictionary<Type, Type[]>();
s_TypeToMinimalName = new Dictionary<Type, string>();
s_TypeToNameWithoutAssemblyDetails = new Dictionary<Type, string>();
}
/// <summary>
@@ -158,6 +162,19 @@ namespace ICD.Common.Utils.Extensions
.Assembly;
}
/// <summary>
/// Returns true if the type is assignable to the given type.
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public static bool IsAssignableTo<T>(this Type from)
{
if (from == null)
throw new ArgumentNullException("from");
return from.IsAssignableTo(typeof(T));
}
/// <summary>
/// Returns true if the type is assignable to the given type.
/// </summary>
@@ -307,6 +324,128 @@ namespace ICD.Common.Utils.Extensions
return index == -1 ? name : name.Substring(0, index);
}
/// <summary>
/// Gets the smallest possible string representation for the given type that
/// can be converted back to a Type via Type.GetType(string).
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetMinimalName(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToMinimalName.TryGetValue(extends, out name))
{
// Generics are a pain
if (extends.IsGenericType)
{
string nameWithoutAssemblyDetails = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
int genericStart = nameWithoutAssemblyDetails.IndexOf('[');
if (genericStart < 0)
{
name = nameWithoutAssemblyDetails;
}
else
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = nameWithoutAssemblyDetails.LastIndexOf(']');
name = new StringBuilder().Append(nameWithoutAssemblyDetails, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(nameWithoutAssemblyDetails, genericEnd - 1,
nameWithoutAssemblyDetails.Length - genericEnd + 1)
.ToString();
}
}
else
{
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
}
s_TypeToMinimalName.Add(extends, name);
}
return name;
}
/// <summary>
/// Gets the string representation for the type.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetNameWithoutAssemblyDetails(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
{
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName);
s_TypeToNameWithoutAssemblyDetails.Add(extends, name);
}
return name;
}
/// <summary>
/// Taken from Newtonsoft.Json.Utilities.ReflectionUtils
/// Removes the assembly details from a type assembly qualified name.
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
{
StringBuilder builder = new StringBuilder();
// loop through the type name and filter out qualified assembly details from nested type names
bool writingAssemblyName = false;
bool skippingAssemblyDetails = false;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ']':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ',':
if (!writingAssemblyName)
{
writingAssemblyName = true;
builder.Append(current);
}
else
{
skippingAssemblyDetails = true;
}
break;
default:
if (!skippingAssemblyDetails)
{
builder.Append(current);
}
break;
}
}
return builder.ToString();
}
/// <summary>
/// Gets the type name as it would appear in code.
/// </summary>

View File

@@ -39,9 +39,9 @@
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
<PackageReference Include="Microsoft.Data.SQLite" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>

View File

@@ -77,7 +77,6 @@
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
<Compile Include="Attributes\RangeAttribute.cs" />
<Compile Include="Collections\BiDictionary.cs" />
<Compile Include="Collections\AsyncEventQueue.cs" />
<Compile Include="Collections\IcdOrderedDictionary.cs" />
<Compile Include="Collections\PriorityQueue.cs" />
<Compile Include="Collections\RateLimitedEventQueue.cs" />
@@ -106,6 +105,7 @@
<Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\DayOfWeekExtensions.cs" />
<Compile Include="Extensions\JsonWriterExtensions.cs" />
<Compile Include="Extensions\ListExtensions.cs" />
<Compile Include="Comparers\PredicateEqualityComparer.cs" />
<Compile Include="Extensions\MethodInfoExtensions.cs" />
@@ -117,6 +117,7 @@
<Compile Include="IO\IcdBinaryReader.cs" />
<Compile Include="IO\eSeekOrigin.cs" />
<Compile Include="IO\IcdStreamWriter.cs" />
<Compile Include="Json\ToStringJsonConverter.cs" />
<Compile Include="ProcessorUtils.SimplSharp.cs" />
<Compile Include="ProcessorUtils.Standard.cs" />
<Compile Include="ProgramUtils.SimplSharp.cs" />
@@ -154,7 +155,7 @@
<Compile Include="Extensions\EnumerableExtensions.cs" />
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="Extensions\EventHandlerExtensions.cs" />
<Compile Include="Extensions\JsonExtensions.cs" />
<Compile Include="Extensions\JsonReaderExtensions.cs" />
<Compile Include="Extensions\QueueExtensions.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Extensions\StringBuilderExtensions.cs" />

View File

@@ -1,6 +1,8 @@
using System;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
@@ -18,6 +20,8 @@ namespace ICD.Common.Utils
Administrator = 2
}
public static event EventHandler<StringEventArgs> OnConsolePrint;
/// <summary>
/// Wraps CrestronConsole.ConsoleCommandResponse for S+ compatibility.
/// </summary>
@@ -41,26 +45,34 @@ namespace ICD.Common.Utils
message = string.Format(message, args);
#if SIMPLSHARP
try
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
{
CrestronConsole.ConsoleCommandResponse(message);
try
{
CrestronConsole.ConsoleCommandResponse(message);
}
catch (NotSupportedException)
{
PrintLine(message);
}
return;
}
catch (NotSupportedException)
{
Print(message);
}
#else
Print(message);
#endif
PrintLine(message);
}
public static void PrintLine(string message)
{
#if SIMPLSHARP
CrestronConsole.PrintLine(message);
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
CrestronConsole.PrintLine(message);
#else
Console.WriteLine(message);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message + IcdEnvironment.NewLine));
}
public static void PrintLine(string message, params object[] args)
@@ -90,10 +102,12 @@ namespace ICD.Common.Utils
public static void Print(string message)
{
#if SIMPLSHARP
CrestronConsole.Print(message);
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
CrestronConsole.Print(message);
#else
Console.Write(message);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message));
}
public static void Print(string message, params object[] args)

View File

@@ -1,5 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
@@ -10,7 +11,10 @@ namespace ICD.Common.Utils.Json
/// Creates a new instance of T.
/// </summary>
/// <returns></returns>
protected abstract T Instantiate();
protected virtual T Instantiate()
{
return ReflectionUtils.CreateInstance<T>();
}
/// <summary>
/// Writes the JSON representation of the object.
@@ -26,12 +30,6 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
if (value == null)
{
writer.WriteNull();
return;
}
WriteJson(writer, (T)value, serializer);
}
@@ -50,7 +48,9 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
{
writer.WriteNull();
return;
@@ -113,30 +113,15 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
T output = default(T);
bool instantiated = false;
if (reader.TokenType == JsonToken.Null)
return default(T);
while (reader.Read())
{
if (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.EndObject)
break;
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, reader.TokenType));
if (!instantiated)
{
instantiated = true;
output = Instantiate();
}
T output = Instantiate();
// Get the property
if (reader.TokenType != JsonToken.PropertyName)
continue;
string property = (string)reader.Value;
// Read into the value
reader.Read();
ReadProperty(property, reader, output, serializer);
}
reader.ReadObject(serializer, (p, r, s) => ReadProperty(p, r, output, s));
return output;
}

View File

@@ -22,28 +22,6 @@ namespace ICD.Common.Utils.Json
private const string MESSAGE_NAME_PROPERTY = "m";
private const string MESSAGE_DATA_PROPERTY = "d";
/// <summary>
/// Forces Newtonsoft to cache the given type for faster subsequent usage.
/// </summary>
/// <typeparam name="T"></typeparam>
public static void CacheType<T>()
where T : new()
{
CacheType(typeof(T));
}
/// <summary>
/// Forces Newtonsoft to cache the given type for faster subsequent usage.
/// </summary>
public static void CacheType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
string serialized = JsonConvert.SerializeObject(ReflectionUtils.CreateInstance(type));
JsonConvert.DeserializeObject(serialized, type);
}
/// <summary>
/// Gets the token as a DateTime value.
/// </summary>
@@ -87,31 +65,6 @@ namespace ICD.Common.Utils.Json
}
}
/// <summary>
/// Pretty-prints the JSON document.
/// </summary>
/// <param name="json"></param>
[PublicAPI]
public static void Print(string json)
{
if (json == null)
throw new ArgumentNullException("json");
string formatted = Format(json);
IcdConsole.PrintLine(formatted);
}
/// <summary>
/// Serializes the given item and pretty-prints to JSON.
/// </summary>
/// <param name="value"></param>
[PublicAPI]
public static void Print(object value)
{
string formatted = Format(value);
IcdConsole.PrintLine(formatted);
}
/// <summary>
/// Serializes the given item and formats the JSON into a human-readable form.
/// </summary>
@@ -234,6 +187,22 @@ namespace ICD.Common.Utils.Json
}
}
/// <summary>
/// Serializes to json, wrapping the object with a message property to differentiate between messages.
/// E.g.
/// { a = 1 }
/// Becomes
/// { m = "Test", d = { a = 1 } }
/// </summary>
/// <param name="value"></param>
/// <param name="messageName"></param>
/// <returns></returns>
[PublicAPI]
public static string SerializeMessage(object value, string messageName)
{
return SerializeMessage(w => new JsonSerializer().Serialize(w, value), messageName);
}
/// <summary>
/// Serializes to json, wrapping the object with a message property to differentiate between messages.
/// E.g.
@@ -317,44 +286,5 @@ namespace ICD.Common.Utils.Json
},
json);
}
/// <summary>
/// Deserializes the given token based on the known type.
/// </summary>
/// <param name="type"></param>
/// <param name="token"></param>
/// <returns></returns>
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());
}
/// <summary>
/// Deserializes the given token based on the known type.
/// </summary>
/// <param name="type"></param>
/// <param name="token"></param>
/// <param name="serializer"></param>
/// <returns></returns>
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);
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{
/// <summary>
/// Simply reads/writes values from/to JSON as their string representation.
/// </summary>
[PublicAPI]
public sealed class ToStringJsonConverter : JsonConverter
{
private static readonly Dictionary<Type, MethodInfo> s_ParseMethods;
public override bool CanRead { get { return true; } }
/// <summary>
/// Static constructor.
/// </summary>
static ToStringJsonConverter()
{
s_ParseMethods = new Dictionary<Type, MethodInfo>();
}
#region Methods
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
MethodInfo parse = GetParseMethod(objectType);
if (parse != null)
return parse.Invoke(null, new[] {reader.Value});
throw new ArgumentException(
string.Format("{0} does not have a 'static {0} Parse(string)' method.", objectType.Name),
"objectType");
}
#endregion
[CanBeNull]
private static MethodInfo GetParseMethod(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
MethodInfo output;
if (!s_ParseMethods.TryGetValue(type, out output))
{
output = type
#if SIMPLSHARP
.GetCType()
.GetMethod("Parse",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
CType.DefaultBinder,
new CType[] {typeof(string)},
new ParameterModifier[] {});
#else
.GetTypeInfo()
.GetMethod("Parse",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
Type.DefaultBinder,
new[] {typeof(string)},
new ParameterModifier[] {});
#endif
s_ParseMethods.Add(type, output);
}
return output;
}
}
}

View File

@@ -3,5 +3,5 @@ using System.Reflection;
[assembly: AssemblyTitle("ICD.Common.Utils")]
[assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2018")]
[assembly: AssemblyVersion("8.3.0.0")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("9.2.0.0")]

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
@@ -97,23 +98,23 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// Returns true if there is a path from the given root to the given destination node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="child"></param>
/// <param name="destination"></param>
/// <param name="getChildren"></param>
/// <returns></returns>
public static bool BreadthFirstSearch<T>(T root, T child, Func<T, IEnumerable<T>> getChildren)
public static bool BreadthFirstSearch<T>(T root, T destination, Func<T, IEnumerable<T>> getChildren)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchPath(root, child, getChildren) != null;
return BreadthFirstSearch(root, destination, getChildren, EqualityComparer<T>.Default);
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// Returns true if there is a path from the given root to the given destination node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
@@ -159,9 +160,9 @@ namespace ICD.Common.Utils
Queue<T> process = new Queue<T>();
process.Enqueue(root);
while (process.Count > 0)
T current;
while (process.Dequeue(out current))
{
T current = process.Dequeue();
yield return current;
foreach (T child in getChildren(current))
@@ -214,10 +215,9 @@ namespace ICD.Common.Utils
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
while (queue.Count > 0)
T current;
while (queue.Dequeue(out current))
{
T current = queue.Dequeue();
foreach (T node in getChildren(current))
{
if (nodeParents.ContainsKey(node))

View File

@@ -337,8 +337,9 @@ namespace ICD.Common.Utils
if (valueType.IsIntegerNumeric())
return Enum.ToObject(type, value);
if (value is string)
return Enum.Parse(type, value as string, false);
string valueAsString = value as string;
if (valueAsString != null)
return Enum.Parse(type, valueAsString, false);
}
return Convert.ChangeType(value, type, null);
@@ -352,12 +353,56 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Subscribes to the event on the given instance using the handler and callback method.
/// Subscribes to the event on the given instance using the event handler.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="handler">The instance with the callback MethodInfo. Null for static types.</param>
/// <param name="callback">The MethodInfo for the callback method.</param>
/// <param name="eventHandler">The EventHandler for the callback.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, Action<object> eventHandler)
{
if (eventInfo == null)
throw new ArgumentNullException("eventInfo");
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
object handler = eventHandler.Target;
MethodInfo callback = EventHandlerExtensions.GetMethodInfo(eventHandler);
return SubscribeEvent(instance, eventInfo, handler, callback);
}
/// <summary>
/// Subscribes to the event on the given instance using the event handler.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="eventHandler">The EventHandler for the callback.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent<T>(object instance, EventInfo eventInfo, Action<object, T> eventHandler)
{
if (eventInfo == null)
throw new ArgumentNullException("eventInfo");
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
object handler = eventHandler.Target;
MethodInfo callback = EventHandlerExtensions.GetMethodInfo(eventHandler);
return SubscribeEvent(instance, eventInfo, handler, callback);
}
/// <summary>
/// Subscribes to the event on the given instance using the handler and eventHandler method.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="handler">The instance with the eventHandler MethodInfo. Null for static types.</param>
/// <param name="callback">The MethodInfo for the eventHandler method.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback)

View File

@@ -12,8 +12,7 @@ namespace ICD.Common.Utils
{
private readonly object m_Instance;
private readonly List<string> m_PropertyOrder;
private readonly Dictionary<string, string> m_PropertyValues;
private readonly List<KeyValuePair<string, string>> m_PropertyOrder;
/// <summary>
/// Constructor.
@@ -23,8 +22,7 @@ namespace ICD.Common.Utils
{
m_Instance = instance;
m_PropertyOrder = new List<string>();
m_PropertyValues = new Dictionary<string, string>();
m_PropertyOrder = new List<KeyValuePair<string, string>>();
}
/// <summary>
@@ -32,14 +30,10 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AppendProperty(string name, object value)
public ReprBuilder AppendProperty(string name, object value)
{
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
string valueString = GetValueStringRepresentation(value);
m_PropertyValues[name] = valueString;
return AppendPropertyRaw(name, valueString);
}
/// <summary>
@@ -47,12 +41,10 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AppendPropertyRaw(string name, string value)
public ReprBuilder AppendPropertyRaw(string name, string value)
{
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
m_PropertyValues[name] = value;
m_PropertyOrder.Add(new KeyValuePair<string, string>(name, value));
return this;
}
/// <summary>
@@ -62,7 +54,7 @@ namespace ICD.Common.Utils
public override string ToString()
{
if (m_Instance == null)
return GetValueStringRepresentation(m_Instance);
return GetValueStringRepresentation(null);
StringBuilder builder = new StringBuilder();
@@ -71,12 +63,11 @@ namespace ICD.Common.Utils
for (int index = 0; index < m_PropertyOrder.Count; index++)
{
string property = m_PropertyOrder[index];
builder.Append(property);
builder.Append('=');
KeyValuePair<string, string> pair = m_PropertyOrder[index];
string valueString = m_PropertyValues[property];
builder.Append(valueString);
builder.Append(pair.Key);
builder.Append('=');
builder.Append(pair.Value);
if (index < m_PropertyOrder.Count - 1)
builder.Append(", ");

View File

@@ -17,7 +17,6 @@ namespace ICD.Common.Utils
private const char INTERSECT = '+';
private readonly List<string[]> m_Rows;
private readonly SafeCriticalSection m_RowsSection;
private readonly string[] m_Columns;
/// <summary>
@@ -39,7 +38,6 @@ namespace ICD.Common.Utils
public TableBuilder(params string[] columns)
{
m_Rows = new List<string[]>();
m_RowsSection = new SafeCriticalSection();
m_Columns = columns;
}
@@ -49,9 +47,10 @@ namespace ICD.Common.Utils
/// Clears all of the rows.
/// </summary>
[PublicAPI]
public void ClearRows()
public TableBuilder ClearRows()
{
m_RowsSection.Execute(() => m_Rows.Clear());
m_Rows.Clear();
return this;
}
/// <summary>
@@ -59,11 +58,11 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AddRow(params object[] row)
public TableBuilder AddRow(params object[] row)
{
string[] stringRow = row.Select(o => string.Format("{0}", o))
.ToArray();
AddRow(stringRow);
return AddRow(stringRow);
}
/// <summary>
@@ -71,31 +70,33 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AddRow(params string[] row)
public TableBuilder AddRow(params string[] row)
{
if (row != null && row.Length != m_Columns.Length)
throw new ArgumentException("Row must match columns length.");
m_RowsSection.Execute(() => m_Rows.Add(row));
m_Rows.Add(row);
return this;
}
/// <summary>
/// Adds an empty row to the builder.
/// </summary>
[PublicAPI]
public void AddEmptyRow()
public TableBuilder AddEmptyRow()
{
AddRow(new string[m_Columns.Length]);
return AddRow(new string[m_Columns.Length]);
}
[PublicAPI]
public void AddSeparator()
public TableBuilder AddSeparator()
{
AddRow(null);
return AddRow(null);
}
[PublicAPI]
public void AddHeader(params string[] row)
public TableBuilder AddHeader(params string[] row)
{
if (row.Length != m_Columns.Length)
throw new ArgumentException("Row must match columns length.");
@@ -103,6 +104,8 @@ namespace ICD.Common.Utils
AddSeparator();
AddRow(row);
AddSeparator();
return this;
}
/// <summary>
@@ -112,30 +115,21 @@ namespace ICD.Common.Utils
{
StringBuilder sb = new StringBuilder();
m_RowsSection.Enter();
int[] columnWidths = GetColumnWidths();
try
AppendRow(sb, m_Columns, columnWidths);
AppendSeparator(sb, columnWidths);
foreach (string[] row in m_Rows)
{
int[] columnWidths = GetColumnWidths();
AppendRow(sb, m_Columns, columnWidths);
AppendSeparator(sb, columnWidths);
foreach (string[] row in m_Rows)
{
if (row == null)
AppendSeparator(sb, columnWidths);
else
AppendRow(sb, row, columnWidths);
}
AppendSeparator(sb, columnWidths);
}
finally
{
m_RowsSection.Leave();
if (row == null)
AppendSeparator(sb, columnWidths);
else
AppendRow(sb, row, columnWidths);
}
AppendSeparator(sb, columnWidths);
return sb.ToString();
}

View File

@@ -85,12 +85,12 @@ namespace ICD.Common.Utils.Timers
if (IsDisposed)
return;
IsDisposed = true;
Stop();
m_Timer.Dispose();
m_Callback = null;
IsDisposed = true;
}
/// <summary>
@@ -160,7 +160,8 @@ namespace ICD.Common.Utils.Timers
// Essentially the meat of this class. There's some weirdness with the garbage collector where
// the reference to the timer will be cleared, and eventually the CTimer will call the callback
// despite being stopped/disposed.
if (m_Timer == null
if (IsDisposed ||
m_Timer == null
#if SIMPLSHARP
|| m_Timer.Disposed
#endif

View File

@@ -72,7 +72,12 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("type");
using (IcdXmlReader reader = new IcdXmlReader(xml))
return DeserializeObject(type, reader);
{
if (reader.ReadToNextElement())
return DeserializeObject(type, reader);
throw new FormatException("Expected element in XML");
}
}
/// <summary>