Compare commits

..

107 Commits

Author SHA1 Message Date
Chris Cameron
0152259a25 chore: Updating changelog, incrementing minor version 2019-06-10 13:50:37 -04:00
Chris Cameron
98c3f17602 docs: Updating changelog 2019-06-10 13:50:23 -04:00
Chris Cameron
bb3fba100d fix: Fixing NetStandard CultureInfo database connection string 2019-06-10 13:48:08 -04:00
Chris Cameron
9adad373f6 fix: Better determine builtin cultures on NetStandard 2019-06-10 13:47:45 -04:00
Chris Cameron
844f8b7b46 refactor: Tidying 2019-06-10 13:47:21 -04:00
Chris Cameron
43d7ea944e Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2019-06-06 15:54:04 -04:00
Chris Cameron
311f2dfaf4 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2019-06-05 15:03:33 -04:00
Chris Cameron
d6d3bfa9e0 Merge branch 'feat/XmlUtilsRoku' of Common/Utils into ConnectPro_v1.3 2019-06-04 17:17:33 +00:00
Austin Noska
536742a1c6 docs: updating changelog 2019-06-04 12:54:39 -04:00
Austin Noska
dc824b2034 feat: Added a URI query builder 2019-06-04 12:19:12 -04:00
Chris Cameron
0cc588ce15 Merge branch 'feat/XmlUtilsRoku' of Common/Utils into ConnectPro_v1.3 2019-05-31 20:16:55 +00:00
Austin Noska
b996a295f6 Merge remote-tracking branch 'origin/ConnectPro_v1.3' into feat/XmlUtilsRoku
# Conflicts:
#	CHANGELOG.md
2019-05-31 16:14:14 -04:00
Austin Noska
4312ad1a61 chore: updated changelog 2019-05-31 15:58:06 -04:00
Austin Noska
7792b01629 feat: Added Shim to read a list from xml with no root element 2019-05-31 15:52:47 -04:00
Chris Cameron
7e3aef4cb3 fix: Fixed threading exception in TypeExtensions 2019-05-28 09:52:06 -04:00
Chris Cameron
a6fbd5a6d2 chore: Updating changelog 2019-05-28 09:51:54 -04:00
Chris Cameron
6f67064d34 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2 2019-05-24 09:59:54 -04:00
Chris Cameron
198bf8dac0 Merge branch 'fix/json-reader-extensions' of Common/Utils into ConnectPro_v1.3 2019-05-22 17:45:08 +00:00
Jeffery Thompson
78f5c4a7b6 fix: use ToString() instead of as string to support .NET Standard 2019-05-22 11:09:32 -04:00
Chris Cameron
c0ed7f9b26 feat: Clarifying neutral culture exception 2019-05-16 16:24:59 -04:00
Chris Cameron
6d08ee3bef Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
#	ICD.Common.Utils/PathUtils.cs
#	ICD.Common.Utils/ProcessorUtils.SimplSharp.cs
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2019-05-16 09:56:43 -04:00
Chris Cameron
9f0e66e4a1 fix: XmlReaderExtensions handling both TimeSpan formats 2019-05-14 16:16:22 -04:00
Chris Cameron
4c93515f83 refactor: Exposing missing XmlConvert methods 2019-05-14 16:15:48 -04:00
Chris Cameron
9ad49540d2 fix: Fixing JSON converter error when handling default existing value with struct types 2019-05-10 16:04:11 -04:00
Chris Cameron
cbf7a3d79c chore: Updating changelog, incrementing minor version 2019-05-10 14:53:27 -04:00
Jeffery Thompson
5a4d3cabac Merge branch 'feat/localization' of Common/Utils into ConnectPro_v1.3 2019-05-10 18:35:34 +00:00
Chris Cameron
9d732ef4b2 feat: Adding RemoveRange method to IcdHashSet 2019-05-10 11:47:00 -04:00
Chris Cameron
ecdb4f4fb5 feat: AbstractGenericJsonConverter exposes virtual methods for overriding object serialization/deserialization 2019-05-09 15:18:21 -04:00
Chris Cameron
6f5188909b fix: Fixing dumb mistake with extension method resolution order 2019-05-09 12:51:55 -04:00
Chris Cameron
3fa089b85e feat: Added methods for serializing additional types, arrays and dictionaries to JSON 2019-05-09 11:58:55 -04:00
Chris Cameron
fef667191b chore: Moving CultureInfo.sqlite into project root so it gets included in clz 2019-05-08 10:21:52 -04:00
Chris Cameron
28abb69c85 feat: Initial commit of IcdCultureInfo and associated CultureInfo.sqlite database 2019-05-07 11:52:13 -04:00
Chris Cameron
0bf88240b2 feat: Adding missing methods to sqlite wrappers 2019-05-07 11:51:08 -04:00
Chris Cameron
1460bf2924 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
2019-05-02 10:28:09 -04:00
Chris Cameron
33b1d29781 fix: IcdUriBuilder constructors behave closer to UriBuilder, Host defaults to "localhost" 2019-05-01 17:05:22 -04:00
Chris Cameron
78c20201ab fix: IcdUriBuilder constructors behave closer to UriBuilder, Host defaults to "localhost" 2019-05-01 16:33:09 -04:00
Chris Cameron
66f4e797ed feat: Adding extension method for getting or adding a new item to a dictionary 2019-04-26 12:17:09 -04:00
Chris Cameron
94ac37b32a refactor: Tidying 2019-04-23 12:01:53 -04:00
Chris Cameron
22978e90c4 feat: Added extension method for peeking queues 2019-04-23 09:57:28 -04:00
Chris Cameron
995a57f8e4 chore: Updating changelog, incrementing minor version 2019-04-16 14:07:52 -04:00
Chris Cameron
d6d767d8f9 Merge remote-tracking branch 'origin/feat/logs' into ConnectPro_v1.2 2019-04-16 14:01:11 -04:00
Chris Cameron
798e9cc0a7 Merge branch 'feat/SPlusUtils' of Common/Utils into dev 2019-04-09 19:58:33 +00:00
Drew Tingen
1dac1bfb67 chore: Changelog + Docs 2019-04-09 15:57:10 -04:00
Drew Tingen
829c24b667 feat: Adding SPlusUtils to convert ushort's to int 2019-04-09 15:46:10 -04:00
Chris Cameron
715c198763 feat: Keeping paths as they were until we can agree on an approach going forwards 2019-04-08 17:00:10 -04:00
Chris Cameron
d8741773c5 refactor: Removing unused code 2019-04-08 16:38:01 -04:00
Chris Cameron
26b924dd48 feat: IcdStreamWriter exposes WriteLine(string) 2019-04-08 16:31:53 -04:00
Chris Cameron
8892e6e223 feat: Adding ProgramLogsPath to PathUtils 2019-04-08 15:48:05 -04:00
Chris Cameron
23afa8e025 feat: Windows and VC4 use Config directory for configuration 2019-04-08 15:47:52 -04:00
Chris Cameron
7d1cc3139f chore: Updating nuget packages 2019-04-08 14:00:47 -04:00
Chris Cameron
30ea6ced9e fix: Fixing bad merge 2019-04-08 10:59:37 -04:00
Chris Cameron
3041d0f402 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2019-04-08 10:55:52 -04:00
Chris Cameron
b2ca43baf1 feat: Adding extension methods for writing properties to JSON 2019-04-02 16:37:18 -04:00
Chris Cameron
f36b9f7856 fix: Fixing format exception when parsing JSON DateTime with timezone info 2019-04-01 16:47:08 -04:00
Chris Cameron
bcf13ef972 perf: Optimizing object instantiation for default constructors 2019-04-01 14:16:56 -04:00
Chris Cameron
100b8e4753 fix: JsonReader GetValueAsString ignores token type 2019-04-01 12:27:06 -04:00
Chris Cameron
134ee85067 feat: Adding JsonReader extension to read current value as DateTime 2019-03-31 20:38:22 -04:00
Chris Cameron
24601b0ef2 fix: Potential fix for sqlite database locked exception 2019-03-14 16:31:13 -04:00
Chris Cameron
4fa46ff5a0 Merge remote-tracking branch 'origin/fix/VC4_Fixes' into ConnectPro_v1.2 2019-03-13 16:28:18 -04:00
Chris Cameron
827ab2cd42 refactor: Removing unused code, loosening attribute type constraints 2019-03-13 16:26:53 -04:00
Drew Tingen
158c261d19 fix: VC4 Compat Fixes 2019-03-09 14:51:44 -05:00
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
54 changed files with 3051 additions and 1727 deletions

