mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-24 18:05:00 +00:00
Compare commits
287 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab58ba721e | ||
|
|
d7bfb07c2c | ||
|
|
736c2aee33 | ||
|
|
5756d9654b | ||
|
|
f60de4321b | ||
|
|
524cdc3e31 | ||
|
|
1d6e8d55ca | ||
|
|
3ba5f11587 | ||
|
|
fb7a6e27b8 | ||
|
|
fffc8e4a60 | ||
|
|
1d7ada87c5 | ||
|
|
3a4438ec1e | ||
|
|
21ce86de0f | ||
|
|
3cb29452a2 | ||
|
|
114a5e4ec7 | ||
|
|
a9d411dcfd | ||
|
|
e708ef9603 | ||
|
|
aae4fb6185 | ||
|
|
8401ab1852 | ||
|
|
27760f2282 | ||
|
|
80a0ca26c5 | ||
|
|
b9ec2f3222 | ||
|
|
a6ad52d492 | ||
|
|
64e11db0bc | ||
|
|
d01e9345d5 | ||
|
|
466dc6deb5 | ||
|
|
ad47c890a8 | ||
|
|
108317212c | ||
|
|
4a411e8990 | ||
|
|
4cd28a8a12 | ||
|
|
470c35cab7 | ||
|
|
a372d97868 | ||
|
|
f208ec521b | ||
|
|
347cf70b6c | ||
|
|
1076795fae | ||
|
|
7d059bc605 | ||
|
|
dd270adf9e | ||
|
|
efb5e014ec | ||
|
|
083280cc64 | ||
|
|
e222ce424b | ||
|
|
ad7176506d | ||
|
|
e4e3e9b91a | ||
|
|
1721116908 | ||
|
|
d8489d31b6 | ||
|
|
a8552ea0e3 | ||
|
|
e700650735 | ||
|
|
ed9c017856 | ||
|
|
0ee1b440ee | ||
|
|
4184f85826 | ||
|
|
42cc8c2cfc | ||
|
|
6b37db3530 | ||
|
|
ad8fa216a5 | ||
|
|
ef415bb20f | ||
|
|
fe614ae8ad | ||
|
|
2d4bc57ed8 | ||
|
|
c048c4fc65 | ||
|
|
2e2eebd95f | ||
|
|
e21830a7d5 | ||
|
|
f53607018c | ||
|
|
e0176741d2 | ||
|
|
742b7e99aa | ||
|
|
cf0c71d39b | ||
|
|
376f9c0837 | ||
|
|
8b1c53ebe1 | ||
|
|
2e0b0da87f | ||
|
|
8311fe5c9d | ||
|
|
fb41d76a9c | ||
|
|
8f107aa209 | ||
|
|
2024dc0171 | ||
|
|
923866dbdf | ||
|
|
a14b4a5803 | ||
|
|
fac2610b83 | ||
|
|
0ef0286a9f | ||
|
|
d084553600 | ||
|
|
2b14a6b65c | ||
|
|
043a50669a | ||
|
|
b58220d3c7 | ||
|
|
03de74a494 | ||
|
|
6eacc21e45 | ||
|
|
db14eb1dde | ||
|
|
24e665de84 | ||
|
|
7e8618d3e5 | ||
|
|
e44a18b111 | ||
|
|
5cec0e10f1 | ||
|
|
e01bc9ede6 | ||
|
|
9e6c7ca6b3 | ||
|
|
2a1d58f2ae | ||
|
|
51dcb41cdf | ||
|
|
11cff4f5bb | ||
|
|
7c29fb72d9 | ||
|
|
fc234af43a | ||
|
|
5afc676fbe | ||
|
|
ba8c1d98a1 | ||
|
|
63af420710 | ||
|
|
25109163fb | ||
|
|
91f64a4fb1 | ||
|
|
78a3373592 | ||
|
|
14cce04c12 | ||
|
|
d3d1dae2e1 | ||
|
|
e9063682ef | ||
|
|
699c734389 | ||
|
|
548220ba0e | ||
|
|
692da3253f | ||
|
|
85ab631ef5 | ||
|
|
23a068d0c9 | ||
|
|
b015c3a3d3 | ||
|
|
566884167f | ||
|
|
015ffb2c35 | ||
|
|
6540ab5177 | ||
|
|
e01e41c449 | ||
|
|
8a260e0475 | ||
|
|
381355beb9 | ||
|
|
2a25c3d733 | ||
|
|
6b8fc19eb3 | ||
|
|
c3d73e1091 | ||
|
|
1f5625218f | ||
|
|
588badbf58 | ||
|
|
e0fae0b112 | ||
|
|
315bd703c7 | ||
|
|
32aae27a7f | ||
|
|
db15ea24d9 | ||
|
|
667c1cdd21 | ||
|
|
df30585917 | ||
|
|
c83bd04c8f | ||
|
|
09603b0537 | ||
|
|
d41aa6d111 | ||
|
|
d0740c915b | ||
|
|
9867eae704 | ||
|
|
e1693bc738 | ||
|
|
2c87d8e988 | ||
|
|
deff0d5408 | ||
|
|
0ccf80c613 | ||
|
|
a3e548290f | ||
|
|
12ee533cbb | ||
|
|
2ae8fa9c2d | ||
|
|
7b30730ea0 | ||
|
|
d3f1fe3425 | ||
|
|
48abd75d30 | ||
|
|
0bfd6c4f48 | ||
|
|
c2b980a691 | ||
|
|
ed299b8d4f | ||
|
|
0c35e3225c | ||
|
|
254ed85d83 | ||
|
|
8871fad40a | ||
|
|
2ff7073385 | ||
|
|
957424e5ca | ||
|
|
9cdb590d9f | ||
|
|
ca10f049e5 | ||
|
|
d79c272df0 | ||
|
|
1c9d311422 | ||
|
|
60a19fe7f8 | ||
|
|
54fbd7eaa8 | ||
|
|
fd931b0b53 | ||
|
|
2a222289ca | ||
|
|
e437a9442c | ||
|
|
478261e888 | ||
|
|
09445da1e9 | ||
|
|
eb7f3033fb | ||
|
|
b1b12c76dd | ||
|
|
5e80ddcc16 | ||
|
|
84fa69d3e0 | ||
|
|
86335e0d44 | ||
|
|
03893c2669 | ||
|
|
1353468da6 | ||
|
|
2f78a25420 | ||
|
|
68caebb28b | ||
|
|
425d651eba | ||
|
|
918e6f5b34 | ||
|
|
6a979f5c12 | ||
|
|
8f17d59694 | ||
|
|
80e6fe33c7 | ||
|
|
668994be18 | ||
|
|
1a87ce9f00 | ||
|
|
3129d3e60c | ||
|
|
fd3143ea6c | ||
|
|
591240d973 | ||
|
|
b8225b7842 | ||
|
|
1193c8e3bb | ||
|
|
aa3559cb4e | ||
|
|
a13daa18db | ||
|
|
fa145644d1 | ||
|
|
5f7d1214e9 | ||
|
|
d8d9f342c9 | ||
|
|
aeb2a5d91e | ||
|
|
74c59bd7f3 | ||
|
|
6fb1e53776 | ||
|
|
0c462ad614 | ||
|
|
f2d32fd355 | ||
|
|
4df2ede630 | ||
|
|
8d0b4ca767 | ||
|
|
035289f056 | ||
|
|
929f816398 | ||
|
|
6f69ea7fde | ||
|
|
b597448bdc | ||
|
|
cc9eaca87a | ||
|
|
a78ff6ad80 | ||
|
|
939f361b54 | ||
|
|
6fb290a0ab | ||
|
|
2166596726 | ||
|
|
073c231ef1 | ||
|
|
220e778a76 | ||
|
|
560d3c861d | ||
|
|
25ebb4b43d | ||
|
|
f7dba764d5 | ||
|
|
f2e39566e2 | ||
|
|
b4ef07fd45 | ||
|
|
69b97779d9 | ||
|
|
e8ce1e94cc | ||
|
|
73af42e0f5 | ||
|
|
aa7a924d2b | ||
|
|
3185593977 | ||
|
|
bfd0f30437 | ||
|
|
7b372b5a72 | ||
|
|
dfcdbba05f | ||
|
|
4494192bdf | ||
|
|
bfbbcff7e7 | ||
|
|
d1b8b5a01f | ||
|
|
1070cb00f8 | ||
|
|
a46987aabf | ||
|
|
c25a82399d | ||
|
|
1f7819e536 | ||
|
|
465ac7c42c | ||
|
|
928f8e5e04 | ||
|
|
ae885974da | ||
|
|
21a583a57c | ||
|
|
268e201f49 | ||
|
|
9e2ec3de63 | ||
|
|
8ed856eb98 | ||
|
|
514c0eaec5 | ||
|
|
621d83d8dc | ||
|
|
588e3df86e | ||
|
|
336f39c9c2 | ||
|
|
5d3b80938e | ||
|
|
eb2a77b772 | ||
|
|
1c420d111b | ||
|
|
e1279fb860 | ||
|
|
bf91d9e87f | ||
|
|
1c89ebc5c2 | ||
|
|
2cd744eb1c | ||
|
|
20350c0ab7 | ||
|
|
177e59edb6 | ||
|
|
d8041bd94c | ||
|
|
f2122596b4 | ||
|
|
653bf361ef | ||
|
|
3689517124 | ||
|
|
6fdd5a138e | ||
|
|
fb267465ce | ||
|
|
f818653298 | ||
|
|
f7b5d07c38 | ||
|
|
1a931e6ffb | ||
|
|
2fab5d6fd4 | ||
|
|
8d683f875b | ||
|
|
74c96aac8a | ||
|
|
edc9fa300e | ||
|
|
37a21131d9 | ||
|
|
f674d4c60d | ||
|
|
ce163629f3 | ||
|
|
18b07abb44 | ||
|
|
4bc6258b62 | ||
|
|
086aee8167 | ||
|
|
cc115bafad | ||
|
|
00db478ef6 | ||
|
|
aa3fc3bccc | ||
|
|
2602380100 | ||
|
|
1f023c14d0 | ||
|
|
6ce52b74ac | ||
|
|
c500f1db5d | ||
|
|
da58379dfa | ||
|
|
1ae6a55c7d | ||
|
|
381b19f2e6 | ||
|
|
2f3b1ef57d | ||
|
|
da31fae213 | ||
|
|
e4b0c9f91a | ||
|
|
38dd85d79d | ||
|
|
c306dd6eb9 | ||
|
|
6fa3cc03ad | ||
|
|
eb58e65574 | ||
|
|
f24f9458ca | ||
|
|
79bbbe277f | ||
|
|
c03833bf9d | ||
|
|
f816ae771b | ||
|
|
a178ebe8cb | ||
|
|
3cddc14880 | ||
|
|
088222db47 | ||
|
|
9dcda7271d | ||
|
|
9dca46add5 | ||
|
|
35593bfa47 |
116
CHANGELOG.md
116
CHANGELOG.md
@@ -6,6 +6,122 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [8.3.2] - 2019-05-02
|
||||
### Changed
|
||||
- Fixed PriorityQueue IndexOutOfRange exception when an inner queue becomes depleted
|
||||
|
||||
## [8.3.1] - 2019-04-05
|
||||
### Changed
|
||||
- Fixed FormatException when parsing some JSON DateTimes
|
||||
|
||||
## [8.3.0] - 2019-01-25
|
||||
### Added
|
||||
- Added SimplSharpProMono to eRuntimeEnvironment enum
|
||||
- Added path support for SimplSharpProMono environment
|
||||
- Added GetApplicationRootDirectory for all platforms
|
||||
|
||||
### Changed
|
||||
- Small fixes for better VC4 support
|
||||
|
||||
## [8.2.0] - 2019-01-10
|
||||
### Added
|
||||
- Added TryGetPortForScheme method to UriExtensions
|
||||
- Added range attribute for clarifying numeric fields, properties and parameters
|
||||
|
||||
### Changed
|
||||
- IcdHashSet preserves comparer when an operation creates a new IcdHashSet
|
||||
- Fixed bug where XML fragments on Net Standard were being prepended with a document header
|
||||
|
||||
## [8.1.0] - 2019-01-02
|
||||
### Added
|
||||
- Added GetAttributeAsEnum xml utils method
|
||||
- Adding short parsing methods to XML utils
|
||||
- Added methods to IcdUriBuilder for appending path
|
||||
- Added RegexUtils method for replacing a single group in a match
|
||||
|
||||
## [8.0.0] - 2018-11-20
|
||||
### Added
|
||||
- XML TryGetAttribute methods
|
||||
|
||||
### Changed
|
||||
- Performance improvements when working with xml attributes
|
||||
- Fixed NullReferenceException when writing null strings to CSV
|
||||
- Fixed bug with string formatting console input on Net Standard
|
||||
|
||||
### Removed
|
||||
- Removed IcdXmlAttribute
|
||||
|
||||
## [7.1.0] - 2018-11-08
|
||||
### Added
|
||||
- IcdXmlTextWriter exposes WriteStartDocument and WriteEndDocument
|
||||
- AttributeUtils method for getting properties with the given attribute type
|
||||
|
||||
### Changed
|
||||
- EnumerableExtensions performance improvements
|
||||
- Fixed bug in StringExtensions when removing a sequence of characters from a string
|
||||
|
||||
## [7.0.0] - 2018-10-30
|
||||
### Changed
|
||||
- Micro-optimizations in string and XML manipulations
|
||||
- Significant hashset optimizations
|
||||
- Deprecated NVRAM in favor of USER directory
|
||||
|
||||
## [6.0.0] - 2018-10-18
|
||||
### Added
|
||||
- CsvWriter for creating CSV files + Settings
|
||||
- AppendText method for IcdFile
|
||||
- IcdStreamWriter, a wrapper for a StreamWriter
|
||||
- New XML conversion framework for performance improvements
|
||||
|
||||
### Changed
|
||||
- XmlUtils is now using the improved XML conversion framework
|
||||
- Better implementation of DictionaryExtensions.ToInverse
|
||||
|
||||
## [5.0.0] - 2018-09-14
|
||||
### Added
|
||||
- Stopwatch profiling methods
|
||||
- Attempt to interpret old assembly naming convention when parsing types
|
||||
- Added RegexUtils
|
||||
|
||||
### Changed
|
||||
- Significant performance improvements across the board
|
||||
|
||||
## [4.0.0] - 2018-07-19
|
||||
### Added
|
||||
- Added extension method for getting type name without trailing generic info
|
||||
- Added DateTime extensions for getting next/last time in a sequence
|
||||
- Added action scheduler service
|
||||
- Added IcdTimer methods for profiling event performance
|
||||
|
||||
### Changed
|
||||
- Re-raise base exception from ReflectionUtils.CreateInstance, TargetInvocationException and TypeLoadException don't say much
|
||||
|
||||
## [3.7.0] - 2018-07-02
|
||||
### Added
|
||||
- Adding SequenceComparer for ordering collections of lists, arrays, etc
|
||||
- Added RateLimitedEventQueue collection for throttling events
|
||||
|
||||
### Changed
|
||||
- Potential fix for timer disposal on Net Standard
|
||||
- Added workaround for older RPC servers where the typestring being broadcast would stil include _SimplSharp, now will be stripped
|
||||
- Fixing bug where Timer.Reset() would continue repeating on an interval in Net Standard
|
||||
|
||||
## [3.6.0] - 2018-06-19
|
||||
### Added
|
||||
- Added ZIP features for examining the contents of an archive
|
||||
- Added FileNameEqualityComparer
|
||||
- Added BiDictionary for one-to-one maps
|
||||
|
||||
## [3.5.1] - 2018-06-04
|
||||
### Changed
|
||||
- PriorityQueue indexing fix
|
||||
- Pathfinding optimizations
|
||||
|
||||
## [3.5.0] - 2018-05-24
|
||||
### Added
|
||||
- Added GetFlagsExceptNone parameterless enum method
|
||||
- Added pathfinding methods for determining if a path exists
|
||||
|
||||
## [3.4.0] - 2018-05-23
|
||||
### Added
|
||||
- Added SetEquals method to IcdHashSet
|
||||
|
||||
90
ICD.Common.Utils.Tests/Collections/AsyncEventQueueTest.cs
Normal file
90
ICD.Common.Utils.Tests/Collections/AsyncEventQueueTest.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
219
ICD.Common.Utils.Tests/Collections/BiDictionaryTest.cs
Normal file
219
ICD.Common.Utils.Tests/Collections/BiDictionaryTest.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class BiDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Keys.Count);
|
||||
|
||||
Assert.IsTrue(dict.Keys.Contains(2));
|
||||
Assert.IsTrue(dict.Keys.Contains(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Values.Count);
|
||||
|
||||
Assert.IsTrue(dict.Values.Contains("20"));
|
||||
Assert.IsTrue(dict.Values.Contains("30"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
|
||||
dict.Clear();
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsKey(1));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsValue("10"));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsValue("10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "10"));
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "20"));
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(2, "10"));
|
||||
|
||||
Assert.DoesNotThrow(() => dict.Add(2, "20"));
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
|
||||
Assert.AreEqual(1, dict.GetKey("20"));
|
||||
Assert.AreEqual(3, dict.GetKey("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(1, dict.GetKey("20"));
|
||||
Assert.AreEqual(3, dict.GetKey("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveKey(1);
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
string value;
|
||||
Assert.IsFalse(dict.TryGetValue(1, out value));
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
Assert.AreEqual(null, value);
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetValue(1, out value));
|
||||
Assert.AreEqual("10", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
int value;
|
||||
Assert.IsFalse(dict.TryGetKey("10", out value));
|
||||
Assert.AreEqual(0, value);
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKey("10", out value));
|
||||
Assert.AreEqual(1, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,6 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[TestFixture]
|
||||
public sealed class IcdHashSetTest
|
||||
{
|
||||
[Test, UsedImplicitly]
|
||||
public void NullSetTest()
|
||||
{
|
||||
Assert.AreEqual(0, IcdHashSet<int>.NullSet.Count);
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void CountTest()
|
||||
{
|
||||
|
||||
@@ -168,6 +168,29 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryDequeueTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
queue.Enqueue(10, 1);
|
||||
queue.Enqueue(20, 2);
|
||||
queue.Enqueue(30, 3);
|
||||
|
||||
int output;
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(10, output);
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(20, output);
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(30, output);
|
||||
|
||||
Assert.IsFalse(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(0, output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
|
||||
100
ICD.Common.Utils.Tests/Collections/RateLimitedEventQueueTest.cs
Normal file
100
ICD.Common.Utils.Tests/Collections/RateLimitedEventQueueTest.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class RateLimitedEventQueueTest
|
||||
{
|
||||
[Test]
|
||||
public void ItemDequeuedFeedbackTest()
|
||||
{
|
||||
List<GenericEventArgs<int>> callbacks = new List<GenericEventArgs<int>>();
|
||||
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 1000 })
|
||||
{
|
||||
queue.OnItemDequeued += (sender, args) => callbacks.Add(args);
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
|
||||
|
||||
queue.OnItemDequeued += (sender, args) => { ThreadingUtils.Sleep(1000); };
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Third enqueue did not wait for process to complete");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(3, callbacks.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
[TestCase(1000)]
|
||||
public void BetweenMillisecondsTest(long milliseconds)
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = milliseconds })
|
||||
Assert.AreEqual(milliseconds, queue.BetweenMilliseconds);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
Assert.True(queue.SequenceEqual(new[] { 10, 20, 30 }));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
ICD.Common.Utils.Tests/Comparers/SequenceComparerTest.cs
Normal file
33
ICD.Common.Utils.Tests/Comparers/SequenceComparerTest.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using ICD.Common.Utils.Comparers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Comparers
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class SequenceComparerTest
|
||||
{
|
||||
[Test]
|
||||
public void CompareTest()
|
||||
{
|
||||
SequenceComparer<int> comparer = new SequenceComparer<int>();
|
||||
|
||||
// Equal
|
||||
int[] a = {1, 2, 3};
|
||||
int[] b = {1, 2, 3};
|
||||
|
||||
Assert.AreEqual(0, comparer.Compare(a, b));
|
||||
|
||||
// A comes before B
|
||||
a = new[] {1, 2};
|
||||
b = new[] {1, 2, 3};
|
||||
|
||||
Assert.AreEqual(-1, comparer.Compare(a, b));
|
||||
|
||||
// B comes before A
|
||||
a = new[] { 2, 2, 3 };
|
||||
b = new[] { 1, 2, 3 };
|
||||
|
||||
Assert.AreEqual(1, comparer.Compare(a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,16 +37,6 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(eTestEnum.C, values[3]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEnumTypeTest()
|
||||
{
|
||||
Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestEnum)));
|
||||
Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestFlagsEnum)));
|
||||
Assert.IsTrue(EnumUtils.IsEnumType(typeof(Enum)));
|
||||
Assert.IsFalse(EnumUtils.IsEnumType(typeof(EnumUtilsTest)));
|
||||
Assert.Throws<ArgumentNullException>(() => EnumUtils.IsEnumType(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEnumTypeGenericTest()
|
||||
{
|
||||
@@ -63,7 +53,7 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(EnumUtils.IsEnum(eTestFlagsEnum.A));
|
||||
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as object));
|
||||
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as Enum));
|
||||
Assert.IsFalse(EnumUtils.IsEnum(null));
|
||||
Assert.IsFalse(EnumUtils.IsEnum<Enum>(null));
|
||||
Assert.IsFalse(EnumUtils.IsEnum(""));
|
||||
}
|
||||
|
||||
@@ -79,15 +69,6 @@ namespace ICD.Common.Utils.Tests
|
||||
|
||||
#region Values
|
||||
|
||||
[Test]
|
||||
public void GetUnderlyingValueTest()
|
||||
{
|
||||
Assert.AreEqual(0, EnumUtils.GetUnderlyingValue(eTestEnum.None));
|
||||
Assert.AreEqual(1, EnumUtils.GetUnderlyingValue(eTestEnum.A));
|
||||
Assert.AreEqual(2, EnumUtils.GetUnderlyingValue(eTestEnum.B));
|
||||
Assert.AreEqual(3, EnumUtils.GetUnderlyingValue(eTestEnum.C));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValuesGenericTest()
|
||||
{
|
||||
@@ -100,13 +81,6 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(values.Contains(eTestEnum.C));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetNoneValueGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.None, EnumUtils.GetNoneValue<eTestEnum>());
|
||||
Assert.AreEqual(eTestFlagsEnum.None, EnumUtils.GetNoneValue<eTestFlagsEnum>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValuesExceptNoneGenericTest()
|
||||
{
|
||||
@@ -119,18 +93,6 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(values.Contains(eTestEnum.C));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValuesExceptNoneTest()
|
||||
{
|
||||
object[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.IsFalse(values.Contains(eTestEnum.None));
|
||||
Assert.IsTrue(values.Contains(eTestEnum.A));
|
||||
Assert.IsTrue(values.Contains(eTestEnum.B));
|
||||
Assert.IsTrue(values.Contains(eTestEnum.C));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Flags
|
||||
@@ -142,13 +104,6 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(EnumUtils.IsFlagsEnum<eTestFlagsEnum>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsFlagsEnumTest()
|
||||
{
|
||||
Assert.IsFalse(EnumUtils.IsFlagsEnum(typeof(eTestEnum)));
|
||||
Assert.IsTrue(EnumUtils.IsFlagsEnum(typeof(eTestFlagsEnum)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFlagsIntersectionGenericTest()
|
||||
{
|
||||
@@ -318,20 +273,6 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(eTestFlagsEnum.None, outputB);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToEnumGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum(eTestEnum.A));
|
||||
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum(eTestEnum.A));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToEnumTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum((object)eTestEnum.A));
|
||||
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum((object)eTestEnum.A));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,5 +301,29 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
Assert.AreEqual(20, ordered[1]);
|
||||
Assert.AreEqual(30, ordered[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToInverseTest()
|
||||
{
|
||||
Dictionary<int, string> forward = new Dictionary<int, string>
|
||||
{
|
||||
{ 1, "testA" },
|
||||
{ 2, "testA" },
|
||||
{ 3, "testB" },
|
||||
{ 4, "testB" }
|
||||
};
|
||||
|
||||
Dictionary<string, List<int>> backwards = new Dictionary<string, List<int>>
|
||||
{
|
||||
{"testA", new List<int> {1, 2}},
|
||||
{"testB", new List<int> {3, 4}}
|
||||
};
|
||||
|
||||
Dictionary<string, List<int>> inverse = forward.ToInverse();
|
||||
|
||||
bool equal = inverse.DictionaryEqual(backwards, (v1, v2) => v1.ScrambledEquals(v2));
|
||||
|
||||
Assert.IsTrue(equal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,9 +335,9 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToDictionaryIntTest()
|
||||
public void ToIndexedDictionaryTest()
|
||||
{
|
||||
Dictionary<int, int> values = new[] {1, 2, 3}.ToDictionary();
|
||||
Dictionary<int, int> values = new[] {1, 2, 3}.ToIndexedDictionary();
|
||||
|
||||
Assert.AreEqual(3, values.Count);
|
||||
Assert.AreEqual(1, values[0]);
|
||||
@@ -346,9 +346,9 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToDictionaryUIntTest()
|
||||
public void ToIndexedDictionaryUIntTest()
|
||||
{
|
||||
Dictionary<uint, int> values = new[] {1, 2, 3}.ToDictionaryUInt();
|
||||
Dictionary<uint, int> values = new[] {1, 2, 3}.ToIndexedDictionaryUInt();
|
||||
|
||||
Assert.AreEqual(3, values.Count);
|
||||
Assert.AreEqual(1, values[0]);
|
||||
@@ -400,10 +400,19 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
[Test]
|
||||
public void UnanimousTest()
|
||||
{
|
||||
Assert.IsTrue(new[] {true, true, true}.Unanimous());
|
||||
Assert.IsTrue(new[] {false, false, false}.Unanimous());
|
||||
Assert.IsFalse(new[] {false, true, false}.Unanimous());
|
||||
Assert.IsFalse(new bool[] { }.Unanimous());
|
||||
bool result;
|
||||
|
||||
Assert.IsTrue(new[] {true, true, true}.Unanimous(out result));
|
||||
Assert.IsTrue(result);
|
||||
|
||||
Assert.IsTrue(new[] {false, false, false}.Unanimous(out result));
|
||||
Assert.IsFalse(result);
|
||||
|
||||
Assert.IsFalse(new[] {false, true, false}.Unanimous(out result));
|
||||
Assert.AreEqual(default(bool), result);
|
||||
|
||||
Assert.IsFalse(new bool[] { }.Unanimous(out result));
|
||||
Assert.AreEqual(default(bool), result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -13,10 +13,10 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
List<int> testList = new List<int>();
|
||||
|
||||
testList.AddSorted(2);
|
||||
testList.AddSorted(3);
|
||||
testList.AddSorted(1);
|
||||
testList.AddSorted(2);
|
||||
Assert.AreEqual(0, testList.AddSorted(2));
|
||||
Assert.AreEqual(1, testList.AddSorted(3));
|
||||
Assert.AreEqual(0, testList.AddSorted(1));
|
||||
Assert.AreEqual(1, testList.AddSorted(2));
|
||||
|
||||
Assert.AreEqual(4, testList.Count);
|
||||
Assert.AreEqual(1, testList[0]);
|
||||
@@ -31,10 +31,10 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
List<int> testList = new List<int>();
|
||||
IComparer<int> comparer = new InverseComparer();
|
||||
|
||||
testList.AddSorted(2, comparer);
|
||||
testList.AddSorted(3, comparer);
|
||||
testList.AddSorted(1, comparer);
|
||||
testList.AddSorted(2, comparer);
|
||||
Assert.AreEqual(0, testList.AddSorted(2, comparer));
|
||||
Assert.AreEqual(0, testList.AddSorted(3, comparer));
|
||||
Assert.AreEqual(2, testList.AddSorted(1, comparer));
|
||||
Assert.AreEqual(1, testList.AddSorted(2, comparer));
|
||||
|
||||
Assert.AreEqual(4, testList.Count);
|
||||
Assert.AreEqual(3, testList[0]);
|
||||
|
||||
@@ -1,91 +1,82 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class StringExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void IndexOfTest()
|
||||
{
|
||||
string first;
|
||||
Assert.AreEqual(5, "test1test3test2".IndexOf(new[] { "test2", "test3" }, out first));
|
||||
Assert.AreEqual("test3", first);
|
||||
}
|
||||
[TestFixture]
|
||||
public sealed class StringExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void IndexOfTest()
|
||||
{
|
||||
string first;
|
||||
Assert.AreEqual(5, "test1test3test2".IndexOf(new[] {"test2", "test3"}, out first));
|
||||
Assert.AreEqual("test3", first);
|
||||
}
|
||||
|
||||
[TestCase(true, "12345", '1')]
|
||||
[TestCase(false, "12345", '2')]
|
||||
public void StartsWithTest(bool expected, string value, char character)
|
||||
{
|
||||
Assert.AreEqual(expected, value.StartsWith(character));
|
||||
}
|
||||
[TestCase(true, "12345", '1')]
|
||||
[TestCase(false, "12345", '2')]
|
||||
public void StartsWithTest(bool expected, string value, char character)
|
||||
{
|
||||
Assert.AreEqual(expected, value.StartsWith(character));
|
||||
}
|
||||
|
||||
[TestCase(true, "12345", '5')]
|
||||
[TestCase(false, "12345", '2')]
|
||||
public void EndsWithTest(bool expected, string value, char character)
|
||||
{
|
||||
Assert.AreEqual(expected, value.EndsWith(character));
|
||||
}
|
||||
[TestCase(true, "12345", '5')]
|
||||
[TestCase(false, "12345", '2')]
|
||||
public void EndsWithTest(bool expected, string value, char character)
|
||||
{
|
||||
Assert.AreEqual(expected, value.EndsWith(character));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitCharCountTest()
|
||||
{
|
||||
string[] split = "123-456-789-0".Split('-', 2).ToArray();
|
||||
[Test]
|
||||
public void SplitCharCountTest()
|
||||
{
|
||||
string[] split = "123-456-789-0".Split('-', 2).ToArray();
|
||||
|
||||
Assert.AreEqual(2, split.Length);
|
||||
Assert.AreEqual("123", split[0]);
|
||||
Assert.AreEqual("456-789-0", split[1]);
|
||||
}
|
||||
Assert.AreEqual(2, split.Length);
|
||||
Assert.AreEqual("123", split[0]);
|
||||
Assert.AreEqual("456-789-0", split[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitChunksizeTest()
|
||||
{
|
||||
string[] split = "1234567890".Split(3).ToArray();
|
||||
[Test]
|
||||
public void SplitChunksizeTest()
|
||||
{
|
||||
string[] split = "1234567890".Split(3).ToArray();
|
||||
|
||||
Assert.AreEqual(4, split.Length);
|
||||
Assert.AreEqual("123", split[0]);
|
||||
Assert.AreEqual("456", split[1]);
|
||||
Assert.AreEqual("789", split[2]);
|
||||
Assert.AreEqual("0", split[3]);
|
||||
}
|
||||
Assert.AreEqual(4, split.Length);
|
||||
Assert.AreEqual("123", split[0]);
|
||||
Assert.AreEqual("456", split[1]);
|
||||
Assert.AreEqual("789", split[2]);
|
||||
Assert.AreEqual("0", split[3]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitStringDelimiterTest()
|
||||
{
|
||||
string[] split = "1234567890".Split("23").ToArray();
|
||||
[TestCase("12345", " 12 3 4 \t 5\n")]
|
||||
public void RemoveWhitespaceTest(string expected, string value)
|
||||
{
|
||||
Assert.AreEqual(expected, value.RemoveWhitespace());
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, split.Length);
|
||||
Assert.AreEqual("14567890", string.Join("", split));
|
||||
}
|
||||
[TestCase("1234567890", "12345", "67890")]
|
||||
[TestCase("foobarfoobar", "bar", "foofoo")]
|
||||
public void RemoveStringTest(string value, string other, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, value.Remove(other));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitStringDelimitersTest()
|
||||
{
|
||||
string[] split = "1234567890".Split(new[] { "23", "67" }).ToArray();
|
||||
[TestCase("1234567890", new[] {'2', '6'}, "13457890")]
|
||||
[TestCase("912529434324", new[] {'-', '(', ')', '.', '+'}, "912529434324")]
|
||||
public void RemoveCharactersTest(string value, IEnumerable<char> characters, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, value.Remove(characters));
|
||||
}
|
||||
|
||||
Assert.AreEqual(3, split.Length);
|
||||
Assert.AreEqual("145890", string.Join("", split));
|
||||
}
|
||||
|
||||
[TestCase("12345", " 12 3 4 \t 5\n")]
|
||||
public void RemoveWhitespaceTest(string expected, string value)
|
||||
{
|
||||
Assert.AreEqual(expected, value.RemoveWhitespace());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveCharactersTest()
|
||||
{
|
||||
Assert.AreEqual("13457890", "1234567890".Remove(new[] { '2', '6' }));
|
||||
}
|
||||
|
||||
[TestCase(true, "27652")]
|
||||
[TestCase(false, "a27652")]
|
||||
public void IsNumericTest(bool expected, string value)
|
||||
{
|
||||
Assert.AreEqual(expected, value.IsNumeric());
|
||||
}
|
||||
}
|
||||
[TestCase(true, "27652")]
|
||||
[TestCase(false, "a27652")]
|
||||
public void IsNumericTest(bool expected, string value)
|
||||
{
|
||||
Assert.AreEqual(expected, value.IsNumeric());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -86,6 +87,13 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
Assert.AreEqual(expected, value.IsIntegerNumeric());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAssemblyTest()
|
||||
{
|
||||
Assembly assembly = typeof(TypeExtensionsTest).GetAssembly();
|
||||
Assert.IsTrue(assembly.FullName.Contains("ICD.Common.Utils"));
|
||||
}
|
||||
|
||||
[TestCase(typeof(string), typeof(object), true)]
|
||||
[TestCase(typeof(object), typeof(string), false)]
|
||||
public void IsAssignableToTest(Type a, Type b, bool expected)
|
||||
@@ -134,6 +142,27 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
Assert.AreEqual(typeof(IEnumerable), interfaces[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMinimalInterfacesTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[TestCase(typeof(int), "Int32")]
|
||||
[TestCase(typeof(List<int>), "List")]
|
||||
public void GetNameWithoutGenericArityTest(Type type, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, type.GetNameWithoutGenericArity());
|
||||
}
|
||||
|
||||
[TestCase(typeof(string), "string")]
|
||||
[TestCase(typeof(int?), "int?")]
|
||||
[TestCase(typeof(List<int?>), "List<int?>")]
|
||||
public void GetSyntaxNameTest(Type type, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, type.GetSyntaxName());
|
||||
}
|
||||
|
||||
private interface C
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Xml;
|
||||
using NUnit.Framework;
|
||||
@@ -49,10 +50,10 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
|
||||
reader.ReadToNextElement();
|
||||
|
||||
IcdXmlAttribute[] attributes = reader.GetAttributes().ToArray();
|
||||
KeyValuePair<string, string>[] attributes = reader.GetAttributes().ToArray();
|
||||
Assert.AreEqual(2, attributes.Length);
|
||||
Assert.AreEqual("attr1", attributes[0].Name);
|
||||
Assert.AreEqual("attr2", attributes[1].Name);
|
||||
Assert.AreEqual("attr1", attributes[0].Key);
|
||||
Assert.AreEqual("attr2", attributes[1].Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
@@ -19,9 +20,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -36,18 +36,10 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[TestCase(-10000, 5)]
|
||||
[TestCase(10000, 5)]
|
||||
public void GetNumberOfDigitsTest(int number, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected, MathUtils.GetNumberOfDigits(number));
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void GetRangesTest()
|
||||
{
|
||||
IEnumerable<int> values = new int[] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
|
||||
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
|
||||
int[][] ranges = MathUtils.GetRanges(values).ToArray();
|
||||
|
||||
Assert.AreEqual(4, ranges.Length);
|
||||
@@ -68,7 +60,7 @@ namespace ICD.Common.Utils.Tests
|
||||
[Test, UsedImplicitly]
|
||||
public void RoundToNearestTest()
|
||||
{
|
||||
IEnumerable<int> values = new int[] { 0, 15, 30, 45 };
|
||||
IEnumerable<int> values = new [] { 0, 15, 30, 45 };
|
||||
Assert.AreEqual(15, MathUtils.RoundToNearest(21, values));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +178,13 @@ namespace ICD.Common.Utils.Tests
|
||||
[Test]
|
||||
public void BreadthFirstSearchManyDestinationsTest()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 1 }, null));
|
||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, null, Graph));
|
||||
Assert.IsEmpty(RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 5 }, Graph));
|
||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] { 1 }, null));
|
||||
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPathManyDestinations(1, null, Graph));
|
||||
Assert.AreEqual(0, RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] { 5 }, Graph).Count());
|
||||
|
||||
Dictionary<int, IEnumerable<int>> paths = RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 21, 22, 31, 43, 62 }, WideGraph);
|
||||
Dictionary<int, IEnumerable<int>> paths =
|
||||
RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] {21, 22, 31, 43, 62}, WideGraph)
|
||||
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
//Make sure all paths were found
|
||||
Assert.IsTrue(paths.Keys.Contains(21));
|
||||
|
||||
38
ICD.Common.Utils.Tests/RegexUtilsTest.cs
Normal file
38
ICD.Common.Utils.Tests/RegexUtilsTest.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class RegexUtilsTest
|
||||
{
|
||||
[Test]
|
||||
public static void MatchesTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void MatchesOptionsTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[TestCase(@"[assembly: AssemblyFileVersion(""1.2.3"")][assembly: AssemblyFileVersion(""1.2.3"")]",
|
||||
@"[assembly: AssemblyFileVersion(""2.0.3.0"")][assembly: AssemblyFileVersion(""2.0.3.0"")]",
|
||||
@"AssemblyFileVersion\(""(?<version>(\d+\.?){4})""\)",
|
||||
"version",
|
||||
"1.2.3")]
|
||||
public static void ReplaceGroupTest(string expected, string input, string pattern, string groupName,
|
||||
string replacement)
|
||||
{
|
||||
string result = RegexUtils.ReplaceGroup(input, pattern, groupName, replacement);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void ReplaceGroupFuncTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,14 +47,14 @@ namespace ICD.Common.Utils.Tests
|
||||
section.Enter();
|
||||
|
||||
// ReSharper disable once NotAccessedVariable
|
||||
object handle = ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
|
||||
ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => result == 1, 1000));
|
||||
|
||||
section.Leave();
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
handle = ThreadingUtils.SafeInvoke(() =>
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
result = section.TryEnter() ? 2 : 0;
|
||||
section.Leave();
|
||||
|
||||
@@ -61,10 +61,10 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual("[-3 - 5]", StringUtils.RangeFormat(-3, 5));
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void UppercaseFirstTest()
|
||||
[TestCase("foobar", "Foobar")]
|
||||
public void UppercaseFirstTest(string value, string expected)
|
||||
{
|
||||
Assert.AreEqual("Foobar", StringUtils.UppercaseFirst("foobar"));
|
||||
Assert.AreEqual(expected, StringUtils.UppercaseFirst(value));
|
||||
}
|
||||
|
||||
[TestCase("test", "Test")]
|
||||
@@ -74,16 +74,24 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(expected, StringUtils.ToTitleCase(input));
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void ToIpIdStringTest()
|
||||
[TestCase((byte)0x67, "0x67")]
|
||||
public void ToIpIdStringTest(byte ipid, string expected)
|
||||
{
|
||||
Assert.AreEqual("0x67", StringUtils.ToIpIdString(0x67));
|
||||
Assert.AreEqual(expected, StringUtils.ToIpIdString(ipid));
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void FromIpIdStringTest()
|
||||
[TestCase("0x67", (byte)0x67)]
|
||||
public void FromIpIdStringTest(string value, byte expected)
|
||||
{
|
||||
Assert.AreEqual(0x67, StringUtils.FromIpIdString("0x67"));
|
||||
Assert.AreEqual(expected, StringUtils.FromIpIdString(value));
|
||||
}
|
||||
|
||||
[TestCase("0x67", true, (byte)0x67)]
|
||||
public void TryFromIpIdStringTest(string value, bool expected, byte expectedOutput)
|
||||
{
|
||||
byte output;
|
||||
Assert.AreEqual(expected, StringUtils.TryFromIpIdString(value, out output));
|
||||
Assert.AreEqual(expectedOutput, output);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
@@ -170,5 +178,20 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(StringUtils.TryParse("true", out testVal));
|
||||
Assert.AreEqual(true, testVal);
|
||||
}
|
||||
|
||||
[TestCase("test", "\"test\"")]
|
||||
[TestCase("\"test\"", "\"test\"")]
|
||||
[TestCase("test test", "\"test test\"")]
|
||||
public void EnquoteTest(string input, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.Enquote(input));
|
||||
}
|
||||
|
||||
[TestCase("\"test\"", "test")]
|
||||
[TestCase("\"test test\"", "test test")]
|
||||
public void UnEnquoteTest(string input, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.UnEnquote(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Xml;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdXmlAttributeTest
|
||||
{
|
||||
[Test, UsedImplicitly]
|
||||
public void ValueAsIntTest()
|
||||
{
|
||||
IcdXmlAttribute attribute = new IcdXmlAttribute("test", "12");
|
||||
Assert.AreEqual("12", attribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,12 +56,12 @@ namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
reader.ReadToNextElement();
|
||||
|
||||
IcdXmlAttribute[] attributes = reader.GetAttributes().ToArray();
|
||||
KeyValuePair<string, string>[] attributes = reader.GetAttributes().ToArray();
|
||||
|
||||
Assert.AreEqual(2, attributes.Length);
|
||||
Assert.AreEqual("attr1", attributes[0].Name);
|
||||
Assert.AreEqual("attr1", attributes[0].Key);
|
||||
Assert.AreEqual("1", attributes[0].Value);
|
||||
Assert.AreEqual("attr2", attributes[1].Name);
|
||||
Assert.AreEqual("attr2", attributes[1].Key);
|
||||
Assert.AreEqual("2", attributes[1].Value);
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
paths.Add(args.Path);
|
||||
nodes.Add(args.Outer);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -176,13 +177,6 @@ namespace ICD.Common.Utils.Tests.Xml
|
||||
}
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void IsValidXmlTest()
|
||||
{
|
||||
Assert.IsFalse(XmlUtils.IsValidXml(@"<Foo></Bar>"));
|
||||
Assert.IsTrue(XmlUtils.IsValidXml(EXAMPLE_XML));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FormatTest()
|
||||
{
|
||||
@@ -253,6 +247,7 @@ namespace ICD.Common.Utils.Tests.Xml
|
||||
[TestCase("<Test><Child>A</Child></Test>", "Child", true, eTestEnum.A)]
|
||||
[TestCase("<Test><Child>A, B</Child></Test>", "Child", true, eTestEnum.A | eTestEnum.B)]
|
||||
public void ReadChildElementContentAsEnumTest<T>(string xml, string childElement, bool ignoreCase, T expected)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
Assert.AreEqual(expected, XmlUtils.ReadChildElementContentAsEnum<T>(xml, childElement, ignoreCase));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace ICD.Common.Utils
|
||||
private static readonly IcdHashSet<Assembly> s_CachedAssemblies;
|
||||
private static readonly IcdHashSet<Type> s_CachedTypes;
|
||||
|
||||
private static readonly Dictionary<Attribute, MethodInfo> s_AttributeToMethodCache;
|
||||
private static readonly Dictionary<Attribute, Type> s_AttributeToTypeCache;
|
||||
private static readonly Dictionary<Type, IcdHashSet<Attribute>> s_TypeToAttributesCache;
|
||||
|
||||
@@ -38,26 +37,12 @@ namespace ICD.Common.Utils
|
||||
s_CachedAssemblies = new IcdHashSet<Assembly>();
|
||||
s_CachedTypes = new IcdHashSet<Type>();
|
||||
|
||||
s_AttributeToMethodCache = new Dictionary<Attribute, MethodInfo>();
|
||||
s_AttributeToTypeCache = new Dictionary<Attribute, Type>();
|
||||
s_TypeToAttributesCache = new Dictionary<Type, IcdHashSet<Attribute>>();
|
||||
}
|
||||
|
||||
#region Caching
|
||||
|
||||
/// <summary>
|
||||
/// Pre-emptively caches the given assemblies for lookup.
|
||||
/// </summary>
|
||||
/// <param name="assemblies"></param>
|
||||
public static void CacheAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
if (assemblies == null)
|
||||
throw new ArgumentNullException("assemblies");
|
||||
|
||||
foreach (Assembly assembly in assemblies)
|
||||
CacheAssembly(assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-emptively caches the given assembly for lookup.
|
||||
/// </summary>
|
||||
@@ -70,6 +55,8 @@ namespace ICD.Common.Utils
|
||||
if (s_CachedAssemblies.Contains(assembly))
|
||||
return true;
|
||||
|
||||
s_CachedAssemblies.Add(assembly);
|
||||
|
||||
#if SIMPLSHARP
|
||||
CType[] types;
|
||||
#else
|
||||
@@ -114,7 +101,6 @@ namespace ICD.Common.Utils
|
||||
foreach (var type in types)
|
||||
CacheType(type);
|
||||
|
||||
s_CachedAssemblies.Add(assembly);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -133,44 +119,23 @@ namespace ICD.Common.Utils
|
||||
|
||||
if (s_CachedTypes.Contains(type))
|
||||
return;
|
||||
s_CachedTypes.Add(type);
|
||||
|
||||
MethodInfo[] methods;
|
||||
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;
|
||||
|
||||
methods = type.GetMethods();
|
||||
}
|
||||
// GetMethods for Open Generic Types is not supported.
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Not sure why this happens :/
|
||||
catch (InvalidProgramException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (MethodInfo method in methods)
|
||||
CacheMethod(method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caches the method.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
private static void CacheMethod(MethodInfo method)
|
||||
{
|
||||
if (method == null)
|
||||
throw new ArgumentNullException("method");
|
||||
|
||||
foreach (Attribute attribute in ReflectionExtensions.GetCustomAttributes<Attribute>(method, false))
|
||||
s_AttributeToMethodCache[attribute] = method;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -183,7 +148,6 @@ namespace ICD.Common.Utils
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetClassAttributes<T>()
|
||||
where T : Attribute
|
||||
{
|
||||
return s_AttributeToTypeCache.Select(kvp => kvp.Key)
|
||||
.OfType<T>();
|
||||
@@ -197,7 +161,6 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static T GetClassAttribute<T>(Type type)
|
||||
where T : Attribute
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
@@ -212,7 +175,6 @@ namespace ICD.Common.Utils
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetClassAttributes<T>(Type type)
|
||||
where T : Attribute
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
@@ -232,8 +194,10 @@ namespace ICD.Common.Utils
|
||||
|
||||
CacheType(type);
|
||||
|
||||
return s_TypeToAttributesCache.ContainsKey(type)
|
||||
? s_TypeToAttributesCache[type].ToArray()
|
||||
IcdHashSet<Attribute> attributes;
|
||||
|
||||
return s_TypeToAttributesCache.TryGetValue(type, out attributes)
|
||||
? attributes.ToArray(attributes.Count)
|
||||
: Enumerable.Empty<Attribute>();
|
||||
}
|
||||
|
||||
@@ -251,29 +215,27 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the cached method attributes of the given type.
|
||||
/// Returns the properties on the given instance with property attributes of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="instance"></param>
|
||||
/// <param name="inherit"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetMethodAttributes<T>()
|
||||
where T : Attribute
|
||||
public static IEnumerable<PropertyInfo> GetProperties<T>(object instance, bool inherit)
|
||||
{
|
||||
return s_AttributeToMethodCache.Select(p => p.Key)
|
||||
.OfType<T>()
|
||||
.ToArray();
|
||||
}
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException("instance");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached method for the given attribute.
|
||||
/// </summary>
|
||||
/// <param name="attribute"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethod(Attribute attribute)
|
||||
{
|
||||
if (attribute == null)
|
||||
throw new ArgumentNullException("attribute");
|
||||
|
||||
return s_AttributeToMethodCache[attribute];
|
||||
return instance.GetType()
|
||||
#if SIMPLSHARP
|
||||
.GetCType()
|
||||
#else
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.GetProperties()
|
||||
// ReSharper disable InvokeAsExtensionMethod
|
||||
.Where(p => ReflectionExtensions.GetCustomAttributes<T>(p, inherit).Any());
|
||||
// ReSharper restore InvokeAsExtensionMethod
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
228
ICD.Common.Utils/Attributes/RangeAttribute.cs
Normal file
228
ICD.Common.Utils/Attributes/RangeAttribute.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the valid ranges for a given value if that range is not equal to the range of the data type.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property |
|
||||
AttributeTargets.Field |
|
||||
AttributeTargets.Parameter |
|
||||
AttributeTargets.ReturnValue,
|
||||
AllowMultiple = false,
|
||||
Inherited = true)]
|
||||
public sealed class RangeAttribute : AbstractIcdAttribute
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[NotNull]
|
||||
public object Min { get; private set; }
|
||||
|
||||
[NotNull]
|
||||
public object Max { get; private set; }
|
||||
|
||||
[NotNull]
|
||||
private Type Type { get { return Min.GetType(); } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RangeAttribute(ushort min, ushort max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(short min, short max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(uint min, uint max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(int min, int max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(ulong min, ulong max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(long min, long max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(float min, float max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(double min, double max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(byte min, byte max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(sbyte min, sbyte max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public RangeAttribute(decimal min, decimal max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given value is within the range of Min to Max.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsInRange(object value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (value.GetType() != Type)
|
||||
throw new ArgumentException("the type of value does not match the type of min / max");
|
||||
|
||||
if (value is ushort)
|
||||
{
|
||||
var castMin = (ushort)Min;
|
||||
var castMax = (ushort)Max;
|
||||
var castVal = (ushort)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is short)
|
||||
{
|
||||
var castMin = (short)Min;
|
||||
var castMax = (short)Max;
|
||||
var castVal = (short)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is uint)
|
||||
{
|
||||
var castMin = (uint)Min;
|
||||
var castMax = (uint)Max;
|
||||
var castVal = (uint)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is int)
|
||||
{
|
||||
var castMin = (int)Min;
|
||||
var castMax = (int)Max;
|
||||
var castVal = (int)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is ulong)
|
||||
{
|
||||
var castMin = (ulong)Min;
|
||||
var castMax = (ulong)Max;
|
||||
var castVal = (ulong)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is long)
|
||||
{
|
||||
var castMin = (long)Min;
|
||||
var castMax = (long)Max;
|
||||
var castVal = (long)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is float)
|
||||
{
|
||||
var castMin = (float)Min;
|
||||
var castMax = (float)Max;
|
||||
var castVal = (float)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is double)
|
||||
{
|
||||
var castMin = (double)Min;
|
||||
var castMax = (double)Max;
|
||||
var castVal = (double)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is decimal)
|
||||
{
|
||||
var castMin = (decimal)Min;
|
||||
var castMax = (decimal)Max;
|
||||
var castVal = (decimal)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is byte)
|
||||
{
|
||||
var castMin = (byte)Min;
|
||||
var castMax = (byte)Max;
|
||||
var castVal = (byte)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
if (value is sbyte)
|
||||
{
|
||||
var castMin = (sbyte)Min;
|
||||
var castMax = (sbyte)Max;
|
||||
var castVal = (sbyte)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
|
||||
throw new ArgumentException("the type of value is not a numeric type.");
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(double value)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((double)Min, (double)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(float value)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((float)Min, (float)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(int value)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((int)Min, (int)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(ushort value)
|
||||
{
|
||||
return MathUtils.MapRange((ushort)Min, (ushort)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
135
ICD.Common.Utils/Collections/AsyncEventQueue.cs
Normal file
135
ICD.Common.Utils/Collections/AsyncEventQueue.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
186
ICD.Common.Utils/Collections/BiDictionary.cs
Normal file
186
ICD.Common.Utils/Collections/BiDictionary.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a 1-to-1 map of Keys to Values with O(1) Value->Key lookup time.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> m_KeyToValue;
|
||||
private readonly Dictionary<TValue, TKey> m_ValueToKey;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_KeyToValue.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
|
||||
|
||||
public ICollection<TValue> Values { get { return m_ValueToKey.Keys; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public BiDictionary()
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>();
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_KeyToValue.Clear();
|
||||
m_ValueToKey.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return m_KeyToValue.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue(TValue value)
|
||||
{
|
||||
return m_ValueToKey.ContainsKey(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ContainsKey(key))
|
||||
throw new ArgumentException("Key is already present in the collection", "key");
|
||||
|
||||
if (ContainsValue(value))
|
||||
throw new ArgumentException("Value is already present in the collection", "value");
|
||||
|
||||
m_KeyToValue.Add(key, value);
|
||||
m_ValueToKey.Add(value, key);
|
||||
}
|
||||
|
||||
public void Set(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
RemoveKey(key);
|
||||
RemoveValue(value);
|
||||
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
public TKey GetKey(TValue value)
|
||||
{
|
||||
return m_ValueToKey[value];
|
||||
}
|
||||
|
||||
public TValue GetValue(TKey key)
|
||||
{
|
||||
return m_KeyToValue[key];
|
||||
}
|
||||
|
||||
public bool RemoveKey(TKey key)
|
||||
{
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
TValue value = m_KeyToValue[key];
|
||||
|
||||
m_KeyToValue.Remove(key);
|
||||
m_ValueToKey.Remove(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveValue(TValue value)
|
||||
{
|
||||
if (!ContainsValue(value))
|
||||
return false;
|
||||
|
||||
TKey key = m_ValueToKey[value];
|
||||
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return m_KeyToValue.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetKey(TValue value, out TKey key)
|
||||
{
|
||||
return m_ValueToKey.TryGetValue(value, out key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary
|
||||
|
||||
TValue IDictionary<TKey, TValue>.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
|
||||
bool IDictionary<TKey, TValue>.Remove(TKey key)
|
||||
{
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (m_KeyToValue as IDictionary<TKey, TValue>).Contains(item);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return RemoveKey(item.Key);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_KeyToValue.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,6 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new, empty hashset.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static IcdHashSet<T> NullSet { get { return new IcdHashSet<T>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </summary>
|
||||
@@ -46,8 +40,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdHashSet()
|
||||
: this(Enumerable.Empty<T>())
|
||||
{
|
||||
m_Dict = new Dictionary<T, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,10 +49,33 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public IcdHashSet(IEnumerable<T> items)
|
||||
: this()
|
||||
: this(EqualityComparer<T>.Default, items)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdHashSet(IEqualityComparer<T> comparer)
|
||||
: this(comparer, Enumerable.Empty<T>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="items"></param>
|
||||
public IcdHashSet(IEqualityComparer<T> comparer, IEnumerable<T> items)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
if (items == null)
|
||||
return;
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
m_Dict = new Dictionary<T, object>(comparer);
|
||||
|
||||
AddRange(items);
|
||||
}
|
||||
@@ -67,26 +84,35 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a set containing all of this sets items plus all of the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Union(IEnumerable<T> set)
|
||||
{
|
||||
IcdHashSet<T> unionSet = new IcdHashSet<T>(this);
|
||||
|
||||
if (set == null)
|
||||
return unionSet;
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
IcdHashSet<T> unionSet = new IcdHashSet<T>(m_Dict.Comparer, this);
|
||||
unionSet.AddRange(set);
|
||||
|
||||
return unionSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new set of this sets items exluding the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Subtract(IEnumerable<T> set)
|
||||
{
|
||||
IcdHashSet<T> subtractSet = new IcdHashSet<T>(this);
|
||||
|
||||
if (set == null)
|
||||
return subtractSet;
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
IcdHashSet<T> subtractSet = new IcdHashSet<T>(m_Dict.Comparer, this);
|
||||
|
||||
foreach (T item in set)
|
||||
subtractSet.Remove(item);
|
||||
@@ -94,23 +120,18 @@ namespace ICD.Common.Utils.Collections
|
||||
return subtractSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the items that are common between this set and the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSubsetOf(IcdHashSet<T> set)
|
||||
public IcdHashSet<T> Intersection(IEnumerable<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
return this.All(setToCompare.Contains);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Intersection(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> intersectionSet = NullSet;
|
||||
|
||||
if (set == null)
|
||||
return intersectionSet;
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
foreach (T item in this.Where(set.Contains))
|
||||
intersectionSet.Add(item);
|
||||
IcdHashSet<T> intersectionSet = new IcdHashSet<T>(m_Dict.Comparer);
|
||||
|
||||
foreach (T item in set.Where(Contains))
|
||||
intersectionSet.Add(item);
|
||||
@@ -124,47 +145,97 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> NonIntersection(IcdHashSet<T> set)
|
||||
public IcdHashSet<T> NonIntersection(IEnumerable<T> set)
|
||||
{
|
||||
return Subtract(set).Union(set.Subtract(this));
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
IcdHashSet<T> output = new IcdHashSet<T>(m_Dict.Comparer, this);
|
||||
|
||||
foreach (T item in set)
|
||||
{
|
||||
if (output.Contains(item))
|
||||
output.Remove(item);
|
||||
else
|
||||
output.Add(item);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given set contains all of the items in this set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSubsetOf(IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
return Count <= set.Count && this.All(set.Contains);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given set contains all of the items in this set, and the sets are not equal.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSubsetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
// Is a proper subset if A is a subset of B and A != B
|
||||
return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this));
|
||||
return Count < set.Count && IsSubsetOf(set);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSupersetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
return setToCompare.IsSubsetOf(this);
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
return set.IsSubsetOf(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set, and the sets are not equal.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSupersetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
// B is a proper superset of A if B is a superset of A and A != B
|
||||
return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this));
|
||||
return set.IsProperSubsetOf(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set, and vice versa.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool SetEquals(IcdHashSet<T> set)
|
||||
{
|
||||
var setToCompare = set ?? NullSet;
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
|
||||
return (IsSubsetOf(setToCompare) && setToCompare.IsSubsetOf(this));
|
||||
return Count == set.Count && set.All(Contains);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<T> Members
|
||||
#region ICollection<T>
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the collection. Returns false if the item already exists.
|
||||
@@ -173,9 +244,9 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <returns></returns>
|
||||
public bool Add(T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("item");
|
||||
|
||||
if (m_Dict.ContainsKey(item))
|
||||
@@ -204,15 +275,7 @@ namespace ICD.Common.Utils.Collections
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T item in items)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new InvalidOperationException("item");
|
||||
|
||||
if (!m_Dict.ContainsKey(item))
|
||||
m_Dict[item] = null;
|
||||
}
|
||||
m_Dict[item] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -231,6 +294,11 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <returns></returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("item");
|
||||
|
||||
return m_Dict.ContainsKey(item);
|
||||
}
|
||||
|
||||
@@ -252,12 +320,17 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("item");
|
||||
|
||||
return m_Dict.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IEnumerable
|
||||
#region IEnumerable<T>
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ICD.Common.Utils.Collections
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly List<TKey> m_OrderedKeys;
|
||||
private readonly List<TValue> m_ValuesOrderedByKey;
|
||||
private readonly Dictionary<TKey, TValue> m_Dictionary;
|
||||
private readonly IComparer<TKey> m_Comparer;
|
||||
|
||||
@@ -20,14 +21,7 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_OrderedKeys.Select(k => m_Dictionary[k])
|
||||
.ToArray(Count);
|
||||
}
|
||||
}
|
||||
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
@@ -39,7 +33,10 @@ namespace ICD.Common.Utils.Collections
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
m_OrderedKeys.AddSorted(key, m_Comparer);
|
||||
{
|
||||
int index = m_OrderedKeys.AddSorted(key, m_Comparer);
|
||||
m_ValuesOrderedByKey.Insert(index, value);
|
||||
}
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
}
|
||||
@@ -60,13 +57,41 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdOrderedDictionary(IComparer<TKey> comparer)
|
||||
: this(comparer, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="equalityComparer"></param>
|
||||
public IcdOrderedDictionary(IComparer<TKey> comparer, IEqualityComparer<TKey> equalityComparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
if (equalityComparer == null)
|
||||
throw new ArgumentNullException("equalityComparer");
|
||||
|
||||
m_Comparer = comparer;
|
||||
m_OrderedKeys = new List<TKey>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>();
|
||||
m_ValuesOrderedByKey = new List<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dictionary"></param>
|
||||
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
|
||||
: this()
|
||||
{
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException("dictionary");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
@@ -92,6 +117,7 @@ namespace ICD.Common.Utils.Collections
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_ValuesOrderedByKey.Clear();
|
||||
m_Dictionary.Clear();
|
||||
}
|
||||
|
||||
@@ -109,7 +135,10 @@ namespace ICD.Common.Utils.Collections
|
||||
if (!m_Dictionary.Remove(key))
|
||||
return false;
|
||||
|
||||
m_OrderedKeys.Remove(key);
|
||||
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
|
||||
|
||||
m_OrderedKeys.RemoveAt(index);
|
||||
m_ValuesOrderedByKey.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,10 +73,14 @@ namespace ICD.Common.Utils.Collections
|
||||
[PublicAPI]
|
||||
public void Enqueue(T item, int priority)
|
||||
{
|
||||
if (!m_PriorityToQueue.ContainsKey(priority))
|
||||
m_PriorityToQueue.Add(priority, new List<T>());
|
||||
List<T> queue;
|
||||
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
|
||||
{
|
||||
queue = new List<T>();
|
||||
m_PriorityToQueue.Add(priority, queue);
|
||||
}
|
||||
|
||||
m_PriorityToQueue[priority].Add(item);
|
||||
queue.Add(item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
@@ -89,10 +93,14 @@ namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
const int priority = int.MinValue;
|
||||
|
||||
if (!m_PriorityToQueue.ContainsKey(priority))
|
||||
m_PriorityToQueue.Add(priority, new List<T>());
|
||||
List<T> queue;
|
||||
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
|
||||
{
|
||||
queue = new List<T>();
|
||||
m_PriorityToQueue.Add(priority, queue);
|
||||
}
|
||||
|
||||
m_PriorityToQueue[priority].Insert(0, item);
|
||||
queue.Insert(0, item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
@@ -128,7 +136,7 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
bool inserted = false;
|
||||
|
||||
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue)
|
||||
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue.ToArray())
|
||||
{
|
||||
int[] removeIndices =
|
||||
kvp.Value
|
||||
@@ -148,11 +156,19 @@ namespace ICD.Common.Utils.Collections
|
||||
if (!inserted)
|
||||
{
|
||||
int insertIndex = removeIndices[0];
|
||||
kvp.Value.Insert(insertIndex, item);
|
||||
|
||||
if (insertIndex >= kvp.Value.Count)
|
||||
kvp.Value.Add(item);
|
||||
else
|
||||
kvp.Value.Insert(insertIndex, item);
|
||||
|
||||
m_Count++;
|
||||
|
||||
inserted = true;
|
||||
}
|
||||
|
||||
if (kvp.Value.Count == 0)
|
||||
m_PriorityToQueue.Remove(kvp.Key);
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
@@ -181,24 +197,30 @@ namespace ICD.Common.Utils.Collections
|
||||
[PublicAPI]
|
||||
public bool TryDequeue(out T output)
|
||||
{
|
||||
output = default(T);
|
||||
while (true)
|
||||
{
|
||||
output = default(T);
|
||||
|
||||
KeyValuePair<int, List<T>> kvp;
|
||||
if (!m_PriorityToQueue.TryFirst(out kvp))
|
||||
return false;
|
||||
KeyValuePair<int, List<T>> kvp;
|
||||
if (!m_PriorityToQueue.TryFirst(out kvp))
|
||||
return false;
|
||||
|
||||
int priority = kvp.Key;
|
||||
List<T> queue = kvp.Value;
|
||||
int priority = kvp.Key;
|
||||
List<T> queue = kvp.Value;
|
||||
|
||||
output = queue[0];
|
||||
queue.RemoveAt(0);
|
||||
bool found = queue.TryFirst(out output);
|
||||
if (found)
|
||||
{
|
||||
queue.RemoveAt(0);
|
||||
m_Count--;
|
||||
}
|
||||
|
||||
if (queue.Count == 0)
|
||||
m_PriorityToQueue.Remove(priority);
|
||||
if (queue.Count == 0)
|
||||
m_PriorityToQueue.Remove(priority);
|
||||
|
||||
m_Count--;
|
||||
|
||||
return true;
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -220,10 +242,7 @@ namespace ICD.Common.Utils.Collections
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
array.SetValue(item, index++);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
182
ICD.Common.Utils/Collections/RateLimitedEventQueue.cs
Normal file
182
ICD.Common.Utils/Collections/RateLimitedEventQueue.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Timers;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
|
||||
/// </summary>
|
||||
public sealed class RateLimitedEventQueue<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 SafeTimer m_DequeueTimer;
|
||||
private readonly Queue<T> m_Queue;
|
||||
private readonly SafeCriticalSection m_QueueSection;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the time between dequeues in milliseconds.
|
||||
/// </summary>
|
||||
public long BetweenMilliseconds { get; set; }
|
||||
|
||||
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 RateLimitedEventQueue()
|
||||
{
|
||||
m_Queue = new Queue<T>();
|
||||
m_QueueSection = new SafeCriticalSection();
|
||||
|
||||
m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Release resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
OnItemDequeued = null;
|
||||
|
||||
m_DequeueTimer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the given item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Enqueue(item);
|
||||
|
||||
if (m_Queue.Count == 1)
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the queued items.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_DequeueTimer.Stop();
|
||||
m_Queue.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next pulse in the queue.
|
||||
/// </summary>
|
||||
private void SendNext()
|
||||
{
|
||||
if (!m_QueueSection.TryEnter())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (m_Queue.Count == 0)
|
||||
return;
|
||||
|
||||
T item = m_Queue.Peek();
|
||||
|
||||
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
|
||||
|
||||
m_DequeueTimer.Reset(BetweenMilliseconds);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dequeue timer elapses.
|
||||
/// </summary>
|
||||
private void DequeueTimerCallback()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Dequeue();
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.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
|
||||
}
|
||||
}
|
||||
29
ICD.Common.Utils/Comparers/FileNameEqualityComparer.cs
Normal file
29
ICD.Common.Utils/Comparers/FileNameEqualityComparer.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
public sealed class FileNameEqualityComparer : IEqualityComparer<string>
|
||||
{
|
||||
private static FileNameEqualityComparer s_Instance;
|
||||
|
||||
public static FileNameEqualityComparer Instance
|
||||
{
|
||||
get { return s_Instance ?? (s_Instance = new FileNameEqualityComparer()); }
|
||||
}
|
||||
|
||||
private FileNameEqualityComparer()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Equals(string x, string y)
|
||||
{
|
||||
return GetHashCode(x) == GetHashCode(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(string obj)
|
||||
{
|
||||
return obj == null ? 0 : IcdPath.GetFileName(obj).GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
ICD.Common.Utils/Comparers/SequenceComparer.cs
Normal file
66
ICD.Common.Utils/Comparers/SequenceComparer.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
public sealed class SequenceComparer<T> : IComparer<IEnumerable<T>>
|
||||
{
|
||||
private readonly IComparer<T> m_ItemComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public SequenceComparer()
|
||||
: this(Comparer<T>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public SequenceComparer(IComparer<T> comparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
m_ItemComparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
|
||||
{
|
||||
if (x == null)
|
||||
throw new ArgumentNullException("x");
|
||||
|
||||
if (y == null)
|
||||
throw new ArgumentNullException("y");
|
||||
|
||||
using (IEnumerator<T> firstPos = x.GetEnumerator())
|
||||
{
|
||||
using (IEnumerator<T> secondPos = y.GetEnumerator())
|
||||
{
|
||||
bool hasFirst = firstPos.MoveNext();
|
||||
bool hasSecond = secondPos.MoveNext();
|
||||
|
||||
while (hasFirst && hasSecond)
|
||||
{
|
||||
int result = m_ItemComparer.Compare(firstPos.Current, secondPos.Current);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
hasFirst = firstPos.MoveNext();
|
||||
hasSecond = secondPos.MoveNext();
|
||||
}
|
||||
|
||||
if (!hasFirst && !hasSecond)
|
||||
return 0;
|
||||
|
||||
if (!hasFirst)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
137
ICD.Common.Utils/Csv/CsvWriter.cs
Normal file
137
ICD.Common.Utils/Csv/CsvWriter.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
public sealed class CsvWriter : IDisposable
|
||||
{
|
||||
private const string QUOTATION_MARK = "\"";
|
||||
private const string DOUBLE_QUOTE_MARK = "\"\"";
|
||||
|
||||
private readonly IcdTextWriter m_Writer;
|
||||
|
||||
private readonly string m_Seperator;
|
||||
private readonly string m_LineTerminator;
|
||||
private readonly bool m_AlwaysEscape;
|
||||
|
||||
private bool m_NewLine;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public CsvWriter(IcdTextWriter writer, bool spaceAfterComma, bool alwaysEscape, string newline, params string[] header)
|
||||
{
|
||||
m_NewLine = true;
|
||||
m_Writer = writer;
|
||||
m_Seperator = spaceAfterComma ? ", " : ",";
|
||||
m_AlwaysEscape = alwaysEscape;
|
||||
m_LineTerminator = newline;
|
||||
|
||||
if(header.Any())
|
||||
AppendRow(header);
|
||||
}
|
||||
|
||||
~CsvWriter()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls ToString() for each item and adds the row to the builder.
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
[PublicAPI]
|
||||
public void AppendRow(params object[] row)
|
||||
{
|
||||
foreach (object value in row)
|
||||
AppendValue(value);
|
||||
AppendNewline();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the row to the builder.
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
[PublicAPI]
|
||||
public void AppendRow(params string[] row)
|
||||
{
|
||||
foreach (string value in row)
|
||||
AppendValue(value);
|
||||
AppendNewline();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls ToString() on the item and adds it to the builder.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
[PublicAPI]
|
||||
public void AppendValue(object value)
|
||||
{
|
||||
AppendValue(string.Format("{0}", value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to the builder.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
[PublicAPI]
|
||||
public void AppendValue(string value)
|
||||
{
|
||||
value = value ?? string.Empty;
|
||||
|
||||
if (!m_NewLine)
|
||||
m_Writer.WrappedTextWriter.Write(m_Seperator);
|
||||
|
||||
if (m_AlwaysEscape || value.Contains(","))
|
||||
{
|
||||
value = value.Replace(QUOTATION_MARK, DOUBLE_QUOTE_MARK);
|
||||
|
||||
// Append the value, surrounded by quotes
|
||||
m_Writer.WrappedTextWriter.Write(QUOTATION_MARK);
|
||||
m_Writer.WrappedTextWriter.Write(value);
|
||||
m_Writer.WrappedTextWriter.Write(QUOTATION_MARK);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Writer.WrappedTextWriter.Write(value);
|
||||
}
|
||||
|
||||
m_NewLine = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a New Line To the Builder
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AppendNewline()
|
||||
{
|
||||
m_Writer.WrappedTextWriter.Write(m_LineTerminator);
|
||||
|
||||
m_NewLine = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Writer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <param name="header"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header)
|
||||
{
|
||||
return new CsvWriter(writer,
|
||||
settings.InsertSpaceAfterComma,
|
||||
settings.AlwaysEscapeEveryValue,
|
||||
settings.NewLineSequence,
|
||||
header);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
ICD.Common.Utils/Csv/CsvWriterSettings.cs
Normal file
46
ICD.Common.Utils/Csv/CsvWriterSettings.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
public sealed class CsvWriterSettings
|
||||
{
|
||||
private bool m_InsertSpaceAfterComma = true;
|
||||
private bool m_AlwaysEscapeEveryValue = true;
|
||||
private string m_NewLineSequence = IcdEnvironment.NewLine;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets whether to insert a space between elements, after the comma
|
||||
/// Defaults to true.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool InsertSpaceAfterComma
|
||||
{
|
||||
get { return m_InsertSpaceAfterComma; }
|
||||
set { m_InsertSpaceAfterComma = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets whether to always escape the values.
|
||||
/// If true, values are recorded surrounded by quotes, regardless of if they contain a comma or not. Quotes are escaped.
|
||||
/// If false, values are recorded as the value without quotes, unless escaping is required.
|
||||
/// Defaults to true.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool AlwaysEscapeEveryValue
|
||||
{
|
||||
get { return m_AlwaysEscapeEveryValue; }
|
||||
set { m_AlwaysEscapeEveryValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the newline character or characters to deliniate records.
|
||||
/// Defaults to System.NewLine.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string NewLineSequence
|
||||
{
|
||||
get { return m_NewLineSequence; }
|
||||
set { m_NewLineSequence = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
91
ICD.Common.Utils/Email/EmailClient.cs
Normal file
91
ICD.Common.Utils/Email/EmailClient.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
{
|
||||
public sealed class EmailClient
|
||||
{
|
||||
private readonly EmailStringCollection m_To;
|
||||
private readonly EmailStringCollection m_Cc;
|
||||
private readonly EmailStringCollection m_Bcc;
|
||||
|
||||
#region Properties
|
||||
|
||||
public string Host { get; set; }
|
||||
public ushort Port { get; set; }
|
||||
public bool Secure { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string From { get; set; }
|
||||
public EmailStringCollection To { get { return m_To; } }
|
||||
public EmailStringCollection Cc { get { return m_Cc; } }
|
||||
public EmailStringCollection Bcc { get { return m_Bcc; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public EmailClient()
|
||||
{
|
||||
m_To = new EmailStringCollection();
|
||||
m_Cc = new EmailStringCollection();
|
||||
m_Bcc = new EmailStringCollection();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sends the email.
|
||||
/// </summary>
|
||||
/// <param name="subject"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns>Error codes.</returns>
|
||||
[PublicAPI]
|
||||
public eMailErrorCode Send(string subject, string message)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
var response = CrestronMailFunctions.SendMail(Host, Port, Secure, Username, Password, From, m_To.ToString(), "",
|
||||
m_Cc.ToString(), m_Bcc.ToString(), subject, message,
|
||||
CrestronMailFunctions.eMailPriority.Normal, 0, "");
|
||||
|
||||
return MailErrorCodeUtils.FromSimplMailCode(response);
|
||||
#else
|
||||
try
|
||||
{
|
||||
MailMessage mailMessage = new MailMessage
|
||||
{
|
||||
From = new MailAddress(From),
|
||||
To = {m_To.ToString()},
|
||||
CC = {m_Cc.ToString()},
|
||||
Bcc = {m_Bcc.ToString()},
|
||||
Subject = subject,
|
||||
Body = message
|
||||
};
|
||||
|
||||
SmtpClient client = new SmtpClient
|
||||
{
|
||||
Port = Port,
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
UseDefaultCredentials = false,
|
||||
Host = Host
|
||||
};
|
||||
|
||||
client.Send(mailMessage);
|
||||
}
|
||||
catch (SmtpException ex)
|
||||
{
|
||||
return MailErrorCodeUtils.FromNetStandardMailCode(ex.StatusCode);
|
||||
}
|
||||
|
||||
return eMailErrorCode.Ok;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
134
ICD.Common.Utils/Email/EmailStringCollection.cs
Normal file
134
ICD.Common.Utils/Email/EmailStringCollection.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores addresses/paths and provides them in a ; delimited string.
|
||||
/// </summary>
|
||||
public sealed class EmailStringCollection : ICollection<string>
|
||||
{
|
||||
private const char DELIMITER = ';';
|
||||
|
||||
private readonly List<string> m_Items;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items.
|
||||
/// </summary>
|
||||
public int Count { get { return m_Items.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public EmailStringCollection()
|
||||
{
|
||||
m_Items = new List<string>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool Add(string item)
|
||||
{
|
||||
if (m_Items.Contains(item))
|
||||
return false;
|
||||
|
||||
m_Items.Add(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the item from the collection.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool Remove(string item)
|
||||
{
|
||||
if (!m_Items.Contains(item))
|
||||
return false;
|
||||
|
||||
m_Items.Remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all items.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_Items.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the collection contains the item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool Contains(string item)
|
||||
{
|
||||
return m_Items.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items as a ; delimited string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(DELIMITER.ToString(), m_Items.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Collection
|
||||
|
||||
void ICollection<string>.Add(string item)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple email addresses, seperated by ;
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public void AddMany(string items)
|
||||
{
|
||||
items = items.RemoveWhitespace();
|
||||
string [] splitItems = items.Split(DELIMITER);
|
||||
foreach (string item in splitItems)
|
||||
Add(item);
|
||||
}
|
||||
|
||||
public void CopyTo(string[] array, int arrayIndex)
|
||||
{
|
||||
m_Items.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enumerable
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
{
|
||||
return m_Items.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
233
ICD.Common.Utils/Email/eMailErrorCode.cs
Normal file
233
ICD.Common.Utils/Email/eMailErrorCode.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
#if STANDARD
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
{
|
||||
public enum eMailErrorCode
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// The transaction could not occur. You receive this error when the specified SMTP
|
||||
// host cannot be found.
|
||||
GeneralFailure = -1,
|
||||
//
|
||||
// Summary:
|
||||
// A system status or system Help reply.
|
||||
SystemStatus = 211,
|
||||
//
|
||||
// Summary:
|
||||
// A Help message was returned by the service.
|
||||
HelpMessage = 214,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service is ready.
|
||||
ServiceReady = 220,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service is closing the transmission channel.
|
||||
ServiceClosingTransmissionChannel = 221,
|
||||
//
|
||||
// Summary:
|
||||
// The email was successfully sent to the SMTP service.
|
||||
Ok = 250,
|
||||
//
|
||||
// Summary:
|
||||
// The user mailbox is not located on the receiving server; the server forwards
|
||||
// the e-mail.
|
||||
UserNotLocalWillForward = 251,
|
||||
//
|
||||
// Summary:
|
||||
// The specified user is not local, but the receiving SMTP service accepted the
|
||||
// message and attempted to deliver it. This status code is defined in RFC 1123,
|
||||
// which is available at http://www.ietf.org/.
|
||||
CannotVerifyUserWillAttemptDelivery = 252,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service is ready to receive the e-mail content.
|
||||
StartMailInput = 354,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service is not available; the server is closing the transmission channel.
|
||||
ServiceNotAvailable = 421,
|
||||
//
|
||||
// Summary:
|
||||
// The destination mailbox is in use.
|
||||
MailboxBusy = 450,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service cannot complete the request. This error can occur if the client's
|
||||
// IP address cannot be resolved (that is, a reverse lookup failed). You can also
|
||||
// receive this error if the client domain has been identified as an open relay
|
||||
// or source for unsolicited e-mail (spam). For details, see RFC 2505, which is
|
||||
// available at http://www.ietf.org/.
|
||||
LocalErrorInProcessing = 451,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service does not have sufficient storage to complete the request.
|
||||
InsufficientStorage = 452,
|
||||
//
|
||||
// Summary:
|
||||
// The client was not authenticated or is not allowed to send mail using the specified
|
||||
// SMTP host.
|
||||
ClientNotPermitted = 454,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service does not recognize the specified command.
|
||||
CommandUnrecognized = 500,
|
||||
//
|
||||
// Summary:
|
||||
// The syntax used to specify a command or parameter is incorrect.
|
||||
SyntaxError = 501,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service does not implement the specified command.
|
||||
CommandNotImplemented = 502,
|
||||
//
|
||||
// Summary:
|
||||
// The commands were sent in the incorrect sequence.
|
||||
BadCommandSequence = 503,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP service does not implement the specified command parameter.
|
||||
CommandParameterNotImplemented = 504,
|
||||
//
|
||||
// Summary:
|
||||
// The SMTP server is configured to accept only TLS connections, and the SMTP client
|
||||
// is attempting to connect by using a non-TLS connection. The solution is for the
|
||||
// user to set EnableSsl=true on the SMTP Client.
|
||||
MustIssueStartTlsFirst = 530,
|
||||
//
|
||||
// Summary:
|
||||
// The destination mailbox was not found or could not be accessed.
|
||||
MailboxUnavailable = 550,
|
||||
//
|
||||
// Summary:
|
||||
// The user mailbox is not located on the receiving server. You should resend using
|
||||
// the supplied address information.
|
||||
UserNotLocalTryAlternatePath = 551,
|
||||
//
|
||||
// Summary:
|
||||
// The message is too large to be stored in the destination mailbox.
|
||||
ExceededStorageAllocation = 552,
|
||||
//
|
||||
// Summary:
|
||||
// The syntax used to specify the destination mailbox is incorrect.
|
||||
MailboxNameNotAllowed = 553,
|
||||
//
|
||||
// Summary:
|
||||
// The transaction failed.
|
||||
TransactionFailed = 554
|
||||
}
|
||||
|
||||
public static class MailErrorCodeUtils
|
||||
{
|
||||
#if STANDARD
|
||||
public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case SmtpStatusCode.BadCommandSequence:
|
||||
return eMailErrorCode.BadCommandSequence;
|
||||
case SmtpStatusCode.CannotVerifyUserWillAttemptDelivery:
|
||||
return eMailErrorCode.CannotVerifyUserWillAttemptDelivery;
|
||||
case SmtpStatusCode.ClientNotPermitted:
|
||||
return eMailErrorCode.ClientNotPermitted;
|
||||
case SmtpStatusCode.CommandNotImplemented:
|
||||
return eMailErrorCode.CommandNotImplemented;
|
||||
case SmtpStatusCode.CommandParameterNotImplemented:
|
||||
return eMailErrorCode.CommandParameterNotImplemented;
|
||||
case SmtpStatusCode.CommandUnrecognized:
|
||||
return eMailErrorCode.CommandUnrecognized;
|
||||
case SmtpStatusCode.ExceededStorageAllocation:
|
||||
return eMailErrorCode.ExceededStorageAllocation;
|
||||
case SmtpStatusCode.GeneralFailure:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case SmtpStatusCode.HelpMessage:
|
||||
return eMailErrorCode.HelpMessage;
|
||||
case SmtpStatusCode.InsufficientStorage:
|
||||
return eMailErrorCode.InsufficientStorage;
|
||||
case SmtpStatusCode.LocalErrorInProcessing:
|
||||
return eMailErrorCode.LocalErrorInProcessing;
|
||||
case SmtpStatusCode.MailboxBusy:
|
||||
return eMailErrorCode.MailboxBusy;
|
||||
case SmtpStatusCode.MailboxNameNotAllowed:
|
||||
return eMailErrorCode.MailboxNameNotAllowed;
|
||||
case SmtpStatusCode.MailboxUnavailable:
|
||||
return eMailErrorCode.MailboxUnavailable;
|
||||
case SmtpStatusCode.MustIssueStartTlsFirst:
|
||||
return eMailErrorCode.MustIssueStartTlsFirst;
|
||||
case SmtpStatusCode.Ok:
|
||||
return eMailErrorCode.Ok;
|
||||
case SmtpStatusCode.ServiceClosingTransmissionChannel:
|
||||
return eMailErrorCode.ServiceClosingTransmissionChannel;
|
||||
case SmtpStatusCode.ServiceNotAvailable:
|
||||
return eMailErrorCode.ServiceNotAvailable;
|
||||
case SmtpStatusCode.ServiceReady:
|
||||
return eMailErrorCode.ServiceReady;
|
||||
case SmtpStatusCode.StartMailInput:
|
||||
return eMailErrorCode.StartMailInput;
|
||||
case SmtpStatusCode.SyntaxError:
|
||||
return eMailErrorCode.SyntaxError;
|
||||
case SmtpStatusCode.SystemStatus:
|
||||
return eMailErrorCode.SystemStatus;
|
||||
case SmtpStatusCode.TransactionFailed:
|
||||
return eMailErrorCode.TransactionFailed;
|
||||
case SmtpStatusCode.UserNotLocalTryAlternatePath:
|
||||
return eMailErrorCode.UserNotLocalTryAlternatePath;
|
||||
case SmtpStatusCode.UserNotLocalWillForward:
|
||||
return eMailErrorCode.UserNotLocalWillForward;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(code), code, null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_OK:
|
||||
return eMailErrorCode.Ok;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_FATAL:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_ILLEGAL_CMD:
|
||||
return eMailErrorCode.CommandUnrecognized;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_CONNECT:
|
||||
return eMailErrorCode.ServiceNotAvailable;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_SEND:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_RECV:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_CONNECT:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_BUFFERS:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTHENTICATION:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTH_LOGIN_UNSUPPORTED:
|
||||
return eMailErrorCode.MustIssueStartTlsFirst;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_INV_PARAM:
|
||||
return eMailErrorCode.SyntaxError;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ETHER_NOT_ENABLED:
|
||||
return eMailErrorCode.ServiceNotAvailable;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_NO_SERVER_ADDRESS:
|
||||
return eMailErrorCode.ServiceNotAvailable;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_SEND_FAILED:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_GENERAL_SENDMAIL_ERROR:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
case CrestronMailFunctions.SendMailErrorCodes.SMTP_INVALID_FIRMWARE:
|
||||
return eMailErrorCode.GeneralFailure;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("code");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if SIMPLSHARP
|
||||
using System.Globalization;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
@@ -14,33 +13,19 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
public static class EnumUtils
|
||||
{
|
||||
private static readonly Dictionary<Type, object[]> s_EnumValuesCache;
|
||||
private static readonly Dictionary<Type, Dictionary<object, object[]>> s_EnumFlagsCache;
|
||||
private static readonly Dictionary<Type, object> s_EnumValuesCache;
|
||||
private static readonly Dictionary<Type, Dictionary<int, object>> s_EnumFlagsCache;
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor.
|
||||
/// </summary>
|
||||
static EnumUtils()
|
||||
{
|
||||
s_EnumValuesCache = new Dictionary<Type, object[]>();
|
||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<object, object[]>>();
|
||||
s_EnumValuesCache = new Dictionary<Type, object>();
|
||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given type is an enum.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsEnumType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return type.IsAssignableTo(typeof(Enum)) || type
|
||||
#if !SIMPLSHARP
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.IsEnum;
|
||||
}
|
||||
#region Validation
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given type is an enum.
|
||||
@@ -51,15 +36,58 @@ namespace ICD.Common.Utils
|
||||
return IsEnumType(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given type is an enum.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsEnumType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return type
|
||||
#if !SIMPLSHARP
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.IsEnum || type.IsAssignableTo(typeof(Enum));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given value is an enum.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsEnum(object value)
|
||||
public static bool IsEnum<T>(T value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
return value != null && IsEnumType(value.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given enum type has the Flags attribute set.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static bool IsFlagsEnum<T>()
|
||||
{
|
||||
return IsFlagsEnum(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given enum type has the Flags attribute set.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsFlagsEnum(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return type
|
||||
#if !SIMPLSHARP
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.IsDefined(typeof(FlagsAttribute), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given value is defined as part of the given enum type.
|
||||
/// </summary>
|
||||
@@ -67,77 +95,82 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsDefined<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType(typeof(T)))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
if (!IsFlagsEnum<T>())
|
||||
return GetValues<T>().Any(v => v.Equals(value));
|
||||
|
||||
int valueInt = (int)GetUnderlyingValue(value);
|
||||
|
||||
// Check if all of the flag values are defined
|
||||
foreach (T flag in GetFlags(value))
|
||||
{
|
||||
int flagInt = (int)GetUnderlyingValue(flag);
|
||||
valueInt = valueInt - flagInt;
|
||||
}
|
||||
|
||||
return valueInt == 0;
|
||||
T all = GetFlagsAllValue<T>();
|
||||
return HasFlags(all, value);
|
||||
}
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying value of the enum.
|
||||
/// Returns true if the given value is defined as part of the given enum type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static object GetUnderlyingValue<T>(T value)
|
||||
public static bool IsDefined(Type type, int value)
|
||||
{
|
||||
if (!IsEnumType(typeof(T)))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
#if SIMPLSHARP
|
||||
return Convert.ChangeType(value, ToEnum(value).GetTypeCode(), CultureInfo.InvariantCulture);
|
||||
#else
|
||||
return Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
|
||||
#endif
|
||||
if (!IsEnumType(type))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
|
||||
|
||||
int all = GetFlagsAllValue(type);
|
||||
return HasFlags(all, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetValues<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetValues(typeof(T)).Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetValues(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
Type type = typeof(T);
|
||||
|
||||
// Reflection is slow and this method is called a lot, so we cache the results.
|
||||
if (!s_EnumValuesCache.ContainsKey(type))
|
||||
s_EnumValuesCache[type] = GetValuesUncached(type).ToArray();
|
||||
object cache;
|
||||
if (!s_EnumValuesCache.TryGetValue(type, out cache))
|
||||
{
|
||||
cache = GetValuesUncached<T>().ToArray();
|
||||
s_EnumValuesCache[type] = cache;
|
||||
}
|
||||
|
||||
return s_EnumValuesCache[type];
|
||||
return cache as T[];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<object> GetValuesUncached(Type type)
|
||||
public static IEnumerable<int> GetValues(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> GetValuesUncached<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetValuesUncached(typeof(T)).Select(i => (T)(object)i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<int> GetValuesUncached(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
@@ -152,20 +185,8 @@ namespace ICD.Common.Utils
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
||||
.Select(x => x.GetValue(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 0 value for the given enum type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T GetNoneValue<T>()
|
||||
{
|
||||
if (!IsEnumType(typeof(T)))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
return (T)(object)0;
|
||||
.Select(x => x.GetValue(null))
|
||||
.Cast<int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,27 +195,21 @@ namespace ICD.Common.Utils
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetValuesExceptNone<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType(typeof(T)))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
return GetValuesExceptNone(typeof(T)).Cast<T>();
|
||||
return GetFlagsExceptNone<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration except the 0 value.
|
||||
/// Gets the values from an enumeration except the 0 value without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetValuesExceptNone(Type type)
|
||||
public static IEnumerable<int> GetValuesExceptNone(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!IsEnumType(type))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
|
||||
|
||||
return GetValues(type).Where(v => (int)v != 0);
|
||||
return GetValues(type).Except(0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -202,35 +217,55 @@ namespace ICD.Common.Utils
|
||||
#region Flags
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given enum type has the Flags attribute set.
|
||||
/// Excludes b from a.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsFlagsEnum<T>()
|
||||
public static T ExcludeFlags<T>(T a, T b)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
int aInt = (int)(object)a;
|
||||
int bInt = (int)(object)b;
|
||||
|
||||
return IsFlagsEnum(typeof(T));
|
||||
return (T)(object)ExcludeFlags(aInt, bInt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given enum type has the Flags attribute set.
|
||||
/// Excludes b from a.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsFlagsEnum(Type type)
|
||||
public static int ExcludeFlags(int a, int b)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
return a & ~b;
|
||||
}
|
||||
|
||||
if (!IsEnumType(type))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
|
||||
/// <summary>
|
||||
/// Includes a and b.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static T IncludeFlags<T>(T a, T b)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
int aInt = (int)(object)a;
|
||||
int bInt = (int)(object)b;
|
||||
|
||||
return type
|
||||
#if !SIMPLSHARP
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.IsDefined(typeof(FlagsAttribute), false);
|
||||
return (T)(object)IncludeFlags(aInt, bInt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Includes a and b.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static int IncludeFlags(int a, int b)
|
||||
{
|
||||
return a | b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -240,15 +275,34 @@ namespace ICD.Common.Utils
|
||||
/// <param name="values"></param>
|
||||
/// <returns></returns>
|
||||
public static T GetFlagsIntersection<T>(params T[] values)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
if (values.Length == 0)
|
||||
return GetNoneValue<T>();
|
||||
return default(T);
|
||||
|
||||
int output = (int)(object)values.First();
|
||||
foreach (T item in values.Skip(1))
|
||||
output &= (int)(object)item;
|
||||
int output = 0;
|
||||
bool first = true;
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), output);
|
||||
foreach (T value in values)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
output = (int)(object)value;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
output &= (int)(object)value;
|
||||
}
|
||||
|
||||
if (output == 0)
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return (T)(object)output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -259,11 +313,12 @@ namespace ICD.Common.Utils
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static T GetFlagsIntersection<T>(T a, T b)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
int aInt = (int)(object)a;
|
||||
int bInt = (int)(object)b;
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), aInt & bInt);
|
||||
return (T)(object)(aInt & bInt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,20 +328,38 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
|
||||
Type type = typeof(T);
|
||||
int valueInt = (int)(object)value;
|
||||
|
||||
if (!s_EnumFlagsCache.ContainsKey(type))
|
||||
s_EnumFlagsCache.Add(type, new Dictionary<object, object[]>());
|
||||
Dictionary<int, object> cache;
|
||||
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
|
||||
{
|
||||
cache = new Dictionary<int, object>();
|
||||
s_EnumFlagsCache[type] = cache;
|
||||
}
|
||||
|
||||
if (!s_EnumFlagsCache[type].ContainsKey(value))
|
||||
s_EnumFlagsCache[type][value] = GetValues<T>().Where(e => HasFlag(value, e)).Cast<object>().ToArray();
|
||||
object flags;
|
||||
if (!cache.TryGetValue(valueInt, out flags))
|
||||
{
|
||||
flags = GetValues<T>().Where(e => HasFlag(value, e)).ToArray();
|
||||
cache[valueInt] = flags;
|
||||
}
|
||||
|
||||
return s_EnumFlagsCache[type][value].Cast<T>();
|
||||
return flags as T[];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum type except 0.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetFlagsExceptNone<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
T allValue = GetFlagsAllValue<T>();
|
||||
return GetFlagsExceptNone(allValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -296,13 +369,9 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetFlagsExceptNone<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
|
||||
T none = GetNoneValue<T>();
|
||||
return GetFlags(value).Except(none);
|
||||
return GetFlags(value).Except(default(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,12 +384,9 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetAllFlagCombinationsExceptNone<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
|
||||
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1 ;
|
||||
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1;
|
||||
return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v));
|
||||
}
|
||||
|
||||
@@ -330,14 +396,25 @@ namespace ICD.Common.Utils
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T GetFlagsAllValue<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
int output = GetValues<T>().Aggregate(0, (current, value) => current | (int)(object)value);
|
||||
return (T)Enum.ToObject(typeof(T), output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enum value of the given type with every flag set.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetFlagsAllValue(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type).Aggregate(0, (current, value) => current | value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
@@ -346,16 +423,20 @@ namespace ICD.Common.Utils
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlag<T>(T value, T flag)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
return HasFlags(value, flag);
|
||||
}
|
||||
|
||||
if (!IsEnum(flag))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", flag == null ? "NULL" : flag.GetType().Name), "flag");
|
||||
|
||||
return ToEnum(value).HasFlag(ToEnum(flag));
|
||||
/// <summary>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlag(int value, int flag)
|
||||
{
|
||||
return HasFlags(value, flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -366,16 +447,23 @@ namespace ICD.Common.Utils
|
||||
/// <param name="flags"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlags<T>(T value, T flags)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
int a = (int)(object)value;
|
||||
int b = (int)(object)flags;
|
||||
|
||||
if (!IsEnum(flags))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", flags == null ? "NULL" : flags.GetType().Name), "flags");
|
||||
return HasFlags(a, b);
|
||||
}
|
||||
|
||||
return ToEnum(value).HasFlags(ToEnum(flags));
|
||||
/// <summary>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="flags"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlags(int value, int flags)
|
||||
{
|
||||
return (value & flags) == flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -385,12 +473,11 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasSingleFlag<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
int numeric = (int)(object)value;
|
||||
|
||||
return (int)(object)value != (int)(object)GetNoneValue<T>() && !HasMultipleFlags(value);
|
||||
return HasAnyFlags(numeric) && !HasMultipleFlags(numeric);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -399,11 +486,8 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasMultipleFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
|
||||
return HasMultipleFlags((int)(object)value);
|
||||
}
|
||||
|
||||
@@ -415,7 +499,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static bool HasMultipleFlags(int value)
|
||||
{
|
||||
return ((value & (value - 1)) != 0);
|
||||
return (value & (value - 1)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -425,8 +509,20 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasAnyFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetFlagsExceptNone(value).Any();
|
||||
return HasAnyFlags((int)(object)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the enum has any flags set.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasAnyFlags(int value)
|
||||
{
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -437,6 +533,7 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasAnyFlags<T>(T value, T other)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
T intersection = GetFlagsIntersection(value, other);
|
||||
return HasAnyFlags(intersection);
|
||||
@@ -454,10 +551,8 @@ namespace ICD.Common.Utils
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <returns></returns>
|
||||
public static T Parse<T>(string data, bool ignoreCase)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
T output;
|
||||
if (TryParse(data, ignoreCase, out output))
|
||||
return output;
|
||||
@@ -466,6 +561,26 @@ namespace ICD.Common.Utils
|
||||
throw new FormatException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing string to enum.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <returns></returns>
|
||||
public static int Parse(Type type, string data, bool ignoreCase)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
int output;
|
||||
if (TryParse(type, data, ignoreCase, out output))
|
||||
return output;
|
||||
|
||||
string message = string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name);
|
||||
throw new FormatException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||
/// </summary>
|
||||
@@ -475,10 +590,8 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryParse<T>(string data, bool ignoreCase, out T result)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
result = default(T);
|
||||
|
||||
try
|
||||
@@ -492,6 +605,32 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryParse(Type type, string data, bool ignoreCase, out int result)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
result = 0;
|
||||
|
||||
try
|
||||
{
|
||||
result = (int)Enum.Parse(type, data, ignoreCase);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing string to enum.
|
||||
/// Will fail if the resulting value is not defined as part of the enum.
|
||||
@@ -501,10 +640,8 @@ namespace ICD.Common.Utils
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <returns></returns>
|
||||
public static T ParseStrict<T>(string data, bool ignoreCase)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
T output;
|
||||
|
||||
try
|
||||
@@ -523,6 +660,37 @@ namespace ICD.Common.Utils
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing string to enum.
|
||||
/// Will fail if the resulting value is not defined as part of the enum.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <returns></returns>
|
||||
public static int ParseStrict(Type type, string data, bool ignoreCase)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
int output;
|
||||
|
||||
try
|
||||
{
|
||||
output = Parse(type, data, ignoreCase);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FormatException(
|
||||
string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name), e);
|
||||
}
|
||||
|
||||
if (!IsDefined(type, output))
|
||||
throw new ArgumentOutOfRangeException(string.Format("{0} is not a valid {1}", output, type.Name));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||
/// Will fail if the resulting value is not defined as part of the enum.
|
||||
@@ -533,10 +701,8 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryParseStrict<T>(string data, bool ignoreCase, out T result)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
result = default(T);
|
||||
|
||||
try
|
||||
@@ -550,21 +716,6 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given enum value to an Enum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static Enum ToEnum<T>(T value)
|
||||
{
|
||||
if (!IsEnum(value))
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
|
||||
|
||||
return (Enum)(object)value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,17 @@
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public BoolEventArgs(bool data) : base(data)
|
||||
public BoolEventArgs(bool data)
|
||||
: base(data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
public BoolEventArgs(BoolEventArgs eventArgs)
|
||||
: this(eventArgs.Data)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,14 @@
|
||||
public StringEventArgs(string data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
public StringEventArgs(StringEventArgs eventArgs)
|
||||
: base(eventArgs.Data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
@@ -25,11 +26,42 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
public static string ToLongTimeStringWithMilliseconds(this DateTime extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// Todo - Better handle different cultures
|
||||
return extends.ToString("HH:mm:ss:fff");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest DateTime to the target time that is greater than the target time
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, params DateTime[] times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("times");
|
||||
|
||||
DateTime earliestTime;
|
||||
bool success = times.OrderBy(dt => dt).TryFirst(dt => inclusive ? target <= dt : target < dt, out earliestTime);
|
||||
return success ? earliestTime : (DateTime?) null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest DateTime to the target time that is less than the target time
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, params DateTime[] times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("null");
|
||||
|
||||
DateTime latestTime;
|
||||
bool success = times.OrderByDescending(dt => dt).TryFirst(dt => inclusive ? target >= dt : target > dt, out latestTime);
|
||||
return success ? latestTime : (DateTime?) null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
ICD.Common.Utils/Extensions/DayOfWeekExtensions.cs
Normal file
17
ICD.Common.Utils/Extensions/DayOfWeekExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class DayOfWeekExtensions
|
||||
{
|
||||
public static bool IsWeekday(this DayOfWeek day)
|
||||
{
|
||||
return !IsWeekend(day);
|
||||
}
|
||||
|
||||
public static bool IsWeekend(this DayOfWeek day)
|
||||
{
|
||||
return day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
[PublicAPI]
|
||||
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
|
||||
{
|
||||
@@ -98,7 +99,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.ContainsKey(key) ? extends[key] : defaultValue;
|
||||
TValue value;
|
||||
return extends.TryGetValue(key, out value) ? value : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,6 +112,7 @@ 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)
|
||||
@@ -121,8 +124,10 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
extends[key] = extends.GetDefault(key, defaultValue);
|
||||
return extends[key];
|
||||
TValue value = extends.GetDefault(key, defaultValue);
|
||||
extends[key] = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,7 +234,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> pair in other)
|
||||
{
|
||||
if (extends.ContainsKey(pair.Key) && comparer.Equals(pair.Value, extends[pair.Key]))
|
||||
TValue value;
|
||||
if (extends.TryGetValue(pair.Key, out value) && comparer.Equals(pair.Value, value))
|
||||
continue;
|
||||
|
||||
extends[pair.Key] = pair.Value;
|
||||
@@ -239,6 +245,26 @@ namespace ICD.Common.Utils.Extensions
|
||||
return change;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> item in items)
|
||||
extends.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
@@ -430,12 +456,26 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<TValue, TKey> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
|
||||
Dictionary<TValue, List<TKey>> output = new Dictionary<TValue, List<TKey>>();
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in extends)
|
||||
{
|
||||
List<TKey> keys;
|
||||
if (!output.TryGetValue(kvp.Value, out keys))
|
||||
{
|
||||
keys = new List<TKey>();
|
||||
output.Add(kvp.Value, keys);
|
||||
}
|
||||
|
||||
keys.Add(kvp.Key);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,52 +12,35 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="value">Flag to check for</param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasFlag(this Enum extends, Enum value)
|
||||
public static bool HasFlag<T>(this T extends, T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (EnumUtils.HasMultipleFlags(value))
|
||||
throw new ArgumentException("Value has multiple flags", "value");
|
||||
|
||||
return extends.HasFlags(value);
|
||||
return EnumUtils.HasFlag(extends, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if a flags enumeration has all of the given flags set.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="values"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasFlags(this Enum extends, Enum value)
|
||||
public static bool HasFlags<T>(this T extends, T values)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
// Not as good as the .NET 4 version of this function, but should be good enough
|
||||
if (extends.GetType() != value.GetType())
|
||||
{
|
||||
string message = string.Format("Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
|
||||
value.GetType(), extends.GetType());
|
||||
throw new ArgumentException(message);
|
||||
}
|
||||
|
||||
int num = (int)(object)value;
|
||||
return ((int)(object)extends & num) == num;
|
||||
return EnumUtils.HasFlags(extends, values);
|
||||
}
|
||||
|
||||
public static ushort ToUShort(this Enum extends)
|
||||
/// <summary>
|
||||
/// Returns the enum value as a
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI("S+")]
|
||||
public static ushort ToUShort<T>(this T extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return (ushort)(object)extends;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,15 +42,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
T output = defaultItem;
|
||||
|
||||
foreach (T item in extends.Where(predicate))
|
||||
{
|
||||
output = item;
|
||||
break;
|
||||
}
|
||||
|
||||
return output;
|
||||
T output;
|
||||
return extends.TryFirst(predicate, out output) ? output : defaultItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,7 +58,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.TryFirst(i => true, out item);
|
||||
IList<T> list = extends as IList<T>;
|
||||
if (list == null)
|
||||
return extends.TryFirst(i => true, out item);
|
||||
|
||||
item = default(T);
|
||||
|
||||
if (list.Count <= 0)
|
||||
return false;
|
||||
|
||||
item = list[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +117,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.TryLast(i => true, out item);
|
||||
IList<T> list = extends as IList<T>;
|
||||
if (list == null)
|
||||
return extends.TryLast(i => true, out item);
|
||||
|
||||
item = default(T);
|
||||
|
||||
if (list.Count <= 0)
|
||||
return false;
|
||||
|
||||
item = list[list.Count - 1];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -164,25 +179,72 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (index < 0)
|
||||
throw new ArgumentException("Index must be greater or equal to 0", "index");
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
|
||||
item = default(T);
|
||||
int eachIndex = 0;
|
||||
|
||||
foreach (T each in extends)
|
||||
IList<T> list = extends as IList<T>;
|
||||
if (list != null)
|
||||
{
|
||||
if (eachIndex == index)
|
||||
if (index >= list.Count)
|
||||
return false;
|
||||
item = list[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
int current = 0;
|
||||
foreach (T value in extends)
|
||||
{
|
||||
if (current == index)
|
||||
{
|
||||
item = each;
|
||||
item = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
eachIndex++;
|
||||
current++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the given index. Returns the specified default value if the index does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public static T ElementAtOrDefault<T>(this IEnumerable<T> extends, int index, T defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
T output;
|
||||
return extends.TryElementAt(index, out output) ? output : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the two sequences for identical values.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SequenceEqual<T>(this IEnumerable<T> extends, IEnumerable<T> other, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return extends.SequenceEqual(other, comparer.Equals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the two sequences for identical values.
|
||||
/// </summary>
|
||||
@@ -196,12 +258,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
// Simple count check
|
||||
ICollection<T> a = extends as ICollection<T>;
|
||||
ICollection<T> b = other as ICollection<T>;
|
||||
if (a != null && b != null && a.Count != b.Count)
|
||||
return false;
|
||||
|
||||
using (IEnumerator<T> firstPos = extends.GetEnumerator())
|
||||
{
|
||||
using (IEnumerator<T> secondPos = other.GetEnumerator())
|
||||
@@ -235,7 +303,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
return extends.ScrambledEquals(other, EqualityComparer<T>.Default);
|
||||
@@ -254,12 +322,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
// Simple count check
|
||||
ICollection<T> a = extends as ICollection<T>;
|
||||
ICollection<T> b = other as ICollection<T>;
|
||||
if (a != null && b != null && a.Count != b.Count)
|
||||
return false;
|
||||
|
||||
Dictionary<T, int> count = new Dictionary<T, int>(comparer);
|
||||
|
||||
foreach (T item in extends)
|
||||
@@ -304,9 +378,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
|
||||
return FindIndicesIterator(extends, match);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the indices that match the predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="match"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<int> FindIndicesIterator<T>(IEnumerable<T> sequence, Predicate<T> match)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
if (match(item))
|
||||
yield return index;
|
||||
@@ -346,7 +432,11 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.ForEach(item => { });
|
||||
// ReSharper disable UnusedVariable
|
||||
foreach (T item in extends)
|
||||
// ReSharper restore UnusedVariable
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -400,8 +490,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return PrependIterator(extends, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the item to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PrependIterator<T>(IEnumerable<T> sequence, T item)
|
||||
{
|
||||
yield return item;
|
||||
foreach (T next in extends)
|
||||
|
||||
foreach (T next in sequence)
|
||||
yield return next;
|
||||
}
|
||||
#endif
|
||||
@@ -422,9 +525,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
return PrependManyIterator(extends, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the items to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PrependManyIterator<T>(IEnumerable<T> sequence, params T[] items)
|
||||
{
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
foreach (T each in extends)
|
||||
|
||||
foreach (T each in sequence)
|
||||
yield return each;
|
||||
}
|
||||
|
||||
@@ -441,8 +557,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
foreach (T first in extends)
|
||||
return AppendIterator(extends, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the item to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> AppendIterator<T>(IEnumerable<T> sequence, T item)
|
||||
{
|
||||
foreach (T first in sequence)
|
||||
yield return first;
|
||||
|
||||
yield return item;
|
||||
}
|
||||
#endif
|
||||
@@ -463,8 +592,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T each in extends)
|
||||
return AppendManyIterator(extends, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the items to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> AppendManyIterator<T>(IEnumerable<T> sequence, params T[] items)
|
||||
{
|
||||
foreach (T each in sequence)
|
||||
yield return each;
|
||||
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
}
|
||||
@@ -482,9 +624,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return PadRightIterator(extends, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads the given sequence to the given count size with default items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PadRightIterator<T>(IEnumerable<T> sequence, int count)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
yield return item;
|
||||
index++;
|
||||
@@ -515,7 +669,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> extends, IComparer<T> comparer)
|
||||
public static IOrderedEnumerable<T> Order<T>(this IEnumerable<T> extends, IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -533,7 +687,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> extends, IComparer<T> comparer)
|
||||
public static IOrderedEnumerable<T> OrderDescending<T>(this IEnumerable<T> extends, IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -591,8 +745,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
ICollection<T> collection = extends as ICollection<T> ?? extends.ToArray();
|
||||
collection.CopyTo(array, index);
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
ICollection<T> collection = extends as ICollection<T>;
|
||||
if (collection != null)
|
||||
{
|
||||
collection.CopyTo(array, index);
|
||||
return;
|
||||
}
|
||||
|
||||
int current = 0;
|
||||
foreach (T item in extends)
|
||||
{
|
||||
array[index + current] = item;
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -606,7 +774,25 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return new IcdHashSet<T>(extends);
|
||||
return extends.ToIcdHashSet(EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sequence as a IcdHashSet.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static IcdHashSet<T> ToIcdHashSet<T>(this IEnumerable<T> extends, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return new IcdHashSet<T>(comparer, extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -659,7 +845,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> extends)
|
||||
public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -676,7 +862,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<uint, T> ToDictionaryUInt<T>(this IEnumerable<T> extends)
|
||||
public static Dictionary<uint, T> ToIndexedDictionaryUInt<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -750,6 +936,28 @@ namespace ICD.Common.Utils.Extensions
|
||||
return extends.Distinct(new PredicateEqualityComparer<TItem, TProperty>(propertyComparer, getProperty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random item from the given sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static T Random<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
IList<T> sequence = extends as IList<T> ?? extends.ToArray();
|
||||
|
||||
if (sequence.Count == 0)
|
||||
throw new InvalidOperationException("Sequence is empty.");
|
||||
|
||||
Random random = new Random(Guid.NewGuid().GetHashCode());
|
||||
int index = random.Next(0, sequence.Count);
|
||||
|
||||
return sequence[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns other if the sequence is empty.
|
||||
/// Returns other if the sequence is non-empty and there are two different elements.
|
||||
@@ -764,8 +972,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
T[] array = extends.Distinct().ToArray();
|
||||
return array.Length == 1 ? array[0] : other;
|
||||
T item;
|
||||
return extends.Unanimous(out item) ? item : other;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -774,14 +982,57 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// Returns true if the sequence is non-empty and all elements are the same.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool Unanimous<T>(this IEnumerable<T> extends)
|
||||
public static bool Unanimous<T>(this IEnumerable<T> extends, out T result)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Distinct().Count() == 1;
|
||||
return extends.Unanimous(EqualityComparer<T>.Default, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if the sequence is empty.
|
||||
/// Returns false if the sequence is non-empty and there are two different elements.
|
||||
/// Returns true if the sequence is non-empty and all elements are the same.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool Unanimous<T>(this IEnumerable<T> extends, IEqualityComparer<T> comparer, out T result)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
result = default(T);
|
||||
T output = default(T);
|
||||
bool empty = true;
|
||||
|
||||
foreach (T entry in extends)
|
||||
{
|
||||
if (empty)
|
||||
{
|
||||
empty = false;
|
||||
output = entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!comparer.Equals(entry, output))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
return false;
|
||||
|
||||
result = output;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -796,7 +1047,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
using (IEnumerator<T> enumerator = extends.GetEnumerator())
|
||||
return PartitionIterator(extends, partitionSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a sequence into sequences of the given length.
|
||||
/// </summary>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="partitionSize"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<IEnumerable<T>> PartitionIterator<T>(IEnumerable<T> sequence, int partitionSize)
|
||||
{
|
||||
using (IEnumerator<T> enumerator = sequence.GetEnumerator())
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
yield return YieldBatchElements(enumerator, partitionSize - 1);
|
||||
@@ -821,9 +1083,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <summary>
|
||||
/// Wraps this object instance into an IEnumerable consisting of a single item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Type of the object. </typeparam>
|
||||
/// <param name="item"> The instance that will be wrapped. </param>
|
||||
/// <returns> An IEnumerable<T> consisting of a single item. </returns>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <param name="item">The instance that will be wrapped.</param>
|
||||
/// <returns>An IEnumerable<T> consisting of a single item.</returns>
|
||||
public static IEnumerable<T> Yield<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
@@ -836,6 +1098,20 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T[]> GetAdjacentPairs<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return GetAdjacentPairsIterator(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a sequence [A, B, C] returns a sequence [[A, B], [B, C]]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T[]> GetAdjacentPairsIterator<T>(IEnumerable<T> extends)
|
||||
{
|
||||
T previous = default(T);
|
||||
bool first = true;
|
||||
@@ -843,7 +1119,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
foreach (T item in extends)
|
||||
{
|
||||
if (!first)
|
||||
yield return new[] {previous, item};
|
||||
yield return new[] { previous, item };
|
||||
|
||||
first = false;
|
||||
previous = item;
|
||||
@@ -927,18 +1203,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
if (!sourceIterator.MoveNext())
|
||||
throw new InvalidOperationException("Sequence contains no elements");
|
||||
|
||||
TSource min = sourceIterator.Current;
|
||||
TKey minKey = selector(min);
|
||||
|
||||
while (sourceIterator.MoveNext())
|
||||
{
|
||||
TSource candidate = sourceIterator.Current;
|
||||
TKey candidateProjected = selector(candidate);
|
||||
|
||||
if (comparer.Compare(candidateProjected, minKey) >= 0)
|
||||
continue;
|
||||
|
||||
min = candidate;
|
||||
minKey = candidateProjected;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
}
|
||||
@@ -1012,16 +1292,33 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return ConsolidateIterator(extends, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips duplicate, consecutive items.
|
||||
/// E.g.
|
||||
/// [1, 2, 2, 3, 1, 1]
|
||||
/// Becomes
|
||||
/// [1, 2, 3, 1]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> ConsolidateIterator<T>(IEnumerable<T> sequence, IComparer<T> comparer)
|
||||
{
|
||||
bool first = true;
|
||||
T last = default(T);
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
if (!first && comparer.Compare(last, item) == 0)
|
||||
continue;
|
||||
|
||||
first = false;
|
||||
last = item;
|
||||
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
@@ -1136,23 +1433,40 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <typeparam name="TFirst"></typeparam>
|
||||
/// <typeparam name="TSecond"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first,
|
||||
IEnumerable<TSecond> second,
|
||||
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> extends,
|
||||
IEnumerable<TSecond> other,
|
||||
Func<TFirst, TSecond, TResult> callback)
|
||||
{
|
||||
if (first == null)
|
||||
throw new ArgumentNullException("first");
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (second == null)
|
||||
throw new ArgumentNullException("second");
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
return ZipIterator(extends, other, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFirst"></typeparam>
|
||||
/// <typeparam name="TSecond"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first,
|
||||
IEnumerable<TSecond> second,
|
||||
Func<TFirst, TSecond, TResult> callback)
|
||||
{
|
||||
using (IEnumerator<TFirst> enumerator1 = first.GetEnumerator())
|
||||
{
|
||||
using (IEnumerator<TSecond> enumerator2 = second.GetEnumerator())
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
@@ -34,5 +39,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends != null)
|
||||
extends(sender, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross-platform shim for getting MethodInfo for the delegate.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo(this Delegate extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
#if SIMPLSHARP
|
||||
return extends.GetMethod();
|
||||
#else
|
||||
return extends.Method;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -154,12 +155,14 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <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();
|
||||
}
|
||||
|
||||
@@ -252,17 +255,30 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("read");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
yield break;
|
||||
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(extends, reader);
|
||||
TItem output = read(serializer, reader);
|
||||
yield return output;
|
||||
|
||||
// Read out of the last value
|
||||
@@ -308,7 +324,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <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)
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends,
|
||||
JsonReader reader)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -317,11 +334,24 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
yield break;
|
||||
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();
|
||||
|
||||
@@ -332,7 +362,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
// Step into the value
|
||||
reader.Read();
|
||||
|
||||
TValue value = extends.Deserialize<TValue>(reader);
|
||||
TValue value = serializer.Deserialize<TValue>(reader);
|
||||
|
||||
yield return new KeyValuePair<TKey, TValue>(key, value);
|
||||
|
||||
|
||||
@@ -82,12 +82,12 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T>(this List<T> extends, T item)
|
||||
public static int AddSorted<T>(this List<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.AddSorted(item, Comparer<T>.Default);
|
||||
return extends.AddSorted(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,7 +98,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer)
|
||||
public static int AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -106,29 +106,13 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
if (extends.Count == 0)
|
||||
{
|
||||
extends.Add(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comparer.Compare(extends[extends.Count - 1], item) <= 0)
|
||||
{
|
||||
extends.Add(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comparer.Compare(extends[0], item) >= 0)
|
||||
{
|
||||
extends.Insert(0, item);
|
||||
return;
|
||||
}
|
||||
|
||||
int index = extends.BinarySearch(item, comparer);
|
||||
if (index < 0)
|
||||
index = ~index;
|
||||
|
||||
extends.Insert(index, item);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,7 +124,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="item"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate)
|
||||
public static int AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -149,7 +133,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
extends.AddSorted(item, comparer);
|
||||
return extends.AddSorted(item, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
127
ICD.Common.Utils/Extensions/MethodInfoExtensions.cs
Normal file
127
ICD.Common.Utils/Extensions/MethodInfoExtensions.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class MethodInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the method signature as a string.
|
||||
/// </summary>
|
||||
/// <param name="method">The Method</param>
|
||||
/// <returns>Method signature</returns>
|
||||
public static string GetSignature(this MethodInfo method)
|
||||
{
|
||||
return method.GetSignature(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the method signature as a string.
|
||||
/// </summary>
|
||||
/// <param name="method">The Method</param>
|
||||
/// <param name="callable">Return as a callable string(public void a(string b) would return a(b))</param>
|
||||
/// <returns>Method signature</returns>
|
||||
public static string GetSignature(this MethodInfo method, bool callable)
|
||||
{
|
||||
bool firstParam = true;
|
||||
StringBuilder sigBuilder = new StringBuilder();
|
||||
|
||||
if (callable == false)
|
||||
{
|
||||
if (method.IsPublic)
|
||||
sigBuilder.Append("public ");
|
||||
else if (method.IsPrivate)
|
||||
sigBuilder.Append("private ");
|
||||
else if (method.IsAssembly)
|
||||
sigBuilder.Append("internal ");
|
||||
if (method.IsFamily)
|
||||
sigBuilder.Append("protected ");
|
||||
if (method.IsStatic)
|
||||
sigBuilder.Append("static ");
|
||||
|
||||
Type returnType = method.ReturnType;
|
||||
|
||||
sigBuilder.Append(returnType.GetSyntaxName());
|
||||
sigBuilder.Append(' ');
|
||||
}
|
||||
|
||||
sigBuilder.Append(method.Name);
|
||||
|
||||
// Add method generics
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
sigBuilder.Append("<");
|
||||
foreach (Type g in method.GetGenericArguments())
|
||||
{
|
||||
if (firstParam)
|
||||
firstParam = false;
|
||||
else
|
||||
sigBuilder.Append(", ");
|
||||
|
||||
sigBuilder.Append(g.GetSyntaxName());
|
||||
}
|
||||
sigBuilder.Append(">");
|
||||
}
|
||||
|
||||
sigBuilder.Append("(");
|
||||
|
||||
firstParam = true;
|
||||
|
||||
#if !SIMPLSHARP
|
||||
bool secondParam = false;
|
||||
#endif
|
||||
|
||||
foreach (ParameterInfo param in method.GetParameters())
|
||||
{
|
||||
if (firstParam)
|
||||
{
|
||||
firstParam = false;
|
||||
|
||||
#if SIMPLSHARP
|
||||
// TODO - RestrictionViolationException: System.Runtime.CompilerServices.ExtensionAttribute - Not allowed due to restrictions
|
||||
#else
|
||||
if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false))
|
||||
{
|
||||
if (callable)
|
||||
{
|
||||
secondParam = true;
|
||||
continue;
|
||||
}
|
||||
sigBuilder.Append("this ");
|
||||
}
|
||||
}
|
||||
else if (secondParam)
|
||||
{
|
||||
secondParam = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
else
|
||||
sigBuilder.Append(", ");
|
||||
|
||||
if (param.ParameterType.IsByRef)
|
||||
sigBuilder.Append("ref ");
|
||||
else if (param.GetIsOut())
|
||||
sigBuilder.Append("out ");
|
||||
if (!callable)
|
||||
{
|
||||
Type parameterType = param.ParameterType;
|
||||
|
||||
sigBuilder.Append(parameterType.GetSyntaxName());
|
||||
sigBuilder.Append(' ');
|
||||
}
|
||||
|
||||
sigBuilder.Append(param.Name);
|
||||
}
|
||||
|
||||
sigBuilder.Append(")");
|
||||
|
||||
return sigBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ICD.Common.Utils/Extensions/ParameterInfoExtensions.cs
Normal file
29
ICD.Common.Utils/Extensions/ParameterInfoExtensions.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class ParameterInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the given parameter is an "out" parameter.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static bool GetIsOut(this ParameterInfo extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
#if SIMPLSHARP
|
||||
return extends.Attributes.HasFlag(ParameterAttributes.Out);
|
||||
#else
|
||||
return extends.IsOut;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="inherits"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetCustomAttributes<T>(this ICustomAttributeProvider extends, bool inherits)
|
||||
where T : Attribute
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
index = thisIndex;
|
||||
first = item;
|
||||
|
||||
if (index == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
@@ -54,7 +57,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.StartsWith(character.ToString());
|
||||
return extends.Length > 0 && character == extends[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +72,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.EndsWith(character.ToString());
|
||||
return extends.Length > 0 && character == extends[extends.Length - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,26 +91,31 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (count < 1)
|
||||
throw new ArgumentException("Value must be greater or equal to 1", "count");
|
||||
|
||||
if (count < 2)
|
||||
return SplitIterator(extends, delimeter, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the string by the given delimiter, returning up to the given number of substrings.
|
||||
/// E.g. "a:b:c".Split(':', 2) returns ["a", "b:c"]
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<string> SplitIterator(string value, char delimeter, int count)
|
||||
{
|
||||
while (count > 1)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
int index = value.IndexOf(delimeter);
|
||||
if (index < 0)
|
||||
break;
|
||||
|
||||
yield return value.Substring(0, index);
|
||||
value = value.Substring(index + 1);
|
||||
count--;
|
||||
}
|
||||
|
||||
int index = extends.IndexOf(delimeter);
|
||||
if (index < 0)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
}
|
||||
|
||||
string first = extends.Substring(0, index);
|
||||
string second = extends.Substring(index + 1);
|
||||
count--;
|
||||
|
||||
yield return first;
|
||||
foreach (string item in second.Split(delimeter, count))
|
||||
yield return item;
|
||||
yield return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,86 +137,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
.Select(i => extends.Substring(i * chunkSize, Math.Min(chunkSize, extends.Length - (i * chunkSize))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by a given substring.
|
||||
/// Taken from
|
||||
/// https://social.msdn.microsoft.com/Forums/en-US/914a350f-e0e9-45e0-91a4-6b4b2168e780/string-split-function
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<string> Split(this string extends, string delimeter)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (delimeter == null)
|
||||
throw new ArgumentNullException("delimeter");
|
||||
|
||||
int dSum = 0;
|
||||
int sSum = 0;
|
||||
int length = extends.Length;
|
||||
int delimiterLength = delimeter.Length;
|
||||
|
||||
if (delimiterLength == 0 || length == 0 || length < delimiterLength)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
}
|
||||
|
||||
char[] cd = delimeter.ToCharArray();
|
||||
char[] cs = extends.ToCharArray();
|
||||
|
||||
for (int i = 0; i < delimiterLength; i++)
|
||||
{
|
||||
dSum += cd[i];
|
||||
sSum += cs[i];
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
for (int i = start; i < length - delimiterLength; i++)
|
||||
{
|
||||
if (i >= start && dSum == sSum && extends.Substring(i, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, i - start);
|
||||
start = i + delimiterLength;
|
||||
}
|
||||
|
||||
sSum += cs[i + delimiterLength] - cs[i];
|
||||
}
|
||||
|
||||
if (dSum == sSum && extends.Substring(length - delimiterLength, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, length - delimiterLength - start);
|
||||
yield return string.Empty;
|
||||
}
|
||||
else
|
||||
yield return extends.Substring(start, length - start);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by the given substrings.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeters"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<string> Split(this string extends, IEnumerable<string> delimeters)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (delimeters == null)
|
||||
throw new ArgumentNullException("delimeters");
|
||||
|
||||
string[] delimitersArray = delimeters as string[] ?? delimeters.ToArray();
|
||||
return delimitersArray.Length == 0
|
||||
? new[] {extends}
|
||||
: extends.Split(delimitersArray.First())
|
||||
.SelectMany(s => s.Split(delimitersArray.Skip(1)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes whitespace from the string.
|
||||
/// </summary>
|
||||
@@ -224,7 +152,31 @@ namespace ICD.Common.Utils.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given characters from the string.
|
||||
/// Removes all occurances of the given string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public static string Remove(this string extends, string other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (string.IsNullOrEmpty(other))
|
||||
return extends;
|
||||
|
||||
int index;
|
||||
while ((index = extends.IndexOf(other, StringComparison.Ordinal)) >= 0)
|
||||
extends = extends.Remove(index, other.Length);
|
||||
|
||||
return extends;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all occurances the given characters from the string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="characters"></param>
|
||||
@@ -237,8 +189,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
if (characters == null)
|
||||
throw new ArgumentNullException("characters");
|
||||
var cSet = characters.ToIcdHashSet();
|
||||
|
||||
return new string(extends.Where(c => !characters.Contains(c)).ToArray());
|
||||
return new string(extends.Where(c => !cSet.Contains(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -254,5 +207,19 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
return extends.All(char.IsDigit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the string contains the given character.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="character"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Contains(this string extends, char character)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Contains(character.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static string ToReadableString(this TimeSpan extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (extends.Days > 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.Collections;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
@@ -184,18 +185,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (!s_TypeAllTypes.ContainsKey(extends))
|
||||
Type[] types;
|
||||
if (!s_TypeAllTypes.TryGetValue(extends, out types))
|
||||
{
|
||||
Type[] types =
|
||||
extends.GetBaseTypes()
|
||||
.Concat(extends.GetInterfaces())
|
||||
.Prepend(extends)
|
||||
.ToArray();
|
||||
types = extends.GetBaseTypes()
|
||||
.Concat(extends.GetInterfaces())
|
||||
.Prepend(extends)
|
||||
.ToArray();
|
||||
|
||||
s_TypeAllTypes.Add(extends, types);
|
||||
s_TypeAllTypes[extends] = types;
|
||||
}
|
||||
|
||||
return s_TypeAllTypes[extends];
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -208,20 +209,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (!s_TypeBaseTypes.ContainsKey(extends))
|
||||
Type[] types;
|
||||
if (!s_TypeBaseTypes.TryGetValue(extends, out types))
|
||||
{
|
||||
Type[] types = GetBaseTypesInternal(extends).ToArray();
|
||||
s_TypeBaseTypes.Add(extends, types);
|
||||
types = GetBaseTypesIterator(extends).ToArray();
|
||||
s_TypeBaseTypes[extends] = types;
|
||||
}
|
||||
|
||||
return s_TypeBaseTypes[extends];
|
||||
return types;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetBaseTypesInternal(Type type)
|
||||
private static IEnumerable<Type> GetBaseTypesIterator(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
do
|
||||
{
|
||||
type = type
|
||||
@@ -246,7 +245,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (!s_TypeImmediateInterfaces.ContainsKey(extends))
|
||||
Type[] immediateInterfaces;
|
||||
if (!s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
|
||||
{
|
||||
IEnumerable<Type> allInterfaces = extends.GetInterfaces();
|
||||
|
||||
@@ -256,12 +256,12 @@ namespace ICD.Common.Utils.Extensions
|
||||
.SelectMany(t => t.GetImmediateInterfaces())
|
||||
.Distinct();
|
||||
|
||||
Type[] immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
|
||||
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
|
||||
|
||||
s_TypeImmediateInterfaces.Add(extends, immediateInterfaces);
|
||||
s_TypeImmediateInterfaces[extends] = immediateInterfaces;
|
||||
}
|
||||
|
||||
return s_TypeImmediateInterfaces[extends];
|
||||
return immediateInterfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -274,17 +274,91 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (!s_TypeMinimalInterfaces.ContainsKey(extends))
|
||||
Type[] minimalInterfaces;
|
||||
if (!s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
|
||||
{
|
||||
Type[] allInterfaces = extends.GetInterfaces();
|
||||
Type[] minimalInterfaces =
|
||||
allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
|
||||
.ToArray();
|
||||
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
|
||||
.ToArray();
|
||||
|
||||
s_TypeMinimalInterfaces.Add(extends, minimalInterfaces);
|
||||
s_TypeMinimalInterfaces[extends] = minimalInterfaces;
|
||||
}
|
||||
|
||||
return s_TypeMinimalInterfaces[extends];
|
||||
return minimalInterfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Type name without any trailing generic information.
|
||||
///
|
||||
/// E.g.
|
||||
/// List`1
|
||||
/// Becomes
|
||||
/// List
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetNameWithoutGenericArity(this Type extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string name = extends.Name;
|
||||
int index = name.IndexOf('`');
|
||||
return index == -1 ? name : name.Substring(0, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type name as it would appear in code.
|
||||
/// </summary>
|
||||
/// <param name="extends">Type. May be generic or nullable</param>
|
||||
/// <returns>Full type name, fully qualified namespaces</returns>
|
||||
public static string GetSyntaxName(this Type extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// Nullable
|
||||
Type nullableType = Nullable.GetUnderlyingType(extends);
|
||||
if (nullableType != null)
|
||||
return nullableType.GetSyntaxName() + "?";
|
||||
|
||||
// Generic
|
||||
if (extends.IsGenericType)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(extends.Name.Substring(0, extends.Name.IndexOf('`')));
|
||||
sb.Append('<');
|
||||
|
||||
bool first = true;
|
||||
foreach (Type t in extends.GetGenericArguments())
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(',');
|
||||
sb.Append(t.GetSyntaxName());
|
||||
first = false;
|
||||
}
|
||||
|
||||
sb.Append('>');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Default
|
||||
switch (extends.Name)
|
||||
{
|
||||
case "String":
|
||||
return "string";
|
||||
case "Int32":
|
||||
return "int";
|
||||
case "Decimal":
|
||||
return "decimal";
|
||||
case "Object":
|
||||
return "object";
|
||||
case "Void":
|
||||
return "void";
|
||||
|
||||
default:
|
||||
return extends.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,46 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Condition=" '$(EAZFUSCATOR_NET_HOME)' != '' and Exists('$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets') " Project="$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>ICD.Common.Utils</AssemblyName>
|
||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Deterministic>true</Deterministic>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Deterministic>true</Deterministic>
|
||||
<Authors>Chris Cameron, Jeff Thompson</Authors>
|
||||
<PackageId>ICD.Common.Utils</PackageId>
|
||||
<PackageProjectUrl></PackageProjectUrl>
|
||||
<RepositoryUrl>https://cs-gogs.icdpf.net/Common/Utils</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<EazfuscatorIntegration>MSBuild</EazfuscatorIntegration>
|
||||
<EazfuscatorActiveConfiguration>Release</EazfuscatorActiveConfiguration>
|
||||
<EazfuscatorCompatibilityVersion>2018.2</EazfuscatorCompatibilityVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE;STANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="SIMPLSharpLogs\**" />
|
||||
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
|
||||
<None Remove="SIMPLSharpLogs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="ObfuscationSettings.cs" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ICD.Common.projectinfo" />
|
||||
<None Remove="ICD.Common_SimplSharp.suo" />
|
||||
<None Remove="ICD.SimplSharp.projectinfo" />
|
||||
<None Remove="Properties\ControlSystem.cfg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.4" />
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -74,12 +75,23 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
|
||||
<Compile Include="Attributes\RangeAttribute.cs" />
|
||||
<Compile Include="Collections\BiDictionary.cs" />
|
||||
<Compile Include="Collections\AsyncEventQueue.cs" />
|
||||
<Compile Include="Collections\IcdOrderedDictionary.cs" />
|
||||
<Compile Include="Collections\PriorityQueue.cs" />
|
||||
<Compile Include="Collections\RateLimitedEventQueue.cs" />
|
||||
<Compile Include="Collections\WeakKeyDictionary.cs" />
|
||||
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
|
||||
<Compile Include="Comparers\PredicateComparer.cs" />
|
||||
<Compile Include="Comparers\SequenceComparer.cs" />
|
||||
<Compile Include="ConsoleColor.cs" />
|
||||
<Compile Include="Email\EmailClient.cs" />
|
||||
<Compile Include="Email\eMailErrorCode.cs" />
|
||||
<Compile Include="Email\EmailStringCollection.cs" />
|
||||
<Compile Include="EncodingUtils.cs" />
|
||||
<Compile Include="Csv\CsvWriter.cs" />
|
||||
<Compile Include="Csv\CsvWriterSettings.cs" />
|
||||
<Compile Include="EventArguments\BoolEventArgs.cs" />
|
||||
<Compile Include="EventArguments\CharEventArgs.cs" />
|
||||
<Compile Include="EventArguments\DateTimeEventArgs.cs" />
|
||||
@@ -93,11 +105,18 @@
|
||||
<None Include="ObfuscationSettings.cs" />
|
||||
<Compile Include="Extensions\BoolExtensions.cs" />
|
||||
<Compile Include="Extensions\ByteExtensions.cs" />
|
||||
<Compile Include="Extensions\DayOfWeekExtensions.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="Extensions\UShortExtensions.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="ProcessorUtils.SimplSharp.cs" />
|
||||
<Compile Include="ProcessorUtils.Standard.cs" />
|
||||
<Compile Include="ProgramUtils.SimplSharp.cs" />
|
||||
@@ -105,7 +124,12 @@
|
||||
<Compile Include="Properties\Annotations.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RecursionUtils.cs" />
|
||||
<Compile Include="RegexUtils.cs" />
|
||||
<Compile Include="ReprBuilder.cs" />
|
||||
<Compile Include="Services\Scheduler\AbstractScheduledAction.cs" />
|
||||
<Compile Include="Services\Scheduler\ActionSchedulerService.cs" />
|
||||
<Compile Include="Services\Scheduler\IActionSchedulerService.cs" />
|
||||
<Compile Include="Services\Scheduler\IScheduledAction.cs" />
|
||||
<Compile Include="Services\Logging\ILoggerService.cs" />
|
||||
<Compile Include="Services\Logging\LogItem.cs" />
|
||||
<Compile Include="Services\Logging\LogItemEventArgs.cs" />
|
||||
@@ -122,7 +146,7 @@
|
||||
<Compile Include="IcdEnvironment.cs" />
|
||||
<Compile Include="IcdEnvironment.SimplSharp.cs" />
|
||||
<Compile Include="IcdEnvironment.Standard.cs" />
|
||||
<Compile Include="IcdZip.cs" />
|
||||
<Compile Include="IO\Compression\IcdZip.cs" />
|
||||
<Compile Include="IO\IcdDirectory.cs" />
|
||||
<Compile Include="EnumUtils.cs" />
|
||||
<Compile Include="Extensions\AssemblyExtensions.cs" />
|
||||
@@ -179,12 +203,16 @@
|
||||
<Compile Include="Timers\SafeTimer.cs" />
|
||||
<Compile Include="TryUtils.cs" />
|
||||
<Compile Include="UriUtils.cs" />
|
||||
<Compile Include="Xml\AbstractGenericXmlConverter.cs" />
|
||||
<Compile Include="Xml\AbstractXmlConverter.cs" />
|
||||
<Compile Include="Xml\DefaultXmlConverter.cs" />
|
||||
<Compile Include="Xml\IcdXmlConvert.cs" />
|
||||
<Compile Include="Xml\IcdXmlDocument.cs" />
|
||||
<Compile Include="Xml\IcdXmlException.cs" />
|
||||
<Compile Include="Xml\IcdXmlReader.cs" />
|
||||
<Compile Include="Xml\IcdXmlTextWriter.cs" />
|
||||
<Compile Include="Xml\IcdXmlAttribute.cs" />
|
||||
<Compile Include="Xml\IXmlConverter.cs" />
|
||||
<Compile Include="Xml\XmlConverterAttribute.cs" />
|
||||
<Compile Include="Xml\XmlReaderExtensions.cs" />
|
||||
<Compile Include="Xml\XmlUtils.cs" />
|
||||
<None Include="Properties\ControlSystem.cfg" />
|
||||
|
||||
173
ICD.Common.Utils/IO/Compression/IcdZip.cs
Normal file
173
ICD.Common.Utils/IO/Compression/IcdZip.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Utils for managing archives.
|
||||
/// </summary>
|
||||
public static class IcdZip
|
||||
{
|
||||
private const int DIRECTORY_SIGNATURE = 0x06054B50;
|
||||
private const int ENTRY_SIGNATURE = 0x02014B50;
|
||||
|
||||
/// <summary>
|
||||
/// Unzips the archive at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
public static void Unzip(string path, string outputPath)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
|
||||
if (result != CrestronZIP.ResultCode.ZR_OK)
|
||||
throw new InvalidOperationException(result.ToString());
|
||||
#else
|
||||
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
|
||||
archive.ExtractToDirectory(outputPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of files names in the archive at the given path.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetFileNames(string path)
|
||||
{
|
||||
return GetEntries(path).Select(e => e.Name)
|
||||
.Where(f => !f.EndsWith("/"))
|
||||
.OrderBy(f => f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sequence of zip file entries in the archive at the given path.
|
||||
/// </summary>
|
||||
public static IEnumerable<IcdZipEntry> GetEntries(string path)
|
||||
{
|
||||
/*
|
||||
Copyright (c) 2012-2013 Alexey Yakovlev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using (IcdStream stream = IcdFile.OpenRead(path))
|
||||
{
|
||||
using (IcdBinaryReader reader = new IcdBinaryReader(stream))
|
||||
{
|
||||
if (stream.Length < 22)
|
||||
yield break;
|
||||
|
||||
stream.Seek(-22, eSeekOrigin.End);
|
||||
|
||||
// find directory signature
|
||||
while (reader.ReadInt32() != DIRECTORY_SIGNATURE)
|
||||
{
|
||||
if (stream.Position <= 5)
|
||||
yield break;
|
||||
|
||||
// move 1 byte back
|
||||
stream.Seek(-5, eSeekOrigin.Current);
|
||||
}
|
||||
|
||||
// read directory properties
|
||||
stream.Seek(6, eSeekOrigin.Current);
|
||||
ushort entries = reader.ReadUInt16();
|
||||
int difSize = reader.ReadInt32();
|
||||
uint dirOffset = reader.ReadUInt32();
|
||||
stream.Seek(dirOffset, eSeekOrigin.Begin);
|
||||
|
||||
// read directory entries
|
||||
for (int i = 0; i < entries; i++)
|
||||
{
|
||||
if (reader.ReadInt32() != ENTRY_SIGNATURE)
|
||||
continue;
|
||||
|
||||
// read file properties
|
||||
reader.ReadInt32();
|
||||
bool utf8 = (reader.ReadInt16() & 0x0800) != 0;
|
||||
short method = reader.ReadInt16();
|
||||
int timestamp = reader.ReadInt32();
|
||||
uint crc32 = reader.ReadUInt32();
|
||||
int compressedSize = reader.ReadInt32();
|
||||
int fileSize = reader.ReadInt32();
|
||||
short fileNameSize = reader.ReadInt16();
|
||||
short extraSize = reader.ReadInt16();
|
||||
short commentSize = reader.ReadInt16();
|
||||
int headerOffset = reader.ReadInt32();
|
||||
reader.ReadInt32();
|
||||
int fileHeaderOffset = reader.ReadInt32();
|
||||
byte[] fileNameBytes = reader.ReadBytes(fileNameSize);
|
||||
stream.Seek(extraSize, eSeekOrigin.Current);
|
||||
byte[] fileCommentBytes = reader.ReadBytes(commentSize);
|
||||
int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset);
|
||||
|
||||
// decode zip file entry
|
||||
Encoding encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
|
||||
|
||||
yield return new IcdZipEntry
|
||||
{
|
||||
Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length),
|
||||
Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length),
|
||||
Crc32 = crc32,
|
||||
CompressedSize = compressedSize,
|
||||
OriginalSize = fileSize,
|
||||
HeaderOffset = fileHeaderOffset,
|
||||
DataOffset = fileDataOffset,
|
||||
Deflated = method == 8,
|
||||
Timestamp = ConvertToDateTime(timestamp)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset)
|
||||
{
|
||||
long position = stream.Position;
|
||||
stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin);
|
||||
short fileNameSize = reader.ReadInt16();
|
||||
short extraSize = reader.ReadInt16();
|
||||
|
||||
int fileOffset = (int)stream.Position + fileNameSize + extraSize;
|
||||
stream.Seek(position, eSeekOrigin.Begin);
|
||||
return fileOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts DOS timestamp to a <see cref="DateTime"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="dosTimestamp">The DOS timestamp.</param>
|
||||
/// <returns>The <see cref="DateTime"/> instance.</returns>
|
||||
private static DateTime ConvertToDateTime(int dosTimestamp)
|
||||
{
|
||||
return new DateTime((dosTimestamp >> 25) + 1980,
|
||||
(dosTimestamp >> 21) & 15,
|
||||
(dosTimestamp >> 16) & 31,
|
||||
(dosTimestamp >> 11) & 31,
|
||||
(dosTimestamp >> 5) & 63,
|
||||
(dosTimestamp & 31) * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ICD.Common.Utils/IO/Compression/IcdZipEntry.cs
Normal file
59
ICD.Common.Utils/IO/Compression/IcdZipEntry.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.IO.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Zip archive entry.
|
||||
/// </summary>
|
||||
public sealed class IcdZipEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of a file or a directory.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment.
|
||||
/// </summary>
|
||||
public string Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CRC32.
|
||||
/// </summary>
|
||||
public uint Crc32 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compressed size of the file.
|
||||
/// </summary>
|
||||
public int CompressedSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original size of the file.
|
||||
/// </summary>
|
||||
public int OriginalSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="IcdZipEntry" /> is deflated.
|
||||
/// </summary>
|
||||
public bool Deflated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a directory.
|
||||
/// </summary>
|
||||
public bool IsDirectory { get { return Name.EndsWith("/"); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a file.
|
||||
/// </summary>
|
||||
public bool IsFile { get { return !IsDirectory; } }
|
||||
|
||||
public int HeaderOffset { get; set; }
|
||||
|
||||
public int DataOffset { get; set; }
|
||||
}
|
||||
}
|
||||
69
ICD.Common.Utils/IO/IcdBinaryReader.cs
Normal file
69
ICD.Common.Utils/IO/IcdBinaryReader.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public sealed class IcdBinaryReader : IDisposable
|
||||
{
|
||||
private readonly BinaryReader m_Reader;
|
||||
|
||||
public BinaryReader WrappedReader { get { return m_Reader; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
public IcdBinaryReader(IcdStream stream)
|
||||
: this(new BinaryReader(stream.WrappedStream))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
public IcdBinaryReader(BinaryReader reader)
|
||||
{
|
||||
m_Reader = reader;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Reader.Dispose();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_Reader.Close();
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
return m_Reader.ReadUInt16();
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
return m_Reader.ReadInt32();
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
return m_Reader.ReadInt16();
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
return m_Reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(short numberOfBytesToRead)
|
||||
{
|
||||
return m_Reader.ReadBytes(numberOfBytesToRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
#endif
|
||||
@@ -19,6 +20,19 @@ namespace ICD.Common.Utils.IO
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This gets the application root directory for Crestron systems
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetApplicationRootDirectory()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
return Directory.GetApplicationRootDirectory();
|
||||
#else
|
||||
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
if (path == null)
|
||||
|
||||
@@ -66,6 +66,11 @@ namespace ICD.Common.Utils.IO
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public static IcdStream OpenRead(string path)
|
||||
{
|
||||
return new IcdFileStream(File.OpenRead(path));
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IcdFileStream OpenWrite(string path)
|
||||
{
|
||||
@@ -89,5 +94,11 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
return new IcdFileStream(File.Create(path));
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IcdStreamWriter AppendText(string path)
|
||||
{
|
||||
return new IcdStreamWriter(File.AppendText(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
using System.Text;
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public sealed class IcdFileStream : IcdStream
|
||||
{
|
||||
public int Position { get; set; }
|
||||
|
||||
public FileStream WrappedFileStream { get { return WrappedStream as FileStream; } }
|
||||
|
||||
/// <summary>
|
||||
@@ -34,5 +33,11 @@ namespace ICD.Common.Utils.IO
|
||||
WrappedFileStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Write(string data, Encoding encoding)
|
||||
{
|
||||
byte[] info = encoding.GetBytes(data);
|
||||
WrappedFileStream.Write(info, 0, info.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public sealed class IcdMemoryStream : IcdStream
|
||||
{
|
||||
public int Position { get; set; }
|
||||
|
||||
public MemoryStream WrappedMemoryStream { get { return WrappedStream as MemoryStream; } }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
@@ -69,16 +70,16 @@ namespace ICD.Common.Utils.IO
|
||||
return Path.ChangeExtension(path, ext);
|
||||
}
|
||||
|
||||
public static string GetRelativePath(string folder, string filespec)
|
||||
{
|
||||
Uri pathUri = new Uri(filespec);
|
||||
// Folders must end in a slash
|
||||
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
folder += Path.DirectorySeparatorChar;
|
||||
}
|
||||
Uri folderUri = new Uri(folder);
|
||||
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
|
||||
}
|
||||
public static string GetRelativePath(string folder, string filespec)
|
||||
{
|
||||
Uri pathUri = new Uri(filespec);
|
||||
|
||||
// Folders must end in a slash
|
||||
if (!folder.EndsWith(Path.DirectorySeparatorChar))
|
||||
folder += Path.DirectorySeparatorChar;
|
||||
|
||||
Uri folderUri = new Uri(folder);
|
||||
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace ICD.Common.Utils.IO
|
||||
|
||||
public Stream WrappedStream { get { return m_Stream; } }
|
||||
|
||||
public long Length { get { return m_Stream.Length; } }
|
||||
|
||||
public long Position { get { return m_Stream.Position; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
@@ -29,5 +33,10 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
m_Stream.Dispose();
|
||||
}
|
||||
|
||||
public void Seek(long offset, eSeekOrigin seekOrigin)
|
||||
{
|
||||
m_Stream.Seek(offset, seekOrigin.ToSeekOrigin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
ICD.Common.Utils/IO/IcdStreamWriter.cs
Normal file
21
ICD.Common.Utils/IO/IcdStreamWriter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#elif STANDARD
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public sealed class IcdStreamWriter : IcdTextWriter
|
||||
{
|
||||
public StreamWriter WrappedStreamWriter { get { return WrappedTextWriter as StreamWriter; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="baseStreamWriter"></param>
|
||||
public IcdStreamWriter(StreamWriter baseStreamWriter) : base(baseStreamWriter)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#if SIMPLSHARP
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public class IcdTextReader
|
||||
public class IcdTextReader : IDisposable
|
||||
{
|
||||
private readonly TextReader m_TextReader;
|
||||
|
||||
@@ -17,5 +17,10 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
m_TextReader = textReader;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_TextReader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
ICD.Common.Utils/IO/eSeekOrigin.cs
Normal file
40
ICD.Common.Utils/IO/eSeekOrigin.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public enum eSeekOrigin
|
||||
{
|
||||
Begin,
|
||||
Current,
|
||||
End,
|
||||
}
|
||||
|
||||
public static class SeekOriginExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the seek origin enum to a system seek origin.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static SeekOrigin ToSeekOrigin(this eSeekOrigin extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case eSeekOrigin.Begin:
|
||||
return SeekOrigin.Begin;
|
||||
case eSeekOrigin.Current:
|
||||
return SeekOrigin.Current;
|
||||
case eSeekOrigin.End:
|
||||
return SeekOrigin.End;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
@@ -48,7 +50,7 @@ namespace ICD.Common.Utils
|
||||
Print(message);
|
||||
}
|
||||
#else
|
||||
Print(message, args);
|
||||
Print(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -74,7 +76,7 @@ namespace ICD.Common.Utils
|
||||
#else
|
||||
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
|
||||
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
|
||||
System.Console.Error.WriteLine(message);
|
||||
System.Console.WriteLine(message);
|
||||
System.Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
@@ -107,7 +109,7 @@ namespace ICD.Common.Utils
|
||||
#else
|
||||
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
|
||||
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
|
||||
System.Console.Error.Write(message);
|
||||
System.Console.Write(message);
|
||||
System.Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
@@ -121,6 +123,10 @@ namespace ICD.Common.Utils
|
||||
public static bool SendControlSystemCommand(string command, ref string result)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
// No console on VC4
|
||||
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
|
||||
return false;
|
||||
|
||||
return CrestronConsole.SendControlSystemCommand(command, ref result);
|
||||
#else
|
||||
return false;
|
||||
@@ -138,8 +144,10 @@ namespace ICD.Common.Utils
|
||||
return false;
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(str => callback(str), command, help, (ConsoleAccessLevelEnum)(int)accessLevel);
|
||||
#endif
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,12 @@ namespace ICD.Common.Utils
|
||||
|
||||
public static eRuntimeEnvironment RuntimeEnvironment
|
||||
{
|
||||
get { return GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment); }
|
||||
get
|
||||
{
|
||||
return CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
|
||||
? eRuntimeEnvironment.SimplSharpProMono
|
||||
: GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
SimplSharp,
|
||||
SimplSharpPro,
|
||||
SimplSharpProMono,
|
||||
Standard
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,6 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
public static class IcdErrorLog
|
||||
{
|
||||
private const string CONSOLE_RED = "\x1B[31;1m";
|
||||
private const string CONSOLE_GREEN = "\x1B[32;1m";
|
||||
private const string CONSOLE_YELLOW = "\x1B[33;1m";
|
||||
private const string CONSOLE_BLUE = "\x1B[34;1m";
|
||||
//private const string CONSOLE_MAGENTA = "\x1B[35;1m";
|
||||
private const string CONSOLE_CYAN = "\x1B[36;1m";
|
||||
//private const string CONSOLE_WHITE = "\x1B[37;1m";
|
||||
private const string CONSOLE_YELLOW_ON_RED_BACKGROUND = "\x1B[93;41m";
|
||||
private const string CONSOLE_RESET = "\x1B[0m";
|
||||
|
||||
private static readonly SafeCriticalSection s_LoggingSection;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +26,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_RED);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_RED);
|
||||
ErrorLog.Error(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Red;
|
||||
@@ -64,7 +54,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_YELLOW);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW);
|
||||
ErrorLog.Warn(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
@@ -92,7 +82,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_BLUE);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_BLUE);
|
||||
ErrorLog.Notice(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Blue;
|
||||
@@ -120,7 +110,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_GREEN);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_GREEN);
|
||||
ErrorLog.Ok(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Green;
|
||||
@@ -148,7 +138,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_YELLOW_ON_RED_BACKGROUND);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW_ON_RED_BACKGROUND);
|
||||
ErrorLog.Exception(message, ex);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
@@ -179,7 +169,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_CYAN);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_CYAN);
|
||||
ErrorLog.Info(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
@@ -207,7 +197,7 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
private static string FormatConsoleColor(string text, string color)
|
||||
{
|
||||
return string.Format("{0}{1}{2}", color, text, CONSOLE_RESET);
|
||||
return string.Format("{0}{1}{2}", color, text, ConsoleColorExtensions.CONSOLE_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
#if !SIMPLSHARP
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
@@ -62,6 +66,7 @@ namespace ICD.Common.Utils
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdUriBuilder()
|
||||
: this((Uri)null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,7 +75,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
public IcdUriBuilder(string uri)
|
||||
: this(new Uri(uri))
|
||||
: this(new Uri(uri, UriKind.RelativeOrAbsolute))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,6 +85,12 @@ namespace ICD.Common.Utils
|
||||
/// <param name="uri"></param>
|
||||
public IcdUriBuilder(Uri uri)
|
||||
{
|
||||
if (uri == null)
|
||||
return;
|
||||
|
||||
if (!uri.IsAbsoluteUri)
|
||||
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
|
||||
|
||||
Fragment = uri.Fragment;
|
||||
Host = uri.Host;
|
||||
Password = uri.GetPassword();
|
||||
@@ -103,7 +114,7 @@ namespace ICD.Common.Utils
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Scheme
|
||||
string scheme = string.IsNullOrEmpty(Scheme) ? "http" : Scheme;
|
||||
string scheme = string.IsNullOrEmpty(Scheme) ? Uri.UriSchemeHttp : Scheme;
|
||||
builder.Append(scheme);
|
||||
builder.Append(':');
|
||||
|
||||
@@ -152,6 +163,125 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the given path to the current path, ensuring only one separator between parts.
|
||||
/// </summary>
|
||||
/// <param name="parts"></param>
|
||||
/// <returns></returns>
|
||||
public void AppendPath(params string[] parts)
|
||||
{
|
||||
parts = parts.Prepend(Path).ToArray(parts.Length + 1);
|
||||
Path = Combine(parts);
|
||||
}
|
||||
|
||||
#region Flurl
|
||||
|
||||
// The following region is taken from Flurl https://github.com/tmenier/Flurl
|
||||
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Todd Menier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Basically a Path.Combine for URLs. Ensures exactly one '/' seperates each segment,
|
||||
/// and exactly on '&' seperates each query parameter.
|
||||
/// URL-encodes illegal characters but not reserved characters.
|
||||
/// </summary>
|
||||
/// <param name="parts">URL parts to combine.</param>
|
||||
public static string Combine(params string[] parts)
|
||||
{
|
||||
if (parts == null)
|
||||
throw new ArgumentNullException("parts");
|
||||
|
||||
string result = "";
|
||||
bool inQuery = false, inFragment = false;
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (string.IsNullOrEmpty(part))
|
||||
continue;
|
||||
|
||||
if (result.EndsWith("?") || part.StartsWith("?"))
|
||||
result = CombineEnsureSingleSeperator(result, part, '?');
|
||||
else if (result.EndsWith("#") || part.StartsWith("#"))
|
||||
result = CombineEnsureSingleSeperator(result, part, '#');
|
||||
else if (inFragment)
|
||||
result += part;
|
||||
else if (inQuery)
|
||||
result = CombineEnsureSingleSeperator(result, part, '&');
|
||||
else
|
||||
result = CombineEnsureSingleSeperator(result, part, '/');
|
||||
|
||||
if (part.Contains("#"))
|
||||
{
|
||||
inQuery = false;
|
||||
inFragment = true;
|
||||
}
|
||||
else if (!inFragment && part.Contains("?"))
|
||||
{
|
||||
inQuery = true;
|
||||
}
|
||||
}
|
||||
return EncodeIllegalCharacters(result, false);
|
||||
}
|
||||
|
||||
private static string CombineEnsureSingleSeperator(string a, string b, char seperator)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a)) return b;
|
||||
if (string.IsNullOrEmpty(b)) return a;
|
||||
return a.TrimEnd(seperator) + seperator + b.TrimStart(seperator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL-encodes characters in a string that are neither reserved nor unreserved. Avoids encoding reserved characters such as '/' and '?'. Avoids encoding '%' if it begins a %-hex-hex sequence (i.e. avoids double-encoding).
|
||||
/// </summary>
|
||||
/// <param name="s">The string to encode.</param>
|
||||
/// <param name="encodeSpaceAsPlus">If true, spaces will be encoded as + signs. Otherwise, they'll be encoded as %20.</param>
|
||||
/// <returns>The encoded URL.</returns>
|
||||
private static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus) {
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return s;
|
||||
|
||||
if (encodeSpaceAsPlus)
|
||||
s = s.Replace(" ", "+");
|
||||
|
||||
// Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
|
||||
// in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600
|
||||
|
||||
// no % characters, so avoid the regex overhead
|
||||
if (!s.Contains("%"))
|
||||
return Uri.EscapeUriString(s);
|
||||
|
||||
// pick out all %-hex-hex matches and avoid double-encoding
|
||||
return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
|
||||
var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
|
||||
var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
|
||||
return Uri.EscapeUriString(a) + b;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Utils for managing archives.
|
||||
/// </summary>
|
||||
public static class IcdZip
|
||||
{
|
||||
/// <summary>
|
||||
/// Unzips the archive at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
/// <param name="message"></param>
|
||||
public static bool Unzip(string path, string outputPath, out string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
|
||||
message = result.ToString();
|
||||
return result == CrestronZIP.ResultCode.ZR_OK;
|
||||
#else
|
||||
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
|
||||
archive.ExtractToDirectory(outputPath);
|
||||
message = "Success";
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message = e.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,7 @@ namespace ICD.Common.Utils.Json
|
||||
/// Creates a new instance of T.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual T Instantiate()
|
||||
{
|
||||
return ReflectionUtils.CreateInstance<T>();
|
||||
}
|
||||
protected abstract T Instantiate();
|
||||
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object.
|
||||
@@ -47,6 +44,18 @@ namespace ICD.Common.Utils.Json
|
||||
[PublicAPI]
|
||||
public virtual void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
|
||||
{
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
if (serializer == null)
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteStartObject();
|
||||
{
|
||||
WriteProperties(writer, value, serializer);
|
||||
@@ -98,6 +107,12 @@ namespace ICD.Common.Utils.Json
|
||||
[PublicAPI]
|
||||
public virtual T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (serializer == null)
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
T output = default(T);
|
||||
bool instantiated = false;
|
||||
|
||||
@@ -120,12 +135,7 @@ namespace ICD.Common.Utils.Json
|
||||
// Read into the value
|
||||
reader.Read();
|
||||
|
||||
switch (property)
|
||||
{
|
||||
default:
|
||||
ReadProperty(property, reader, output, serializer);
|
||||
break;
|
||||
}
|
||||
ReadProperty(property, reader, output, serializer);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -71,7 +72,26 @@ namespace ICD.Common.Utils.Json
|
||||
string itemString = (string)token.SelectToken(ITEM_TOKEN);
|
||||
Type type = Type.GetType(typeString);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
typeString = typeString.Replace("_SimplSharp", "").Replace("_NetStandard", "");
|
||||
type = Type.GetType(typeString);
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
typeString = AddSimplSharpSuffix(typeString);
|
||||
type = Type.GetType(typeString);
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject(itemString, type);
|
||||
}
|
||||
|
||||
private static string AddSimplSharpSuffix(string typeString)
|
||||
{
|
||||
return Regex.Replace(typeString,
|
||||
"(?'prefix'[^,]+, )(?'assembly'[^,]*)(?'suffix', .*)",
|
||||
"${prefix}${assembly}_SimplSharp${suffix}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Properties;
|
||||
@@ -16,9 +15,6 @@ namespace ICD.Common.Utils.Json
|
||||
[PublicAPI]
|
||||
public static class JsonUtils
|
||||
{
|
||||
// 2016-02-26T19:24:59
|
||||
private const string DATE_FORMAT = @"yyyy-MM-dd\THH:mm:ss";
|
||||
|
||||
private const string MESSAGE_NAME_PROPERTY = "m";
|
||||
private const string MESSAGE_DATA_PROPERTY = "d";
|
||||
|
||||
@@ -44,6 +40,16 @@ namespace ICD.Common.Utils.Json
|
||||
JsonConvert.DeserializeObject(serialized, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data as a DateTime value.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime ParseDateTime(string data)
|
||||
{
|
||||
return DateTime.Parse(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token as a DateTime value.
|
||||
/// </summary>
|
||||
@@ -55,11 +61,7 @@ namespace ICD.Common.Utils.Json
|
||||
if (token == null)
|
||||
throw new ArgumentNullException("token");
|
||||
|
||||
#if SIMPLSHARP
|
||||
return DateTime.ParseExact((string)token, DATE_FORMAT, CultureInfo.CurrentCulture);
|
||||
#else
|
||||
return (DateTime)token;
|
||||
#endif
|
||||
return ParseDateTime((string)token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,6 +83,10 @@ namespace ICD.Common.Utils.Json
|
||||
output = ParseDateTime(token);
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
return false;
|
||||
@@ -205,8 +211,11 @@ namespace ICD.Common.Utils.Json
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
using (JsonTextWriter writer = new JsonTextWriter(new IcdStringWriter(builder).WrappedStringWriter))
|
||||
serializeMethod(writer);
|
||||
using (IcdStringWriter stringWriter = new IcdStringWriter(builder))
|
||||
{
|
||||
using (JsonTextWriter writer = new JsonTextWriter(stringWriter.WrappedStringWriter))
|
||||
serializeMethod(writer);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
@@ -224,8 +233,11 @@ namespace ICD.Common.Utils.Json
|
||||
if (deserializeMethod == null)
|
||||
throw new ArgumentNullException("deserializeMethod");
|
||||
|
||||
using (JsonTextReader reader = new JsonTextReader(new IcdStringReader(json).WrappedStringReader))
|
||||
return deserializeMethod(reader);
|
||||
using (IcdStringReader stringReader = new IcdStringReader(json))
|
||||
{
|
||||
using (JsonTextReader reader = new JsonTextReader(stringReader.WrappedStringReader))
|
||||
return deserializeMethod(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -137,30 +137,6 @@ namespace ICD.Common.Utils
|
||||
0.0f, 1.0f, new TimeSpan(value.Ticks).TotalSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the digit count for the given number.
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetNumberOfDigits(int number)
|
||||
{
|
||||
int output = number.ToString().Length;
|
||||
if (number < 0)
|
||||
output--;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the digit count for the given number.
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetNumberOfDigits(uint number)
|
||||
{
|
||||
return number.ToString().Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a sequence of numbers:
|
||||
/// 1, 3, 5, 6, 7, 8, 9, 10, 12
|
||||
@@ -172,18 +148,29 @@ namespace ICD.Common.Utils
|
||||
if (numbers == null)
|
||||
throw new ArgumentNullException("numbers");
|
||||
|
||||
return GetRangesIterator(numbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a sequence of numbers:
|
||||
/// 1, 3, 5, 6, 7, 8, 9, 10, 12
|
||||
/// And calculates the continuous ranges:
|
||||
/// (1, 1), (3, 3), (5, 10), (12, 12)
|
||||
/// </summary>
|
||||
private static IEnumerable<int[]> GetRangesIterator(IEnumerable<int> numbers)
|
||||
{
|
||||
int[] currentRange = null;
|
||||
|
||||
foreach (int number in numbers.Order())
|
||||
{
|
||||
if (currentRange == null)
|
||||
currentRange = new[] {number, number};
|
||||
currentRange = new[] { number, number };
|
||||
else if (currentRange[1] == number - 1)
|
||||
currentRange = new[] {currentRange[0], number};
|
||||
currentRange = new[] { currentRange[0], number };
|
||||
else
|
||||
{
|
||||
yield return currentRange;
|
||||
currentRange = new[] {number, number};
|
||||
currentRange = new[] { number, number };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,15 @@ namespace ICD.Common.Utils
|
||||
/// Gets the path to the root directory of the processor.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static string RootPath { get { return IcdDirectory.GetDirectoryRoot("\\"); } }
|
||||
public static string RootPath {
|
||||
get
|
||||
{
|
||||
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
|
||||
return IcdDirectory.GetApplicationRootDirectory();
|
||||
|
||||
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the program directory
|
||||
@@ -26,10 +34,23 @@ namespace ICD.Common.Utils
|
||||
public static string ProgramPath { get { return IcdDirectory.GetApplicationDirectory(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the NVRAM directory.
|
||||
/// Gets the path to the root config directory,
|
||||
/// which contains common and program-specific config directories.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static string NvramPath { get { return Join(RootPath, "NVRAM"); } }
|
||||
public static string RootConfigPath
|
||||
{
|
||||
get
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
return Join(RootPath, "User");
|
||||
#elif LINUX
|
||||
return Join(RootPath, "opt", "ICD.Connect");
|
||||
#else
|
||||
return Join(RootPath, "ProgramData", "ICD.Connect");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute path to the configuration directory.
|
||||
@@ -40,8 +61,11 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
|
||||
return Join(RootConfigPath, "ProgramConfig");
|
||||
|
||||
string directoryName = string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
|
||||
return Join(NvramPath, directoryName);
|
||||
return Join(RootConfigPath, directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +73,7 @@ namespace ICD.Common.Utils
|
||||
/// Returns the absolute path to the common configuration directory.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static string CommonConfigPath { get { return Join(NvramPath, "CommonConfig"); } }
|
||||
public static string CommonConfigPath { get { return Join(RootConfigPath, "CommonConfig"); } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute path to the common config library directory.
|
||||
|
||||
@@ -97,8 +97,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void RestartProgram()
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,8 +106,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void Reboot()
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace ICD.Common.Utils
|
||||
return output;
|
||||
}
|
||||
|
||||
foreach (string line in progInfo.Split(new[] {"\n\r", "\r\n", "\n", "\r"}))
|
||||
foreach (string line in progInfo.Split(new[] {'\r', '\n'}))
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
|
||||
@@ -8,17 +8,11 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
public static partial class ProgramUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the program number.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static uint ProgramNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the program number.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static uint ProgramNumber { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compile date of the program.
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace ICD.Common.Utils
|
||||
name = name.Substring(0, proLength).PadRight(26);
|
||||
break;
|
||||
|
||||
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
|
||||
// No console
|
||||
return;
|
||||
|
||||
case IcdEnvironment.eRuntimeEnvironment.Standard:
|
||||
name += ' ';
|
||||
break;
|
||||
|
||||
@@ -3,5 +3,5 @@ using System.Reflection;
|
||||
[assembly: AssemblyTitle("ICD.Common.Utils")]
|
||||
[assembly: AssemblyCompany("ICD Systems")]
|
||||
[assembly: AssemblyProduct("ICD.Common.Utils")]
|
||||
[assembly: AssemblyCopyright("Copyright © ICD Systems 2018")]
|
||||
[assembly: AssemblyVersion("3.4.0.0")]
|
||||
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
|
||||
[assembly: AssemblyVersion("8.3.1.0")]
|
||||
|
||||
@@ -3,7 +3,6 @@ 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
|
||||
{
|
||||
@@ -71,22 +70,68 @@ namespace ICD.Common.Utils
|
||||
if (node == null)
|
||||
throw new ArgumentNullException("node");
|
||||
|
||||
if (visited.Contains(node))
|
||||
return GetCliqueIterator(map, visited, node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the clique containing the node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="visited"></param>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> GetCliqueIterator<T>(IDictionary<T, IEnumerable<T>> map, IcdHashSet<T> visited, T node)
|
||||
{
|
||||
if (!visited.Add(node))
|
||||
yield break;
|
||||
|
||||
if (!map.ContainsKey(node))
|
||||
IEnumerable<T> adjacent;
|
||||
if (!map.TryGetValue(node, out adjacent))
|
||||
yield break;
|
||||
|
||||
visited.Add(node);
|
||||
|
||||
yield return node;
|
||||
|
||||
IEnumerable<T> adjacent = map.GetDefault(node, Enumerable.Empty<T>());
|
||||
|
||||
foreach (T item in adjacent.SelectMany(a => GetClique(map, visited, a)))
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a path from the given root to the given child node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="child"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <returns></returns>
|
||||
public static bool BreadthFirstSearch<T>(T root, T child, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
return BreadthFirstSearchPath(root, child, getChildren) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a path from the given root to the given child node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static bool BreadthFirstSearch<T>(T root, T destination, Func<T, IEnumerable<T>> getChildren, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return BreadthFirstSearchPath(root, destination, getChildren, comparer) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the nodes in the tree via breadth-first search.
|
||||
/// </summary>
|
||||
@@ -99,6 +144,18 @@ namespace ICD.Common.Utils
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
return BreadthFirstSearchIterator(root, getChildren);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the nodes in the tree via breadth-first search.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> BreadthFirstSearchIterator<T>(T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
Queue<T> process = new Queue<T>();
|
||||
process.Enqueue(root);
|
||||
|
||||
@@ -155,7 +212,7 @@ namespace ICD.Common.Utils
|
||||
Queue<T> queue = new Queue<T>();
|
||||
queue.Enqueue(root);
|
||||
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
@@ -178,26 +235,38 @@ namespace ICD.Common.Utils
|
||||
return null;
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchManyDestinations<T>(T root,
|
||||
IEnumerable<T> destinations,
|
||||
Func<T, IEnumerable<T>>
|
||||
getChildren)
|
||||
/// <summary>
|
||||
/// Returns the shortest path from root to each destination via breadth-first search.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="destinations"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<KeyValuePair<T, IEnumerable<T>>>
|
||||
BreadthFirstSearchPathManyDestinations<T>(T root, IEnumerable<T> destinations, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
if (destinations == null)
|
||||
throw new ArgumentNullException("destinations");
|
||||
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchPathManyDestinations<T>(T root,
|
||||
IEnumerable<T>
|
||||
destinations,
|
||||
Func<T, IEnumerable<T>>
|
||||
getChildren,
|
||||
IEqualityComparer<T>
|
||||
comparer)
|
||||
/// <summary>
|
||||
/// Returns the shortest path from root to each destination via breadth-first search.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="destinations"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<KeyValuePair<T, IEnumerable<T>>>
|
||||
BreadthFirstSearchPathManyDestinations<T>(T root, IEnumerable<T> destinations, Func<T, IEnumerable<T>> getChildren,
|
||||
IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (destinations == null)
|
||||
throw new ArgumentNullException("destinations");
|
||||
@@ -209,27 +278,22 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
IcdHashSet<T> destinationsToBeProcessed = new IcdHashSet<T>(destinations);
|
||||
IcdHashSet<T> destinationsProcessed = new IcdHashSet<T>();
|
||||
Dictionary<T, IEnumerable<T>> pathsToReturn = new Dictionary<T, IEnumerable<T>>();
|
||||
|
||||
// Edge case, root is the destination
|
||||
foreach (T destination in
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)))
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)).ToArray())
|
||||
{
|
||||
destinationsProcessed.Add(destination);
|
||||
pathsToReturn.Add(destination, new[] {root});
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
yield return new KeyValuePair<T, IEnumerable<T>>(destination, new[] {root});
|
||||
}
|
||||
|
||||
foreach (T destination in destinationsProcessed)
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
destinationsProcessed.Clear();
|
||||
if (destinationsToBeProcessed.Count == 0)
|
||||
return pathsToReturn;
|
||||
yield break;
|
||||
|
||||
Queue<T> queue = new Queue<T>();
|
||||
queue.Enqueue(root);
|
||||
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
@@ -242,23 +306,18 @@ namespace ICD.Common.Utils
|
||||
|
||||
T closureNode = node;
|
||||
foreach (T destination in
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)))
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)).ToArray())
|
||||
{
|
||||
destinationsProcessed.Add(destination);
|
||||
pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
|
||||
}
|
||||
|
||||
foreach (T destination in destinationsProcessed)
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
|
||||
destinationsProcessed.Clear();
|
||||
yield return
|
||||
new KeyValuePair<T, IEnumerable<T>>(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
|
||||
}
|
||||
|
||||
if (destinationsToBeProcessed.Count == 0)
|
||||
return pathsToReturn;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
return pathsToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.IO;
|
||||
#if SIMPLSHARP
|
||||
@@ -8,7 +9,6 @@ using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using Activator = Crestron.SimplSharp.Reflection.Activator;
|
||||
#else
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System.Runtime.Loader;
|
||||
@@ -33,18 +33,11 @@ namespace ICD.Common.Utils
|
||||
if (parameters == null)
|
||||
throw new ArgumentNullException("parameters");
|
||||
|
||||
#if SIMPLSHARP
|
||||
IEnumerable<CType>
|
||||
#else
|
||||
IEnumerable<Type>
|
||||
#endif
|
||||
parameterTypes = constructor.GetParameters().Select(p => p.ParameterType);
|
||||
|
||||
return ParametersMatchTypes(parameterTypes, parameters);
|
||||
return MatchesMethodParameters(constructor, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the parameters match the method parameters.
|
||||
/// Returns true if the parameter values match the method signature parameters.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="parameters"></param>
|
||||
@@ -78,14 +71,7 @@ namespace ICD.Common.Utils
|
||||
if (property == null)
|
||||
throw new ArgumentNullException("property");
|
||||
|
||||
#if SIMPLSHARP
|
||||
CType
|
||||
#else
|
||||
Type
|
||||
#endif
|
||||
propertyType = property.PropertyType;
|
||||
|
||||
return ParametersMatchTypes(new[] {propertyType}, new[] {parameter});
|
||||
return ParameterMatchesType(property.PropertyType, parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,7 +83,7 @@ namespace ICD.Common.Utils
|
||||
private static bool ParametersMatchTypes(
|
||||
#if SIMPLSHARP
|
||||
IEnumerable<CType>
|
||||
#else
|
||||
#else
|
||||
IEnumerable<Type>
|
||||
#endif
|
||||
types, IEnumerable<object> parameters)
|
||||
@@ -109,21 +95,21 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("parameters");
|
||||
|
||||
#if SIMPLSHARP
|
||||
CType[]
|
||||
IList<CType>
|
||||
#else
|
||||
Type[]
|
||||
IList<Type>
|
||||
#endif
|
||||
typesArray = types as
|
||||
#if SIMPLSHARP
|
||||
CType[]
|
||||
IList<CType>
|
||||
#else
|
||||
Type[]
|
||||
IList<Type>
|
||||
#endif
|
||||
?? types.ToArray();
|
||||
?? types.ToArray();
|
||||
|
||||
object[] parametersArray = parameters as object[] ?? parameters.ToArray();
|
||||
IList<object> parametersArray = parameters as IList<object> ?? parameters.ToArray();
|
||||
|
||||
if (parametersArray.Length != typesArray.Length)
|
||||
if (parametersArray.Count != typesArray.Count)
|
||||
return false;
|
||||
|
||||
// Compares each pair of items in the two arrays.
|
||||
@@ -175,26 +161,6 @@ namespace ICD.Common.Utils
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given type has a public parameterless constructor.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPublicParameterlessConstructor(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
const BindingFlags binding = BindingFlags.Instance | BindingFlags.Public;
|
||||
|
||||
#if SIMPLSHARP
|
||||
return ((CType)type).GetConstructor(binding, null, new CType[0], null)
|
||||
#else
|
||||
return type.GetConstructor(binding, null, new Type[0], null)
|
||||
#endif
|
||||
!= null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Platform independant delegate instantiation.
|
||||
/// </summary>
|
||||
@@ -202,6 +168,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="firstArgument"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method)
|
||||
{
|
||||
return
|
||||
@@ -213,11 +180,25 @@ namespace ICD.Common.Utils
|
||||
.CreateDelegate(type, firstArgument, method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the given type, calling the default constructor.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static T CreateInstance<T>(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return (T)CreateInstance(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the given type, calling the default constructor.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static T CreateInstance<T>(params object[] parameters)
|
||||
{
|
||||
if (parameters == null)
|
||||
@@ -230,6 +211,7 @@ namespace ICD.Common.Utils
|
||||
/// Creates an instance of the given type, calling the default constructor.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static object CreateInstance(Type type, params object[] parameters)
|
||||
{
|
||||
if (type == null)
|
||||
@@ -238,63 +220,49 @@ namespace ICD.Common.Utils
|
||||
if (parameters == null)
|
||||
throw new ArgumentNullException("parameters");
|
||||
|
||||
ConstructorInfo constructor =
|
||||
type
|
||||
#if SIMPLSHARP
|
||||
.GetCType()
|
||||
#endif
|
||||
.GetConstructors()
|
||||
.FirstOrDefault(c => MatchesConstructorParameters(c, parameters));
|
||||
ConstructorInfo constructor = GetConstructor(type, parameters);
|
||||
|
||||
try
|
||||
{
|
||||
if (constructor != null)
|
||||
return constructor.Invoke(parameters);
|
||||
return constructor.Invoke(parameters);
|
||||
}
|
||||
catch (TypeLoadException e)
|
||||
{
|
||||
throw new TypeLoadException(e.GetBaseException().Message);
|
||||
throw e.GetBaseException();
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.GetBaseException();
|
||||
}
|
||||
|
||||
string message = string.Format("Unable to find constructor for {0}", type.Name);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the given type, calling the default constructor.
|
||||
/// Gets the constructor matching the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static T CreateInstance<T>(Type type)
|
||||
[NotNull]
|
||||
public static ConstructorInfo GetConstructor(Type type, params object[] parameters)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsAssignableTo(typeof(T)))
|
||||
throw new InvalidOperationException("Type is not assignable to T");
|
||||
if (parameters == null)
|
||||
throw new ArgumentNullException("parameters");
|
||||
|
||||
return (T)CreateInstance(type);
|
||||
}
|
||||
ConstructorInfo info;
|
||||
|
||||
if(!type
|
||||
#if SIMPLSHARP
|
||||
.GetCType()
|
||||
#endif
|
||||
.GetConstructors()
|
||||
.Where(c => MatchesConstructorParameters(c, parameters))
|
||||
.TryFirst(out info))
|
||||
throw new ArgumentException("Couldn't find a constructor matching the given parameters.");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom attributes added to the given assembly.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="assembly"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetCustomAttributes<T>(Assembly assembly)
|
||||
where T : Attribute
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException("assembly");
|
||||
|
||||
try
|
||||
{
|
||||
return assembly.GetCustomAttributes<T>();
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -302,6 +270,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static Assembly LoadAssemblyFromPath(string path)
|
||||
{
|
||||
if (path == null)
|
||||
@@ -336,39 +305,6 @@ namespace ICD.Common.Utils
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the corresponding property info on the given type.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
public static PropertyInfo GetImplementation(Type type, PropertyInfo property)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (property == null)
|
||||
throw new ArgumentNullException("property");
|
||||
|
||||
if (type.IsInterface)
|
||||
throw new InvalidOperationException("Type must not be an interface");
|
||||
|
||||
property = type
|
||||
#if SIMPLSHARP
|
||||
.GetCType()
|
||||
#else
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.GetProperty(property.Name, property.PropertyType);
|
||||
|
||||
if (property == null)
|
||||
return null;
|
||||
|
||||
return property.DeclaringType == type
|
||||
? property
|
||||
: GetImplementation(property.DeclaringType, property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the given value to the given type.
|
||||
/// </summary>
|
||||
@@ -423,6 +359,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="handler">The instance with the callback MethodInfo. Null for static types.</param>
|
||||
/// <param name="callback">The MethodInfo for the callback method.</param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback)
|
||||
{
|
||||
if (eventInfo == null)
|
||||
|
||||
115
ICD.Common.Utils/RegexUtils.cs
Normal file
115
ICD.Common.Utils/RegexUtils.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class RegexUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Shim to perform Match and Matches in one call.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="match"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Matches(string input, string pattern, out Match match)
|
||||
{
|
||||
match = Regex.Match(input, pattern);
|
||||
return match.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shim to perform Match and Matches in one call.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="match"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Matches(string input, string pattern, RegexOptions options, out Match match)
|
||||
{
|
||||
match = Regex.Match(input, pattern, options);
|
||||
return match.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the pattern to replace the specified group with the provided replacement string.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="groupName"></param>
|
||||
/// <param name="replacement"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceGroup(string input, string pattern, string groupName, string replacement)
|
||||
{
|
||||
return ReplaceGroup(input, pattern, groupName, replacement, RegexOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the pattern to replace the specified group with the provided replacement string.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="groupName"></param>
|
||||
/// <param name="replacement"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceGroup(string input, string pattern, string groupName, Func<Match, string> replacement)
|
||||
{
|
||||
return ReplaceGroup(input, pattern, groupName, replacement, RegexOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the pattern to replace the specified group with the provided replacement string.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="groupName"></param>
|
||||
/// <param name="replacement"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceGroup(string input, string pattern, string groupName, string replacement, RegexOptions options)
|
||||
{
|
||||
return ReplaceGroup(input, pattern, groupName, match => replacement, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the pattern to replace the specified group with the provided replacement string.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="groupName"></param>
|
||||
/// <param name="replacement"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceGroup(string input, string pattern, string groupName, Func<Match, string> replacement, RegexOptions options)
|
||||
{
|
||||
MatchEvaluator evaluator =
|
||||
m =>
|
||||
{
|
||||
string replacementString = replacement(m);
|
||||
|
||||
Group group = m.Groups[groupName];
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int previousCaptureEnd = 0;
|
||||
foreach (Capture capture in group.Captures.Cast<Capture>())
|
||||
{
|
||||
int currentCaptureEnd = capture.Index + capture.Length - m.Index;
|
||||
int currentCaptureLength = capture.Index - m.Index - previousCaptureEnd;
|
||||
|
||||
sb.Append(m.Value.Substring(previousCaptureEnd, currentCaptureLength));
|
||||
sb.Append(replacementString);
|
||||
|
||||
previousCaptureEnd = currentCaptureEnd;
|
||||
}
|
||||
sb.Append(m.Value.Substring(previousCaptureEnd));
|
||||
|
||||
return sb.ToString();
|
||||
};
|
||||
|
||||
return Regex.Replace(input, pattern, evaluator, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace ICD.Common.Utils
|
||||
private readonly object m_Instance;
|
||||
|
||||
private readonly List<string> m_PropertyOrder;
|
||||
private readonly Dictionary<string, object> m_PropertyValues;
|
||||
private readonly Dictionary<string, string> m_PropertyValues;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
@@ -24,11 +24,11 @@ namespace ICD.Common.Utils
|
||||
m_Instance = instance;
|
||||
|
||||
m_PropertyOrder = new List<string>();
|
||||
m_PropertyValues = new Dictionary<string, object>();
|
||||
m_PropertyValues = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the property with the given name and value to the
|
||||
/// Adds the property with the given name and value to the builder.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
@@ -37,6 +37,21 @@ namespace ICD.Common.Utils
|
||||
m_PropertyOrder.Remove(name);
|
||||
m_PropertyOrder.Add(name);
|
||||
|
||||
string valueString = GetValueStringRepresentation(value);
|
||||
|
||||
m_PropertyValues[name] = valueString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the property with the given name and value to the builder without any additonal formatting.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
public void AppendPropertyRaw(string name, string value)
|
||||
{
|
||||
m_PropertyOrder.Remove(name);
|
||||
m_PropertyOrder.Add(name);
|
||||
|
||||
m_PropertyValues[name] = value;
|
||||
}
|
||||
|
||||
@@ -60,8 +75,7 @@ namespace ICD.Common.Utils
|
||||
builder.Append(property);
|
||||
builder.Append('=');
|
||||
|
||||
object value = m_PropertyValues[property];
|
||||
string valueString = GetValueStringRepresentation(value);
|
||||
string valueString = m_PropertyValues[property];
|
||||
builder.Append(valueString);
|
||||
|
||||
if (index < m_PropertyOrder.Count - 1)
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Releasing a disposed mutex in this case is valid behaviour
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
/// <summary>
|
||||
/// Log Entry Item
|
||||
/// </summary>
|
||||
public struct LogItem
|
||||
public struct LogItem : IEquatable<LogItem>
|
||||
{
|
||||
private readonly string m_Message;
|
||||
private readonly eSeverity m_Severity;
|
||||
@@ -93,7 +93,14 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(LogItem a1, LogItem a2)
|
||||
{
|
||||
return !(a1 == a2);
|
||||
return !a1.Equals(a2);
|
||||
}
|
||||
|
||||
public bool Equals(LogItem other)
|
||||
{
|
||||
return m_Severity == other.m_Severity &&
|
||||
m_Timestamp == other.m_Timestamp &&
|
||||
m_Message == other.m_Message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -103,10 +110,7 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (other == null || GetType() != other.GetType())
|
||||
return false;
|
||||
|
||||
return GetHashCode() == ((LogItem)other).GetHashCode();
|
||||
return other is LogItem && Equals((LogItem)other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Services.Scheduler
|
||||
{
|
||||
public abstract class AbstractScheduledAction : IScheduledAction
|
||||
{
|
||||
public event EventHandler OnScheduledRunTimeChanged;
|
||||
|
||||
private DateTime? m_NextRunTime;
|
||||
|
||||
public DateTime? NextRunTime
|
||||
{
|
||||
get { return m_NextRunTime; }
|
||||
private set
|
||||
{
|
||||
if (m_NextRunTime == value)
|
||||
return;
|
||||
|
||||
m_NextRunTime = value;
|
||||
|
||||
OnScheduledRunTimeChanged.Raise(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
RunFinal();
|
||||
NextRunTime = GetNextRunTime();
|
||||
}
|
||||
|
||||
public void UpdateNextRunTime()
|
||||
{
|
||||
NextRunTime = GetNextRunTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs when the action has hit its scheduled time
|
||||
/// </summary>
|
||||
public abstract void RunFinal();
|
||||
|
||||
/// <summary>
|
||||
/// Runs after RunFinal in order to set the next run time of this action
|
||||
/// </summary>
|
||||
public abstract DateTime? GetNextRunTime();
|
||||
}
|
||||
}
|
||||
210
ICD.Common.Utils/Services/Scheduler/ActionSchedulerService.cs
Normal file
210
ICD.Common.Utils/Services/Scheduler/ActionSchedulerService.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Services.Logging;
|
||||
using ICD.Common.Utils.Timers;
|
||||
|
||||
namespace ICD.Common.Utils.Services.Scheduler
|
||||
{
|
||||
public sealed class ActionSchedulerService : IActionSchedulerService, IDisposable
|
||||
{
|
||||
private readonly List<IScheduledAction> m_Actions;
|
||||
private readonly SafeTimer m_Timer;
|
||||
private readonly SafeCriticalSection m_CriticalSection;
|
||||
|
||||
private DateTime m_LastRunTime;
|
||||
|
||||
public ActionSchedulerService()
|
||||
{
|
||||
m_Actions = new List<IScheduledAction>();
|
||||
m_Timer = new SafeTimer(TimerCallback, -1);
|
||||
m_CriticalSection = new SafeCriticalSection();
|
||||
m_LastRunTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
|
||||
m_Timer.Stop();
|
||||
m_Timer.Dispose();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Add(IScheduledAction action)
|
||||
{
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
Subscribe(action);
|
||||
m_Actions.AddSorted(action, a => a.NextRunTime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
RescheduleTimer();
|
||||
}
|
||||
|
||||
public void Remove(IScheduledAction action)
|
||||
{
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
Unsubscribe(action);
|
||||
m_Actions.Remove(action);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
RescheduleTimer();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
foreach (IScheduledAction action in m_Actions)
|
||||
{
|
||||
Unsubscribe(action);
|
||||
}
|
||||
|
||||
m_Actions.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
RescheduleTimer();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "ActionSchedulerService";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void TimerCallback()
|
||||
{
|
||||
DateTime currentTime = IcdEnvironment.GetLocalTime();
|
||||
IScheduledAction[] actionsToRun;
|
||||
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
actionsToRun = m_Actions
|
||||
.Where(a => a.NextRunTime <= currentTime && a.NextRunTime > m_LastRunTime)
|
||||
.OrderBy(a => a.NextRunTime)
|
||||
.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
// leave the critical section so any actions that run can enter
|
||||
foreach (IScheduledAction action in actionsToRun)
|
||||
{
|
||||
try
|
||||
{
|
||||
action.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(eSeverity.Error, ex, "Error occurred while running scheduled action");
|
||||
}
|
||||
}
|
||||
|
||||
m_LastRunTime = currentTime;
|
||||
RescheduleTimer();
|
||||
}
|
||||
|
||||
private void RescheduleTimer()
|
||||
{
|
||||
// enter again to check the closest next run time
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
var action = m_Actions.FirstOrDefault(a => a.NextRunTime != null && a.NextRunTime > m_LastRunTime);
|
||||
if (action == null || action.NextRunTime == null)
|
||||
{
|
||||
m_Timer.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
long msToNextAction = (long)(action.NextRunTime.Value - IcdEnvironment.GetLocalTime()).TotalMilliseconds;
|
||||
if (msToNextAction < 0)
|
||||
msToNextAction = 0;
|
||||
m_Timer.Reset(msToNextAction);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
private void Log(eSeverity severity, string message, params object[] args)
|
||||
{
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger == null)
|
||||
return;
|
||||
|
||||
logger.AddEntry(severity, string.Format("{0} - {1}", this, message), args);
|
||||
}
|
||||
|
||||
private void Log(eSeverity severity, Exception ex, string message, params object[] args)
|
||||
{
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger == null)
|
||||
return;
|
||||
|
||||
logger.AddEntry(severity, ex, string.Format("{0} - {1}", this, message), args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Action Callbacks
|
||||
|
||||
private void Subscribe(IScheduledAction action)
|
||||
{
|
||||
action.OnScheduledRunTimeChanged += ActionOnScheduledRunTimeChanged;
|
||||
}
|
||||
|
||||
private void Unsubscribe(IScheduledAction action)
|
||||
{
|
||||
action.OnScheduledRunTimeChanged -= ActionOnScheduledRunTimeChanged;
|
||||
}
|
||||
|
||||
private void ActionOnScheduledRunTimeChanged(object sender, EventArgs eventArgs)
|
||||
{
|
||||
IScheduledAction action = sender as IScheduledAction;
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
m_CriticalSection.Enter();
|
||||
try
|
||||
{
|
||||
m_Actions.Remove(action);
|
||||
m_Actions.AddSorted(action, a => a.NextRunTime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CriticalSection.Leave();
|
||||
}
|
||||
|
||||
RescheduleTimer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ICD.Common.Utils.Services.Scheduler
|
||||
{
|
||||
public interface IActionSchedulerService
|
||||
{
|
||||
void Add(IScheduledAction action);
|
||||
|
||||
void Remove(IScheduledAction action);
|
||||
}
|
||||
}
|
||||
19
ICD.Common.Utils/Services/Scheduler/IScheduledAction.cs
Normal file
19
ICD.Common.Utils/Services/Scheduler/IScheduledAction.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Services.Scheduler
|
||||
{
|
||||
public interface IScheduledAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when the scheduled run time has changed. The sender of the event must be the action itself
|
||||
/// </summary>
|
||||
event EventHandler OnScheduledRunTimeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time this action should be run
|
||||
/// </summary>
|
||||
DateTime? NextRunTime { get; }
|
||||
|
||||
void Run();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
@@ -228,18 +227,7 @@ namespace ICD.Common.Utils.Services
|
||||
if (tService == null)
|
||||
throw new ArgumentNullException("tService");
|
||||
|
||||
try
|
||||
{
|
||||
m_ServicesSection.Enter();
|
||||
|
||||
object service;
|
||||
m_Services.TryGetValue(tService, out service);
|
||||
return service;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_ServicesSection.Leave();
|
||||
}
|
||||
return m_ServicesSection.Execute(() => m_Services.GetDefault(tService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -265,7 +253,7 @@ namespace ICD.Common.Utils.Services
|
||||
/// <returns></returns>
|
||||
private IEnumerable<object> GetServicesInstance()
|
||||
{
|
||||
return m_ServicesSection.Execute(() => m_Services.Values.ToList());
|
||||
return m_ServicesSection.Execute(() => m_Services.Values.ToArray(m_Services.Count));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -345,10 +333,11 @@ namespace ICD.Common.Utils.Services
|
||||
{
|
||||
m_ServicesSection.Enter();
|
||||
|
||||
if (!m_Services.ContainsKey(tService))
|
||||
object stored;
|
||||
if (!m_Services.TryGetValue(tService, out stored))
|
||||
return false;
|
||||
|
||||
return m_Services[tService] == service && m_Services.Remove(tService);
|
||||
return service == stored && m_Services.Remove(tService);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -47,8 +47,12 @@ namespace ICD.Common.Utils
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
string[] strings = input.Select(c => ToHexLiteral(c)).ToArray();
|
||||
return string.Join("", strings);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
foreach (string literal in input.Select(c => ToHexLiteral(c)))
|
||||
builder.Append(literal);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +84,12 @@ namespace ICD.Common.Utils
|
||||
if (data == null)
|
||||
throw new ArgumentNullException("data");
|
||||
|
||||
return string.Join("", data.Split(4).Select(s => FromHexLiteralCharacter(s).ToString()).ToArray());
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
foreach (char item in data.Split(4).Select(s => FromHexLiteralCharacter(s)))
|
||||
builder.Append(item);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,8 +121,12 @@ namespace ICD.Common.Utils
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
string[] strings = input.Select(c => ToMixedReadableHexLiteral(c)).ToArray();
|
||||
return string.Join("", strings);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
foreach (string item in input.Select(c => ToMixedReadableHexLiteral(c)))
|
||||
builder.Append(item);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,6 +178,9 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static byte[] ToBytes(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
return Encoding.GetEncoding(28591).GetBytes(input);
|
||||
}
|
||||
|
||||
@@ -277,21 +293,20 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("convertFunc");
|
||||
|
||||
result = default(T);
|
||||
bool retVal = false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
result = convertFunc(value);
|
||||
retVal = true;
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -316,6 +331,9 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static string NiceName(string name)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException("name");
|
||||
|
||||
Regex regex = new Regex(@"
|
||||
(?<=[A-Z])(?=[A-Z][a-z]) |
|
||||
(?<=[^A-Z])(?=[A-Z]) |
|
||||
@@ -405,7 +423,12 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static string Repeat(string input, int count)
|
||||
{
|
||||
return count == 0 ? string.Empty : new StringBuilder().Insert(0, input, count).ToString();
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
return input == null || count == 0
|
||||
? string.Empty
|
||||
: new StringBuilder().Insert(0, input, count).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -419,7 +442,23 @@ namespace ICD.Common.Utils
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
return string.Format("[{0}]", string.Join(", ", items.Select(i => ToString(i)).ToArray()));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append('[');
|
||||
|
||||
bool first = true;
|
||||
|
||||
foreach (T item in items)
|
||||
{
|
||||
if (!first)
|
||||
builder.Append(", ");
|
||||
first = false;
|
||||
|
||||
builder.Append(ToString(item));
|
||||
}
|
||||
|
||||
builder.Append(']');
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -525,6 +564,30 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats "0xFF" to an IPID.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryFromIpIdString(string value, out byte result)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
result = 0;
|
||||
|
||||
try
|
||||
{
|
||||
result = (byte)Convert.ToInt64(value, 16);
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all whitespace from the string.
|
||||
/// </summary>
|
||||
@@ -532,7 +595,7 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static string RemoveWhitespace(string text)
|
||||
{
|
||||
return text == null ? null : new string(text.Where(c => !Char.IsWhiteSpace(c)).ToArray());
|
||||
return text == null ? null : text.RemoveWhitespace();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -544,6 +607,7 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return true;
|
||||
|
||||
string trimmed = text.Trim();
|
||||
return trimmed.Length == 0;
|
||||
}
|
||||
@@ -682,5 +746,40 @@ namespace ICD.Common.Utils
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the value starts and ends with a double quote.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string Enquote(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!value.StartsWith('"'))
|
||||
value = '"' + value;
|
||||
|
||||
if (!value.EndsWith('"'))
|
||||
value += '"';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the start and trailing double quote from the string if BOTH are present.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string UnEnquote(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (value.StartsWith('"') && value.EndsWith('"'))
|
||||
return value.Substring(1, value.Length - 2);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
using System;
|
||||
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;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Threading.Tasks;
|
||||
using MethodInfo = System.Reflection.MethodInfo;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class ThreadingUtils
|
||||
{
|
||||
private static readonly IcdHashSet<ThreadState> s_Threads;
|
||||
private static readonly SafeCriticalSection s_ThreadsSection;
|
||||
|
||||
/// <summary>
|
||||
/// Static contstructor.
|
||||
/// </summary>
|
||||
static ThreadingUtils()
|
||||
{
|
||||
s_Threads = new IcdHashSet<ThreadState>();
|
||||
s_ThreadsSection = new SafeCriticalSection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until the given condition is true.
|
||||
/// </summary>
|
||||
@@ -52,9 +70,12 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
[PublicAPI]
|
||||
public static object SafeInvoke(Action callback)
|
||||
public static void SafeInvoke(Action callback)
|
||||
{
|
||||
return SafeInvoke<object>(unused => callback(), null);
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
SafeInvoke<object>(callback.GetMethodInfo(), unused => callback(), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,12 +85,41 @@ namespace ICD.Common.Utils
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="param"></param>
|
||||
[PublicAPI]
|
||||
public static object SafeInvoke<T>(Action<T> callback, T param)
|
||||
public static void SafeInvoke<T>(Action<T> callback, T param)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
SafeInvoke(callback.GetMethodInfo(), callback, param);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the callback as a short-lived, threaded task.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="param"></param>
|
||||
[PublicAPI]
|
||||
private static void SafeInvoke<T>(MethodInfo methodInfo, Action<T> callback, T param)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
ThreadState state = new ThreadState
|
||||
{
|
||||
MethodInfo = methodInfo,
|
||||
};
|
||||
state.Callback = GetHandledCallback(state, callback, param);
|
||||
|
||||
// Add the state here so the created thread doesn't get garbage collected
|
||||
AddThreadState(state);
|
||||
|
||||
state.Handle =
|
||||
#if SIMPLSHARP
|
||||
return CrestronInvoke.BeginInvoke(unused => GetHandledCallback(callback, param)(), null);
|
||||
CrestronInvoke.BeginInvoke(unused => state.Callback(), null);
|
||||
#else
|
||||
return Task.Run(GetHandledCallback(callback, param));
|
||||
Task.Run(state.Callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -78,15 +128,19 @@ namespace ICD.Common.Utils
|
||||
/// http://www.crestronlabs.com/showthread.php?12205-Exception-in-CrestronInvoke-thread-crashes-the-program
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="param"></param>
|
||||
private static Action GetHandledCallback<T>(Action<T> callback, T param)
|
||||
private static Action GetHandledCallback<T>(ThreadState state, Action<T> callback, T param)
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
state.Started = IcdEnvironment.GetLocalTime();
|
||||
|
||||
callback(param);
|
||||
RemoveThreadState(state);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -95,5 +149,78 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<ThreadState> GetActiveThreads()
|
||||
{
|
||||
return s_ThreadsSection.Execute(() => s_Threads.ToArray(s_Threads.Count));
|
||||
}
|
||||
|
||||
private static void AddThreadState(ThreadState state)
|
||||
{
|
||||
s_ThreadsSection.Execute(() => s_Threads.Add(state));
|
||||
}
|
||||
|
||||
private static void RemoveThreadState(ThreadState state)
|
||||
{
|
||||
s_ThreadsSection.Execute(() => s_Threads.Remove(state));
|
||||
}
|
||||
|
||||
public static string PrintThreads()
|
||||
{
|
||||
TableBuilder builder = new TableBuilder("Name", "Started", "Duration");
|
||||
|
||||
IEnumerable<ThreadState> states = GetActiveThreads().OrderBy(s => s.Name)
|
||||
.ThenBy(s => s.Started);
|
||||
|
||||
foreach (ThreadState state in states)
|
||||
builder.AddRow(state.Name, state.Started, state.Duration);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private sealed class ThreadState
|
||||
{
|
||||
private string m_CachedName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time the thread was instantiated.
|
||||
/// </summary>
|
||||
public DateTime? Started { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of the thread.
|
||||
/// </summary>
|
||||
public TimeSpan? Duration { get { return IcdEnvironment.GetLocalTime() - Started; } }
|
||||
|
||||
/// <summary>
|
||||
/// Threads can be garbage collected before they execute so we keep a reference.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public object Handle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human readable name for the thread.
|
||||
/// </summary>
|
||||
public string Name { get { return m_CachedName = m_CachedName ?? BuildName(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The action that is being performed by the thread.
|
||||
/// </summary>
|
||||
public Action Callback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for providing debug information on the executing method.
|
||||
/// </summary>
|
||||
public MethodInfo MethodInfo { get; set; }
|
||||
|
||||
private string BuildName()
|
||||
{
|
||||
Type declaring = MethodInfo.DeclaringType;
|
||||
|
||||
return declaring == null
|
||||
? MethodInfo.GetSignature(true)
|
||||
: string.Format("{0}.{1}", declaring.Name, MethodInfo.GetSignature(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
@@ -17,8 +19,12 @@ namespace ICD.Common.Utils.Timers
|
||||
|
||||
private readonly Stopwatch m_Stopwatch;
|
||||
|
||||
private static int s_ProfileIndentCount;
|
||||
|
||||
#region Properties
|
||||
|
||||
public long ElapsedTicks { get { return m_Stopwatch.ElapsedTicks; } }
|
||||
|
||||
public long ElapsedMilliseconds { get { return m_Stopwatch.ElapsedMilliseconds; } }
|
||||
|
||||
public bool IsRunning { get { return m_Stopwatch.IsRunning; } }
|
||||
@@ -101,9 +107,18 @@ namespace ICD.Common.Utils.Timers
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
|
||||
IcdStopwatch stopwatch = StartNew();
|
||||
action();
|
||||
PrintProfile(stopwatch, name);
|
||||
s_ProfileIndentCount++;
|
||||
|
||||
try
|
||||
{
|
||||
IcdStopwatch stopwatch = StartNew();
|
||||
action();
|
||||
PrintProfile(stopwatch.ElapsedTicks, name);
|
||||
}
|
||||
finally
|
||||
{
|
||||
s_ProfileIndentCount--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,14 +134,69 @@ namespace ICD.Common.Utils.Timers
|
||||
if (func == null)
|
||||
throw new ArgumentNullException("func");
|
||||
|
||||
T output = default(T);
|
||||
s_ProfileIndentCount++;
|
||||
|
||||
Profile(() =>
|
||||
{
|
||||
output = func();
|
||||
}, name);
|
||||
try
|
||||
{
|
||||
IcdStopwatch stopwatch = StartNew();
|
||||
T output = func();
|
||||
PrintProfile(stopwatch.ElapsedTicks, name);
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
finally
|
||||
{
|
||||
s_ProfileIndentCount--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the action the given number of iterations and prints timing information to the console.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="iterations"></param>
|
||||
/// <param name="name"></param>
|
||||
public static void Profile(Action action, int iterations, string name)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
|
||||
if (iterations <= 0)
|
||||
throw new ArgumentOutOfRangeException("iterations", "Iterations must be greater than 0");
|
||||
|
||||
name = string.Format("{0} ({1} Iterations)", name, iterations);
|
||||
|
||||
long totalTicks = 0;
|
||||
List<long> orderedMs = new List<long>(iterations);
|
||||
|
||||
for (int index = 0; index < iterations; index++)
|
||||
{
|
||||
IcdStopwatch stopwatch = StartNew();
|
||||
{
|
||||
action();
|
||||
}
|
||||
long duration = stopwatch.ElapsedTicks;
|
||||
stopwatch.Stop();
|
||||
|
||||
orderedMs.AddSorted(duration);
|
||||
totalTicks += duration;
|
||||
}
|
||||
|
||||
float totalMs = totalTicks / (float)TimeSpan.TicksPerMillisecond;
|
||||
float averageMs = (totalTicks / (float)iterations) / TimeSpan.TicksPerMillisecond;
|
||||
float medianMs = (orderedMs[iterations / 2]) / (float)TimeSpan.TicksPerMillisecond;
|
||||
float shortestMs = orderedMs[0] / (float)TimeSpan.TicksPerMillisecond;
|
||||
float longestMs = orderedMs[iterations - 1] / (float)TimeSpan.TicksPerMillisecond;
|
||||
|
||||
TableBuilder builder = new TableBuilder(name, "Duration (ms)");
|
||||
|
||||
builder.AddRow("Total", string.Format("{0:n}", totalMs));
|
||||
builder.AddRow("Average", string.Format("{0:n}", averageMs));
|
||||
builder.AddRow("Median", string.Format("{0:n}", medianMs));
|
||||
builder.AddRow("Shortest", string.Format("{0:n}", shortestMs));
|
||||
builder.AddRow("Longest", string.Format("{0:n}", longestMs));
|
||||
|
||||
IcdConsole.PrintLine(builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -138,17 +208,99 @@ namespace ICD.Common.Utils.Timers
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Profile<T>(IEnumerable<T> enumerable, string name)
|
||||
{
|
||||
if (enumerable == null)
|
||||
throw new ArgumentNullException("enumerable");
|
||||
|
||||
// TODO - Print a fancy table with a total duration for the sequence
|
||||
|
||||
List<T> output = new List<T>();
|
||||
|
||||
using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
|
||||
{
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
while (Profile(() => enumerator.MoveNext(), name))
|
||||
yield return enumerator.Current;
|
||||
output.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the event handler and profiles each of the attached subscribers.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="name"></param>
|
||||
public static void Profile(EventHandler eventHandler, object sender, string name)
|
||||
{
|
||||
s_ProfileIndentCount++;
|
||||
|
||||
try
|
||||
{
|
||||
if (eventHandler == null)
|
||||
{
|
||||
PrintProfile(0, string.Format("{0} - No invocations", name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
s_ProfileIndentCount--;
|
||||
}
|
||||
|
||||
// TODO - Print a fancy table with a total duration for the event
|
||||
|
||||
foreach (EventHandler subscriber in eventHandler.GetInvocationList().Cast<EventHandler>())
|
||||
{
|
||||
string subscriberName = string.Format("{0} - {1}.{2}", name, subscriber.Target.GetType().Name,
|
||||
subscriber.GetMethodInfo().GetSignature(true));
|
||||
|
||||
EventHandler subscriber1 = subscriber;
|
||||
Profile(() => subscriber1(sender, EventArgs.Empty), subscriberName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintProfile(IcdStopwatch stopwatch, string name)
|
||||
/// <summary>
|
||||
/// Executes the event handler and profiles each of the attached subscribers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="eventHandler"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <param name="name"></param>
|
||||
public static void Profile<T>(EventHandler<T> eventHandler, object sender, T eventArgs, string name)
|
||||
where T : EventArgs
|
||||
{
|
||||
long elapsed = stopwatch.ElapsedMilliseconds;
|
||||
s_ProfileIndentCount++;
|
||||
|
||||
try
|
||||
{
|
||||
if (eventHandler == null)
|
||||
{
|
||||
PrintProfile(0, string.Format("{0} - No invocations", name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
s_ProfileIndentCount--;
|
||||
}
|
||||
|
||||
// TODO - Print a fancy table with a total duration for the event
|
||||
|
||||
foreach (EventHandler<T> subscriber in eventHandler.GetInvocationList().Cast<EventHandler<T>>())
|
||||
{
|
||||
string subscriberName = string.Format("{0} - {1}.{2}", name, subscriber.Target.GetType().Name,
|
||||
subscriber.GetMethodInfo().GetSignature(true));
|
||||
|
||||
EventHandler<T> subscriber1 = subscriber;
|
||||
Profile(() => subscriber1(sender, eventArgs), subscriberName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintProfile(long ticks, string name)
|
||||
{
|
||||
float elapsed = ticks / (float)TimeSpan.TicksPerMillisecond;
|
||||
|
||||
eConsoleColor color = eConsoleColor.Green;
|
||||
if (elapsed >= YELLOW_MILLISECONDS)
|
||||
@@ -156,8 +308,13 @@ namespace ICD.Common.Utils.Timers
|
||||
if (elapsed >= RED_MILLISECONDS)
|
||||
color = eConsoleColor.Red;
|
||||
|
||||
IcdConsole.Print(color, "{0}ms", elapsed);
|
||||
IcdConsole.PrintLine(" to execute {0}", name);
|
||||
string padding = null;
|
||||
|
||||
if (s_ProfileIndentCount > 0)
|
||||
padding = StringUtils.Repeat(" ", s_ProfileIndentCount - 1);
|
||||
|
||||
IcdConsole.Print(color, "{0}{1:n}ms ", padding, elapsed);
|
||||
IcdConsole.PrintLine("to execute {0}", name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ICD.Common.Utils.Timers
|
||||
private readonly Timer m_Timer;
|
||||
private int m_RepeatPeriod;
|
||||
#endif
|
||||
private readonly Action m_Callback;
|
||||
private Action m_Callback;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this instance has been disposed.
|
||||
@@ -82,8 +82,14 @@ namespace ICD.Common.Utils.Timers
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
|
||||
Stop();
|
||||
m_Timer.Dispose();
|
||||
|
||||
m_Callback = null;
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
@@ -113,7 +119,7 @@ namespace ICD.Common.Utils.Timers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback is called after the dueTime milliseconds.
|
||||
/// Callback is called once after the dueTime milliseconds.
|
||||
/// </summary>
|
||||
/// <param name="dueTime"></param>
|
||||
public void Reset(long dueTime)
|
||||
@@ -121,7 +127,7 @@ namespace ICD.Common.Utils.Timers
|
||||
#if SIMPLSHARP
|
||||
m_Timer.Reset(dueTime);
|
||||
#else
|
||||
m_Timer.Change((int)dueTime, m_RepeatPeriod);
|
||||
m_Timer.Change((int)dueTime, Timeout.Infinite);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -167,16 +173,11 @@ namespace ICD.Common.Utils.Timers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogException(e);
|
||||
string message = string.Format("{0} failed to execute callback - {1}", GetType().Name, e.Message);
|
||||
ServiceProvider.TryGetService<ILoggerService>().AddEntry(eSeverity.Error, e, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogException(Exception e)
|
||||
{
|
||||
string message = string.Format("{0} failed to execute callback - {1}", GetType().Name, e.Message);
|
||||
ServiceProvider.TryGetService<ILoggerService>().AddEntry(eSeverity.Error, e, message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class UriUtils
|
||||
{
|
||||
private static readonly Dictionary<string, ushort> s_SchemeToPort =
|
||||
new Dictionary<string, ushort>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{Uri.UriSchemeHttp, 80},
|
||||
{Uri.UriSchemeHttps, 443}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the given URI string into a System.Uri instance.
|
||||
/// </summary>
|
||||
@@ -27,5 +35,16 @@ namespace ICD.Common.Utils
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number for the given URI scheme.
|
||||
/// </summary>
|
||||
/// <param name="scheme"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryGetPortForScheme(string scheme, out ushort port)
|
||||
{
|
||||
return s_SchemeToPort.TryGetValue(scheme, out port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user