mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-14 04:54:55 +00:00
Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0de417b665 | ||
|
|
da57fcb988 | ||
|
|
c6b6711eb2 | ||
|
|
138a6f3470 | ||
|
|
e84bc721ee | ||
|
|
2988912d44 | ||
|
|
be94503e6a | ||
|
|
6877b5c800 | ||
|
|
569066edc4 | ||
|
|
73a716f9b9 | ||
|
|
4e430731cc | ||
|
|
2b8f2a125a | ||
|
|
cc25c41805 | ||
|
|
2487a70891 | ||
|
|
5c5a25775b | ||
|
|
27f6267f54 | ||
|
|
430eac8cb6 | ||
|
|
b47e305d9f | ||
|
|
0c8f8242e8 | ||
|
|
a52e77c1e7 | ||
|
|
2aa05f3ee8 | ||
|
|
2700f5018a | ||
|
|
b09b22266b | ||
|
|
1c206c5539 | ||
|
|
56a48175c6 | ||
|
|
0acd0ae5d4 | ||
|
|
d2856c1983 | ||
|
|
fa65a9de65 | ||
|
|
e4b292f145 | ||
|
|
11a5533fcd | ||
|
|
e0d0763306 | ||
|
|
7d42158bc7 | ||
|
|
92a28813e0 | ||
|
|
3b313a442c | ||
|
|
16fb3683dd | ||
|
|
bb9bcf6cdf | ||
|
|
0490ffd572 | ||
|
|
3bf0aff0a0 | ||
|
|
e5b10a96f7 | ||
|
|
0c3c87308c | ||
|
|
68d1f1033a | ||
|
|
af66147648 | ||
|
|
16067bca20 | ||
|
|
57e64788c4 | ||
|
|
bc605bf8ae | ||
|
|
be50aa3314 | ||
|
|
1b79bd21df | ||
|
|
7d5ec0e636 | ||
|
|
79c1c60d79 | ||
|
|
033008616f | ||
|
|
84b5b636f6 | ||
|
|
451cf08c0f | ||
|
|
63d76d8cef | ||
|
|
c08a6283b8 | ||
|
|
df42a6674f | ||
|
|
ae53812a98 | ||
|
|
74ff651798 | ||
|
|
29013a2bf5 | ||
|
|
baa00f7b00 | ||
|
|
67a2b11ee6 | ||
|
|
8f5fee2401 | ||
|
|
d00d2febf3 | ||
|
|
af3d335079 | ||
|
|
375e3154c6 | ||
|
|
37d799295c | ||
|
|
06f40e5d22 | ||
|
|
11c3e33fc1 | ||
|
|
fc60e2b675 | ||
|
|
531e9dfcf8 | ||
|
|
f601f9cda7 | ||
|
|
841b692727 | ||
|
|
5f2c5e6dcb | ||
|
|
7ae9e86e1d | ||
|
|
e6dec641f3 | ||
|
|
8b848e5127 | ||
|
|
f328598f63 | ||
|
|
79db70211f | ||
|
|
1390af967f | ||
|
|
28335ad99c | ||
|
|
6967e9b013 | ||
|
|
7784b99f75 | ||
|
|
b3c1daaab5 | ||
|
|
ae10abd71e | ||
|
|
5aca963da0 | ||
|
|
40330d0007 | ||
|
|
a26783bd67 | ||
|
|
d58b8a9db9 | ||
|
|
5083f3d7ab | ||
|
|
43fd348fae | ||
|
|
8cdddc94bc | ||
|
|
25d799fe7d | ||
|
|
f807db480e | ||
|
|
985a81f961 | ||
|
|
0a9a382355 | ||
|
|
86fabce6da | ||
|
|
a7ab2ab3fe | ||
|
|
370cadbaeb | ||
|
|
e7bdcdfca5 | ||
|
|
cab1e237d5 | ||
|
|
da5e1d83a0 | ||
|
|
63237b6fba | ||
|
|
a55480f8e5 | ||
|
|
d1f8096933 | ||
|
|
c41e61391e | ||
|
|
766e2da4e3 | ||
|
|
b83fba337f | ||
|
|
766c265dac | ||
|
|
204d2a475d | ||
|
|
b5542fb0d2 | ||
|
|
4ff4e9f3c6 | ||
|
|
456a8f74f9 | ||
|
|
ffb00f960d | ||
|
|
b44cbe0093 | ||
|
|
cc79e88702 | ||
|
|
1db7d4b45d | ||
|
|
8427f2c0c6 | ||
|
|
8411a27173 | ||
|
|
e0287cfc7b | ||
|
|
e044c97af7 | ||
|
|
c7e8f09eeb | ||
|
|
9b53d77d6b |
103
CHANGELOG.md
103
CHANGELOG.md
@@ -6,6 +6,109 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [17.2.0] - 2023-06-18
|
||||
### Added
|
||||
- Added BigEndianBitConverter
|
||||
- Added EnumUtils.GetInverseFlags
|
||||
- Added ReverseLookupDictionary collection
|
||||
- Added NotifyFlagsChanged type
|
||||
|
||||
### Changed
|
||||
- SafeMutex now implements IDisposable
|
||||
|
||||
## [17.1.0] - 2023-03-22
|
||||
### Changed
|
||||
- Improved methods for human formatted timespan, including without milliseconds.
|
||||
- Removed Obfuscation
|
||||
|
||||
## [17.0.0] 2022-12-02
|
||||
### Changed
|
||||
- IcdEnvironment - Added CrestronDevicePlatform, removed from CrestronRuntimeEnvironment, to support SimplWindows on VC-4
|
||||
- Fixed IcdEnvironment CrestronRuntimeEnvironment uses in IcdConsole, PathUtils, and ProgramUtils
|
||||
- Fixed preprocessors in IcdDirectory
|
||||
- Added exception handling to ThreadedWorkerQueue
|
||||
|
||||
## [16.0.5] 2022-07-11
|
||||
### Changed
|
||||
- Fixed console command responses in Simpl+ Runtime Environment
|
||||
- Changed Crestron apps to not report as an interactive console
|
||||
- Fixed NETSTANDARD vs SIMPLSHARP preprocessors in various files
|
||||
- Fixed package reference conditions for NetStandard pakcages
|
||||
|
||||
## [16.0.4] 2022-07-01
|
||||
### Changed
|
||||
- Fixed PreProcessors for NETSTANDARD vs SIMPLSHARP for 4-series builds
|
||||
- Updated Crestron SDK to 2.18.96
|
||||
|
||||
## [16.0.3] 2022-06-23
|
||||
### Changed
|
||||
- Throwing better exception when trying to get unknown timezones
|
||||
|
||||
## [16.0.2] 2022-05-23
|
||||
### Changed
|
||||
- Fixed an issue in IcdUriBuilder where relative pathes were not being built into a valid URI.
|
||||
|
||||
## [16.0.1] 2021-10-28
|
||||
### Changed
|
||||
- Changed sqlite connection strings in IcdCultureInfo & IcdTimeZoneInfo to work with SimplSharp.
|
||||
|
||||
## [16.0.0] 2021-10-04
|
||||
### Added
|
||||
- Added IcdAutoResetEvent and IcdManaualResetEvent
|
||||
|
||||
### Changed
|
||||
- EnumUtils - Fixed bug where not all values were returned for GetValueExceptNone
|
||||
- ThreadedWorkerQueue - Added BetweenTime property, to wait between process callbacks
|
||||
- ThreadedWorkerQueue - Added RunProcess option to stop queue from processing items
|
||||
- ThreadedWorkerQueue - Added WaitForFlush events to wait until the queue is empty
|
||||
- ThreadedWorkerQueue - Added count property
|
||||
- ThreadedWorkerQueue - Now implements IDisposable
|
||||
|
||||
### Removed
|
||||
- Removed RateLimitedEventQueue and tests - features added to ThreadedWorkerQueue
|
||||
- Removed TryEnter from SafeCriticalSection - incorrect behavior on Crestron systems
|
||||
|
||||
## [15.2.0] - 2021-08-18
|
||||
### Added
|
||||
- TryParse overload in StringUtils, attempts to parse a GUID from a string
|
||||
|
||||
## [15.1.0] - 2021-08-03
|
||||
### Added
|
||||
- Enum Extension "SetFlags", takes a bool to set or unset the given flags
|
||||
- BiDictionay - Added constructors with TKey and TValue comparers
|
||||
- ILoggerService - Added Flush() method
|
||||
- Added log entries for intentional reboot and program restart
|
||||
- Added ThreadingUtils TimeSpan overloads
|
||||
|
||||
### Changed
|
||||
- IcdTimeZoneInfo - fixed issue when unable to read SQL
|
||||
- WeakKeyDictionary - Fixe GetHashCode to handle null values
|
||||
- ProcessorUtils - NetStandard - removed "Microsoft" from model name
|
||||
- Fixed null handing in SequenceComparer and UndefinedVersionComparer
|
||||
- EnumUtils - GetFlags no longer returns composite flags
|
||||
|
||||
## [15.0.0] - 2021-05-14
|
||||
### Added
|
||||
- Ported CsvReader for CF 3.5 compatibility from: https://github.com/tspence/csharp-csv-reader
|
||||
- Added enum extension method for cycling to the next enum value
|
||||
- Added GetLocalTimeZoneName method to IcdEnvironment
|
||||
- Added MatchAny method to RegexUtils
|
||||
- Added OnSystemDeviceAddedRemoved and associated raise methods to IcdEnvironment for NETSTANDARD
|
||||
- Added GetParentUri method to UriExtensions
|
||||
- Added RegistryExtensions for working with Windows registry
|
||||
- Added session change event to IcdEnvironment for login/logout feedback
|
||||
- Added OrderedDictionary collection
|
||||
|
||||
### Changed
|
||||
- Updated TimeZones.sqlite to include daylight time zone info, added a new display name column.
|
||||
- Implemented ProcessorUtils for Windows
|
||||
- Renamed OrderedDictionary to SortedDictionary for consistency with .Net
|
||||
- Fixed a bug where SafeTimer.Trigger() would run the callback twice on .Net Standard
|
||||
- Fixed a bug where XML deserialization would fail to read out of empty elements
|
||||
|
||||
### Removed
|
||||
- ANSI color is no longer enabled on .Net Standard by default - it must be enabled by the calling application
|
||||
|
||||
## [14.2.0] - 2021-02-04
|
||||
### Changed
|
||||
- ProcessorUtils Uptime methods changed to StartTime
|
||||
|
||||
332
ICD.Common.Utils.Tests/BigEndianBitConverterTest.cs
Normal file
332
ICD.Common.Utils.Tests/BigEndianBitConverterTest.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class BigEndianBitConverterTest
|
||||
{
|
||||
[TestCase(ushort.MaxValue, new byte[] { 0xFF, 0xFF }, 0)]
|
||||
[TestCase(ushort.MinValue, new byte[] { 0x00, 0x00 }, 0)]
|
||||
[TestCase((ushort)255, new byte[] { 0x00, 0xFF }, 0)]
|
||||
[TestCase((ushort)65280, new byte[] { 0xFF, 0x00 }, 0)]
|
||||
[TestCase(ushort.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(ushort.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((ushort)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase((ushort)65280, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((ushort)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
|
||||
[TestCase((ushort)15, new byte[] { 0x00, 0x0F }, 0)]
|
||||
public void ToUshortTest(ushort expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUshort(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUshortExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUshort(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUshort(values, 8));
|
||||
}
|
||||
|
||||
[TestCase(short.MaxValue, new byte[] { 0x7F, 0xFF }, 0)]
|
||||
[TestCase(short.MinValue, new byte[] { 0x80, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00 }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF }, 0)]
|
||||
[TestCase((short)255, new byte[] { 0x00, 0xFF }, 0)]
|
||||
[TestCase((short)-256, new byte[] { 0xFF, 0x00 }, 0)]
|
||||
[TestCase(short.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(short.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((short)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase((short)-256, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((short)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
|
||||
[TestCase((short)15, new byte[] { 0x00, 0x0F }, 0)]
|
||||
public void ToShortTest(short expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToShort(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToShortExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToShort(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToShort(values, 8));
|
||||
}
|
||||
|
||||
[TestCase(uint.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(uint.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase((uint)255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(4278190080, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(uint.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(uint.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((uint)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(4278190080, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((uint)15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase((uint)1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
|
||||
public void ToUintTest(uint expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUint(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUintExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUint(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUint(values, 6));
|
||||
}
|
||||
|
||||
[TestCase(int.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(int.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(-16777216, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(int.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(int.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase(255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(-16777216, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase(15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase(1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
|
||||
public void ToIntTest(int expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToInt(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToIntExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToInt(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToInt(values, 6));
|
||||
}
|
||||
|
||||
[TestCase(ulong.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(ulong.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase((ulong)255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(18374686479671623680, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(ulong.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
|
||||
2)]
|
||||
[TestCase(ulong.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((ulong)255,
|
||||
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(18374686479671623680,
|
||||
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((ulong)67555089642946815,
|
||||
new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase((ulong)4487102673719295, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, 0)]
|
||||
public void ToUlongTest(ulong expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUlong(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUlongExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUlong(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUlong(values, 2));
|
||||
}
|
||||
|
||||
[TestCase(long.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(long.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(-72057594037927936, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(long.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
|
||||
2)]
|
||||
[TestCase(long.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase(255,
|
||||
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(-72057594037927936,
|
||||
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase(67555093906456320, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00, 0xF0 },
|
||||
2)]
|
||||
[TestCase(4487102659035120, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 0)]
|
||||
public void ToLongTest(long expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToLong(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToLongExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToLong(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToLong(values, 2));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, int.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00 }, int.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, 255)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, -1)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, -16777216)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, 15728895)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 1044735)]
|
||||
public void GetBytesIntTest(byte[] expectedResult, int value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, uint.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, uint.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 4278190080)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, (uint)15728895)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, (uint)1044735)]
|
||||
public void GetBytesUintTest(byte[] expectedResult, uint value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF }, short.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00 }, short.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00 }, (short)0)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF }, (short)-1)]
|
||||
[TestCase(new byte[] { 0x00, 0xFF }, (short)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00 }, (short)-256)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0 }, (short)240)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F }, (short)15)]
|
||||
public void GetBytesShortTest(byte[] expectedResult, short value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF }, ushort.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00 }, ushort.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0xFF }, (ushort)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00 }, (ushort)65280)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0 }, (ushort)240)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F }, (ushort)15)]
|
||||
public void GetBytesUshortTest(byte[] expectedResult, ushort value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, long.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },long.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, (long)0)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (long)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, (long)-1)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -72057594037927936)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00 }, 67555093906456320)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 4487102659035120)]
|
||||
public void GetBytesLongTest(byte[] expectedResult, long value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ulong.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ulong.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (ulong)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 18374686479671623680)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF }, (ulong)67555089642946815)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, (ulong)4487102673719295)]
|
||||
public void GetBytesUlongTest(byte[] expectedResult, ulong value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -7,130 +8,85 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[TestFixture]
|
||||
public sealed class IcdOrderedDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
public void OrderingTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{2, 20}
|
||||
{"1", "a"},
|
||||
{"2", "b"},
|
||||
{"3", "c"}
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] {"1", "2", "3"}));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "b", "c" }));
|
||||
|
||||
// Remove the key and add to the end of the dictionary
|
||||
dict.Remove("2");
|
||||
dict["2"] = "d";
|
||||
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "c", "d" }));
|
||||
|
||||
// No key change
|
||||
dict["1"] = "e";
|
||||
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "e", "c", "d" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadOnlyTest()
|
||||
public void GetTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
|
||||
|
||||
Assert.IsFalse(dict.IsReadOnly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
{"1", "a"},
|
||||
{"2", "b"},
|
||||
{"3", "c"}
|
||||
};
|
||||
|
||||
int[] keys = dict.Keys.ToArray();
|
||||
KeyValuePair<string, string> kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("a", kvp.Value);
|
||||
|
||||
Assert.AreEqual(3, keys.Length);
|
||||
Assert.AreEqual(-1, keys[0]);
|
||||
Assert.AreEqual(0, keys[1]);
|
||||
Assert.AreEqual(1, keys[2]);
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("b", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
// Remove the key and add to the end of the dictionary
|
||||
dict.Remove("2");
|
||||
dict["2"] = "d";
|
||||
|
||||
kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("a", kvp.Value);
|
||||
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("d", kvp.Value);
|
||||
|
||||
// No key change
|
||||
dict["1"] = "e";
|
||||
|
||||
kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("e", kvp.Value);
|
||||
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("d", kvp.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] values = dict.Values.ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.AreEqual(-10, values[0]);
|
||||
Assert.AreEqual(0, values[1]);
|
||||
Assert.AreEqual(10, values[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerTest()
|
||||
{
|
||||
// ReSharper disable UseObjectOrCollectionInitializer
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
|
||||
// ReSharper restore UseObjectOrCollectionInitializer
|
||||
|
||||
dict[0] = 0;
|
||||
dict[1] = 10;
|
||||
dict[-1] = -10;
|
||||
dict[-1] = -11;
|
||||
|
||||
Assert.AreEqual(0, dict[0]);
|
||||
Assert.AreEqual(10, dict[1]);
|
||||
Assert.AreEqual(-11, dict[-1]);
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
|
||||
int[] expectedKeys = {-1, 0, 1 };
|
||||
int[] expectedValues = {-11, 0, 10};
|
||||
|
||||
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
|
||||
Assert.AreEqual(expectedValues, dict.Values.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
136
ICD.Common.Utils.Tests/Collections/IcdSortedDictionaryTest.cs
Normal file
136
ICD.Common.Utils.Tests/Collections/IcdSortedDictionaryTest.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdSortedDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{2, 20}
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadOnlyTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
|
||||
|
||||
Assert.IsFalse(dict.IsReadOnly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] keys = dict.Keys.ToArray();
|
||||
|
||||
Assert.AreEqual(3, keys.Length);
|
||||
Assert.AreEqual(-1, keys[0]);
|
||||
Assert.AreEqual(0, keys[1]);
|
||||
Assert.AreEqual(1, keys[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] values = dict.Values.ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.AreEqual(-10, values[0]);
|
||||
Assert.AreEqual(0, values[1]);
|
||||
Assert.AreEqual(10, values[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerTest()
|
||||
{
|
||||
// ReSharper disable UseObjectOrCollectionInitializer
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
|
||||
// ReSharper restore UseObjectOrCollectionInitializer
|
||||
|
||||
dict[0] = 0;
|
||||
dict[1] = 10;
|
||||
dict[-1] = -10;
|
||||
dict[-1] = -11;
|
||||
|
||||
Assert.AreEqual(0, dict[0]);
|
||||
Assert.AreEqual(10, dict[1]);
|
||||
Assert.AreEqual(-11, dict[-1]);
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
|
||||
int[] expectedKeys = {-1, 0, 1 };
|
||||
int[] expectedValues = {-11, 0, 10};
|
||||
|
||||
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
|
||||
Assert.AreEqual(expectedValues, dict.Values.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public class ReverseLookupDictionaryTest
|
||||
{
|
||||
|
||||
//todo: Finish!
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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));
|
||||
Assert.IsFalse(dict.Keys.Contains(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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"));
|
||||
Assert.IsFalse(dict.Values.Contains("40"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsKey(1));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsValue("10"));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsValue("10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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.DoesNotThrow(() => dict.Add(2, "10"));
|
||||
Assert.DoesNotThrow(() => dict.Add(3, "20"));
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
dict.Set(4, "20");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
Assert.AreEqual("40", dict.GetValue(4));
|
||||
|
||||
CollectionAssert.AreEquivalent(new[]{1,4}, dict.GetKeys("20"));
|
||||
CollectionAssert.AreEquivalent(new[]{3}, dict.GetKeys("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetKeysTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "Odd");
|
||||
dict.Add(2, "Even");
|
||||
dict.Set(3, "Odd");
|
||||
dict.Add(4, "Value");
|
||||
dict.Set(4, "Even");
|
||||
dict.Add(5, "Odd");
|
||||
dict.Add(6, "Even");
|
||||
|
||||
CollectionAssert.AreEquivalent(new[]{1,3,5}, dict.GetKeys("Odd"));
|
||||
CollectionAssert.AreEquivalent(new[]{2,4,6}, dict.GetKeys("Even"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
dict.Set(2, "20");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
Assert.AreEqual("20", dict.GetValue(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveKeyTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveKey(1);
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueSingleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueMultipleTest1()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueWithOthersTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
dict.Add(4, "20");
|
||||
dict.Add(5, "20");
|
||||
dict.Add(6, "some other string");
|
||||
|
||||
Assert.AreEqual(6, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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 TryGetKeysMultipleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
IEnumerable<int> value;
|
||||
Assert.IsFalse(dict.TryGetKeys("10", out value));
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(11, "100");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1}, value);
|
||||
Assert.IsTrue(dict.TryGetKeys("100", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{11}, value);
|
||||
|
||||
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
dict.Add(12, "100");
|
||||
dict.Add(13, "100");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1,2,3}, value);
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("100", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{11,12,13}, value);
|
||||
|
||||
Assert.IsFalse(dict.TryGetKeys("string", out value));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetKeysSingleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
IEnumerable<int> value;
|
||||
Assert.IsFalse(dict.TryGetKeys("10", out value));
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1}, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ namespace ICD.Common.Utils.Tests
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 4,
|
||||
D = 32
|
||||
D = 32,
|
||||
BandC = B | C
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -185,6 +186,16 @@ namespace ICD.Common.Utils.Tests
|
||||
value);
|
||||
}
|
||||
|
||||
[TestCase(eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.BandC, eTestFlagsEnum.A | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, eTestFlagsEnum.None)]
|
||||
[TestCase(eTestFlagsEnum.None, eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.D, eTestFlagsEnum.A | eTestFlagsEnum.BandC)]
|
||||
public void GetInverseFlagsTest(eTestFlagsEnum expected, eTestFlagsEnum value)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.GetInverseFlags(value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasFlagGenericTest()
|
||||
{
|
||||
|
||||
@@ -32,5 +32,17 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
Assert.AreEqual(expected, value.HasFlags(flags));
|
||||
}
|
||||
|
||||
[TestCase(eTestEnum.A, eTestEnum.B)]
|
||||
[TestCase(eTestEnum.B, eTestEnum.C)]
|
||||
[TestCase(eTestEnum.C, eTestEnum.A)]
|
||||
[TestCase(eTestEnum.A | eTestEnum.B, null)]
|
||||
public void CycleNextTest(eTestEnum value, eTestEnum? expected)
|
||||
{
|
||||
if (EnumUtils.HasMultipleFlags(value))
|
||||
Assert.Catch(typeof(InvalidOperationException), () => value.CycleNext());
|
||||
else
|
||||
Assert.AreEqual(expected, value.CycleNext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.IO;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
|
||||
@@ -18,5 +18,13 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetPassword());
|
||||
}
|
||||
|
||||
[TestCase("http://www.test.com/a/b/c", "http://www.test.com/a/b/")]
|
||||
[TestCase("http://www.test.com/a/b/", "http://www.test.com/a/")]
|
||||
[TestCase("http://www.test.com/", "http://www.test.com/")]
|
||||
public void GetParentUri(string uriString, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetParentUri().ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
|
||||
<RootNamespace>ICD.Common.Utils.Tests</RootNamespace>
|
||||
<AssemblyName>ICD.Common.Utils.Tests</AssemblyName>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
@@ -11,24 +11,25 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE;STANDARD</DefineConstants>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="binNetCoreApp\**" />
|
||||
<EmbeddedResource Remove="binNetCoreApp\**" />
|
||||
<None Remove="binNetCoreApp\**" />
|
||||
<Compile Remove="bin\**" />
|
||||
<EmbeddedResource Remove="bin\**" />
|
||||
<None Remove="bin\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
188
ICD.Common.Utils.Tests/IcdAutoResetEventTest.cs
Normal file
188
ICD.Common.Utils.Tests/IcdAutoResetEventTest.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
class IcdAutoResetEventTest
|
||||
{
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void InitialStateTest(bool initialState)
|
||||
{
|
||||
using (var waitHandleEvent = new IcdAutoResetEvent(initialState))
|
||||
{
|
||||
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultInitialStateTest()
|
||||
{
|
||||
using (var waitHandleEvent = new IcdAutoResetEvent())
|
||||
{
|
||||
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadSetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
countSection.Execute(() => releasedCount++);
|
||||
;
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
|
||||
|
||||
///Auto reset should only release one thread at a time
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(i, countSection.Execute(() => releasedCount),
|
||||
"Incorrect number of threads released");
|
||||
}
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadResetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(true);
|
||||
|
||||
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
|
||||
|
||||
waitHandleEvent.Reset();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
if (waitHandleEvent.WaitOne(100))
|
||||
countSection.Execute(() => releasedCount++);
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(200)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimeoutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne(waitTime * 2);
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimedOutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
bool? returned = null;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
returned = waitHandleEvent.WaitOne(waitTime);
|
||||
released = true;
|
||||
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
ThreadingUtils.Sleep(waitTime * 2);
|
||||
|
||||
Assert.True(released, "Thread didn't release after timeout");
|
||||
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
183
ICD.Common.Utils.Tests/IcdManualResetEventTest.cs
Normal file
183
ICD.Common.Utils.Tests/IcdManualResetEventTest.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
class IcdManualResetEventTest
|
||||
{
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void InitialStateTest(bool initialState)
|
||||
{
|
||||
using (var waitHandleEvent = new IcdManualResetEvent(initialState))
|
||||
{
|
||||
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultInitialStateTest()
|
||||
{
|
||||
using (var waitHandleEvent = new IcdManualResetEvent())
|
||||
{
|
||||
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadSetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
countSection.Execute(() => releasedCount++);
|
||||
;
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(500);
|
||||
|
||||
Assert.AreEqual(count, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadResetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(true);
|
||||
|
||||
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
|
||||
|
||||
waitHandleEvent.Reset();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
if (waitHandleEvent.WaitOne(100))
|
||||
countSection.Execute(() => releasedCount++);
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(200)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimeoutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne(waitTime * 2);
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimedOutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
bool? returned = null;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
returned = waitHandleEvent.WaitOne(waitTime);
|
||||
released = true;
|
||||
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
ThreadingUtils.Sleep(waitTime * 2);
|
||||
|
||||
Assert.True(released, "Thread didn't release after timeout");
|
||||
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Json;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Utils.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Json
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.IO;
|
||||
using ICD.Common.Utils.Json;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Json
|
||||
|
||||
@@ -37,30 +37,5 @@ namespace ICD.Common.Utils.Tests
|
||||
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryEnterTest()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
SafeCriticalSection section = new SafeCriticalSection();
|
||||
section.Enter();
|
||||
|
||||
// ReSharper disable once NotAccessedVariable
|
||||
ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => result == 1, 1000));
|
||||
|
||||
section.Leave();
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
result = section.TryEnter() ? 2 : 0;
|
||||
section.Leave();
|
||||
});
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => result == 2, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ICD.Common.Properties;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
@@ -24,6 +25,21 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual("\x08\x22\x00\x00\x00\x02", output);
|
||||
}
|
||||
|
||||
[TestCase("FF", new byte[] {0xFF})]
|
||||
[TestCase("01FF", new byte[] { 0x01, 0xFF })]
|
||||
public void HexToBytes(string value, byte[] expected)
|
||||
{
|
||||
byte[] bytes = StringUtils.HexToBytes(value);
|
||||
Assert.IsTrue(bytes.SequenceEqual(expected));
|
||||
}
|
||||
|
||||
[TestCase("1", 1)]
|
||||
[TestCase("FF", 0xFF)]
|
||||
public void HexToByte(string value, byte expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.HexToByte(value));
|
||||
}
|
||||
|
||||
[TestCase("Test", "Test")]
|
||||
[TestCase("test", "Test")]
|
||||
[TestCase("TodayILiveInTheUSAWithSimon", "Today I Live In The USA With Simon")]
|
||||
|
||||
205
ICD.Common.Utils.Tests/ThreadedWorkerQueueTest.cs
Normal file
205
ICD.Common.Utils.Tests/ThreadedWorkerQueueTest.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class ThreadedWorkerQueueTest
|
||||
{
|
||||
[Test]
|
||||
public void BetweenTimeTest()
|
||||
{
|
||||
List<int> callbacks = new List<int>();
|
||||
|
||||
using (ThreadedWorkerQueue<int> queue = new ThreadedWorkerQueue<int>((d) => callbacks.Add(d), true, 1000))
|
||||
{
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
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)]
|
||||
[TestCase(0)]
|
||||
[TestCase(long.MaxValue)]
|
||||
public void BetweenMillisecondsTest(long milliseconds)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>((d) => { }, true, milliseconds))
|
||||
Assert.AreEqual(milliseconds, queue.BetweenTime);
|
||||
}
|
||||
|
||||
[TestCase(5)]
|
||||
[TestCase(0)]
|
||||
[TestCase(30)]
|
||||
public void CountTest(int count)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
queue.Enqueue(1);
|
||||
|
||||
Assert.AreEqual(count, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProcessBetweenTimeTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
Assert.AreEqual(0, processed.Count, "Queue processed item early");
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, processed.Count, "First item not processed");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, processed.Count, "Second item not processed");
|
||||
|
||||
queue.SetRunProcess(false);
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(2, processed.Count, "Item processed after stopping run process");
|
||||
Assert.AreEqual(1, queue.Count, "Incorrect number of items in queue");
|
||||
|
||||
// Queue lower priority item
|
||||
queue.Enqueue(5, 1);
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(3, processed.Count, "Third item not processed");
|
||||
Assert.AreEqual(5, processed[2], "Dequeued incorrect priority item");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(4, processed.Count, "Didn't process all items");
|
||||
Assert.True(processed.SequenceEqual(new[] { 1, 2, 5, 3 }), "Processed sequence incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlushQueueBetweenTimeTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
|
||||
{
|
||||
Assert.True(queue.WaitForFlush(1), "WaitForFlush on empty queue failed");
|
||||
|
||||
queue.Enqueue(11);
|
||||
queue.Enqueue(21);
|
||||
queue.Enqueue(31);
|
||||
queue.Enqueue(41);
|
||||
queue.Enqueue(51);
|
||||
queue.Enqueue(61);
|
||||
queue.Enqueue(71);
|
||||
queue.Enqueue(81);
|
||||
queue.Enqueue(91);
|
||||
queue.Enqueue(101);
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
Assert.False(queue.WaitForFlush(1250), "WaitForFlush didn't time out");
|
||||
Assert.AreEqual(2, processed.Count, "Didn't process correct number of items in time frame");
|
||||
|
||||
Assert.True(queue.WaitForFlush(), "WaitForFlush failed");
|
||||
Assert.AreEqual(10, processed.Count, "Not all items processed");
|
||||
Assert.AreEqual(0, queue.Count, "Queue not empty");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void SetRunProcessTest(bool runProcess)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, !runProcess))
|
||||
{
|
||||
Assert.AreEqual(!runProcess, queue.RunProcess, "Initial state wrong");
|
||||
queue.SetRunProcess(runProcess);
|
||||
Assert.AreEqual(runProcess, queue.RunProcess, "Didn't set to correct state 1st time");
|
||||
queue.SetRunProcess(!runProcess);
|
||||
Assert.AreEqual(!runProcess, queue.RunProcess, "Didn't set to correct state 2nd time");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false))
|
||||
{
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
Assert.AreEqual(3, queue.Count, "First queue count wrong");
|
||||
|
||||
queue.Enqueue(40);
|
||||
queue.Enqueue(50);
|
||||
queue.Enqueue(60);
|
||||
|
||||
Assert.AreEqual(6, queue.Count, "Second queue count wrong");
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
Assert.True(queue.WaitForFlush(),"Queue didn't flush after processing");
|
||||
|
||||
|
||||
Assert.True(processed.SequenceEqual(new[] { 10, 20, 30, 40, 50, 60 }), "Processed sequence wrong");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -49,12 +49,14 @@ namespace ICD.Common.Utils.Tests.Timers
|
||||
[Test]
|
||||
public void TriggerTest()
|
||||
{
|
||||
bool called = false;
|
||||
SafeTimer timer = SafeTimer.Stopped(() => called = true);
|
||||
int called = 0;
|
||||
SafeTimer timer = SafeTimer.Stopped(() => called++);
|
||||
|
||||
timer.Trigger();
|
||||
|
||||
Assert.IsTrue(called);
|
||||
ThreadingUtils.Sleep(50);
|
||||
|
||||
Assert.AreEqual(1, called);
|
||||
|
||||
timer.Dispose();
|
||||
}
|
||||
|
||||
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal file
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Types;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Types
|
||||
{
|
||||
[TestFixture]
|
||||
public class GenericNotifyFlagsChangedTest
|
||||
{
|
||||
[Flags]
|
||||
private enum eTestFlagsEnum
|
||||
{
|
||||
None = 0,
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 4,
|
||||
D = 32,
|
||||
BandC = B | C
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSingleFlags()
|
||||
{
|
||||
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
|
||||
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
|
||||
|
||||
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
|
||||
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
|
||||
|
||||
// Initial State
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add No flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add Flag
|
||||
genericNotify.Data = eTestFlagsEnum.B;
|
||||
Assert.AreEqual(1, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, addedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, genericNotify.Data);
|
||||
|
||||
// Add Another Flag
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, genericNotify.Data);
|
||||
|
||||
// Remove a Flag
|
||||
genericNotify.Data = genericNotify.Data & ~ eTestFlagsEnum.B;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, removedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, genericNotify.Data);
|
||||
|
||||
// Add Already Existing Flags
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
|
||||
// Clear Flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(2, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, removedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleFlags()
|
||||
{
|
||||
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
|
||||
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
|
||||
|
||||
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
|
||||
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
|
||||
|
||||
// Initial State
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add No flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add Flag
|
||||
genericNotify.Data = eTestFlagsEnum.B | eTestFlagsEnum.D;
|
||||
Assert.AreEqual(1, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, addedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Add Another Flag
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Remove a Flag
|
||||
genericNotify.Data = eTestFlagsEnum.D;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, removedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Add Already Existing Flags
|
||||
genericNotify.Data |= eTestFlagsEnum.D;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
|
||||
// Clear Flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(2, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.D, removedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
155
ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs
Normal file
155
ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System.Xml;
|
||||
using ICD.Common.Utils.Xml;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class GenericXmlConverterTest
|
||||
{
|
||||
[XmlConverter(typeof(TestClassConverter))]
|
||||
private sealed class TestClass
|
||||
{
|
||||
public string A { get; set; }
|
||||
public int B { get; set; }
|
||||
}
|
||||
|
||||
private sealed class TestClassConverter : AbstractGenericXmlConverter<TestClass>
|
||||
{
|
||||
private const string ELEMENT_A = "A";
|
||||
private const string ATTRIBUTE_B = "b";
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle the current attribute.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="instance"></param>
|
||||
protected override void ReadAttribute(IcdXmlReader reader, TestClass instance)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case ATTRIBUTE_B:
|
||||
instance.B = int.Parse(reader.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
base.ReadAttribute(reader, instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle the current element.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="instance"></param>
|
||||
protected override void ReadElement(IcdXmlReader reader, TestClass instance)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case ELEMENT_A:
|
||||
instance.A = reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
base.ReadElement(reader, instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to write attributes to the root element.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="value"></param>
|
||||
protected override void WriteAttributes(IcdXmlTextWriter writer, TestClass value)
|
||||
{
|
||||
base.WriteAttributes(writer, value);
|
||||
|
||||
writer.WriteAttributeString(ATTRIBUTE_B, value.B.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to write elements to the writer.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="value"></param>
|
||||
protected override void WriteElements(IcdXmlTextWriter writer, TestClass value)
|
||||
{
|
||||
base.WriteElements(writer, value);
|
||||
|
||||
writer.WriteElementString(ELEMENT_A, value.A);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestElement()
|
||||
{
|
||||
const string xml = @"<Instances>
|
||||
<Instance b=""1"">
|
||||
<A>Test</A>
|
||||
</Instance>
|
||||
</Instances>";
|
||||
|
||||
using (IcdXmlReader reader = new IcdXmlReader(xml))
|
||||
{
|
||||
// Read into the Instances node
|
||||
reader.Read();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
|
||||
// Read into the Instance node
|
||||
reader.Read();
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instance", reader.Name);
|
||||
|
||||
// Deserialize
|
||||
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
|
||||
|
||||
Assert.IsNotNull(instance);
|
||||
Assert.AreEqual("Test", instance.A);
|
||||
Assert.AreEqual(1, instance.B);
|
||||
|
||||
// Deserialization should land on the following node
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyElement()
|
||||
{
|
||||
const string xml = @"<Instances>
|
||||
<Instance b=""1"" />
|
||||
</Instances>";
|
||||
|
||||
using (IcdXmlReader reader = new IcdXmlReader(xml))
|
||||
{
|
||||
// Read into the Instances node
|
||||
reader.Read();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
|
||||
// Read into the Instance node
|
||||
reader.Read();
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instance", reader.Name);
|
||||
|
||||
// Deserialize
|
||||
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
|
||||
|
||||
Assert.IsNotNull(instance);
|
||||
Assert.IsNull(instance.A);
|
||||
Assert.AreEqual(1, instance.B);
|
||||
|
||||
// Deserialization should land on the following node
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,15 +70,32 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
public static IDictionary<string, string> PuttyColors { get { return s_PuttyColors; } }
|
||||
|
||||
#if NETSTANDARD
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Enables ANSI color in the console.
|
||||
/// </summary>
|
||||
static AnsiUtils()
|
||||
public static void EnableAnsiColor()
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
// Enables ANSI color output in windows/linux console
|
||||
Pastel.ConsoleExtensions.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables ANSI color in the console.
|
||||
/// </summary>
|
||||
public static void DisableAnsiColor()
|
||||
{
|
||||
Pastel.ConsoleExtensions.Disable();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Removes ANSI control sequences from the string.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static string StripAnsi(string data)
|
||||
{
|
||||
return Regex.Replace(data, ANSI_REGEX, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
153
ICD.Common.Utils/BigEndianBitConverter.cs
Normal file
153
ICD.Common.Utils/BigEndianBitConverter.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class BigEndianBitConverter
|
||||
{
|
||||
private const byte FULL_BYTE = 0xFF;
|
||||
|
||||
public static ushort ToUshort([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((ushort)ToShort(value, startIndex));
|
||||
}
|
||||
|
||||
public static short ToShort([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt16(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(short);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
short result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= (short)(value[i + startIndex] << GetBitShift(i, bytes));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint ToUint([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((uint)ToInt(value, startIndex));
|
||||
}
|
||||
|
||||
public static int ToInt([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt32(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(int);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= value[i + startIndex] << GetBitShift(i, bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ulong ToUlong([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((ulong)ToLong(value, startIndex));
|
||||
}
|
||||
|
||||
public static long ToLong([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt64(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(long);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
long result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= (long)(value[i + startIndex]) << GetBitShift(i, bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(int value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(int);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
public static byte[] GetBytes(uint value)
|
||||
{
|
||||
return unchecked(GetBytes((int)value));
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(short value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(short);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(ushort value)
|
||||
{
|
||||
return unchecked(GetBytes((short)value));
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(long value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(long);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(ulong value)
|
||||
{
|
||||
return unchecked(GetBytes((long)value));
|
||||
}
|
||||
private static int GetBitShift(int byteNumber, int totalBytes)
|
||||
{
|
||||
return ((totalBytes - 1 - byteNumber) * 8);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,36 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public BiDictionary()
|
||||
public BiDictionary() :
|
||||
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>();
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public BiDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>(valueComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
: this(keyComparer, valueComparer)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException("dict");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dict)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,13 +75,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict)
|
||||
: this()
|
||||
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException("dict");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dict)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
@@ -1,213 +1,651 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
|
||||
{
|
||||
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;
|
||||
private readonly IcdOrderedDictionary m_PrivateDictionary;
|
||||
|
||||
#region Properties
|
||||
public int Count { get { return m_PrivateDictionary.Count; } }
|
||||
|
||||
public int Count { get { return m_Dictionary.Count; } }
|
||||
int ICollection.Count { get { return m_PrivateDictionary.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
|
||||
|
||||
[CanBeNull]
|
||||
public TValue this[[NotNull] TKey key]
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get { return m_Dictionary[key]; }
|
||||
set
|
||||
get
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
Remove(key);
|
||||
Add(key, value);
|
||||
if (m_PrivateDictionary.Contains(key))
|
||||
return (TValue)m_PrivateDictionary[key];
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
m_PrivateDictionary[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
object IDictionary.this[object key]
|
||||
{
|
||||
get { return m_PrivateDictionary[key]; }
|
||||
set { m_PrivateDictionary[key] = value; }
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
List<TKey> keys = new List<TKey>(m_PrivateDictionary.Count);
|
||||
|
||||
foreach (TKey key in m_PrivateDictionary.Keys)
|
||||
{
|
||||
keys.Add(key);
|
||||
}
|
||||
|
||||
// Keys should be put in a ReadOnlyCollection,
|
||||
// but since this is an internal class, for performance reasons,
|
||||
// we choose to avoid creating yet another collection.
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
ICollection IDictionary.Keys { get { return m_PrivateDictionary.Keys; } }
|
||||
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
List<TValue> values = new List<TValue>(m_PrivateDictionary.Count);
|
||||
|
||||
foreach (TValue value in m_PrivateDictionary.Values)
|
||||
values.Add(value);
|
||||
|
||||
// Values should be put in a ReadOnlyCollection,
|
||||
// but since this is an internal class, for performance reasons,
|
||||
// we choose to avoid creating yet another collection.
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
ICollection IDictionary.Values { get { return m_PrivateDictionary.Values; } }
|
||||
|
||||
bool IDictionary.IsFixedSize { get { return ((IDictionary)m_PrivateDictionary).IsFixedSize; } }
|
||||
|
||||
bool IDictionary.IsReadOnly { get { return m_PrivateDictionary.IsReadOnly; } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return ((ICollection)m_PrivateDictionary).IsSynchronized; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return m_PrivateDictionary; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdOrderedDictionary()
|
||||
: this(Comparer<TKey>.Default)
|
||||
{
|
||||
m_PrivateDictionary = new IcdOrderedDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer)
|
||||
: this(comparer, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="equalityComparer"></param>
|
||||
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] 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_ValuesOrderedByKey = new List<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dictionary"></param>
|
||||
public IcdOrderedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
|
||||
: this()
|
||||
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException("dictionary");
|
||||
return;
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
m_PrivateDictionary = new IcdOrderedDictionary();
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
|
||||
{
|
||||
m_PrivateDictionary.Add(pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
|
||||
.GetEnumerator();
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_Dictionary.ContainsKey(key))
|
||||
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
|
||||
m_PrivateDictionary.Add(key, value);
|
||||
}
|
||||
|
||||
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
|
||||
m_ValuesOrderedByKey.Insert(index, value);
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
public KeyValuePair<TKey, TValue> Get(int index)
|
||||
{
|
||||
DictionaryEntry entry = (DictionaryEntry)m_PrivateDictionary.ObjectsArray[index];
|
||||
return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_ValuesOrderedByKey.Clear();
|
||||
m_Dictionary.Clear();
|
||||
m_PrivateDictionary.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!m_Dictionary.Remove(key))
|
||||
if (item.Key == null || !m_PrivateDictionary.Contains(item.Key))
|
||||
return false;
|
||||
|
||||
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
|
||||
|
||||
m_OrderedKeys.RemoveAt(index);
|
||||
m_ValuesOrderedByKey.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
return m_PrivateDictionary[item.Key].Equals(item.Value);
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.TryGetValue(key, out value);
|
||||
return m_PrivateDictionary.Contains(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException("array");
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
if (arrayIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("arrayIndex");
|
||||
}
|
||||
|
||||
if (array.Rank > 1 || arrayIndex >= array.Length || array.Length - arrayIndex < m_PrivateDictionary.Count)
|
||||
{
|
||||
throw new ArgumentException("array");
|
||||
}
|
||||
|
||||
int index = arrayIndex;
|
||||
foreach (DictionaryEntry entry in m_PrivateDictionary)
|
||||
{
|
||||
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
foreach (DictionaryEntry entry in m_PrivateDictionary)
|
||||
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value) &&
|
||||
EqualityComparer<TValue>.Default.Equals(value, item.Value);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in this)
|
||||
if (Contains(item))
|
||||
{
|
||||
array.SetValue(kvp, index);
|
||||
index++;
|
||||
m_PrivateDictionary.Remove(item.Key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_PrivateDictionary.Contains(key))
|
||||
{
|
||||
m_PrivateDictionary.Remove(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
bool keyExists = m_PrivateDictionary.Contains(key);
|
||||
value = keyExists ? (TValue)m_PrivateDictionary[key] : default(TValue);
|
||||
|
||||
return keyExists;
|
||||
}
|
||||
|
||||
void IDictionary.Add(object key, object value)
|
||||
{
|
||||
m_PrivateDictionary.Add(key, value);
|
||||
}
|
||||
|
||||
void IDictionary.Clear()
|
||||
{
|
||||
m_PrivateDictionary.Clear();
|
||||
}
|
||||
|
||||
bool IDictionary.Contains(object key)
|
||||
{
|
||||
return m_PrivateDictionary.Contains(key);
|
||||
}
|
||||
|
||||
IDictionaryEnumerator IDictionary.GetEnumerator()
|
||||
{
|
||||
return m_PrivateDictionary.GetEnumerator();
|
||||
}
|
||||
|
||||
void IDictionary.Remove(object key)
|
||||
{
|
||||
m_PrivateDictionary.Remove(key);
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
m_PrivateDictionary.CopyTo(array, index);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IcdOrderedDictionary : IDictionary
|
||||
{
|
||||
private ArrayList m_ObjectsArray;
|
||||
private Hashtable m_ObjectsTable;
|
||||
private readonly int m_InitialCapacity;
|
||||
private readonly IEqualityComparer m_Comparer;
|
||||
private readonly bool m_ReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the table.
|
||||
/// </summary>
|
||||
public int Count { get { return ObjectsArray.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the collection can grow.
|
||||
/// </summary>
|
||||
bool IDictionary.IsFixedSize { get { return m_ReadOnly; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the collection is not read-only
|
||||
/// </summary>
|
||||
public bool IsReadOnly { get { return m_ReadOnly; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this class is not synchronized
|
||||
/// </summary>
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of keys in the table in order.
|
||||
/// </summary>
|
||||
public ICollection Keys { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, true); } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an arrayList of the values in the table
|
||||
/// </summary>
|
||||
public ICollection Values { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, false); } }
|
||||
|
||||
public ArrayList ObjectsArray
|
||||
{
|
||||
get { return m_ObjectsArray ?? (m_ObjectsArray = new ArrayList(m_InitialCapacity)); }
|
||||
}
|
||||
|
||||
private Hashtable ObjectsTable
|
||||
{
|
||||
get { return m_ObjectsTable ?? (m_ObjectsTable = new Hashtable(m_InitialCapacity, m_Comparer)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The SyncRoot object. Not used because IsSynchronized is false
|
||||
/// </summary>
|
||||
object ICollection.SyncRoot { get { return this; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object at the specified index
|
||||
/// </summary>
|
||||
public object this[int index]
|
||||
{
|
||||
get { return ((DictionaryEntry)ObjectsArray[index]).Value; }
|
||||
set
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index < 0 || index >= ObjectsArray.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
|
||||
ObjectsArray[index] = new DictionaryEntry(key, value);
|
||||
ObjectsTable[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
/// <summary>
|
||||
/// Gets or sets the object with the specified key
|
||||
/// </summary>
|
||||
public object this[object key]
|
||||
{
|
||||
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
|
||||
get { return ObjectsTable[key]; }
|
||||
set
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (ObjectsTable.Contains(key))
|
||||
{
|
||||
ObjectsTable[key] = value;
|
||||
ObjectsArray[IndexOfKey(key)] = new DictionaryEntry(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary() : this(0)
|
||||
{
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary(int capacity) : this(capacity, null)
|
||||
{
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary(int capacity, IEqualityComparer comparer)
|
||||
{
|
||||
m_InitialCapacity = capacity;
|
||||
m_Comparer = comparer;
|
||||
}
|
||||
|
||||
private IcdOrderedDictionary(IcdOrderedDictionary dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
throw new ArgumentNullException("dictionary");
|
||||
}
|
||||
|
||||
m_ReadOnly = true;
|
||||
m_ObjectsArray = dictionary.m_ObjectsArray;
|
||||
m_ObjectsTable = dictionary.m_ObjectsTable;
|
||||
m_Comparer = dictionary.m_Comparer;
|
||||
m_InitialCapacity = dictionary.m_InitialCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry to the table with the lowest-available index.
|
||||
/// </summary>
|
||||
public void Add(object key, object value)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
ObjectsTable.Add(key, value);
|
||||
ObjectsArray.Add(new DictionaryEntry(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all elements in the table.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
ObjectsTable.Clear();
|
||||
ObjectsArray.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a readonly IcdOrderedDictionary for the given IcdOrderedDictionary.
|
||||
/// </summary>
|
||||
public IcdOrderedDictionary AsReadOnly()
|
||||
{
|
||||
return new IcdOrderedDictionary(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the key exists in the table, false otherwise.
|
||||
/// </summary>
|
||||
public bool Contains(object key)
|
||||
{
|
||||
return ObjectsTable.Contains(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the table to an array. This will not preserve order.
|
||||
/// </summary>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
ObjectsTable.CopyTo(array, index);
|
||||
}
|
||||
|
||||
private int IndexOfKey(object key)
|
||||
{
|
||||
for (int i = 0; i < ObjectsArray.Count; i++)
|
||||
{
|
||||
object o = ((DictionaryEntry)ObjectsArray[i]).Key;
|
||||
if (m_Comparer != null)
|
||||
{
|
||||
if (m_Comparer.Equals(o, key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o.Equals(key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new object at the given index with the given key.
|
||||
/// </summary>
|
||||
public void Insert(int index, object key, object value)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index > Count || index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
ObjectsTable.Add(key, value);
|
||||
ObjectsArray.Insert(index, new DictionaryEntry(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the entry at the given index.
|
||||
/// </summary>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index >= Count || index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
|
||||
ObjectsArray.RemoveAt(index);
|
||||
ObjectsTable.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the entry with the given key.
|
||||
/// </summary>
|
||||
public void Remove(object key)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
|
||||
int index = IndexOfKey(key);
|
||||
if (index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectsTable.Remove(key);
|
||||
ObjectsArray.RemoveAt(index);
|
||||
}
|
||||
|
||||
#region IDictionary implementation
|
||||
|
||||
/// <internalonly/>
|
||||
public IDictionaryEnumerator GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable implementation
|
||||
|
||||
/// <internalonly/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries
|
||||
/// in the order by index.
|
||||
/// </summary>
|
||||
private sealed class OrderedDictionaryEnumerator : IDictionaryEnumerator
|
||||
{
|
||||
internal const int KEYS = 1;
|
||||
internal const int VALUES = 2;
|
||||
internal const int DICTIONARY_ENTRY = 3;
|
||||
|
||||
private readonly int m_ObjectReturnType;
|
||||
private readonly IEnumerator m_ArrayEnumerator;
|
||||
|
||||
internal OrderedDictionaryEnumerator(ArrayList array, int objectReturnType)
|
||||
{
|
||||
m_ArrayEnumerator = array.GetEnumerator();
|
||||
m_ObjectReturnType = objectReturnType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current DictionaryEntry. This is the same as Entry, but not strongly-typed.
|
||||
/// </summary>
|
||||
public object Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ObjectReturnType == KEYS)
|
||||
{
|
||||
return ((DictionaryEntry)m_ArrayEnumerator.Current).Key;
|
||||
}
|
||||
if (m_ObjectReturnType == VALUES)
|
||||
{
|
||||
return ((DictionaryEntry)m_ArrayEnumerator.Current).Value;
|
||||
}
|
||||
return Entry;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current DictionaryEntry
|
||||
/// </summary>
|
||||
public DictionaryEntry Entry
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DictionaryEntry(((DictionaryEntry)m_ArrayEnumerator.Current).Key,
|
||||
((DictionaryEntry)m_ArrayEnumerator.Current).Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the key of the current DictionaryEntry
|
||||
/// </summary>
|
||||
public object Key { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Key; } }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value of the current DictionaryEntry
|
||||
/// </summary>
|
||||
public object Value { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Moves the enumerator pointer to the next member
|
||||
/// </summary>
|
||||
public bool MoveNext()
|
||||
{
|
||||
return m_ArrayEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumerator pointer to the beginning.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
m_ArrayEnumerator.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OrderedDictionaryKeyValueCollection implements a collection for the Values and Keys properties
|
||||
/// that is "live"- it will reflect changes to the IcdOrderedDictionary on the collection made after the getter
|
||||
/// was called.
|
||||
/// </summary>
|
||||
private sealed class OrderedDictionaryKeyValueCollection : ICollection
|
||||
{
|
||||
private readonly ArrayList m_Objects;
|
||||
private readonly bool m_IsKeys;
|
||||
|
||||
public OrderedDictionaryKeyValueCollection(ArrayList array, bool isKeys)
|
||||
{
|
||||
m_Objects = array;
|
||||
m_IsKeys = isKeys;
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
foreach (object o in m_Objects)
|
||||
{
|
||||
array.SetValue(m_IsKeys ? ((DictionaryEntry)o).Key : ((DictionaryEntry)o).Value, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
int ICollection.Count { get { return m_Objects.Count; } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return m_Objects.SyncRoot; } }
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(m_Objects,
|
||||
m_IsKeys
|
||||
? OrderedDictionaryEnumerator.KEYS
|
||||
: OrderedDictionaryEnumerator.VALUES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
213
ICD.Common.Utils/Collections/IcdSortedDictionary.cs
Normal file
213
ICD.Common.Utils/Collections/IcdSortedDictionary.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class IcdSortedDictionary<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;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_Dictionary.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
|
||||
|
||||
[CanBeNull]
|
||||
public TValue this[[NotNull] TKey key]
|
||||
{
|
||||
get { return m_Dictionary[key]; }
|
||||
set
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
Remove(key);
|
||||
Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdSortedDictionary()
|
||||
: this(Comparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer)
|
||||
: this(comparer, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="equalityComparer"></param>
|
||||
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] 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_ValuesOrderedByKey = new List<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dictionary"></param>
|
||||
public IcdSortedDictionary([NotNull] 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
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
|
||||
.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_Dictionary.ContainsKey(key))
|
||||
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
|
||||
|
||||
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
|
||||
m_ValuesOrderedByKey.Insert(index, value);
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_ValuesOrderedByKey.Clear();
|
||||
m_Dictionary.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!m_Dictionary.Remove(key))
|
||||
return false;
|
||||
|
||||
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
|
||||
|
||||
m_OrderedKeys.RemoveAt(index);
|
||||
m_ValuesOrderedByKey.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value) &&
|
||||
EqualityComparer<TValue>.Default.Equals(value, item.Value);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in this)
|
||||
{
|
||||
array.SetValue(kvp, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace ICD.Common.Utils.Collections
|
||||
#endif
|
||||
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
|
||||
{
|
||||
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
|
||||
private readonly IcdSortedDictionary<int, List<T>> m_PriorityToQueue;
|
||||
private int m_Count;
|
||||
|
||||
#region Properties
|
||||
@@ -46,7 +46,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
public PriorityQueue()
|
||||
{
|
||||
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
|
||||
m_PriorityToQueue = new IcdSortedDictionary<int, List<T>>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Timers;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
|
||||
/// </summary>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
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; } }
|
||||
|
||||
[NotNull]
|
||||
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_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_DequeueTimer.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the given item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue([CanBeNull] 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
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo([NotNull] Array array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
299
ICD.Common.Utils/Collections/ReverseLookupDictionary.cs
Normal file
299
ICD.Common.Utils/Collections/ReverseLookupDictionary.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public class ReverseLookupDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> m_KeyToValue;
|
||||
private readonly Dictionary<TValue, IcdHashSet<TKey>> m_ValueToKeys;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_KeyToValue.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValueToKeys.Keys; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ReverseLookupDictionary() :
|
||||
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public ReverseLookupDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
|
||||
m_ValueToKeys = new Dictionary<TValue, IcdHashSet<TKey>>(valueComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
: this(keyComparer, valueComparer)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException("dict");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dict)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict)
|
||||
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_KeyToValue.Clear();
|
||||
m_ValueToKeys.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue([NotNull] TValue value)
|
||||
{
|
||||
return m_ValueToKeys.ContainsKey(value);
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<TValue, IEnumerable<TKey>>> GetReverseDictionary()
|
||||
{
|
||||
// Cast stuff a layer deep cause weird
|
||||
return m_ValueToKeys.Select(kvp => new KeyValuePair<TValue, IEnumerable<TKey>>(kvp.Key, kvp.Value.ToArray(kvp.Value.Count)));
|
||||
}
|
||||
|
||||
public void Add([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ContainsKey(key))
|
||||
throw new ArgumentException("Key is already present in the collection", "key");
|
||||
|
||||
|
||||
m_KeyToValue.Add(key, value);
|
||||
m_ValueToKeys.GetOrAddNew(value, () => new IcdHashSet<TKey>()).Add(key);
|
||||
}
|
||||
|
||||
public void Set([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
RemoveKey(key);
|
||||
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public IEnumerable<TKey> GetKeys([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
return m_ValueToKeys[value];
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public TValue GetValue([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue[key];
|
||||
}
|
||||
|
||||
public bool RemoveKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
TValue value = m_KeyToValue[key];
|
||||
|
||||
m_KeyToValue.Remove(key);
|
||||
|
||||
IcdHashSet<TKey> keys = m_ValueToKeys[value];
|
||||
keys.Remove(key);
|
||||
|
||||
if (keys.Count == 0)
|
||||
m_ValueToKeys.Remove(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the value from the collection, and any keys that were using it
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns>true if items were removed, false if not</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public bool RemoveValue([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!ContainsValue(value))
|
||||
return false;
|
||||
|
||||
IcdHashSet<TKey> keys = m_ValueToKeys[value];
|
||||
|
||||
m_ValueToKeys.Remove(value);
|
||||
|
||||
foreach (TKey key in keys)
|
||||
{
|
||||
m_KeyToValue.Remove(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetKeys([NotNull] TValue value, out IEnumerable<TKey> keys)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
IcdHashSet<TKey> keysInternal;
|
||||
if (m_ValueToKeys.TryGetValue(value, out keysInternal))
|
||||
{
|
||||
keys = keysInternal;
|
||||
return true;
|
||||
}
|
||||
keys = Enumerable.Empty<TKey>();
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary
|
||||
|
||||
[NotNull]
|
||||
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
|
||||
bool IDictionary<TKey, TValue>.Remove([NotNull] 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([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_KeyToValue.GetEnumerator();
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -151,11 +151,11 @@ namespace ICD.Common.Utils.Collections
|
||||
return m_Collection.GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array myArr, int index)
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
foreach (TContents item in m_Collection)
|
||||
{
|
||||
myArr.SetValue(item, index);
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ namespace ICD.Common.Utils.Collections
|
||||
m_Comparer = comparer;
|
||||
}
|
||||
|
||||
public int GetHashCode(WeakKeyReference<T> weakKey)
|
||||
public int GetHashCode(WeakKeyReference<T> obj)
|
||||
{
|
||||
return weakKey.HashCode;
|
||||
return obj == null ? 0 : obj.HashCode;
|
||||
}
|
||||
|
||||
// Note: There are actually 9 cases to handle here.
|
||||
|
||||
@@ -29,11 +29,12 @@ namespace ICD.Common.Utils.Comparers
|
||||
|
||||
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
throw new ArgumentNullException("x");
|
||||
|
||||
return -1;
|
||||
if (y == null)
|
||||
throw new ArgumentNullException("y");
|
||||
return 1;
|
||||
|
||||
using (IEnumerator<T> firstPos = x.GetEnumerator())
|
||||
{
|
||||
|
||||
@@ -19,6 +19,13 @@ namespace ICD.Common.Utils.Comparers
|
||||
|
||||
public int Compare(Version x, Version y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
|
||||
return x.ClearUndefined()
|
||||
.CompareTo(y.ClearUndefined());
|
||||
}
|
||||
|
||||
310
ICD.Common.Utils/Csv/Csv.cs
Normal file
310
ICD.Common.Utils/Csv/Csv.cs
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.IO;
|
||||
#if SIMPLSHARP
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Root class that contains static functions for straightforward Csv parsing
|
||||
/// </summary>
|
||||
public static class Csv
|
||||
{
|
||||
/// <summary>
|
||||
/// The default Csv field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_CSV_DELIMITER = ',';
|
||||
|
||||
/// <summary>
|
||||
/// The default Csv text qualifier. This is used to encode strings that contain the field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_CSV_QUALIFIER = '"';
|
||||
|
||||
/// <summary>
|
||||
/// The default TSV (tab delimited file) field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_TSV_DELIMITER = '\t';
|
||||
|
||||
/// <summary>
|
||||
/// The default TSV (tabe delimited file) text qualifier. This is used to encode strings that contain the field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_TSV_QUALIFIER = '"';
|
||||
|
||||
|
||||
#region Methods to read Csv data
|
||||
/// <summary>
|
||||
/// Parse a Csv stream into IEnumerable<string[]>, while permitting embedded newlines
|
||||
/// </summary>
|
||||
/// <param name="inStream">The stream to read</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An enumerable object that can be examined to retrieve rows from the stream.</returns>
|
||||
public static IEnumerable<string[]> ParseStream(IcdStreamReader inStream, CsvReaderSettings settings)
|
||||
{
|
||||
string line = "";
|
||||
int i = -1;
|
||||
List<string> list = new List<string>();
|
||||
var work = new StringBuilder();
|
||||
|
||||
// Ensure settings are non-null
|
||||
if (settings == null) {
|
||||
settings = CsvReaderSettings.CSV;
|
||||
}
|
||||
|
||||
// Begin reading from the stream
|
||||
while (i < line.Length || !inStream.EndOfStream)
|
||||
{
|
||||
// Consume the next character of data
|
||||
i++;
|
||||
if (i >= line.Length) {
|
||||
var newLine = inStream.ReadLine();
|
||||
line += newLine + settings.LineSeparator;
|
||||
}
|
||||
char c = line[i];
|
||||
|
||||
// Are we at a line separator? If so, yield our work and begin again
|
||||
if (String.Equals(line.Substring(i, settings.LineSeparator.Length), settings.LineSeparator)) {
|
||||
list.Add(work.ToString());
|
||||
yield return list.ToArray();
|
||||
list.Clear();
|
||||
work.Clear();
|
||||
if (inStream.EndOfStream)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Read in next line
|
||||
if (i + settings.LineSeparator.Length >= line.Length)
|
||||
{
|
||||
line = inStream.ReadLine() + settings.LineSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = line.Substring(i + settings.LineSeparator.Length);
|
||||
}
|
||||
i = -1;
|
||||
|
||||
// While starting a field, do we detect a text qualifier?
|
||||
}
|
||||
else if ((c == settings.TextQualifier) && (work.Length == 0))
|
||||
{
|
||||
// Our next task is to find the end of this qualified-text field
|
||||
int p2 = -1;
|
||||
while (p2 < 0) {
|
||||
|
||||
// If we don't see an end in sight, read more from the stream
|
||||
p2 = line.IndexOf(settings.TextQualifier, i + 1);
|
||||
if (p2 < 0) {
|
||||
|
||||
// No text qualifiers yet? Let's read more from the stream and continue
|
||||
work.Append(line.Substring(i + 1));
|
||||
i = -1;
|
||||
var newLine = inStream.ReadLine();
|
||||
if (String.IsNullOrEmpty(newLine) && inStream.EndOfStream)
|
||||
{
|
||||
break;
|
||||
}
|
||||
line = newLine + settings.LineSeparator;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append the text between the qualifiers
|
||||
work.Append(line.Substring(i + 1, p2 - i - 1));
|
||||
i = p2;
|
||||
|
||||
// If the user put in a doubled-up qualifier, e.g. `""`, insert a single one and continue
|
||||
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
|
||||
{
|
||||
work.Append(settings.TextQualifier);
|
||||
i++;
|
||||
p2 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Does this start a new field?
|
||||
}
|
||||
else if (c == settings.FieldDelimiter)
|
||||
{
|
||||
// Is this a null token, and do we permit null tokens?
|
||||
AddToken(list, work, settings);
|
||||
|
||||
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
|
||||
// Checks if the second parameter of the if statement will pass through successfully
|
||||
// e.g. `"bob", "mary", "bill"`
|
||||
if (i + 2 <= line.Length - 1)
|
||||
{
|
||||
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
work.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single row of data from a Csv line into an array of objects, while permitting embedded newlines
|
||||
/// DEPRECATED - Please use ParseStream instead.
|
||||
/// </summary>
|
||||
/// <param name="inStream">The stream to read</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
|
||||
public static string[] ParseMultiLine(IcdStreamReader inStream, CsvReaderSettings settings)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string[] array = null;
|
||||
while (!inStream.EndOfStream)
|
||||
{
|
||||
// Read in a line
|
||||
sb.Append(inStream.ReadLine());
|
||||
|
||||
// Does it parse?
|
||||
string s = sb.ToString();
|
||||
if (TryParseLine(s, out array, settings))
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
// We didn't succeed on the first try - our text must have an embedded newline in it.
|
||||
// Let's assume that we were in the middle of parsing a field when we encountered a newline,
|
||||
// and continue parsing.
|
||||
sb.Append(settings.LineSeparator);
|
||||
}
|
||||
|
||||
// Fails to parse - return the best array we were able to get
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a line from a Csv file and return an array of fields, or null if
|
||||
/// </summary>
|
||||
/// <param name="line">One line of text from a Csv file</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
|
||||
public static string[] ParseLine(string line, CsvReaderSettings settings)
|
||||
{
|
||||
string[] row;
|
||||
TryParseLine(line, out row, settings);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to parse a line of Csv data. Can only return false if an unterminated text qualifier is encountered.
|
||||
/// </summary>
|
||||
/// <returns>False if there was an unterminated text qualifier in the <paramref name="line"/></returns>
|
||||
/// <param name="line">The line of text to parse</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <param name="row">The array of fields found in the line</param>
|
||||
public static bool TryParseLine(string line, out string[] row, CsvReaderSettings settings)
|
||||
{
|
||||
// Ensure settings are non-null
|
||||
if (settings == null) settings = CsvReaderSettings.CSV;
|
||||
|
||||
// Okay, let's begin parsing
|
||||
List<string> list = new List<string>();
|
||||
var work = new StringBuilder();
|
||||
for (int i = 0; i < line.Length; i++)
|
||||
{
|
||||
char c = line[i];
|
||||
|
||||
// If we are starting a new field, is this field text qualified?
|
||||
if ((c == settings.TextQualifier) && (work.Length == 0))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int p2 = line.IndexOf(settings.TextQualifier, i + 1);
|
||||
|
||||
// If no closing qualifier is found, this string is broken; return failure.
|
||||
if (p2 < 0)
|
||||
{
|
||||
work.Append(line.Substring(i + 1));
|
||||
list.Add(work.ToString());
|
||||
row = list.ToArray();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Append this qualified string
|
||||
work.Append(line.Substring(i + 1, p2 - i - 1));
|
||||
i = p2;
|
||||
|
||||
// If this is a double quote, keep going!
|
||||
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
|
||||
{
|
||||
work.Append(settings.TextQualifier);
|
||||
i++;
|
||||
|
||||
// otherwise, this is a single qualifier, we're done
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Does this start a new field?
|
||||
}
|
||||
else if (c == settings.FieldDelimiter)
|
||||
{
|
||||
// Is this a null token, and do we permit null tokens?
|
||||
AddToken(list, work, settings);
|
||||
|
||||
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
|
||||
// Checks if the second parameter of the if statement will pass through successfully
|
||||
// e.g. "bob", "mary", "bill"
|
||||
if (i + 2 <= line.Length - 1)
|
||||
{
|
||||
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
work.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// We always add the last work as an element. That means `alice,bob,charlie,` will be four items long.
|
||||
AddToken(list, work, settings);
|
||||
row = list.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a single token to the list
|
||||
/// </summary>
|
||||
/// <param name="list">List.</param>
|
||||
/// <param name="work">Work.</param>
|
||||
/// <param name="settings">Settings.</param>
|
||||
private static void AddToken(List<string> list, StringBuilder work, CsvReaderSettings settings)
|
||||
{
|
||||
var s = work.ToString();
|
||||
if (settings.AllowNull && String.Equals(s, settings.NullToken, StringComparison.Ordinal))
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(s);
|
||||
}
|
||||
work.Length = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
100
ICD.Common.Utils/Csv/CsvReader.cs
Normal file
100
ICD.Common.Utils/Csv/CsvReader.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
public sealed class CsvReader : IEnumerable<string[]>, IDisposable
|
||||
{
|
||||
private readonly CsvReaderSettings m_Settings;
|
||||
private readonly IcdStreamReader m_Instream;
|
||||
|
||||
#region Public Variables
|
||||
|
||||
/// <summary>
|
||||
/// If the first row in the file is a header row, this will be populated
|
||||
/// </summary>
|
||||
private string[] m_Headers;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Construct a new Csv reader off a streamed source
|
||||
/// </summary>
|
||||
/// <param name="source">The stream source</param>
|
||||
/// <param name="settings">The Csv settings to use for this reader (Default: Csv)</param>
|
||||
public CsvReader(IcdStreamReader source, [CanBeNull] CsvReaderSettings settings)
|
||||
{
|
||||
m_Instream = source;
|
||||
m_Settings = settings ?? CsvReaderSettings.CSV;
|
||||
|
||||
// Do we need to parse headers?
|
||||
if (m_Settings.HeaderRowIncluded)
|
||||
{
|
||||
m_Headers = NextLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Headers = m_Settings.AssumedHeaders != null ? m_Settings.AssumedHeaders.ToArray() : null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterate through a Csv File
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return Lines().GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
IEnumerator<string[]> IEnumerable<string[]>.GetEnumerator()
|
||||
{
|
||||
return Lines().GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
public IEnumerable<string[]> Lines()
|
||||
{
|
||||
return Csv.ParseStream(m_Instream, m_Settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the next line from the file.
|
||||
/// DEPRECATED -
|
||||
/// </summary>
|
||||
/// <returns>One line from the file.</returns>
|
||||
public string[] NextLine()
|
||||
{
|
||||
return Csv.ParseMultiLine(m_Instream, m_Settings);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Disposal
|
||||
/// <summary>
|
||||
/// Close our resources - specifically, the stream reader
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Instream.Dispose();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
105
ICD.Common.Utils/Csv/CsvReaderSettings.cs
Normal file
105
ICD.Common.Utils/Csv/CsvReaderSettings.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings to configure how a Csv file is parsed
|
||||
/// </summary>
|
||||
public sealed class CsvReaderSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor picks Csv as the default
|
||||
/// </summary>
|
||||
public CsvReaderSettings()
|
||||
{
|
||||
FieldDelimiter = ',';
|
||||
TextQualifier = '"';
|
||||
ForceQualifiers = false;
|
||||
LineSeparator = IcdEnvironment.NewLine;
|
||||
NullToken = null;
|
||||
AllowNull = false;
|
||||
IgnoreDimensionErrors = true;
|
||||
AssumedHeaders = null;
|
||||
HeaderRowIncluded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character used to delimit individual fields in the Csv.
|
||||
/// </summary>
|
||||
public char FieldDelimiter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character used to enclose fields that contain the delimiter character.
|
||||
/// </summary>
|
||||
public char TextQualifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The separator used to indicate the end of a line in the Csv file.
|
||||
/// </summary>
|
||||
public string LineSeparator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to enclose all fields in the text qualifier character.
|
||||
/// </summary>
|
||||
public bool ForceQualifiers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to allow nulls to be rendered.
|
||||
/// Csv files by default do not permit null fields. If this field is set to true, all non-null fields
|
||||
/// will be enclosed by the text qualifier
|
||||
/// </summary>
|
||||
public bool AllowNull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If AllowNull is set to true, this token will be used to represent NULL values.
|
||||
/// </summary>
|
||||
public string NullToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first line of the Csv file will include the names of each field.
|
||||
/// </summary>
|
||||
public bool HeaderRowIncluded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If HeaderRowIncluded is false, use these values for the headers
|
||||
/// </summary>
|
||||
public List<string> AssumedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to allow parsing for files where each row has a different number of fields
|
||||
/// </summary>
|
||||
public bool IgnoreDimensionErrors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to ignore header errors when deserializing
|
||||
/// </summary>
|
||||
public bool IgnoreHeaderErrors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Standard comma-separated value (Csv) file settings
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings CSV = new CsvReaderSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Standard comma-separated value (Csv) file settings that permit rendering of NULL values
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings CSV_PERMIT_NULL = new CsvReaderSettings
|
||||
{
|
||||
AllowNull = true,
|
||||
NullToken = "NULL"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Standard tab-separated value (TSV) file settings
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings TSV = new CsvReaderSettings
|
||||
{
|
||||
FieldDelimiter = '\t'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
#if STANDARD
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
@@ -126,7 +125,7 @@ namespace ICD.Common.Utils.Email
|
||||
|
||||
public static class MailErrorCodeUtils
|
||||
{
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code)
|
||||
{
|
||||
switch (code)
|
||||
@@ -185,8 +184,7 @@ namespace ICD.Common.Utils.Email
|
||||
throw new ArgumentOutOfRangeException(nameof(code), code, null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
#else
|
||||
public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code)
|
||||
{
|
||||
switch (code)
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace ICD.Common.Utils
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetFlagsExceptNone(type);
|
||||
return GetValues(type).Where(v => (int)v != 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -352,10 +352,11 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetFlags(Type type, object value)
|
||||
{
|
||||
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache.GetOrAddNew(type, () => new Dictionary<object, object[]>())
|
||||
.GetOrAddNew(value, () => GetValues(type)
|
||||
.Where(f => HasFlag(value, f))
|
||||
.ToArray()));
|
||||
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache
|
||||
.GetOrAddNew(type, () => new Dictionary<object, object[]>())
|
||||
.GetOrAddNew(value, () => GetValues(type)
|
||||
.Where(f => !HasMultipleFlags((int)f) && HasFlag(value, f))
|
||||
.ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -447,7 +448,20 @@ namespace ICD.Common.Utils
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type).Aggregate(0, (current, value) => current | (int)value);
|
||||
return GetValues(type).Aggregate(0, (current, value) => current | (int)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enum value of the given type with the inverse of the flags set
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T GetInverseFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
int output = GetFlagsAllValue(typeof(T)) & ~(int)(object)value;
|
||||
return (T)Enum.ToObject(typeof(T), output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
#endif
|
||||
.CodeBase;
|
||||
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
if (string.IsNullOrEmpty(path))
|
||||
path = extends.Location;
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
@@ -59,6 +61,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
return EnumUtils.IncludeFlags(extends, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum, with the other flags set or unset
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
public static T SetFlags<T>(this T extends, T other, bool set)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return set ?
|
||||
EnumUtils.IncludeFlags(extends, other) :
|
||||
EnumUtils.ExcludeFlags(extends, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum value as a ushort.
|
||||
/// </summary>
|
||||
@@ -83,5 +101,25 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
return EnumUtils.ToStringUndefined(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the next defined enum value for the enum type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static T CycleNext<T>(this T extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (EnumUtils.IsFlagsEnum(typeof(T)) && !EnumUtils.HasSingleFlag(extends))
|
||||
throw new InvalidOperationException(string.Format("Cannot cycle enum with multiple flags - {0}", extends));
|
||||
|
||||
IEnumerable<T> values = EnumUtils.GetValues<T>();
|
||||
IList<T> list = values as IList<T> ?? values.ToArray();
|
||||
|
||||
int index = list.BinarySearch(extends);
|
||||
|
||||
return index == list.Count - 1 ? list[0] : list[index + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
@@ -27,24 +32,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
#else
|
||||
JsonSerializer.CreateDefault();
|
||||
#endif
|
||||
return extends.ReadAsObject<T>(serializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the current token in the reader and deserializes to the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="serializer"></param>
|
||||
/// <returns></returns>
|
||||
public static T ReadAsObject<T>([NotNull] this JsonReader extends, [NotNull] JsonSerializer serializer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (serializer == null)
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
return serializer.Deserialize<T>(extends);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
|
||||
@@ -349,6 +349,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
#region Binary Search
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the item in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.BinarySearch(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the item in the list.
|
||||
/// </summary>
|
||||
|
||||
@@ -218,7 +218,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic);
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.FlattenHierarchy);
|
||||
if (info == null)
|
||||
{
|
||||
instance = null;
|
||||
@@ -238,7 +239,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic);
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.FlattenHierarchy);
|
||||
|
||||
if (output == null)
|
||||
instance = null;
|
||||
@@ -309,7 +311,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic);
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.FlattenHierarchy);
|
||||
if (method == null)
|
||||
return false;
|
||||
|
||||
|
||||
25
ICD.Common.Utils/Extensions/RegistryExtensions.cs
Normal file
25
ICD.Common.Utils/Extensions/RegistryExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
#if NETSTANDARD
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class RegistryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens subkeys for the given registry key.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<RegistryKey> OpenSubKeys([NotNull] this RegistryKey extends)
|
||||
{
|
||||
return extends.GetSubKeyNames()
|
||||
.Select(extends.OpenSubKey)
|
||||
.Where(k => k != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
@@ -142,6 +143,46 @@ namespace ICD.Common.Utils.Extensions
|
||||
Math.Min(chunkSize, extends.Length - (i * chunkSize))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Joins the strings in the enumerable.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string Join([NotNull] this IEnumerable<string> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Join(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Joins the strings in the enumerable.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimiter"></param>
|
||||
/// <returns></returns>
|
||||
public static string Join([NotNull] this IEnumerable<string> extends, string delimiter)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
bool isFirst = true;
|
||||
|
||||
foreach (string item in extends)
|
||||
{
|
||||
if (!isFirst)
|
||||
builder.Append(delimiter);
|
||||
|
||||
builder.Append(item);
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes whitespace from the string.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,34 +13,61 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
public static string ToReadableString(this TimeSpan extends)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (extends.Days == 1)
|
||||
builder.AppendFormat("{0} day, ", extends.Days);
|
||||
else if (extends.Days > 1)
|
||||
builder.AppendFormat("{0} days, ", extends.Days);
|
||||
|
||||
if (extends.Hours == 1)
|
||||
builder.AppendFormat("{0} hour, ", extends.Hours);
|
||||
else if (extends.Hours > 1)
|
||||
builder.AppendFormat("{0} hours, ", extends.Hours);
|
||||
|
||||
if (extends.Minutes == 1)
|
||||
builder.AppendFormat("{0} minute, ", extends.Minutes);
|
||||
else if (extends.Minutes > 1)
|
||||
builder.AppendFormat("{0} minutes, ", extends.Minutes);
|
||||
|
||||
if (extends.Seconds == 1)
|
||||
builder.AppendFormat("{0} second, ", extends.Seconds);
|
||||
else if (extends.Seconds > 1)
|
||||
builder.AppendFormat("{0} seconds, ", extends.Seconds);
|
||||
|
||||
if (extends.Milliseconds > 0)
|
||||
builder.AppendFormat("{0} ms", extends.Milliseconds);
|
||||
|
||||
return builder.ToString().TrimEnd(',', ' ');
|
||||
return extends.ToReadableString(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the TimeSpan to a string in the format "A day/s, B hour/s, C minute/s, D second/s, E ms"
|
||||
/// Omits any items that are 0.
|
||||
/// Optionally hides the miliseconds
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="hideMilliseconds"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToReadableString(this TimeSpan extends, bool hideMilliseconds)
|
||||
{
|
||||
int zeroComparison = extends.CompareTo(TimeSpan.Zero);
|
||||
|
||||
if (zeroComparison == 0)
|
||||
return "Zero Time";
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Handle negative time spans
|
||||
if (zeroComparison < 0)
|
||||
builder.Append("-");
|
||||
|
||||
// Get absolute value so negatives can be ignored
|
||||
TimeSpan timeSpan = extends.Duration();
|
||||
|
||||
if (timeSpan.Days == 1)
|
||||
builder.AppendFormat("{0} day, ", timeSpan.Days);
|
||||
else if (timeSpan.Days > 1)
|
||||
builder.AppendFormat("{0} days, ", timeSpan.Days);
|
||||
|
||||
if (timeSpan.Hours == 1)
|
||||
builder.AppendFormat("{0} hour, ", timeSpan.Hours);
|
||||
else if (timeSpan.Hours > 1)
|
||||
builder.AppendFormat("{0} hours, ", timeSpan.Hours);
|
||||
|
||||
if (timeSpan.Minutes == 1)
|
||||
builder.AppendFormat("{0} minute, ", timeSpan.Minutes);
|
||||
else if (timeSpan.Minutes > 1)
|
||||
builder.AppendFormat("{0} minutes, ", timeSpan.Minutes);
|
||||
|
||||
if (timeSpan.Seconds == 1)
|
||||
builder.AppendFormat("{0} second, ", timeSpan.Seconds);
|
||||
else if (timeSpan.Seconds > 1)
|
||||
builder.AppendFormat("{0} seconds, ", timeSpan.Seconds);
|
||||
else if (hideMilliseconds && (long)timeSpan.TotalSeconds == 0)
|
||||
builder.AppendFormat("0 seconds");
|
||||
|
||||
if (!hideMilliseconds && timeSpan.Milliseconds > 0)
|
||||
builder.AppendFormat("{0} ms", timeSpan.Milliseconds);
|
||||
|
||||
return builder.ToString().TrimEnd(',', ' ');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of hours to the time, wrapping every 24 hours without modifying the day.
|
||||
/// </summary>
|
||||
|
||||
@@ -62,5 +62,23 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new Uri representing the Uri for the parent path.
|
||||
/// E.g.
|
||||
/// www.test.com/A/B/C
|
||||
/// Becomes
|
||||
/// www.test.com/A/B/
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static Uri GetParentUri([NotNull] this Uri extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string parentUriString = extends.AbsoluteUri.Substring(0, extends.AbsoluteUri.Length - extends.Segments.Last().Length);
|
||||
return new Uri(parentUriString, UriKind.Absolute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
|
||||
@@ -96,11 +96,8 @@ namespace ICD.Common.Utils.Globalization
|
||||
{
|
||||
private const string SQL_LOCAL_DATABASE_FILE = "CultureInfo.sqlite";
|
||||
private const string SQL_CONNECTION_STRING_FORMAT =
|
||||
#if SIMPLSHARP
|
||||
"Data Source={0};Version=3;ReadOnly=True";
|
||||
#else
|
||||
"Data Source={0}";
|
||||
#endif
|
||||
|
||||
private const string SQL_CMD_SELECT_BY_NAME = "select * from cultureinfo where name = @name collate nocase";
|
||||
private const string SQL_CMD_SELECT_BY_LCID = "select * from cultureinfo where lcid = @lcid";
|
||||
private const string SQL_CMD_SELECT_BY_ID = "select * from cultureinfo where id = @id";
|
||||
@@ -466,8 +463,9 @@ namespace ICD.Common.Utils.Globalization
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
IcdErrorLog.Exception(e, "Error populating IcdCultureInfo cache - {0}", e.Message);
|
||||
s_IsDatabasePresent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
<?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>
|
||||
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
|
||||
<AssemblyName>ICD.Common.Utils</AssemblyName>
|
||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||
<RootNamespace>ICD.Common.Utils</RootNamespace>
|
||||
<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>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE;STANDARD</DefineConstants>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="bin\**" />
|
||||
<Compile Remove="SIMPLSharpLogs\**" />
|
||||
<EmbeddedResource Remove="bin\**" />
|
||||
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
|
||||
<None Remove="bin\**" />
|
||||
<None Remove="SIMPLSharpLogs\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="ICD.Common.projectinfo" />
|
||||
<None Remove="ICD.Common_SimplSharp.suo" />
|
||||
@@ -38,12 +30,17 @@
|
||||
<None Remove="Properties\ControlSystem.cfg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Pastel" Version="1.3.1" />
|
||||
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Crestron.SimplSharp.SDK.Library" Version="2.18.96" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Data.SQLite" Version="5.0.4" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Pastel" Version="2.1.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Net.NetworkInformation" Version="4.3.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Security.Principal.Windows" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="CultureInfo.sqlite">
|
||||
@@ -53,5 +50,4 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -25,7 +25,7 @@
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<OutputPath>bin\Debug\net3.5\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;SIMPLSHARP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
@@ -38,7 +38,7 @@
|
||||
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<OutputPath>bin\Release\net3.5\</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
@@ -79,17 +79,22 @@
|
||||
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
|
||||
<Compile Include="Attributes\IIcdAttribute.cs" />
|
||||
<Compile Include="Attributes\RangeAttribute.cs" />
|
||||
<Compile Include="BigEndianBitConverter.cs" />
|
||||
<Compile Include="Collections\BiDictionary.cs" />
|
||||
<Compile Include="Collections\IcdOrderedDictionary.cs" />
|
||||
<Compile Include="Collections\IcdSortedDictionary.cs" />
|
||||
<Compile Include="Collections\INotifyCollectionChanged.cs" />
|
||||
<Compile Include="Collections\PriorityQueue.cs" />
|
||||
<Compile Include="Collections\RateLimitedEventQueue.cs" />
|
||||
<Compile Include="Collections\ReverseLookupDictionary.cs" />
|
||||
<Compile Include="Collections\WeakKeyDictionary.cs" />
|
||||
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
|
||||
<Compile Include="Comparers\PredicateComparer.cs" />
|
||||
<Compile Include="Comparers\SequenceComparer.cs" />
|
||||
<Compile Include="Comparers\UndefinedVersionComparer.cs" />
|
||||
<Compile Include="Comparers\UndefinedVersionEqualityComparer.cs" />
|
||||
<Compile Include="Csv\Csv.cs" />
|
||||
<Compile Include="Csv\CsvReader.cs" />
|
||||
<Compile Include="Csv\CsvReaderSettings.cs" />
|
||||
<Compile Include="eConsoleColor.cs" />
|
||||
<Compile Include="DateTimeUtils.cs" />
|
||||
<Compile Include="eDaysOfWeek.cs" />
|
||||
@@ -115,10 +120,14 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Compile Include="Extensions\CollectionExtensions.cs" />
|
||||
<Compile Include="Extensions\RegistryExtensions.cs" />
|
||||
<Compile Include="Extensions\VersionExtensions.cs" />
|
||||
<Compile Include="ThreadedWorkerQueue.cs" />
|
||||
<Compile Include="Threading\AbstractIcdResetEvent.cs" />
|
||||
<Compile Include="Threading\IcdAutoResetEvent.cs" />
|
||||
<Compile Include="IcdManualResetEvent.cs" />
|
||||
<Compile Include="Threading\KeyedLock.cs" />
|
||||
<Compile Include="Threading\ThreadedWorkerQueue.cs" />
|
||||
<Compile Include="TimeZoneInfo\IcdTimeZoneInfo.cs" />
|
||||
<Compile Include="ObfuscationSettings.cs" />
|
||||
<Compile Include="Extensions\BoolExtensions.cs" />
|
||||
<Compile Include="Extensions\ByteExtensions.cs" />
|
||||
<Compile Include="Extensions\CultureInfoExtensions.cs" />
|
||||
@@ -209,10 +218,10 @@
|
||||
<Compile Include="PrettyPrint.cs" />
|
||||
<Compile Include="ProgramUtils.cs" />
|
||||
<Compile Include="ReflectionUtils.cs" />
|
||||
<Compile Include="SafeCriticalSection.cs" />
|
||||
<Compile Include="SafeCriticalSection.SimplSharp.cs" />
|
||||
<Compile Include="SafeCriticalSection.Standard.cs" />
|
||||
<Compile Include="SafeMutex.cs" />
|
||||
<Compile Include="Threading\SafeCriticalSection.cs" />
|
||||
<Compile Include="Threading\SafeCriticalSection.SimplSharp.cs" />
|
||||
<Compile Include="Threading\SafeCriticalSection.Standard.cs" />
|
||||
<Compile Include="Threading\SafeMutex.cs" />
|
||||
<Compile Include="SPlusUtils.cs" />
|
||||
<Compile Include="Sqlite\eDbType.cs" />
|
||||
<Compile Include="Sqlite\IcdDbDataReader.cs" />
|
||||
@@ -225,7 +234,7 @@
|
||||
<Compile Include="Sqlite\IIcdDataRecord.cs" />
|
||||
<Compile Include="StringUtils.cs" />
|
||||
<Compile Include="TableBuilder.cs" />
|
||||
<Compile Include="ThreadingUtils.cs" />
|
||||
<Compile Include="Threading\ThreadingUtils.cs" />
|
||||
<Compile Include="Timers\IcdStopwatch.cs" />
|
||||
<Compile Include="Timers\IcdTimer.cs" />
|
||||
<Compile Include="Timers\Repeater.cs" />
|
||||
@@ -233,6 +242,8 @@
|
||||
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoAdjustmentRule.cs" />
|
||||
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoTransitionTime.cs" />
|
||||
<Compile Include="TryUtils.cs" />
|
||||
<Compile Include="Types\AbstractNotifyFlagsChanged.cs" />
|
||||
<Compile Include="Types\GenericNotifyFlagsChanged.cs" />
|
||||
<Compile Include="UriQueryBuilder.cs" />
|
||||
<Compile Include="UriUtils.cs" />
|
||||
<Compile Include="VersionSpan.cs" />
|
||||
@@ -254,8 +265,4 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>if /I "$(ConfigurationName)" == "Release" Eazfuscator.NET.exe "$(TargetPath)" --msbuild-project-path "$(ProjectPath)" --msbuild-project-configuration "$(ConfigurationName)" --msbuild-project-platform "$(PlatformName)" --msbuild-solution-path "$(SolutionPath)" -n --newline-flush -v 5.2 --configuration-file="$(ProjectDir)ObfuscationSettings.cs"
|
||||
rem S# Pro preparation will execute after these operations</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -132,7 +132,7 @@ namespace ICD.Common.Utils.IO.Compression
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
private uint m_CrcValue = 0xffffffff;
|
||||
@@ -299,7 +299,6 @@ namespace ICD.Common.Utils.IO.Compression
|
||||
|
||||
private Entry GetEntry(string fileName)
|
||||
{
|
||||
fileName = fileName.Replace("\\", "/").Trim().TrimStart('/');
|
||||
var entry = Entries.FirstOrDefault(e => e.Name == fileName);
|
||||
|
||||
if (entry == null)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
@@ -13,10 +13,11 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public static string GetApplicationDirectory()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
return Directory.GetApplicationDirectory();
|
||||
#else
|
||||
return ApplicationEnvironment.ApplicationBasePath;
|
||||
string pathToDll = Assembly.GetExecutingAssembly().GetPath();
|
||||
return pathToDll == null ? null : IcdPath.GetDirectoryName(pathToDll);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -26,10 +27,10 @@ namespace ICD.Common.Utils.IO
|
||||
/// <returns></returns>
|
||||
public static string GetApplicationRootDirectory()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
return Directory.GetApplicationRootDirectory();
|
||||
#else
|
||||
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetPath());
|
||||
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,12 @@ namespace ICD.Common.Utils.IO
|
||||
return File.GetCreationTime(path);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static long GetLength(string path)
|
||||
{
|
||||
return new FileInfo(path).Length;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IcdFileStream Create(string path)
|
||||
{
|
||||
@@ -120,5 +126,18 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
File.Move(sourceFileName, destFileName);
|
||||
}
|
||||
|
||||
#if !SIMPLSHARP
|
||||
public static void SetAttributes(string path, FileAttributes attributes, bool recursive)
|
||||
{
|
||||
File.SetAttributes(path, attributes);
|
||||
|
||||
if (!recursive || !Directory.Exists(path))
|
||||
return;
|
||||
|
||||
foreach (string innerPath in Directory.GetFileSystemEntries(path))
|
||||
SetAttributes(innerPath, attributes, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#elif STANDARD
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
private readonly StreamReader m_StreamReader;
|
||||
|
||||
public StreamReader WrappedStreamReader { get { return m_StreamReader; } }
|
||||
|
||||
public bool EndOfStream { get { return m_StreamReader.EndOfStream; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
@@ -23,6 +27,21 @@ namespace ICD.Common.Utils.IO
|
||||
m_StreamReader = new StreamReader(memoryStream.WrappedMemoryStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public IcdStreamReader(string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException("path");
|
||||
|
||||
if (!IcdFile.Exists(path))
|
||||
throw new FileNotFoundException("Error creating stream reader, file not found");
|
||||
|
||||
m_StreamReader = new StreamReader(path);
|
||||
}
|
||||
|
||||
~IcdStreamReader()
|
||||
{
|
||||
Dispose();
|
||||
@@ -37,5 +56,10 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
m_StreamReader.Dispose();
|
||||
}
|
||||
|
||||
public string ReadLine()
|
||||
{
|
||||
return m_StreamReader.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#elif STANDARD
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Diagnostics;
|
||||
@@ -25,9 +25,47 @@ namespace ICD.Common.Utils
|
||||
public static event EventHandler<StringEventArgs> OnConsolePrint;
|
||||
|
||||
private static readonly SafeCriticalSection s_Section;
|
||||
|
||||
private static readonly Regex s_NewLineRegex;
|
||||
|
||||
private static bool? s_IsConsoleApp;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the application is being run from an interactive console,
|
||||
/// false if the application is being run as a headless service.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public static bool IsConsoleApp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_IsConsoleApp == null)
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
s_IsConsoleApp = false;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Hack
|
||||
int unused = Console.WindowHeight;
|
||||
s_IsConsoleApp = true;
|
||||
|
||||
if (Console.Title.Length > 0)
|
||||
s_IsConsoleApp = true;
|
||||
|
||||
if (!Environment.UserInteractive)
|
||||
s_IsConsoleApp = false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
s_IsConsoleApp = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return s_IsConsoleApp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor.
|
||||
/// </summary>
|
||||
@@ -56,13 +94,17 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void ConsoleCommandResponse(string message, params object[] args)
|
||||
{
|
||||
// No console in Server
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return;
|
||||
|
||||
if (args != null && args.Any())
|
||||
message = string.Format(message, args);
|
||||
|
||||
message = FixLineEndings(message);
|
||||
|
||||
#if SIMPLSHARP
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
|
||||
#if !NETSTANDARD
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -72,11 +114,10 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
PrintLine(message);
|
||||
}
|
||||
return;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
PrintLine(message);
|
||||
PrintLine(message);
|
||||
}
|
||||
|
||||
public static void PrintLine(string message)
|
||||
@@ -87,11 +128,14 @@ namespace ICD.Common.Utils
|
||||
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
#if !NETSTANDARD
|
||||
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
CrestronConsole.PrintLine(fixedMessage);
|
||||
#else
|
||||
Console.WriteLine(fixedMessage);
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(fixedMessage));
|
||||
|
||||
if (IsConsoleApp)
|
||||
Console.WriteLine(fixedMessage);
|
||||
#endif
|
||||
}
|
||||
finally
|
||||
@@ -128,11 +172,14 @@ namespace ICD.Common.Utils
|
||||
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
#if !NETSTANDARD
|
||||
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
CrestronConsole.Print(fixedMessage);
|
||||
#else
|
||||
Console.Write(message);
|
||||
Trace.Write(AnsiUtils.StripAnsi(fixedMessage));
|
||||
|
||||
if (IsConsoleApp)
|
||||
Console.Write(fixedMessage);
|
||||
#endif
|
||||
}
|
||||
finally
|
||||
@@ -163,9 +210,9 @@ namespace ICD.Common.Utils
|
||||
|
||||
public static bool SendControlSystemCommand(string command, ref string result)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
// No console on VC4
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
#if !NETSTANDARD
|
||||
// No console on Crestron Server
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return false;
|
||||
|
||||
return CrestronConsole.SendControlSystemCommand(command, ref result);
|
||||
@@ -176,9 +223,13 @@ namespace ICD.Common.Utils
|
||||
|
||||
public static bool AddNewConsoleCommand(Action<string> callback, string command, string help, eAccessLevel accessLevel)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
// Avoid crashing Simpl applications
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
|
||||
return false;
|
||||
|
||||
// No console in Crestron Server
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return false;
|
||||
|
||||
if (CrestronConsole.ConsoleRegistered)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
@@ -194,14 +194,16 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
// Cache the runtime environment
|
||||
s_Framework = eFramework.Crestron;
|
||||
|
||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL)
|
||||
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Simpl;
|
||||
else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
|
||||
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Appliance;
|
||||
else
|
||||
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Server;
|
||||
|
||||
s_CrestronRuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL
|
||||
? eCrestronRuntimeEnvironment.SimplPlus
|
||||
: eCrestronRuntimeEnvironment.SimplSharpPro;
|
||||
|
||||
s_CrestronDevicePlatform = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
|
||||
? eCrestronDevicePlatform.Appliance
|
||||
: eCrestronDevicePlatform.Server;
|
||||
|
||||
// todo: Make this check more robust
|
||||
s_CrestronSeries = Type.GetType("Mono.Runtime") != null ? eCrestronSeries.FourSeries : eCrestronSeries.ThreeSeries;
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler;
|
||||
@@ -210,6 +212,15 @@ namespace ICD.Common.Utils
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the local time zone.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetLocalTimeZoneName()
|
||||
{
|
||||
return CrestronEnvironment.GetTimeZone().Name;
|
||||
}
|
||||
|
||||
public static DateTime GetLocalTime()
|
||||
{
|
||||
return CrestronEnvironment.GetLocalTime();
|
||||
|
||||
@@ -1,17 +1,36 @@
|
||||
#if STANDARD
|
||||
#if NETSTANDARD
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Timers;
|
||||
using AddressFamily = System.Net.Sockets.AddressFamily;
|
||||
using Dns = System.Net.Dns;
|
||||
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static partial class IcdEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// We get several device added/removed events for a single device
|
||||
/// To reduce noise, we use timers in an attempt to compress them to a single event
|
||||
/// </summary>
|
||||
private const long DEVICE_ADDED_REMOVED_TIME = 500;
|
||||
|
||||
private static readonly SafeTimer s_DeviceAddedTimer = SafeTimer.Stopped(DeviceAddedTimerCallback);
|
||||
|
||||
private static readonly SafeTimer s_DeviceRemovedTimer = SafeTimer.Stopped(DeviceRemovedTimerCallback);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a system device (eg USB) is added
|
||||
/// May be raised once for multiple device additions
|
||||
/// </summary>
|
||||
public static event EventHandler<BoolEventArgs> OnSystemDeviceAddedRemoved;
|
||||
|
||||
public static string NewLine { get { return Environment.NewLine; } }
|
||||
|
||||
/// <summary>
|
||||
@@ -91,6 +110,18 @@ namespace ICD.Common.Utils
|
||||
s_Framework = eFramework.Standard;
|
||||
s_CrestronSeries = eCrestronSeries.Na;
|
||||
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Na;
|
||||
s_CrestronDevicePlatform = eCrestronDevicePlatform.Na;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the local time zone.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetLocalTimeZoneName()
|
||||
{
|
||||
return System.TimeZoneInfo.Local.IsDaylightSavingTime(GetLocalTime())
|
||||
? System.TimeZoneInfo.Local.DaylightName
|
||||
: System.TimeZoneInfo.Local.StandardName;
|
||||
}
|
||||
|
||||
public static DateTime GetLocalTime()
|
||||
@@ -119,6 +150,63 @@ namespace ICD.Common.Utils
|
||||
const string replace = "$1:$2:$3:$4:$5:$6";
|
||||
return Regex.Replace(address, regex, replace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the application to raise the program status.
|
||||
/// </summary>
|
||||
/// <param name="status"></param>
|
||||
public static void SetProgramStatus(eProgramStatusEventType status)
|
||||
{
|
||||
ProgramStatusCallback handler = OnProgramStatusEvent;
|
||||
if (handler != null)
|
||||
handler(status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the application to raise session change events.
|
||||
/// </summary>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <param name="reasonCode"></param>
|
||||
public static void HandleSessionChange(int sessionId, eSessionChangeEventType reasonCode)
|
||||
{
|
||||
SessionChangeEventCallback handler = OnSessionChangedEvent;
|
||||
if (handler != null)
|
||||
handler(sessionId, reasonCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to raise the device added/removed event for an added device
|
||||
/// Uses a timer to attempt to compress multiple events into a single event
|
||||
/// </summary>
|
||||
public static void RaiseSystemDeviceAddedEvent()
|
||||
{
|
||||
s_DeviceAddedTimer.Reset(DEVICE_ADDED_REMOVED_TIME);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to raise the device added/removed event for a removed device
|
||||
/// Uses a timer to attempt to compress multiple events into a single event
|
||||
/// </summary>
|
||||
public static void RaiseSystemDeviceRemovedEvent()
|
||||
{
|
||||
s_DeviceRemovedTimer.Reset(DEVICE_ADDED_REMOVED_TIME);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually fire the added event after the timer expires
|
||||
/// </summary>
|
||||
private static void DeviceAddedTimerCallback()
|
||||
{
|
||||
OnSystemDeviceAddedRemoved.Raise(null, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually fire the removed event after the timer expires
|
||||
/// </summary>
|
||||
private static void DeviceRemovedTimerCallback()
|
||||
{
|
||||
OnSystemDeviceAddedRemoved.Raise(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace ICD.Common.Utils
|
||||
public enum eFramework
|
||||
{
|
||||
Crestron,
|
||||
Framework,
|
||||
Standard
|
||||
}
|
||||
|
||||
@@ -31,9 +32,15 @@ namespace ICD.Common.Utils
|
||||
public enum eCrestronRuntimeEnvironment
|
||||
{
|
||||
Na, //Non-Crestron
|
||||
Simpl, // Running in Simpl, Non-Pro
|
||||
Appliance, // S#Pro running on a Crestron hardware appliance
|
||||
Server // S#Pro running on a server (VC-4)
|
||||
SimplPlus, // Running in Simpl+, Non-Pro
|
||||
SimplSharpPro // Running in Simpl#Pro
|
||||
}
|
||||
|
||||
public enum eCrestronDevicePlatform
|
||||
{
|
||||
Na, // Non-Crestron
|
||||
Appliance, // Running on Crestron hardware appliance
|
||||
Server // Running on VC-4 Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,14 +75,44 @@ namespace ICD.Common.Utils
|
||||
EthernetLan2Adapter = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for session change events.
|
||||
/// </summary>
|
||||
public enum eSessionChangeEventType
|
||||
{
|
||||
None = 0,
|
||||
ConsoleConnect = 1,
|
||||
ConsoleDisconnect = 2,
|
||||
RemoteConnect = 3,
|
||||
RemoteDisconnect = 4,
|
||||
SessionLogon = 5,
|
||||
SessionLogoff = 6,
|
||||
SessionLock = 7,
|
||||
SessionUnlock = 8,
|
||||
SessionRemoteControl = 9
|
||||
}
|
||||
|
||||
public delegate void ProgramStatusCallback(eProgramStatusEventType type);
|
||||
|
||||
public delegate void EthernetEventCallback(eEthernetAdapterType adapter, eEthernetEventType type);
|
||||
|
||||
public delegate void SessionChangeEventCallback(int sessionId, eSessionChangeEventType type);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the program status changes.
|
||||
/// </summary>
|
||||
public static event ProgramStatusCallback OnProgramStatusEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a network adapter connects/disconnects.
|
||||
/// </summary>
|
||||
public static event EthernetEventCallback OnEthernetEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a session changes, such as user logging in/out.
|
||||
/// </summary>
|
||||
public static event SessionChangeEventCallback OnSessionChangedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the program has completed initialization.
|
||||
/// </summary>
|
||||
@@ -86,19 +123,27 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
public static event EventHandler OnSystemDateTimeChanged;
|
||||
|
||||
private static eFramework s_Framework;
|
||||
|
||||
private static eCrestronSeries s_CrestronSeries;
|
||||
|
||||
private static eCrestronRuntimeEnvironment s_CrestronRuntimeEnvironment;
|
||||
private static readonly eFramework s_Framework;
|
||||
private static readonly eCrestronSeries s_CrestronSeries;
|
||||
private static readonly eCrestronRuntimeEnvironment s_CrestronRuntimeEnvironment;
|
||||
private static readonly eCrestronDevicePlatform s_CrestronDevicePlatform;
|
||||
|
||||
private static readonly SafeCriticalSection s_ProgramInitializationSection = new SafeCriticalSection();
|
||||
private static bool s_ProgramInitializationComplete;
|
||||
|
||||
public static eFramework Framework {get { return s_Framework; }}
|
||||
public static eCrestronSeries CrestronSeries {get { return s_CrestronSeries; }}
|
||||
|
||||
/// <summary>
|
||||
/// Crestron environment being run in, SimplPlus or SimplSharpPro
|
||||
/// </summary>
|
||||
public static eCrestronRuntimeEnvironment CrestronRuntimeEnvironment {get { return s_CrestronRuntimeEnvironment; }}
|
||||
|
||||
/// <summary>
|
||||
/// Crestron platform being run on, Appliance (crestron hardware) or Server (VC-4)
|
||||
/// </summary>
|
||||
public static eCrestronDevicePlatform CrestronDevicePlatform { get { return s_CrestronDevicePlatform; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the program has been flagged as completely initialized.
|
||||
/// </summary>
|
||||
@@ -128,12 +173,12 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets UTC time
|
||||
/// Uses GetLocalTime so Crestron Env will have ms percision
|
||||
/// Gets UTC time.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DateTime GetUtcTime()
|
||||
{
|
||||
// Use GetLocalTime so Crestron Env will have ms precision
|
||||
return GetLocalTime().ToUniversalTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
@@ -30,7 +32,7 @@ namespace ICD.Common.Utils
|
||||
private static readonly Dictionary<string, Action<string, Exception>> s_LogMethods =
|
||||
new Dictionary<string, Action<string, Exception>>
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
{ERROR, (m, e) => ErrorLog.Error(m)},
|
||||
{WARN, (m, e) => ErrorLog.Warn(m)},
|
||||
{NOTICE, (m, e) => ErrorLog.Notice(m)},
|
||||
@@ -38,12 +40,49 @@ namespace ICD.Common.Utils
|
||||
{EXCEPTION, ErrorLog.Exception},
|
||||
{INFO, (m, e) => ErrorLog.Info(m)}
|
||||
#else
|
||||
{ERROR, (m, e) => Console.Error.WriteLine(m)},
|
||||
{WARN, (m, e) => Console.Error.WriteLine(m)},
|
||||
{NOTICE, (m, e) => Console.Error.WriteLine(m)},
|
||||
{OK, (m, e) => Console.Error.WriteLine(m)},
|
||||
{EXCEPTION, (m, e) => Console.Error.WriteLine(m)},
|
||||
{INFO, (m, e) => Console.Error.WriteLine(m)}
|
||||
{
|
||||
ERROR, (m, e) =>
|
||||
{
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
},
|
||||
{
|
||||
WARN, (m, e) =>
|
||||
{
|
||||
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
},
|
||||
{
|
||||
NOTICE, (m, e) =>
|
||||
{
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
},
|
||||
{
|
||||
OK, (m, e) =>
|
||||
{
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
},
|
||||
{
|
||||
EXCEPTION, (m, e) =>
|
||||
{
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
},
|
||||
{
|
||||
INFO, (m, e) =>
|
||||
{
|
||||
Trace.WriteLine(AnsiUtils.StripAnsi(m));
|
||||
Console.Error.WriteLine(m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
40
ICD.Common.Utils/IcdManualResetEvent.cs
Normal file
40
ICD.Common.Utils/IcdManualResetEvent.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Notifies one or more waiting threads that an event has occurred.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class IcdManualResetEvent : AbstractIcdResetEvent
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdManualResetEvent class with the initial state to nonsignaled.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IcdManualResetEvent() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdManualResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
|
||||
/// </summary>
|
||||
/// <param name="initialState">true to set the initial state signaled; false to set the initial state to nonsignaled.</param>
|
||||
[PublicAPI]
|
||||
public IcdManualResetEvent(bool initialState) :
|
||||
|
||||
#if SIMPLSHARP
|
||||
base(new CEvent(false, initialState))
|
||||
#else
|
||||
base(new ManualResetEvent(initialState))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("uri");
|
||||
|
||||
if (!uri.IsAbsoluteUri)
|
||||
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
|
||||
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + "localhost" + uri);
|
||||
|
||||
Fragment = uri.Fragment;
|
||||
Host = uri.Host;
|
||||
@@ -169,10 +169,11 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="parts"></param>
|
||||
/// <returns></returns>
|
||||
public void AppendPath(params string[] parts)
|
||||
public IcdUriBuilder AppendPath(params string[] parts)
|
||||
{
|
||||
parts = parts.Prepend(Path).ToArray(parts.Length + 1);
|
||||
Path = Combine(parts);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Flurl
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
@@ -128,23 +133,28 @@ namespace ICD.Common.Utils.Json
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return default(T);
|
||||
return existingValue;
|
||||
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, reader.TokenType));
|
||||
|
||||
return ReadObject(reader, serializer);
|
||||
return ReadObject(reader, existingValue, serializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle deserialization of the current StartObject token.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="existingValue"></param>
|
||||
/// <param name="serializer"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual T ReadObject(JsonReader reader, JsonSerializer serializer)
|
||||
protected virtual T ReadObject(JsonReader reader, T existingValue, JsonSerializer serializer)
|
||||
{
|
||||
T output = Instantiate();
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable ConvertConditionalTernaryToNullCoalescing
|
||||
T output = existingValue == null ? Instantiate() : existingValue;
|
||||
// ReSharper restore ConvertConditionalTernaryToNullCoalescing
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
|
||||
reader.ReadObject(serializer, (p, r, s) => ReadProperty(p, r, output, s));
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
#endif
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ICD.Common.Utils.Json
|
||||
{
|
||||
@@ -54,7 +60,12 @@ namespace ICD.Common.Utils.Json
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.InnerException;
|
||||
#if SIMPLSHARP
|
||||
throw e.InnerException ?? e;
|
||||
#else
|
||||
ExceptionDispatchInfo.Capture(e.InnerException ?? e).Throw();
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: Obfuscation(Feature = "platform api: System.Threading.Thread, System.Reflection.*, System.IO.Stream, System.Windows.Forms.*", Exclude = true)]
|
||||
[assembly: Obfuscation(Feature = "rename symbol names with printable characters", Exclude = false)]
|
||||
[assembly: Obfuscation(Feature = "code control flow obfuscation", Exclude = false)]
|
||||
[assembly: Obfuscation(Feature = "Apply to type * when class: renaming", Exclude = true, ApplyToMembers = false)]
|
||||
@@ -26,7 +26,7 @@ namespace ICD.Common.Utils
|
||||
public static string RootPath {
|
||||
get
|
||||
{
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return IcdDirectory.GetApplicationRootDirectory();
|
||||
|
||||
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
|
||||
@@ -62,14 +62,15 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
switch (IcdEnvironment.CrestronSeries)
|
||||
{
|
||||
case IcdEnvironment.eCrestronSeries.FourSeries:
|
||||
return Join(RootPath, "user");
|
||||
default:
|
||||
return Join(RootPath, "User");
|
||||
}
|
||||
#if !NETSTANDARD
|
||||
|
||||
// Server uses "User" and rest of the e-seres uses "user" :facepalm:
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return Join(RootPath, "User");
|
||||
|
||||
// Rest of processors
|
||||
// 3-series traditionally used "User", but isn't case sensitive
|
||||
return Join(RootPath, "user");
|
||||
#elif LINUX
|
||||
return Join(RootPath, "opt", "ICD.Connect");
|
||||
#else
|
||||
@@ -94,7 +95,7 @@ namespace ICD.Common.Utils
|
||||
get
|
||||
{
|
||||
// Crestron Server doesn't have meaningful program number
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return "ProgramConfig";
|
||||
|
||||
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
|
||||
@@ -117,7 +118,7 @@ namespace ICD.Common.Utils
|
||||
get
|
||||
{
|
||||
// Crestron Server doesn't have meaningful program number
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return "ProgramData";
|
||||
|
||||
return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber);
|
||||
@@ -157,7 +158,7 @@ namespace ICD.Common.Utils
|
||||
string directoryName;
|
||||
|
||||
// Crestron Server doesn't have meaningful program number
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
directoryName = "ProgramLogs";
|
||||
else
|
||||
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
|
||||
@@ -175,24 +176,25 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IcdEnvironment.Framework == IcdEnvironment.eFramework.Crestron)
|
||||
if (IcdEnvironment.Framework != IcdEnvironment.eFramework.Standard)
|
||||
{
|
||||
// 3-series
|
||||
if (IcdEnvironment.CrestronSeries == IcdEnvironment.eCrestronSeries.ThreeSeries)
|
||||
return Join(RootPath, "HTML");
|
||||
|
||||
// 4-series non-server (because Crestron)
|
||||
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
|
||||
return Join(RootPath, "html");
|
||||
|
||||
// 4-series server (because Crestron)
|
||||
return Join(RootPath, "Html");
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return Join(RootPath, "Html");
|
||||
|
||||
|
||||
// 4-series non-server (because Crestron)
|
||||
return Join(RootPath, "html");
|
||||
}
|
||||
|
||||
#if LINUX
|
||||
return Join(RootPath, "var", "www", "html");
|
||||
return Join(RootPath, "var", "www", "html");
|
||||
#else
|
||||
return "C:\\INetPub";
|
||||
return "C:\\INetPub";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using System.Globalization;
|
||||
using Crestron.SimplSharp;
|
||||
using ICD.Common.Utils.Services;
|
||||
@@ -213,6 +213,10 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void RestartProgram()
|
||||
{
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger != null)
|
||||
logger.AddEntry(eSeverity.Informational, "Intentional Restart of Program");
|
||||
|
||||
string consoleResult = string.Empty;
|
||||
string command = string.Format("progreset -p:{0:D2}", ProgramUtils.ProgramNumber);
|
||||
IcdConsole.SendControlSystemCommand(command, ref consoleResult);
|
||||
@@ -224,6 +228,10 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void Reboot()
|
||||
{
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger != null)
|
||||
logger.AddEntry(eSeverity.Informational, "Intentional Reboot of Processor");
|
||||
|
||||
string consoleResult = string.Empty;
|
||||
IcdConsole.SendControlSystemCommand("reboot", ref consoleResult);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
using ICD.Common.Utils.Services;
|
||||
using ICD.Common.Utils.Services.Logging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
@@ -14,7 +21,15 @@ namespace ICD.Common.Utils
|
||||
/// Gets the model name of the processor.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static string ModelName { get { return Environment.MachineName; } }
|
||||
public static string ModelName
|
||||
{
|
||||
get
|
||||
{
|
||||
string productName = RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName");
|
||||
string csdVersion = RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CSDVersion");
|
||||
return string.Format("{0} {1}", productName, csdVersion).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the processor firmware version.
|
||||
@@ -24,8 +39,7 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
return RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuild");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +51,8 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO
|
||||
return DateTime.MinValue;
|
||||
long time = RegistryLocalMachineGetLong(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallTime");
|
||||
return time == 0 ? DateTime.MinValue : DateTime.FromFileTime(132448642489109028).ToUniversalTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +64,8 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
ManagementObject os = new ManagementObject("Win32_OperatingSystem=@");
|
||||
return (string)os["SerialNumber"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +76,22 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO
|
||||
return 0.0f;
|
||||
ManagementObjectSearcher wmiObject = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
|
||||
|
||||
var memoryValues =
|
||||
wmiObject.Get()
|
||||
.Cast<ManagementObject>()
|
||||
.Select(mo => new
|
||||
{
|
||||
FreePhysicalMemory = double.Parse(mo["FreePhysicalMemory"].ToString()),
|
||||
TotalVisibleMemorySize = double.Parse(mo["TotalVisibleMemorySize"].ToString())
|
||||
})
|
||||
.FirstOrDefault();
|
||||
|
||||
return memoryValues == null
|
||||
? 0
|
||||
: (float)((memoryValues.TotalVisibleMemorySize - memoryValues.FreePhysicalMemory) /
|
||||
memoryValues.TotalVisibleMemorySize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +153,16 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void RestartProgram()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
string filename = Process.GetCurrentProcess().MainModule?.FileName;
|
||||
if (string.IsNullOrEmpty(filename) || !IcdFile.Exists(filename))
|
||||
throw new InvalidOperationException("Failed to find program filename");
|
||||
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger != null)
|
||||
logger.AddEntry(eSeverity.Informational, "Intentional Restart of Program");
|
||||
|
||||
Process.Start(filename);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,7 +171,19 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void Reboot()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
|
||||
if (logger != null)
|
||||
logger.AddEntry(eSeverity.Informational, "Intentional Reboot of Processor");
|
||||
|
||||
// TODO - Linux
|
||||
ProcessStartInfo psi =
|
||||
new ProcessStartInfo("shutdown", "/r /t 0")
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
Process.Start(psi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,10 +194,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static DateTime? GetSystemStartTime()
|
||||
{
|
||||
if (s_SystemStartTime == null)
|
||||
s_SystemStartTime = IcdEnvironment.GetUtcTime() - TimeSpan.FromMilliseconds(Environment.TickCount);
|
||||
|
||||
return s_SystemStartTime;
|
||||
return s_SystemStartTime ?? (s_SystemStartTime = Process.GetCurrentProcess().StartTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,6 +209,36 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static string RegistryLocalMachineGetString(string path, string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
|
||||
return rk == null ? string.Empty : (string)rk.GetValue(key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static long RegistryLocalMachineGetLong(string path, string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
|
||||
return rk == null ? 0 : (long)rk.GetValue(key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using ICD.Common.Utils.IO;
|
||||
using ICD.Common.Utils.Services;
|
||||
using ICD.Common.Utils.Services.Logging;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using Crestron.SimplSharp;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
using ICD.Common.Utils.IO;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Security.Principal;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
@@ -64,6 +65,19 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
get { return IcdFile.GetCreationTime(PathUtils.Join(PathUtils.ProgramPath, ProgramFile)); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current executing user is an admin.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static bool IsElevated
|
||||
{
|
||||
get
|
||||
{
|
||||
WindowsIdentity current = WindowsIdentity.GetCurrent();
|
||||
return new WindowsPrincipal(current).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,23 +24,25 @@ namespace ICD.Common.Utils
|
||||
switch (IcdEnvironment.Framework)
|
||||
{
|
||||
case IcdEnvironment.eFramework.Crestron:
|
||||
// No console in servers
|
||||
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
|
||||
return;
|
||||
|
||||
switch (IcdEnvironment.CrestronRuntimeEnvironment)
|
||||
{
|
||||
case IcdEnvironment.eCrestronRuntimeEnvironment.Simpl:
|
||||
case IcdEnvironment.eCrestronRuntimeEnvironment.SimplPlus:
|
||||
int length = Math.Min(13, name.Length);
|
||||
name = name.Substring(0, length).PadRight(13);
|
||||
break;
|
||||
case IcdEnvironment.eCrestronRuntimeEnvironment.Appliance:
|
||||
case IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro:
|
||||
int proLength = Math.Min(26 - 1, name.Length);
|
||||
name = name.Substring(0, proLength).PadRight(26);
|
||||
break;
|
||||
case IcdEnvironment.eCrestronRuntimeEnvironment.Server:
|
||||
// No console
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
break;
|
||||
case IcdEnvironment.eFramework.Framework:
|
||||
case IcdEnvironment.eFramework.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 2021")]
|
||||
[assembly: AssemblyVersion("14.2.0.0")]
|
||||
[assembly: AssemblyCopyright("Copyright © ICD Systems 2023")]
|
||||
[assembly: AssemblyVersion("17.2.0.0")]
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
using System;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
||||
namespace System.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -11,9 +11,11 @@ using Crestron.SimplSharp.Reflection;
|
||||
using Activator = Crestron.SimplSharp.Reflection.Activator;
|
||||
#else
|
||||
using System.Reflection;
|
||||
using Activator = System.Activator;
|
||||
#endif
|
||||
#if NETSTANDARD
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System.Runtime.Loader;
|
||||
using Activator = System.Activator;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
@@ -178,7 +180,7 @@ namespace ICD.Common.Utils
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return (T)CreateInstance(type);
|
||||
return (T)CreateInstance(type, new object[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -274,17 +276,12 @@ namespace ICD.Common.Utils
|
||||
|
||||
string fileNameWithOutExtension = IcdPath.GetFileNameWithoutExtension(path);
|
||||
|
||||
#if SIMPLSHARP
|
||||
|
||||
#if !NETSTANDARD
|
||||
try
|
||||
{
|
||||
return Assembly.Load(new AssemblyName {Name = fileNameWithOutExtension});
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return Assembly.LoadFrom(path);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
catch (Exception)
|
||||
{
|
||||
return Assembly.LoadFrom(path);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
@@ -111,5 +113,34 @@ namespace ICD.Common.Utils
|
||||
|
||||
return Regex.Replace(input, pattern, evaluator, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first successful match or the last failure.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="patterns"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static Match MatchAny([NotNull] string input, [NotNull] IEnumerable<string> patterns)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
if (patterns == null)
|
||||
throw new ArgumentNullException("patterns");
|
||||
|
||||
Match last = null;
|
||||
foreach (string pattern in patterns)
|
||||
{
|
||||
last = Regex.Match(input, pattern);
|
||||
if (last.Success)
|
||||
break;
|
||||
}
|
||||
|
||||
if (last == null)
|
||||
throw new InvalidOperationException("No patterns provided");
|
||||
|
||||
return last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the property with the given name and value to the builder without any additonal formatting.
|
||||
/// Adds the property with the given name and value to the builder without any additional formatting.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace ICD.Common.Utils
|
||||
/// Convert two ushort's to an int
|
||||
/// </summary>
|
||||
/// <param name="lowWord">ushort for the least significant 16 bits</param>
|
||||
/// <param name="highWord">ushort for the most significant 1 bits</param>
|
||||
/// <param name="highWord">ushort for the most significant 16 bits</param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI("S+")]
|
||||
public static int ConvertToInt(ushort lowWord, ushort highWord)
|
||||
@@ -20,4 +20,4 @@ namespace ICD.Common.Utils
|
||||
return (highWord << 16) + lowWord;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Like the CCriticalSection, the CMutex tends to get disposed before the parent is
|
||||
/// done with it. This class is an attempt to gracefully handle the ObjectDisposedExceptions
|
||||
/// we see on program termination, ocassionally causing the program to restart instead of stop.
|
||||
/// </summary>
|
||||
public sealed class SafeMutex
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
private readonly CMutex m_Mutex;
|
||||
#else
|
||||
private readonly Mutex m_Mutex;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public SafeMutex()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
m_Mutex = new CMutex();
|
||||
#else
|
||||
m_Mutex = new Mutex();
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Waits the given number of milliseconds to aquire the mutex.
|
||||
/// </summary>
|
||||
/// <param name="timeout"></param>
|
||||
/// <returns>True if the mutex was aquired.</returns>
|
||||
public bool WaitForMutex(int timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
return m_Mutex.WaitForMutex(timeout);
|
||||
#else
|
||||
return m_Mutex.WaitOne(timeout);
|
||||
#endif
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mutex.
|
||||
/// </summary>
|
||||
public void ReleaseMutex()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Mutex.ReleaseMutex();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Releasing a disposed mutex in this case is valid behaviour
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
using System.Text;
|
||||
#endif
|
||||
using ICD.Common.Properties;
|
||||
@@ -53,6 +53,11 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
[PublicAPI]
|
||||
[NotNull]
|
||||
IEnumerable<KeyValuePair<int, LogItem>> GetHistory();
|
||||
|
||||
/// <summary>
|
||||
/// Writes all enqueued logs.
|
||||
/// </summary>
|
||||
void Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -101,7 +106,7 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
if (e == null)
|
||||
throw new ArgumentNullException("e");
|
||||
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
if (e is AggregateException)
|
||||
{
|
||||
extends.AddEntry(severity, e as AggregateException, message);
|
||||
@@ -112,7 +117,7 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
IcdEnvironment.NewLine, e.Message, e.StackTrace));
|
||||
}
|
||||
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
/// <summary>
|
||||
/// Logs an aggregate exception as a formatted list of inner exceptions.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
using System;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
using RealNewtonsoft.Newtonsoft.Json.Converters;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Services.Logging
|
||||
{
|
||||
|
||||
@@ -25,6 +25,31 @@ namespace ICD.Common.Utils.Services
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the registered service of the given type.
|
||||
/// Creates a new instance using the given function if it does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <param name="constructor"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[NotNull]
|
||||
public static TService GetOrAddService<TService>([NotNull] Func<TService> constructor)
|
||||
{
|
||||
if (constructor == null)
|
||||
throw new ArgumentNullException("constructor");
|
||||
|
||||
TService output = TryGetService<TService>();
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
output = constructor();
|
||||
AddService(output);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the registered service of the given type. Use this for required dependencies.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using SqliteCommand = Crestron.SimplSharp.SQLite.SQLiteCommand;
|
||||
#else
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.IO;
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using SqliteConnection = Crestron.SimplSharp.SQLite.SQLiteConnection;
|
||||
#else
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using SqliteDataReader = Crestron.SimplSharp.SQLite.SQLiteDataReader;
|
||||
#else
|
||||
using System;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using SqliteParameter = Crestron.SimplSharp.SQLite.SQLiteParameter;
|
||||
#else
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using SqliteParameterCollection = Crestron.SimplSharp.SQLite.SQLiteParameterCollection;
|
||||
#else
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
using Crestron.SimplSharp.CrestronData;
|
||||
#else
|
||||
using System;
|
||||
@@ -41,14 +41,14 @@ namespace ICD.Common.Utils.Sqlite
|
||||
public static class DbTypeExtensions
|
||||
{
|
||||
public static
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
DbType
|
||||
#else
|
||||
SqliteType
|
||||
#endif
|
||||
ToParamType(this eDbType extends)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
return (DbType)extends;
|
||||
#else
|
||||
switch (extends)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="numeric"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToHexLiteral(int numeric)
|
||||
{
|
||||
return string.Format("\\x{0:X2}", numeric);
|
||||
@@ -30,7 +30,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToHexLiteral(char c)
|
||||
{
|
||||
return ToHexLiteral(Convert.ToInt32(c));
|
||||
@@ -41,7 +41,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToHexLiteral([NotNull] string input)
|
||||
{
|
||||
if (input == null)
|
||||
@@ -78,7 +78,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string FromHexLiteral([NotNull] string data)
|
||||
{
|
||||
if (data == null)
|
||||
@@ -98,7 +98,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToMixedReadableHexLiteral(char c)
|
||||
{
|
||||
int numeric = Convert.ToInt32(c);
|
||||
@@ -115,7 +115,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToMixedReadableHexLiteral([NotNull] string input)
|
||||
{
|
||||
if (input == null)
|
||||
@@ -134,7 +134,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToString(object value)
|
||||
[NotNull]
|
||||
public static string ToString([CanBeNull] object value)
|
||||
{
|
||||
return string.Format("{0}", value);
|
||||
}
|
||||
@@ -144,7 +145,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToString([NotNull] IEnumerable<byte> bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
@@ -160,7 +161,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToString([NotNull] IEnumerable<byte> bytes, int length)
|
||||
{
|
||||
if (bytes == null)
|
||||
@@ -175,6 +176,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
[PublicAPI]
|
||||
public static byte[] ToBytes([NotNull] string input)
|
||||
{
|
||||
@@ -184,6 +186,43 @@ namespace ICD.Common.Utils
|
||||
return Encoding.GetEncoding(28591).GetBytes(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the hex string ("01AB23") to a byte array ({1, 171, 23}).
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
[PublicAPI]
|
||||
public static byte[] HexToBytes([NotNull] string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (value.Length % 2 != 0)
|
||||
throw new ArgumentException("The binary key cannot have an odd number of digits");
|
||||
|
||||
return value.Split(2)
|
||||
.Select(s => HexToByte(s))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the hex string ("AB") to a byte (171).
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static byte HexToByte([NotNull] string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (value.Length == 0 || value.Length > 2)
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
|
||||
return Convert.ToByte(value, 16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the string as an integer.
|
||||
/// </summary>
|
||||
@@ -191,7 +230,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out int result)
|
||||
public static bool TryParse([NotNull] string value, out int result)
|
||||
{
|
||||
return TryConvert(Convert.ToInt32, value, out result);
|
||||
}
|
||||
@@ -203,7 +242,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out uint result)
|
||||
public static bool TryParse([NotNull] string value, out uint result)
|
||||
{
|
||||
return TryConvert(Convert.ToUInt32, value, out result);
|
||||
}
|
||||
@@ -215,7 +254,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out short result)
|
||||
public static bool TryParse([NotNull] string value, out short result)
|
||||
{
|
||||
return TryConvert(Convert.ToInt16, value, out result);
|
||||
}
|
||||
@@ -227,7 +266,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out ushort result)
|
||||
public static bool TryParse([NotNull] string value, out ushort result)
|
||||
{
|
||||
return TryConvert(Convert.ToUInt16, value, out result);
|
||||
}
|
||||
@@ -239,7 +278,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out long result)
|
||||
public static bool TryParse([NotNull] string value, out long result)
|
||||
{
|
||||
return TryConvert(Convert.ToInt64, value, out result);
|
||||
}
|
||||
@@ -251,7 +290,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out ulong result)
|
||||
public static bool TryParse([NotNull] string value, out ulong result)
|
||||
{
|
||||
return TryConvert(Convert.ToUInt64, value, out result);
|
||||
}
|
||||
@@ -263,7 +302,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out float result)
|
||||
public static bool TryParse([NotNull] string value, out float result)
|
||||
{
|
||||
return TryConvert(Convert.ToSingle, value, out result);
|
||||
}
|
||||
@@ -275,7 +314,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out double result)
|
||||
public static bool TryParse([NotNull] string value, out double result)
|
||||
{
|
||||
return TryConvert(Convert.ToDouble, value, out result);
|
||||
}
|
||||
@@ -287,7 +326,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out byte result)
|
||||
public static bool TryParse([NotNull] string value, out byte result)
|
||||
{
|
||||
return TryConvert(Convert.ToByte, value, out result);
|
||||
}
|
||||
@@ -299,7 +338,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out char result)
|
||||
public static bool TryParse([NotNull] string value, out char result)
|
||||
{
|
||||
return TryConvert(Convert.ToChar, value, out result);
|
||||
}
|
||||
@@ -311,11 +350,32 @@ namespace ICD.Common.Utils
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse(string value, out bool result)
|
||||
public static bool TryParse([NotNull] string value, out bool result)
|
||||
{
|
||||
return TryConvert(Convert.ToBoolean, value, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the string as a guid.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryParse([NotNull] string value, out Guid result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = new Guid(value);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the string via the given conversion function.
|
||||
/// </summary>
|
||||
@@ -350,6 +410,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string NiceName([NotNull] object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
@@ -363,6 +424,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string NiceName([NotNull] string name)
|
||||
{
|
||||
if (name == null)
|
||||
@@ -399,6 +461,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="phoneFormat"></param>
|
||||
/// <param name="number"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string SafeNumericFormat([NotNull] string phoneFormat, [NotNull] string number)
|
||||
{
|
||||
if (phoneFormat == null)
|
||||
@@ -441,7 +504,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string Reverse([NotNull] string input)
|
||||
{
|
||||
if (input == null)
|
||||
@@ -458,6 +521,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="input"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string Repeat(char input, int count)
|
||||
{
|
||||
return Repeat(input.ToString(), count);
|
||||
@@ -469,7 +533,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="input"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string Repeat(string input, int count)
|
||||
{
|
||||
if (count < 0)
|
||||
@@ -485,7 +549,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ArrayFormat<T>([NotNull] IEnumerable<T> items)
|
||||
{
|
||||
if (items == null)
|
||||
@@ -516,7 +580,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ArrayRangeFormat([NotNull] IEnumerable<int> items)
|
||||
{
|
||||
if (items == null)
|
||||
@@ -537,7 +601,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string ArrayRangeFormat([NotNull] IEnumerable<ushort> items)
|
||||
{
|
||||
if (items == null)
|
||||
@@ -552,7 +616,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[PublicAPI, NotNull]
|
||||
public static string RangeFormat(object a, object b)
|
||||
{
|
||||
return string.Format("[{0} - {1}]", a, b);
|
||||
@@ -563,8 +627,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string UppercaseFirst(string input)
|
||||
[PublicAPI, CanBeNull]
|
||||
public static string UppercaseFirst([CanBeNull] string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
@@ -577,8 +641,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string ToTitleCase(string input)
|
||||
[PublicAPI, NotNull]
|
||||
public static string ToTitleCase([NotNull] string input)
|
||||
{
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input);
|
||||
}
|
||||
@@ -588,6 +652,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="ipid"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string ToIpIdString(byte ipid)
|
||||
{
|
||||
return string.Format("0x{0:X2}", ipid);
|
||||
@@ -642,7 +707,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string RemoveWhitespace(string text)
|
||||
[CanBeNull]
|
||||
public static string RemoveWhitespace([CanBeNull] string text)
|
||||
{
|
||||
return text == null ? null : text.RemoveWhitespace();
|
||||
}
|
||||
@@ -652,7 +718,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string RemoveDuplicateWhitespace(string text)
|
||||
[CanBeNull]
|
||||
public static string RemoveDuplicateWhitespace([CanBeNull] string text)
|
||||
{
|
||||
return text == null ? null : text.RemoveDuplicateWhitespace();
|
||||
}
|
||||
@@ -662,7 +729,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNullOrWhitespace(string text)
|
||||
public static bool IsNullOrWhitespace([CanBeNull] string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return true;
|
||||
@@ -676,7 +743,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
public static string PasswordFormat(string password)
|
||||
[CanBeNull]
|
||||
public static string PasswordFormat([CanBeNull] string password)
|
||||
{
|
||||
return password == null ? null : Repeat('*', password.Length);
|
||||
}
|
||||
@@ -686,7 +754,8 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToRepresentation(string value)
|
||||
[NotNull]
|
||||
public static string ToRepresentation([CanBeNull] string value)
|
||||
{
|
||||
return value == null ? "NULL" : string.Format("\"{0}\"", value);
|
||||
}
|
||||
@@ -696,6 +765,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string SerialComma([NotNull] IEnumerable<string> items)
|
||||
{
|
||||
if (items == null)
|
||||
@@ -729,7 +799,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI, CanBeNull]
|
||||
public static string Trim(string value)
|
||||
public static string Trim([CanBeNull] string value)
|
||||
{
|
||||
return value == null ? null : value.ToUpper();
|
||||
}
|
||||
@@ -768,6 +838,7 @@ namespace ICD.Common.Utils
|
||||
/// <param name="ignoreCase"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[CanBeNull]
|
||||
public static string GetLongestCommonIntersectionFromStart([NotNull] IEnumerable<string> items, bool ignoreCase)
|
||||
{
|
||||
if (items == null)
|
||||
@@ -811,6 +882,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string Enquote([NotNull] string value)
|
||||
{
|
||||
if (value == null)
|
||||
@@ -830,6 +902,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static string UnEnquote([NotNull] string value)
|
||||
{
|
||||
if (value == null)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
public sealed class TableBuilder
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
private const char HORIZONTAL = '-';
|
||||
private const char VERTICAL = '|';
|
||||
private const char INTERSECT = '+';
|
||||
@@ -230,9 +230,8 @@ namespace ICD.Common.Utils
|
||||
|
||||
private void AppendTopSeparator(StringBuilder builder, IList<int> columnWidths)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
// Can't do fancy tables so don't bother drawing the top row
|
||||
return;
|
||||
#else
|
||||
builder.Append(DOWN_RIGHT).Append(HORIZONTAL);
|
||||
|
||||
@@ -257,9 +256,8 @@ namespace ICD.Common.Utils
|
||||
|
||||
private void AppendBottomSeparator(StringBuilder builder, IList<int> columnWidths)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
#if !NETSTANDARD
|
||||
AppendSeparator(builder, columnWidths);
|
||||
return;
|
||||
#else
|
||||
builder.Append(UP_RIGHT).Append(HORIZONTAL);
|
||||
|
||||
@@ -284,7 +282,7 @@ namespace ICD.Common.Utils
|
||||
|
||||
private static void AppendRow(StringBuilder builder, IList<string> row, IList<int> columnWidths)
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
builder.Append(VERTICAL).Append(' ');
|
||||
#endif
|
||||
|
||||
@@ -300,7 +298,7 @@ namespace ICD.Common.Utils
|
||||
builder.Append(VERTICAL);
|
||||
}
|
||||
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
builder.Append(VERTICAL);
|
||||
#endif
|
||||
|
||||
@@ -309,7 +307,7 @@ namespace ICD.Common.Utils
|
||||
|
||||
private static void AppendSeparator(StringBuilder builder, IList<int> columnWidths)
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
builder.Append(VERTICAL_RIGHT).Append(HORIZONTAL);
|
||||
#endif
|
||||
|
||||
@@ -326,7 +324,7 @@ namespace ICD.Common.Utils
|
||||
builder.Append(INTERSECT);
|
||||
}
|
||||
|
||||
#if !SIMPLSHARP
|
||||
#if NETSTANDARD
|
||||
builder.Append(VERTICAL_LEFT);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Collections;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilizes a priority queue to store items
|
||||
/// Dequeues items in priority order and processes
|
||||
/// them in a worker thread one at a time
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class ThreadedWorkerQueue<T>
|
||||
{
|
||||
|
||||
private readonly PriorityQueue<T> m_Queue;
|
||||
|
||||
private readonly SafeCriticalSection m_QueueSection;
|
||||
private readonly SafeCriticalSection m_ProcessSection;
|
||||
|
||||
private readonly Action<T> m_ProcessAction;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="processItemAction">Action to process the dequeued items</param>
|
||||
public ThreadedWorkerQueue([NotNull] Action<T> processItemAction)
|
||||
{
|
||||
if (processItemAction == null)
|
||||
throw new ArgumentNullException("processItemAction");
|
||||
|
||||
m_Queue = new PriorityQueue<T>();
|
||||
m_QueueSection = new SafeCriticalSection();
|
||||
m_ProcessSection = new SafeCriticalSection();
|
||||
|
||||
m_ProcessAction = processItemAction;
|
||||
}
|
||||
|
||||
|
||||
#region Queue Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clears the collection.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Clear()
|
||||
{
|
||||
m_QueueSection.Execute(() => m_Queue.Clear());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the end of the queue.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue([CanBeNull] T item)
|
||||
{
|
||||
Enqueue(() => m_Queue.Enqueue(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the queue with the given priority.
|
||||
/// Lower values are dequeued first.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue([CanBeNull] T item, int priority)
|
||||
{
|
||||
Enqueue(() => m_Queue.Enqueue(item, priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the queue with the given priority at the given index.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="position"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue([CanBeNull] T item, int priority, int position)
|
||||
{
|
||||
Enqueue(() => m_Queue.Enqueue(item, priority, position));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the item at the beginning of the queue.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueFirst([CanBeNull] T item)
|
||||
{
|
||||
Enqueue(() => m_Queue.EnqueueFirst(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove)
|
||||
{
|
||||
Enqueue(() => m_Queue.EnqueueRemove(item, remove));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority)
|
||||
{
|
||||
Enqueue(() => m_Queue.EnqueueRemove(item, remove, priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="deDuplicateToEndOfQueue"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority, bool deDuplicateToEndOfQueue)
|
||||
{
|
||||
Enqueue(() => m_Queue.EnqueueRemove(item, remove, priority, deDuplicateToEndOfQueue));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void Enqueue(Action enqueueAction)
|
||||
{
|
||||
m_QueueSection.Execute(() => enqueueAction());
|
||||
ThreadingUtils.SafeInvoke(ProcessQueue);
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
if (!m_ProcessSection.TryEnter())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
T item = default(T);
|
||||
while (m_QueueSection.Execute(() => m_Queue.TryDequeue(out item)))
|
||||
m_ProcessAction(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_ProcessSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
106
ICD.Common.Utils/Threading/AbstractIcdResetEvent.cs
Normal file
106
ICD.Common.Utils/Threading/AbstractIcdResetEvent.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public abstract class AbstractIcdResetEvent : IDisposable
|
||||
{
|
||||
|
||||
#if SIMPLSHARP
|
||||
private readonly CEvent m_Event;
|
||||
#else
|
||||
private readonly EventWaitHandle m_Event;
|
||||
#endif
|
||||
|
||||
#if SIMPLSHARP
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdManualResetEvent class with the CEvent
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
protected AbstractIcdResetEvent(CEvent eventHandle)
|
||||
{
|
||||
m_Event = eventHandle;
|
||||
}
|
||||
#else
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdManualResetEvent class with the CEvent
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
protected AbstractIcdResetEvent(EventWaitHandle eventHandle)
|
||||
{
|
||||
m_Event = eventHandle;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
|
||||
/// </summary>
|
||||
/// <returns>true if the operation succeeds; otherwise, false.</returns>
|
||||
[PublicAPI]
|
||||
public bool Set()
|
||||
{
|
||||
return m_Event.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the state of the event to nonsignaled, causing threads to block.
|
||||
/// </summary>
|
||||
/// <returns>true if the operation succeeds; otherwise, false.</returns>
|
||||
[PublicAPI]
|
||||
public bool Reset()
|
||||
{
|
||||
return m_Event.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function to wait for the event to be signaled. This will block indefinitely until the event is signaled.
|
||||
/// </summary>
|
||||
/// <returns>True if the current instance receives a signal otherwise false.</returns>
|
||||
[PublicAPI]
|
||||
public bool WaitOne()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
return m_Event.Wait();
|
||||
#else
|
||||
return m_Event.WaitOne();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function to wait for the event to be signaled.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Timeout in milliseconds or Timeout.Infinite to wait indefinitely.</param>
|
||||
/// <returns>True if the current instance receives a signal otherwise false.</returns>
|
||||
public bool WaitOne(int timeout)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
return m_Event.Wait(timeout);
|
||||
#else
|
||||
return m_Event.WaitOne(timeout);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up of resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Event.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the event to release all resources used by this instance.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Close()
|
||||
{
|
||||
m_Event.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
ICD.Common.Utils/Threading/IcdAutoResetEvent.cs
Normal file
39
ICD.Common.Utils/Threading/IcdAutoResetEvent.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
# else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Notifies one or more waiting threads that an event has occurred. Every thread that passes WaitOne causes the event to be reset
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class IcdAutoResetEvent : AbstractIcdResetEvent
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdAutoResetEvent class with the initial state to nonsignaled.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IcdAutoResetEvent() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IcdAutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
|
||||
/// </summary>
|
||||
/// <param name="initialState">true to set the initial state signaled; false to set the initial state to nonsignaled.</param>
|
||||
[PublicAPI]
|
||||
public IcdAutoResetEvent(bool initialState) :
|
||||
#if SIMPLSHARP
|
||||
base(new CEvent(true, initialState))
|
||||
#else
|
||||
base(new AutoResetEvent(initialState))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user