View File

@@ -6,17 +6,68 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [8.7.0] - 2019-06-24
## [9.5.0] - 2019-06-10
### Added
- IcdXmlException exposes line number and position properties
- Added Shim to read a list from xml with no root element
- Added a URI query builder
## [8.6.1] - 2019-06-14
### Changed
- Fixed a bug where stopped timers on NetStandard would still have a periodic callback duration
- Fixed JSON DateTime parsing in .Net Standard
- Fixed threading exception in TypeExtensions
- Fixes for platform-agnostic culture handling
## [9.4.0] - 2019-05-10
### Added
- Added extension method for peeking queues
- Added extension method for getting or adding a new item to a dictionary
- Added methods for serializing additional types, arrays and dictionaries to JSON
- AbstractGenericJsonConverter exposes virtual methods for overriding object serialization/deserialization
- Added RemoveRange method to IcdHashSet
- Added IcdCultureInfo and CultureInfo database for localization
## [8.6.0] - 2019-06-14
### Changed
- Overhaul of RangeAttribute remap methods to better avoid overflows
- IcdUriBuilder constructors behave closer to UriBuilder, Host defaults to "localhost"
## [9.3.0] - 2019-04-16
### Added
- Added SPlusUtils with ConvertToInt method taking LowWord/HighWord ushorts
- Added JsonReader extension methods for reading DateTimes
- Added JsonReader extension methods for writing properties
- IcdStreamWriter exposes WriteLine(string)
- Added ProgramLogsPath to PathUtils
### Changed
- Fixes for VC4 compatibility
- Fixed JSON DateTime parsing for timezone information
- Small reflection optimizations
## [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.5.0] - 2019-06-06
### Added

View File

@@ -1,86 +0,0 @@
using System;
using NUnit.Framework;
using RangeAttribute = ICD.Common.Utils.Attributes.RangeAttribute;
namespace ICD.Common.Utils.Tests.Attributes
{
[TestFixture]
public sealed class RangeAttributeTest : AbstractIcdAttributeTest<RangeAttribute>
{
#region Properties
[TestCase(1)]
[TestCase(1.0f)]
[TestCase(1.0)]
public void MinTest(object min)
{
Assert.AreEqual(min, new RangeAttribute(min, min).Min);
}
[TestCase(1)]
[TestCase(1.0f)]
[TestCase(1.0)]
public void MaxTest(object max)
{
Assert.AreEqual(max, new RangeAttribute(max, max).Max);
}
#endregion
#region Methods
[TestCase((double)0, (double)0)]
[TestCase((double)1, (double)1)]
[TestCase(ushort.MaxValue, double.MaxValue)]
[TestCase(short.MinValue, double.MinValue)]
public void RemapToDoubleTest(object value, double expected)
{
Assert.AreEqual(expected, RangeAttribute.RemapToDouble(value));
}
[TestCase((double)0, typeof(ushort), (ushort)32767)]
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
public void RemapFromDoubleTest(double value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.RemapFromDouble(value, type));
}
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(short.MaxValue, typeof(ushort), short.MaxValue)]
public static void Clamp(object value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
}
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
public void Clamp(double value, Type type, double expected)
{
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
}
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(short.MaxValue, typeof(ushort), ushort.MaxValue)]
public void RemapTest(object value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.Remap(value, type));
}
[TestCase(0, 100, ushort.MaxValue, typeof(ushort), ushort.MaxValue)]
[TestCase(0, 100, ushort.MaxValue, typeof(short), short.MaxValue)]
public void ClampMinMaxThenRemapTest(object min, object max, object value, Type type, object expected)
{
Assert.AreEqual(expected, new RangeAttribute(min, max).ClampMinMaxThenRemap(value, type));
}
[TestCase(0, 100, ushort.MaxValue, 100)]
[TestCase(0, 100, ushort.MinValue, 0)]
public void RemapMinMaxTest(object min, object max, object value, object expected)
{
Assert.AreEqual(expected, new RangeAttribute(min, max).RemapMinMax(value));
}
#endregion
}
}

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

@@ -20,9 +20,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -63,14 +63,15 @@ namespace ICD.Common.Utils.Tests
#endregion
[TestCase("http://localhost/", null, null, null, null, (ushort)0, null, null, null)]
[TestCase("http://localhost:80/", null, null, null, null, (ushort)80, null, null, null)]
[TestCase("http://username@localhost/", null, null, null, null, (ushort)0, null, null, "username")]
[TestCase("http://localhost/", null, null, "password", null, (ushort)0, null, null, null)]
[TestCase("https://localhost/", null, null, null, null, (ushort)0, null, "https", null)]
[TestCase("http://localhost/test", null, null, null, "test", (ushort)0, null, null, null)]
[TestCase("http://localhost/test", null, null, null, "/test", (ushort)0, null, null, null)]
[TestCase("http://localhost//test", null, null, null, "//test", (ushort)0, null, null, null)]
[TestCase("/", null, null, null, null, (ushort)0, null, null, null)]
[TestCase("http:///", null, null, null, null, (ushort)0, null, "http", null)]
[TestCase("http://localhost:80/", null, "localhost", null, null, (ushort)80, null, "http", null)]
[TestCase("http://username@localhost/", null, "localhost", null, null, (ushort)0, null, "http", "username")]
[TestCase("http://localhost/", null, "localhost", "password", null, (ushort)0, null, "http", null)]
[TestCase("https://localhost/", null, "localhost", null, null, (ushort)0, null, "https", null)]
[TestCase("http://localhost/test", null, "localhost", null, "test", (ushort)0, null, "http", null)]
[TestCase("http://localhost/test", null, "localhost", null, "/test", (ushort)0, null, "http", null)]
[TestCase("http://localhost//test", null, "localhost", null, "//test", (ushort)0, null, "http", null)]
public void ToStringTest(string expected, string fragment, string address, string password, string path, ushort port,
string query, string scheme, string userName)
{

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
@@ -19,7 +18,11 @@ namespace ICD.Common.Utils.Tests.Json
[Test]
public void ParseDateTimeTest()
{
Assert.Inconclusive();
const string dataA = "2016-02-26T19:24:59";
const string dataB = "2019-04-01T12:41:15-04:00";
Assert.DoesNotThrow(() => JsonUtils.ParseDateTime(dataA));
Assert.DoesNotThrow(() => JsonUtils.ParseDateTime(dataB));
}
[Test]
@@ -141,32 +144,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

@@ -27,10 +27,6 @@ namespace ICD.Common.Utils.Tests
[Test, UsedImplicitly]
public void MapRangeTest()
{
Assert.AreEqual(5, MathUtils.MapRange(-100, 100, 0, 10, 0));
Assert.AreEqual(7, MathUtils.MapRange(-100, 100, 0, 10, 50));
Assert.AreEqual(10, MathUtils.MapRange(-100, 100, 0, 10, 100));
Assert.AreEqual(0, MathUtils.MapRange(0, 100, 0, 10, 0));
Assert.AreEqual(5, MathUtils.MapRange(0, 100, 0, 10, 50));
Assert.AreEqual(10, MathUtils.MapRange(0, 100, 0, 10, 100));
@@ -40,7 +36,7 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
}
[Test, UsedImplicitly]
[Test, UsedImplicitly]
public void GetRangesTest()
{
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };

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

@@ -2,10 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.Services;
using ICD.Common.Utils.Services.Logging;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -20,139 +17,6 @@ namespace ICD.Common.Utils
/// </summary>
public static class AttributeUtils
{
// Avoid caching the same assembly multiple times.
private static readonly IcdHashSet<Assembly> s_CachedAssemblies;
private static readonly IcdHashSet<Type> s_CachedTypes;
private static readonly Dictionary<Attribute, Type> s_AttributeToTypeCache;
private static readonly Dictionary<Type, IcdHashSet<Attribute>> s_TypeToAttributesCache;
private static ILoggerService Logger { get { return ServiceProvider.TryGetService<ILoggerService>(); } }
/// <summary>
/// Constructor.
/// </summary>
static AttributeUtils()
{
s_CachedAssemblies = new IcdHashSet<Assembly>();
s_CachedTypes = new IcdHashSet<Type>();
s_AttributeToTypeCache = new Dictionary<Attribute, Type>();
s_TypeToAttributesCache = new Dictionary<Type, IcdHashSet<Attribute>>();
}
#region Caching
/// <summary>
/// Pre-emptively caches the given assembly for lookup.
/// </summary>
/// <param name="assembly"></param>
public static bool CacheAssembly(Assembly assembly)
{
if (assembly == null)
throw new ArgumentNullException("assembly");
if (s_CachedAssemblies.Contains(assembly))
return true;
s_CachedAssemblies.Add(assembly);
#if SIMPLSHARP
CType[] types;
#else
Type[] types;
#endif
try
{
types = assembly.GetTypes();
}
#if STANDARD
catch (ReflectionTypeLoadException e)
{
foreach (Exception inner in e.LoaderExceptions)
{
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",
typeof(AttributeUtils).Name, assembly.GetName().Name);
continue;
}
Logger.AddEntry(eSeverity.Error, inner, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
assembly.GetName().Name);
}
return false;
}
#endif
catch (TypeLoadException e)
{
#if SIMPLSHARP
Logger.AddEntry(eSeverity.Error, e, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
assembly.GetName().Name);
#else
Logger.AddEntry(eSeverity.Error, e, "{0} failed to cache assembly {1} - could not load type {2}",
typeof(AttributeUtils).Name, assembly.GetName().Name, e.TypeName);
#endif
return false;
}
foreach (var type in types)
CacheType(type);
return true;
}
/// <summary>
/// Pre-emptively caches the given type for lookup.
/// </summary>
/// <param name="type"></param>
#if SIMPLSHARP
public static void CacheType(CType type)
#else
public static void CacheType(Type type)
#endif
{
if (type == null)
throw new ArgumentNullException("type");
if (s_CachedTypes.Contains(type))
return;
s_CachedTypes.Add(type);
try
{
s_TypeToAttributesCache[type] = new IcdHashSet<Attribute>(type.GetCustomAttributes<Attribute>(false));
foreach (Attribute attribute in s_TypeToAttributesCache[type])
s_AttributeToTypeCache[attribute] = type;
}
// GetMethods for Open Generic Types is not supported.
catch (NotSupportedException)
{
}
// Not sure why this happens :/
catch (InvalidProgramException)
{
}
}
#endregion
#region Lookup
/// <summary>
/// Gets the class attributes of the given generic type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetClassAttributes<T>()
{
return s_AttributeToTypeCache.Select(kvp => kvp.Key)
.OfType<T>();
}
/// <summary>
/// Gets the first attribute on the given class type matching the generic type.
/// </summary>
@@ -162,56 +26,29 @@ namespace ICD.Common.Utils
[CanBeNull]
public static T GetClassAttribute<T>(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetClassAttributes<T>(type).FirstOrDefault();
return GetClassAttribute<T>(type, false);
}
/// <summary>
/// Gets the attributes on the given class type matching the generic type.
/// Gets the first attribute on the given class type matching the generic type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type"></param>
/// <param name="inherit"></param>
/// <returns></returns>
public static IEnumerable<T> GetClassAttributes<T>(Type type)
[CanBeNull]
public static T GetClassAttribute<T>(Type type, bool inherit)
{
if (type == null)
throw new ArgumentNullException("type");
return GetClassAttributes(type).OfType<T>();
}
/// <summary>
/// Gets the attributes on the given class.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<Attribute> GetClassAttributes(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
CacheType(type);
IcdHashSet<Attribute> attributes;
return s_TypeToAttributesCache.TryGetValue(type, out attributes)
? attributes.ToArray(attributes.Count)
: Enumerable.Empty<Attribute>();
}
/// <summary>
/// Gets the type with the given attribute.
/// </summary>
/// <param name="attribute"></param>
/// <returns></returns>
public static Type GetClass(Attribute attribute)
{
if (attribute == null)
throw new ArgumentNullException("attribute");
return s_AttributeToTypeCache[attribute];
// ReSharper disable InvokeAsExtensionMethod
return ReflectionExtensions.GetCustomAttributes<T>(
#if SIMPLSHARP
(CType)
#endif
type, inherit).FirstOrDefault();
// ReSharper restore InvokeAsExtensionMethod
}
/// <summary>
@@ -237,7 +74,5 @@ namespace ICD.Common.Utils
.Where(p => ReflectionExtensions.GetCustomAttributes<T>(p, inherit).Any());
// ReSharper restore InvokeAsExtensionMethod
}
#endregion
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Attributes
{
@@ -11,509 +8,295 @@ namespace ICD.Common.Utils.Attributes
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.ReturnValue)]
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = true)]
public sealed class RangeAttribute : AbstractIcdAttribute
{
/// <summary>
/// Remaps from the source numeric min/max to double min/max.
/// </summary>
private static readonly Dictionary<Type, Func<double, double>> s_Clamp =
new Dictionary<Type, Func<double, double>>
{
// Duh
{typeof(double), o => o},
// Signed
{typeof(short), o => o < short.MinValue ? short.MinValue : o > short.MaxValue ? short.MaxValue : o},
{typeof(int), o => o < int.MinValue ? int.MinValue : o > int.MaxValue ? int.MaxValue : o},
{typeof(long), o => o < long.MinValue ? long.MinValue : o > long.MaxValue ? long.MaxValue : o},
{typeof(float),o => o < float.MinValue ? float.MinValue : o > float.MaxValue ? float.MaxValue : o},
{typeof(decimal), o => o < (double)decimal.MinValue ? (double)decimal.MinValue : o > (double)decimal.MaxValue ? (double)decimal.MaxValue : o},
// Unsigned
{typeof(ushort), o => o < ushort.MinValue ? ushort.MinValue : o > ushort.MaxValue ? ushort.MaxValue : o},
{typeof(uint), o => o < uint.MinValue ? uint.MinValue : o > uint.MaxValue ? uint.MaxValue : o},
{typeof(ulong), o => o < ulong.MinValue ? ulong.MinValue : o > ulong.MaxValue ? ulong.MaxValue : o},
{typeof(byte), o => o < byte.MinValue ? byte.MinValue : o > byte.MaxValue ? byte.MaxValue : o}
};
/// <summary>
/// Remaps from the source numeric min/max to double min/max.
/// </summary>
private static readonly Dictionary<Type, Func<object, double>> s_RemapToDouble =
new Dictionary<Type, Func<object, double>>
{
// Duh
{ typeof(double), o => (double)o},
// Signed - Clamping prevents an overflow due to loss of precision
{ typeof(short), o => MathUtils.Clamp(Convert.ToDouble(o) / short.MaxValue, -1, 1) * double.MaxValue},
{ typeof(int), o => MathUtils.Clamp(Convert.ToDouble(o) / int.MaxValue, -1, 1) * double.MaxValue},
{ typeof(long), o => MathUtils.Clamp(Convert.ToDouble(o) / long.MaxValue, -1, 1) * double.MaxValue},
{ typeof(float), o => MathUtils.Clamp(Convert.ToDouble(o) / float.MaxValue, -1, 1) * double.MaxValue},
{ typeof(decimal), o => MathUtils.Clamp(Convert.ToDouble(o) / (double)decimal.MaxValue, -1, 1) * double.MaxValue},
// Unsigned
{ typeof(ushort), o => MathUtils.Clamp((Convert.ToDouble(o) / ushort.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(uint), o => MathUtils.Clamp((Convert.ToDouble(o) / uint.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(ulong), o => MathUtils.Clamp((Convert.ToDouble(o) / ulong.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(byte), o => MathUtils.Clamp((Convert.ToDouble(o) / byte.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue}
};
/// <summary>
/// Remaps from the double min/max to target numeric min/max.
/// </summary>
private static readonly Dictionary<Type, Func<double, object>> s_RemapFromDouble =
new Dictionary<Type, Func<double, object>>
{
// Duh
{typeof(double), v => v},
// Signed
{typeof(short), v => (short)(v / double.MaxValue * short.MaxValue)},
{typeof(int), v => (int)(v / double.MaxValue * int.MaxValue)},
{typeof(long), v => (long)(v / double.MaxValue * long.MaxValue)},
{typeof(float), v => (float)(v / double.MaxValue * float.MaxValue)},
{typeof(decimal), v => (decimal)(v / double.MaxValue) * decimal.MaxValue},
// Unsigned
{typeof(ushort), v => (ushort)((v / double.MaxValue + 1) / 2 * ushort.MaxValue)},
{typeof(uint), v => (uint)((v / double.MaxValue + 1) / 2 * uint.MaxValue)},
{typeof(ulong), v => (ulong)((v / double.MaxValue + 1) / 2 * ulong.MaxValue)},
{typeof(byte), v => (byte)((v / double.MaxValue + 1) / 2 * byte.MaxValue)}
};
/// <summary>
/// Gets the min value of a given numeric type as a double.
/// </summary>
private static readonly Dictionary<Type, double> s_MinAsDouble =
new Dictionary<Type, double>
{
// Duh
{typeof(double), double.MinValue},
// Signed
{typeof(short), Convert.ToDouble(short.MinValue)},
{typeof(int), Convert.ToDouble(int.MinValue)},
{typeof(long), Convert.ToDouble(long.MinValue)},
{typeof(float), Convert.ToDouble(float.MinValue)},
{typeof(decimal), Convert.ToDouble(decimal.MinValue)},
// Unsigned
{typeof(ushort), Convert.ToDouble(ushort.MinValue)},
{typeof(uint), Convert.ToDouble(uint.MinValue)},
{typeof(ulong), Convert.ToDouble(ulong.MinValue)},
{typeof(byte), Convert.ToDouble(byte.MinValue)}
};
/// <summary>
/// Gets the min value of a given numeric type as a double.
/// </summary>
private static readonly Dictionary<Type, double> s_MaxAsDouble =
new Dictionary<Type, double>
{
// Duh
{typeof(double), double.MaxValue},
// Signed
{typeof(short), Convert.ToDouble(short.MaxValue)},
{typeof(int), Convert.ToDouble(int.MaxValue)},
{typeof(long), Convert.ToDouble(long.MaxValue)},
{typeof(float), Convert.ToDouble(float.MaxValue)},
{typeof(decimal), Convert.ToDouble(decimal.MaxValue)},
// Unsigned
{typeof(ushort), Convert.ToDouble(ushort.MaxValue)},
{typeof(uint), Convert.ToDouble(uint.MaxValue)},
{typeof(ulong), Convert.ToDouble(ulong.MaxValue)},
{typeof(byte), Convert.ToDouble(byte.MaxValue)}
};
private readonly object m_Min;
private readonly object m_Max;
#region Properties
/// <summary>
/// Gets the min value for this range.
/// </summary>
public object Min { get { return m_Min; } }
/// <summary>
/// Gets the max value for this range.
/// </summary>
public object Max { get { return m_Max; } }
public object Min { get; private set; }
public object Max { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(ushort min, ushort max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(short min, short max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(uint min, uint max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(int min, int max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(ulong min, ulong max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(long min, long max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(float min, float max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(double min, double max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(byte min, byte max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(sbyte min, sbyte max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(decimal min, decimal max)
: this((object)min, (object)max)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(object min, object max)
{
if (min == null)
throw new ArgumentNullException("min");
if (max == null)
throw new ArgumentNullException("max");
if (min.GetType() != max.GetType())
throw new ArgumentException("Min and Max types do not match");
if (!min.GetType().IsNumeric())
throw new ArgumentException("Given types are not numeric");
m_Min = min;
m_Max = max;
Min = min;
Max = max;
}
#endregion
#region Methods
/// <summary>
/// Remaps the given numeric value from its min/max range into double min/max range.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double RemapToDouble(object value)
public T GetMin<T>()
{
if (value == null)
throw new ArgumentNullException("value");
Func<object, double> remap;
if (!s_RemapToDouble.TryGetValue(value.GetType(), out remap))
throw new NotSupportedException("Value type is not supported.");
return remap(value);
return (T)Convert.ChangeType(Min, typeof(T), null);
}
/// <summary>
/// Remaps the given double value from its min/max range into the target type min/max range.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object RemapFromDouble(double value, Type type)
public T GetMax<T>()
{
if (type == null)
throw new ArgumentNullException("type");
Func<double, object> remap;
if (!s_RemapFromDouble.TryGetValue(type, out remap))
throw new NotSupportedException("Value type is not supported.");
return remap(value);
return (T)Convert.ChangeType(Max, typeof(T), null);
}
/// <summary>
/// Clamps the given numeric value into the valid ranges of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Clamp(object value, Type type)
public bool IsInRange(object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (value is ushort)
{
if (!(Min is ushort))
throw new ArgumentException("the type of value does not match the type of min / max");
if (type == null)
throw new ArgumentNullException("type");
var castVal = (ushort)value;
return (castVal >= GetMin<ushort>() && castVal <= GetMax<ushort>());
}
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (value is short)
{
if (!(Min is short))
throw new ArgumentException("the type of value does not match the type of min / max");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
var castVal = (short)value;
return (castVal >= GetMin<short>() && castVal <= GetMax<short>());
}
double doubleValue = Convert.ToDouble(value);
double clamped = Clamp(doubleValue, type);
if (value is uint)
{
if (!(Min is uint))
throw new ArgumentException("the type of value does not match the type of min / max");
return Convert.ChangeType(clamped, value.GetType(), CultureInfo.InvariantCulture);
var castVal = (uint)value;
return (castVal >= GetMin<uint>() && castVal <= GetMax<uint>());
}
if (value is int)
{
if (!(Min is int))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (int)value;
return (castVal >= GetMin<int>() && castVal <= GetMax<int>());
}
if (value is ulong)
{
if (!(Min is ulong))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (ulong)value;
return (castVal >= GetMin<ulong>() && castVal <= GetMax<ulong>());
}
if (value is long)
{
if (!(Min is long))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (long)value;
return (castVal >= GetMin<long>() && castVal <= GetMax<long>());
}
if (value is float)
{
if (!(Min is float))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (float)value;
return (castVal >= GetMin<float>() && castVal <= GetMax<float>());
}
if (value is double)
{
if (!(Min is double))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (double)value;
return (castVal >= GetMin<double>() && castVal <= GetMax<double>());
}
if (value is decimal)
{
if (!(Min is decimal))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (decimal)value;
return (castVal >= GetMin<decimal>() && castVal <= GetMax<decimal>());
}
if (value is byte)
{
if (!(Min is byte))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (byte)value;
return (castVal >= GetMin<byte>() && castVal <= GetMax<byte>());
}
if (value is sbyte)
{
if (!(Min is sbyte))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (sbyte)value;
return (castVal >= GetMin<sbyte>() && castVal <= GetMax<sbyte>());
}
throw new ArgumentException("the type of value is not a numeric type.");
}
/// <summary>
/// Clamps the given double value into the valid ranges of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static double Clamp(double value, Type type)
#region Range -> UShort
public ushort RemapRangeToUshort(double value)
{
if (type == null)
throw new ArgumentNullException("type");
Func<double, double> clamp;
if (!s_Clamp.TryGetValue(type, out clamp))
throw new NotSupportedException("Value type is not supported.");
return clamp(value);
return (ushort)MathUtils.MapRange(GetMin<double>(), GetMax<double>(), ushort.MinValue, ushort.MaxValue, value);
}
/// <summary>
/// Remaps the numeric value into the min-max range of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Remap(object value, Type type)
public ushort RemapRangeToUshort(float value)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double intermediate = RemapToDouble(value);
return RemapFromDouble(intermediate, type);
return (ushort)MathUtils.MapRange(GetMin<float>(), GetMax<float>(), ushort.MinValue, ushort.MaxValue, value);
}
/// <summary>
/// Clamps the given numeric value to the defined min/max then remaps to the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public object ClampMinMaxThenRemap(object value, Type type)
public ushort RemapRangeToUshort(int value)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double min = Convert.ToDouble(Min);
double max = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double clamped = MathUtils.Clamp(doubleValue, min, max);
object remapped = RemapMinMax(clamped, type);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
return (ushort)MathUtils.MapRange(GetMin<int>(), GetMax<int>(), ushort.MinValue, ushort.MaxValue, value);
}
/// <summary>
/// Remaps the given numeric value to the defined min/max.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public object RemapMinMax(object value)
public ushort RemapRangeToUshort(ushort value)
{
if (value == null)
throw new ArgumentNullException("value");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double sourceMin = GetMinAsDouble(value.GetType());
double sourceMax = GetMaxAsDouble(value.GetType());
double targetMin = Convert.ToDouble(Min);
double targetMax = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
private object RemapMinMax(object value, Type type)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double sourceMin = Convert.ToDouble(Min);
double sourceMax = Convert.ToDouble(Max);
double targetMin = GetMinAsDouble(type);
double targetMax = GetMaxAsDouble(type);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, type, CultureInfo.InvariantCulture);
return MathUtils.MapRange(GetMin<ushort>(), GetMax<ushort>(), ushort.MinValue, ushort.MaxValue, value);
}
#endregion
#region Private Methods
#region UShort -> Range
/// <summary>
/// Gets the min value for the given numeric type as a double.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMinAsDouble(Type type)
public object RemapUshortToRange(ushort value)
{
if (type == null)
throw new ArgumentNullException("type");
if (Min is ushort)
{
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<ushort>(), GetMax<ushort>(), value);
}
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (Min is short)
{
var castVal = (short)value;
return (short)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<short>(), GetMax<short>(), castVal);
}
double min;
if (!s_MinAsDouble.TryGetValue(type, out min))
throw new NotSupportedException("Type is not supported.");
if (Min is uint)
{
var castVal = (uint)value;
return (uint)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<uint>(), GetMax<uint>(), castVal);
}
return min;
if (Min is int)
{
var castVal = (int)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<int>(), GetMax<int>(), castVal);
}
if (Min is ulong)
{
var castVal = (ulong)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<ulong>(), GetMax<ulong>(), castVal);
}
if (Min is long)
{
var castVal = (long)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<long>(), GetMax<long>(), castVal);
}
if (Min is float)
{
var castVal = (float)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<float>(), GetMax<float>(), castVal);
}
if (Min is double)
{
var castVal = (double)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<double>(), GetMax<double>(), castVal);
}
if (Min is decimal)
{
var castVal = (decimal)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<decimal>(), GetMax<decimal>(), castVal);
}
if (Min is byte)
{
var castVal = (byte)value;
return (byte)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<byte>(), GetMax<byte>(), castVal);
}
throw new NotSupportedException("Value type of range attribute is not supported.");
}
/// <summary>
/// Gets the max value for the given numeric type as a double.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMaxAsDouble(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
double max;
if (!s_MaxAsDouble.TryGetValue(type, out max))
throw new NotSupportedException("Type is not supported.");
return max;
}
#endregion
#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

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
@@ -328,6 +329,18 @@ namespace ICD.Common.Utils.Collections
return m_Dict.Remove(item);
}
/// <summary>
/// Removes each of the items in the sequence from the collection.
/// </summary>
/// <param name="items"></param>
public void RemoveRange(IEnumerable<T> items)
{
if (items == null)
throw new ArgumentNullException("items");
m_Dict.RemoveAll(items);
}
#endregion
#region IEnumerable<T>

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

Binary file not shown.

View File

@@ -19,6 +19,9 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (keys == null)
throw new ArgumentNullException("keys");
foreach (TKey key in keys)
extends.Remove(key);
}
@@ -112,7 +115,6 @@ namespace ICD.Common.Utils.Extensions
/// <param name="key"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
[CanBeNull]
[PublicAPI]
public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key,
TValue defaultValue)
@@ -130,6 +132,35 @@ namespace ICD.Common.Utils.Extensions
return value;
}
/// <summary>
/// If the key is present in the dictionary return the value, otherwise add a new value to the dictionary and return it.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="key"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
where TValue : new()
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
TValue value;
if (!extends.TryGetValue(key, out value))
{
value = ReflectionUtils.CreateInstance<TValue>();
extends.Add(key, value);
}
return value;
}
/// <summary>
/// Gets a key for the given value.
/// </summary>

View File

@@ -1,374 +0,0 @@
using System;
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
{
/// <summary>
/// Writes the object value.
/// </summary>
/// <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)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (serializer == null)
throw new ArgumentNullException("serializer");
if (converter == null)
throw new ArgumentNullException("converter");
JObject jObject = JObject.FromObject(value, serializer);
jObject.WriteTo(extends, converter);
}
/// <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;
}
// 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>
/// Gets the current value as a Type.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Type GetValueAsType(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string value = extends.GetValueAsString();
return Type.GetType(value);
}
/// <summary>
/// Gets the current value as an unsigned integer.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// Gets the current value as an integer.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static int GetValueAsInt(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Integer)
return (int)(long)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a string.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static string GetValueAsString(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.String || extends.TokenType == JsonToken.Null)
return extends.Value as string;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.String);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a bool.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static bool GetValueAsBool(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Boolean)
return (bool)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Boolean);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as an enum.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static T GetValueAsEnum<T>(this JsonReader extends)
where T : struct, IConvertible
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.String)
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
return (T)(object)extends.GetValueAsInt();
}
/// <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();
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
return extends.DeserializeArray(reader, (s, r) => extends.Deserialize<TItem>(reader));
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
if (read == null)
throw new ArgumentNullException("read");
if (reader.TokenType == JsonToken.Null)
return Enumerable.Empty<TItem>();
if (reader.TokenType != JsonToken.StartArray)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
return DeserializeArrayIterator(extends, reader, read);
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="serializer"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
{
// Step into the first value
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
TItem output = read(serializer, reader);
yield return output;
// Read out of the last value
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,217 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Json;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{
/// <summary>
/// Extension methods for working with JSON.
/// </summary>
public static class JsonReaderExtensions
{
/// <summary>
/// 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="serializer"></param>
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends, JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (serializer == null)
throw new ArgumentNullException("serializer");
return serializer.Deserialize<T>(extends);
}
/// <summary>
/// Reads through the current object token and calls the callback for each property value.
/// </summary>
/// <param name="extends"></param>
/// <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 (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);
}
}
/// <summary>
/// Gets the current value as a Type.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Type GetValueAsType(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string value = extends.GetValueAsString();
return Type.GetType(value);
}
/// <summary>
/// Gets the current value as an unsigned integer.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// Gets the current value as an integer.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static int GetValueAsInt(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Integer)
return (int)(long)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a string.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static string GetValueAsString(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Value == null ? null : extends.Value.ToString();
}
/// <summary>
/// Gets the current value as a bool.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static bool GetValueAsBool(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Boolean)
return (bool)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Boolean);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as an enum.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static T GetValueAsEnum<T>(this JsonReader extends)
where T : struct, IConvertible
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.String)
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
return (T)(object)extends.GetValueAsInt();
}
/// <summary>
/// Gets the current value as a guid.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Guid GetValueAsGuid(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string stringValue = extends.GetValueAsString();
return new Guid(stringValue);
}
/// <summary>
/// Gets the current value as a date.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static DateTime GetValueAsDateTime(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string stringValue = extends.GetValueAsString();
return JsonUtils.ParseDateTime(stringValue);
}
}
}

View File

@@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{
public static class JsonSerializerExtensions
{
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader)
{
return extends.DeserializeArray(reader, (s, r) => extends.Deserialize<TItem>(reader));
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
if (read == null)
throw new ArgumentNullException("read");
if (reader.TokenType == JsonToken.Null)
return Enumerable.Empty<TItem>();
if (reader.TokenType != JsonToken.StartArray)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
// ToArray to ensure everything gets read before moving onto the next token
return DeserializeArrayIterator(extends, reader, read).ToArray();
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="serializer"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
{
// Step into the first value
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
TItem output = read(serializer, reader);
yield return output;
// Step out of the value
reader.Read();
}
}
/// <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>> DeserializeDict<TKey, TValue>(this JsonSerializer extends,
JsonReader reader)
{
return extends.DeserializeDict<TKey, TValue>(reader, p => (TKey)Convert.ChangeType(p, typeof(TKey), CultureInfo.InvariantCulture));
}
/// <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>
/// <param name="getKey"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(this JsonSerializer extends,
JsonReader reader,
Func<string, TKey> getKey)
{
return extends.DeserializeDict(reader, getKey, (s, r) => extends.Deserialize<TValue>(reader));
}
/// <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>
/// <param name="getKey"></param>
/// <param name="readValue"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(this JsonSerializer extends,
JsonReader reader,
Func<string, TKey> getKey,
Func<JsonSerializer, JsonReader,
TValue> readValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
if (getKey == null)
throw new ArgumentNullException("getKey");
if (readValue == null)
throw new ArgumentNullException("readValue");
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));
// ToArray to ensure everything gets read before moving onto the next token
return DeserializeDictIterator(extends, reader, getKey, readValue).ToArray();
}
/// <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>
/// <param name="getKey"></param>
/// <param name="readValue"></param>
private static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictIterator<TKey, TValue>(
JsonSerializer serializer, JsonReader reader,
Func<string, TKey> getKey,
Func<JsonSerializer, JsonReader, TValue> readValue)
{
// Step into the first property
reader.Read();
while (reader.TokenType != JsonToken.EndObject)
{
if (reader.TokenType != JsonToken.PropertyName)
throw new FormatException();
TKey key = getKey((string)reader.Value);
// Step into the value
reader.Read();
TValue value = readValue(serializer, reader);
yield return new KeyValuePair<TKey, TValue>(key, value);
// Step out of the value
reader.Read();
}
}
/// <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)
{
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 (write == null)
throw new ArgumentNullException("write");
if (items == null)
{
writer.WriteNull();
return;
}
writer.WriteStartArray();
{
foreach (TItem item in items)
write(extends, writer, item);
}
writer.WriteEndArray();
}
/// <summary>
/// Serializes the given sequence of items 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 SerializeDict<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items)
{
extends.SerializeDict(writer, items, k => k.ToString());
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="getPropertyName"></param>
public static void SerializeDict<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items,
Func<TKey, string> getPropertyName)
{
extends.SerializeDict(writer, items, getPropertyName, (s, w, v) => s.Serialize(w, v));
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="getPropertyName"></param>
/// <param name="writeValue"></param>
public static void SerializeDict<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items,
Func<TKey, string> getPropertyName,
Action<JsonSerializer, JsonWriter, TValue> writeValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (getPropertyName == null)
throw new ArgumentNullException("getPropertyName");
if (writeValue == null)
throw new ArgumentNullException("writeValue");
if (items == null)
{
writer.WriteNull();
return;
}
writer.WriteStartObject();
{
foreach (KeyValuePair<TKey, TValue> kvp in items)
{
string propertyName = getPropertyName(kvp.Key);
writer.WritePropertyName(propertyName);
writeValue(extends, writer, kvp.Value);
}
}
writer.WriteEndObject();
}
}
}

View File

@@ -0,0 +1,135 @@
using System;
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>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, object value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, string value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, DateTime value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, bool value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, int value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, Guid value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, Type value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteType(value);
}
}
}

View File

@@ -43,5 +43,26 @@ namespace ICD.Common.Utils.Extensions
item = extends.Dequeue();
return true;
}
/// <summary>
/// Peeks the next item in the queue. Returns false if the queue is empty.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="item"></param>
/// <returns></returns>
public static bool Peek<T>(this Queue<T> extends, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
item = default(T);
if (extends.Count == 0)
return false;
item = extends.Peek();
return true;
}
}
}

View File

@@ -21,7 +21,6 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributes<T>(this ICustomAttributeProvider extends)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -59,7 +58,6 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
public static T GetCustomAttribute<T>(this ICustomAttributeProvider extends)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -75,7 +73,6 @@ namespace ICD.Common.Utils.Extensions
/// <param name="inherits"></param>
/// <returns></returns>
public static T GetCustomAttribute<T>(this ICustomAttributeProvider extends, bool inherits)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");

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,127 @@ 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[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[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;
foreach (char current in fullyQualifiedTypeName)
{
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>

File diff suppressed because it is too large Load Diff

View File

@@ -39,11 +39,16 @@
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
<PackageReference Include="Microsoft.Data.SQLite" Version="2.2.3" />
<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>
<ItemGroup>
<None Update="CultureInfo.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -78,7 +78,6 @@
<Compile Include="Attributes\IIcdAttribute.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" />
@@ -103,22 +102,29 @@
<Compile Include="EventArguments\StringEventArgs.cs" />
<Compile Include="EventArguments\UShortEventArgs.cs" />
<Compile Include="EventArguments\XmlRecursionEventArgs.cs" />
<None Include="CultureInfo.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ObfuscationSettings.cs" />
<Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\DayOfWeekExtensions.cs" />
<Compile Include="Extensions\JsonSerializerExtensions.cs" />
<Compile Include="Extensions\JsonWriterExtensions.cs" />
<Compile Include="Extensions\ListExtensions.cs" />
<Compile Include="Comparers\PredicateEqualityComparer.cs" />
<Compile Include="Extensions\MethodInfoExtensions.cs" />
<Compile Include="Extensions\ParameterInfoExtensions.cs" />
<Compile Include="Extensions\UriExtensions.cs" />
<Compile Include="Extensions\UShortExtensions.cs" />
<Compile Include="Globalization\IcdCultureInfo.cs" />
<Compile Include="GuidUtils.cs" />
<Compile Include="IcdUriBuilder.cs" />
<Compile Include="IO\Compression\IcdZipEntry.cs" />
<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" />
@@ -156,7 +162,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" />
@@ -187,6 +193,7 @@
<Compile Include="SafeCriticalSection.SimplSharp.cs" />
<Compile Include="SafeCriticalSection.Standard.cs" />
<Compile Include="SafeMutex.cs" />
<Compile Include="SPlusUtils.cs" />
<Compile Include="Sqlite\eDbType.cs" />
<Compile Include="Sqlite\IcdDbDataReader.cs" />
<Compile Include="Sqlite\IcdSqliteCommand.cs" />
@@ -204,6 +211,7 @@
<Compile Include="Timers\Repeater.cs" />
<Compile Include="Timers\SafeTimer.cs" />
<Compile Include="TryUtils.cs" />
<Compile Include="UriQueryBuilder.cs" />
<Compile Include="UriUtils.cs" />
<Compile Include="Xml\AbstractGenericXmlConverter.cs" />
<Compile Include="Xml\AbstractXmlConverter.cs" />

View File

@@ -17,5 +17,10 @@ namespace ICD.Common.Utils.IO
public IcdStreamWriter(StreamWriter baseStreamWriter) : base(baseStreamWriter)
{
}
public void WriteLine(string value)
{
WrappedStreamWriter.WriteLine(value);
}
}
}

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

@@ -66,7 +66,7 @@ namespace ICD.Common.Utils
/// Constructor.
/// </summary>
public IcdUriBuilder()
: this((Uri)null)
: this(new Uri("http://localhost/"))
{
}
@@ -86,7 +86,7 @@ namespace ICD.Common.Utils
public IcdUriBuilder(Uri uri)
{
if (uri == null)
return;
throw new ArgumentNullException("uri");
if (!uri.IsAbsoluteUri)
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
@@ -107,20 +107,20 @@ namespace ICD.Common.Utils
/// <returns></returns>
public override string ToString()
{
// URI = scheme:[//authority]path[?query][#fragment]
// URI = [scheme://][authority]path[?query][#fragment]
// authority = [userinfo@]host[:port]
// userinfo = username[:password]
StringBuilder builder = new StringBuilder();
// Scheme
string scheme = string.IsNullOrEmpty(Scheme) ? Uri.UriSchemeHttp : Scheme;
builder.Append(scheme);
builder.Append(':');
if (!string.IsNullOrEmpty(Scheme))
{
builder.Append(Scheme);
builder.Append("://");
}
// Authority
builder.Append("//");
if (!string.IsNullOrEmpty(UserName))
{
builder.Append(UserName);
@@ -134,8 +134,7 @@ namespace ICD.Common.Utils
builder.Append('@');
}
string host = string.IsNullOrEmpty(Host) ? "localhost" : Host;
builder.Append(host);
builder.Append(Host);
if (Port != 0)
{

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,12 +48,25 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
{
writer.WriteNull();
return;
}
WriteObject(writer, value, serializer);
}
/// <summary>
/// Override to write the object value to the writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
protected virtual void WriteObject(JsonWriter writer, T value, JsonSerializer serializer)
{
writer.WriteStartObject();
{
WriteProperties(writer, value, serializer);
@@ -92,7 +103,10 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
return ReadJson(reader, (T)existingValue, serializer);
// Casting null blows up struct casts
T cast = (T)(existingValue ?? default(T));
return ReadJson(reader, cast, serializer);
}
/// <summary>
@@ -113,30 +127,26 @@ 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();
}
return ReadObject(reader, serializer);
}
// Get the property
if (reader.TokenType != JsonToken.PropertyName)
continue;
string property = (string)reader.Value;
/// <summary>
/// Override to handle deserialization of the current StartObject token.
/// </summary>
/// <param name="reader"></param>
/// <param name="serializer"></param>
/// <returns></returns>
protected virtual T ReadObject(JsonReader reader, JsonSerializer serializer)
{
T output = Instantiate();
// 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

@@ -18,28 +18,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 data as a DateTime value.
/// </summary>
@@ -93,31 +71,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>
@@ -240,6 +193,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.
@@ -323,44 +292,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

@@ -138,6 +138,42 @@ namespace ICD.Common.Utils
return (ushort)MapRange((double)inputStart, inputEnd, outputStart, outputEnd, value);
}
/// <summary>
/// Returns the value after the input range has been mapped to a new range
/// </summary>
/// <param name="inputStart">Input start.</param>
/// <param name="inputEnd">Input end.</param>
/// <param name="outputStart">Output start.</param>
/// <param name="outputEnd">Output end.</param>
/// <param name="value">Value.</param>
/// <returns>The newly mapped value</returns>
public static ulong MapRange(ulong inputStart, ulong inputEnd, ulong outputStart, ulong outputEnd, ulong value)
{
if (inputStart.Equals(inputEnd))
throw new DivideByZeroException();
ulong slope = (outputEnd - outputStart) / (inputEnd - inputStart);
return outputStart + slope * (value - inputStart);
}
/// <summary>
/// Returns the value after the input range has been mapped to a new range
/// </summary>
/// <param name="inputStart">Input start.</param>
/// <param name="inputEnd">Input end.</param>
/// <param name="outputStart">Output start.</param>
/// <param name="outputEnd">Output end.</param>
/// <param name="value">Value.</param>
/// <returns>The newly mapped value</returns>
public static long MapRange(long inputStart, long inputEnd, long outputStart, long outputEnd, long value)
{
if (inputStart.Equals(inputEnd))
throw new DivideByZeroException();
long slope = (outputEnd - outputStart) / (inputEnd - inputStart);
return outputStart + slope * (value - inputStart);
}
/// <summary>
/// Maps the date in the given range to the float range 0.0f to 1.0f.
/// 0.5f - The date is half way between the end points.

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
@@ -57,23 +58,29 @@ namespace ICD.Common.Utils
/// </summary>
/// <value></value>
[PublicAPI]
public static string ProgramConfigPath
{
get
{
return Join(RootConfigPath, ProgramConfigDirectory);
}
}
public static string ProgramConfigPath { get { return Join(RootConfigPath, ProgramConfigDirectory); } }
/// <summary>
/// Returns the name of the program config directory.
/// </summary>
[PublicAPI]
public static string ProgramConfigDirectory
{
get
{
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
return "ProgramConfig";
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.Standard:
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
return "ProgramConfig";
default:
throw new ArgumentOutOfRangeException();
}
}
}
@@ -98,6 +105,37 @@ namespace ICD.Common.Utils
[PublicAPI]
public static string ProgramLibPath { get { return Join(ProgramConfigPath, "Lib"); } }
/// <summary>
/// Returns the absolute path to the logs directory.
/// </summary>
/// <value></value>
[PublicAPI]
public static string ProgramLogsPath
{
get
{
string directoryName;
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.Standard:
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
directoryName = "ProgramLogs";
break;
default:
throw new ArgumentOutOfRangeException();
}
return Join(RootConfigPath, directoryName);
}
}
#endregion
#region Methods

View File

@@ -55,11 +55,15 @@ namespace ICD.Common.Utils
{
get
{
Regex regex = new Regex(VER_REGEX);
Match match = regex.Match(VersionResult);
string versionResult = VersionResult;
if (!String.IsNullOrEmpty(versionResult))
{
Regex regex = new Regex(VER_REGEX);
Match match = regex.Match(versionResult);
if (match.Success)
return match.Groups["model"].Value;
if (match.Success)
return match.Groups["model"].Value;
}
ServiceProvider.TryGetService<ILoggerService>()
.AddEntry(eSeverity.Warning, "Unable to get model name from \"{0}\"", VersionResult);
@@ -75,11 +79,15 @@ namespace ICD.Common.Utils
{
get
{
Regex regex = new Regex(VER_REGEX);
Match match = regex.Match(VersionResult);
string versionResult = VersionResult;
if (!String.IsNullOrEmpty(versionResult))
{
Regex regex = new Regex(VER_REGEX);
Match match = regex.Match(VersionResult);
if (match.Success)
return new Version(match.Groups["version"].Value);
if (match.Success)
return new Version(match.Groups["version"].Value);
}
ServiceProvider.TryGetService<ILoggerService>()
.AddEntry(eSeverity.Warning, "Unable to get model version from \"{0}\"", VersionResult);

View File

@@ -4,4 +4,4 @@ using System.Reflection;
[assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("8.7.0.0")]
[assembly: AssemblyVersion("9.5.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

@@ -220,6 +220,9 @@ namespace ICD.Common.Utils
if (parameters == null)
throw new ArgumentNullException("parameters");
if (parameters.Length == 0)
return Activator.CreateInstance(type);
ConstructorInfo constructor = GetConstructor(type, parameters);
try
@@ -337,8 +340,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 +356,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

@@ -0,0 +1,23 @@
using ICD.Common.Properties;
namespace ICD.Common.Utils
{
/// <summary>
/// Static class containing useful utilities for use in S+ programs
/// </summary>
[PublicAPI("S+")]
public static class SPlusUtils
{
/// <summary>
/// Convert two ushort's to an int
/// </summary>
/// <param name="lowWord">ushort for the least significant 16 bits</param>
/// <param name="highWord">ushort for the most significant 1 bits</param>
/// <returns></returns>
[PublicAPI("S+")]
public static int ConvertToInt(ushort lowWord, ushort highWord)
{
return (highWord << 16) + lowWord;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Text;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Services.Logging
@@ -21,11 +20,6 @@ namespace ICD.Common.Utils.Services.Logging
[PublicAPI]
public DateTime Timestamp { get { return m_Timestamp; } }
/// <summary>
/// Gets the log time in local time.
/// </summary>
public DateTime LocalTimestamp { get { return Timestamp.ToLocalTime(); } }
/// <summary>
/// Get/Set for severity level.
/// </summary>
@@ -56,24 +50,6 @@ namespace ICD.Common.Utils.Services.Logging
#region Methods
/// <summary>
/// Return the text format to send to Fusion
/// </summary>
/// <returns>text format for fusion, including timestamp, severity, and message</returns>
[PublicAPI]
public string GetFusionLogText()
{
StringBuilder s = new StringBuilder();
s.Append(Timestamp.ToString("yyyyMMddHHmmss"));
s.Append("||");
s.Append((int)Severity);
s.Append("||");
s.Append(Message);
return s.ToString();
}
/// <summary>
/// Implementing default equality.
/// </summary>

View File

@@ -32,8 +32,8 @@ namespace ICD.Common.Utils.Sqlite
/// <param name="path"></param>
public static void CreateFile(string path)
{
IcdFileStream fs = IcdFile.Create(path);
fs.Close();
using (IcdFileStream fs = IcdFile.Create(path))
fs.Close();
}
/// <summary>

View File

@@ -34,5 +34,30 @@ namespace ICD.Common.Utils.Sqlite
{
return m_Reader.Read();
}
public int GetInt32(int ordinal)
{
return m_Reader.GetInt32(ordinal);
}
public bool GetBoolean(int ordinal)
{
return m_Reader.GetBoolean(ordinal);
}
public string GetString(int ordinal)
{
return m_Reader.GetString(ordinal);
}
public int GetOrdinal(string name)
{
return m_Reader.GetOrdinal(name);
}
public void Close()
{
m_Reader.Close();
}
}
}

View File

@@ -6,22 +6,27 @@ using Microsoft.Data.Sqlite;
namespace ICD.Common.Utils.Sqlite
{
public sealed class IcdSqliteParameterCollection
{
private readonly SqliteParameterCollection m_Parameters;
public sealed class IcdSqliteParameterCollection
{
private readonly SqliteParameterCollection m_Parameters;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="commandParameters"></param>
public IcdSqliteParameterCollection(SqliteParameterCollection commandParameters)
public IcdSqliteParameterCollection(SqliteParameterCollection commandParameters)
{
m_Parameters = commandParameters;
}
public IcdSqliteParameter Add(string name, eDbType type)
{
{
return new IcdSqliteParameter(m_Parameters.Add(name, type.ToParamType()));
}
}
}
public void AddWithValue(string parameterName, object value)
{
m_Parameters.AddWithValue(parameterName, value);
}
}
}

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

@@ -67,8 +67,8 @@ namespace ICD.Common.Utils.Timers
/// <returns></returns>
public static SafeTimer Stopped(Action callback)
{
//No due time or repeat period on a stopped timer
SafeTimer output = new SafeTimer(callback, -1, -1);
// Some arbitrarily large number that shouldn't timeout before we call stop.
SafeTimer output = new SafeTimer(callback, 100 * 1000, 100 * 1000);
output.Stop();
return output;
}
@@ -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

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Text;
namespace ICD.Common.Utils
{
public sealed class UriQueryBuilder
{
private readonly Dictionary<string, string> m_Parameters;
public UriQueryBuilder()
{
m_Parameters = new Dictionary<string, string>();
}
public UriQueryBuilder Append(string key, string value)
{
m_Parameters.Add(key, value);
return this;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder("?");
bool first = true;
foreach (KeyValuePair<string, string> kvp in m_Parameters)
{
if (!first)
builder.Append('&');
first = false;
builder.Append(kvp.Key);
builder.Append('=');
builder.Append(kvp.Value);
}
return builder.ToString();
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
@@ -72,7 +71,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>
@@ -208,37 +212,127 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("type");
if (type == typeof(bool))
return bool.Parse(value);
return ToBool(value);
if (type == typeof(byte))
return byte.Parse(value);
return ToByte(value);
if (type == typeof(decimal))
return decimal.Parse(value);
return ToDecimal(value);
if (type == typeof(char))
return value.Single();
return ToChar(value);
if (type == typeof(double))
return double.Parse(value);
return ToDouble(value);
if (type == typeof(Guid))
return new Guid(value);
return ToGuid(value);
if (type == typeof(float))
return float.Parse(value);
return ToSingle(value);
if (type == typeof(int))
return int.Parse((value));
return ToInt32(value);
if (type == typeof(long))
return long.Parse(value);
return ToInt64(value);
if (type == typeof(sbyte))
return sbyte.Parse(value);
return ToSByte(value);
if (type == typeof(short))
return short.Parse(value);
return ToInt16(value);
if (type == typeof(TimeSpan))
return TimeSpan.Parse(value);
return ToTimeSpan(value);
if (type == typeof(uint))
return uint.Parse(value);
return ToUInt32(value);
if (type == typeof(ulong))
return ulong.Parse(value);
return ToUInt64(value);
if (type == typeof(ushort))
return ushort.Parse(value);
return ToUInt16(value);
return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
}
public static bool ToBool(string data)
{
return XmlConvert.ToBoolean(data);
}
public static byte ToByte(string data)
{
return XmlConvert.ToByte(data);
}
public static char ToChar(string data)
{
return XmlConvert.ToChar(data);
}
public static DateTime ToDateTime(string data, string format)
{
return XmlConvert.ToDateTime(data, format);
}
public static DateTime ToDateTime(string data, string[] formats)
{
return XmlConvert.ToDateTime(data, formats);
}
public static DateTime ToDateTime(string data, XmlDateTimeSerializationMode dateTimeOption)
{
return XmlConvert.ToDateTime(data, dateTimeOption);
}
public static decimal ToDecimal(string data)
{
return XmlConvert.ToDecimal(data);
}
public static double ToDouble(string data)
{
return XmlConvert.ToDouble(data);
}
public static Guid ToGuid(string data)
{
return XmlConvert.ToGuid(data);
}
public static short ToInt16(string data)
{
return XmlConvert.ToInt16(data);
}
public static int ToInt32(string data)
{
return XmlConvert.ToInt32(data);
}
public static long ToInt64(string data)
{
return XmlConvert.ToInt64(data);
}
public static sbyte ToSByte(string data)
{
return XmlConvert.ToSByte(data);
}
public static float ToSingle(string data)
{
return XmlConvert.ToSingle(data);
}
public static TimeSpan ToTimeSpan(string data)
{
return XmlConvert.ToTimeSpan(data);
}
public static ushort ToUInt16(string data)
{
return XmlConvert.ToUInt16(data);
}
public static uint ToUInt32(string data)
{
return XmlConvert.ToUInt32(data);
}
public static ulong ToUInt64(string data)
{
return XmlConvert.ToUInt64(data);
}
}
}

View File

@@ -9,12 +9,8 @@ namespace ICD.Common.Utils.Xml
{
public sealed class IcdXmlException : Exception
{
private readonly int m_LineNumber;
private readonly int m_LinePosition;
public int LineNumber { get { return m_LineNumber; } }
public int LinePosition { get { return m_LinePosition; } }
private int m_LineNumber;
private int m_LinePosition;
/// <summary>
/// Constructor.

View File

@@ -349,7 +349,7 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
return uint.Parse(content);
return IcdXmlConvert.ToUInt32(content);
}
/// <summary>
@@ -364,7 +364,7 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
return int.Parse(content);
return IcdXmlConvert.ToInt32(content);
}
/// <summary>
@@ -379,7 +379,7 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
return ushort.Parse(content);
return IcdXmlConvert.ToUInt16(content);
}
/// <summary>
@@ -394,7 +394,33 @@ namespace ICD.Common.Utils.Xml
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
return short.Parse(content);
return IcdXmlConvert.ToInt16(content);
}
/// <summary>
/// Parses the element content as a short.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static TimeSpan ReadElementContentAsTimeSpan(this IcdXmlReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
try
{
// 00:00:00 format
return TimeSpan.Parse(content);
}
catch (FormatException)
{
}
// PT0S format
return IcdXmlConvert.ToTimeSpan(content);
}
/// <summary>

View File

@@ -458,6 +458,19 @@ namespace ICD.Common.Utils.Xml
return reader.ReadElementContentAsByte();
}
/// <summary>
/// Gets the content of an immediate child.
/// </summary>
/// <param name="xml"></param>
/// <param name="childElement"></param>
/// <returns></returns>
[PublicAPI]
public static TimeSpan? ReadChildElementContentAsTimeSpan(string xml, string childElement)
{
using (IcdXmlReader reader = GetChildElement(xml, childElement))
return reader.ReadElementContentAsTimeSpan();
}
/// <summary>
/// Gets the content of an immediate child.
/// </summary>
@@ -663,6 +676,25 @@ namespace ICD.Common.Utils.Xml
}
}
/// <summary>
/// Gets the content of the immediate child. Returns null if the child element was not found.
/// </summary>
/// <param name="xml"></param>
/// <param name="childElement"></param>
/// <returns></returns>
[PublicAPI]
public static TimeSpan? TryReadChildElementContentAsTimeSpan(string xml, string childElement)
{
try
{
return ReadChildElementContentAsTimeSpan(xml, childElement);
}
catch (FormatException)
{
return null;
}
}
/// <summary>
/// Gets the content of the immediate child. Returns default if the child element could not be parsed.
/// </summary>
@@ -940,6 +972,22 @@ namespace ICD.Common.Utils.Xml
return ReadDictFromXml(xml, rootElement, childElement, keyElement, valueElement, readKey, readValue);
}
/// <summary>
/// Calls childElementCallback for each item in the list.
/// </summary>
/// <param name="xml"></param>
/// <param name="rootElement"></param>
/// <param name="childElement"></param>
/// <param name="readChild"></param>
public static IEnumerable<T> ReadListFromXml<T>(string xml, string childElement, Func<string, T> readChild)
{
if (readChild == null)
throw new ArgumentNullException("readChild");
foreach (string child in GetChildElementsAsString(xml, childElement))
yield return readChild(child);
}
/// <summary>
/// Calls childElementCallback for each item in the list.
/// </summary>
@@ -956,10 +1004,22 @@ namespace ICD.Common.Utils.Xml
if (!TryGetChildElementAsString(xml, rootElement, out xml))
yield break;
foreach (string child in GetChildElementsAsString(xml, childElement))
yield return readChild(child);
foreach (T child in ReadListFromXml(xml, childElement, readChild))
yield return child;
}
/// <summary>
/// Deserializes the xml to a list.
/// </summary>
/// <param name="xml"></param>
/// <param name="rootElement"></param>
/// <param name="childElement"></param>
public static IEnumerable<T> ReadListFromXml<T>(string xml, string childElement)
{
Func<string, T> readChild = IcdXmlConvert.DeserializeObject<T>;
return ReadListFromXml(xml, childElement, readChild);
}
/// <summary>
/// Deserializes the xml to a list.