Compare commits

..

183 Commits

Author SHA1 Message Date
Drew Tingen
0de417b665 chore: Update Changelog, increment assembly minor version 2023-06-18 09:55:30 -04:00
Drew Tingen
da57fcb988 test[BigEndianBitConverter]: finished unit testing 2023-06-18 09:52:47 -04:00
Drew Tingen
c6b6711eb2 refactor[BigEndianBitConverter]: Reduce code reuse in GetBytes methods 2023-06-18 09:52:18 -04:00
Drew Tingen
138a6f3470 fix[BigEndianBitConverter]: Fix ToLong and ToUlong overflow errors 2023-06-18 09:51:22 -04:00
Scott Pidzarko
e84bc721ee chore: Update SafeMutex.cs to remove commented out boilerplate
IDisposable boilerplate generates commented out finalizer. Removing commented out finalizer at request of upstream maintainer.
2023-06-18 08:38:38 -04:00
Scott Pidzarko
2988912d44 Update SafeMutex.cs to use correct class name in boilerplate commented out finalizer 2023-06-18 08:38:38 -04:00
Scott Pidzarko
be94503e6a Update SafeMutex.cs to implement IDisposable 2023-06-18 08:38:38 -04:00
Drew Tingen
6877b5c800 chore: changelog 2023-06-16 15:01:06 -04:00
Drew Tingen
569066edc4 feat[Types]: Add NotifyFlagsChanged, with abstract and generic classes 2023-06-16 14:59:33 -04:00
Drew Tingen
73a716f9b9 feat[Collections]: ReverseLookupDictionary for fast value access 2023-06-16 14:59:16 -04:00
Drew Tingen
4e430731cc feat[BigEndianBitConverter]: Added BigEndianBitConverter 2023-06-16 14:58:59 -04:00
Drew Tingen
2b8f2a125a feat[EnumUtils]: add GetinverseFlags method 2023-06-16 12:12:29 -04:00
Drew Tingen
cc25c41805 chore: Update changelog, increment assembly minor version 2023-03-22 08:13:59 -04:00
Drew Tingen
2487a70891 chore: Update copyright year 2023-03-22 08:12:27 -04:00
Drew Tingen
5c5a25775b chore: Remove Obfuscation 2023-03-21 16:57:38 -04:00
Drew Tingen
27f6267f54 feat: Nicer timespan readable formatting 2023-02-10 09:41:25 -05:00
Drew Tingen
430eac8cb6 Merge pull request #1 from scottpidzarko/patch-1
PR: Update SPlusUtils.cs to fix typo in XMLdoc
2023-01-10 11:32:40 -05:00
Scott Pidzarko
b47e305d9f Update SPlusUtils.cs
Fix typo.
2023-01-06 15:07:11 -08:00
astingen
0c8f8242e8 Create LICENSE.md 2022-12-24 17:45:39 -05:00
Drew Tingen
a52e77c1e7 chore:Increment major version, update changelog 2022-12-02 14:40:36 -05:00
Drew Tingen
2aa05f3ee8 fix(ThreadedWorkerQueue): Exception handling of exceptions in process action 2022-11-30 12:29:50 -05:00
Drew Tingen
2700f5018a fix: Fixed preprocessors in IcdDirectory 2022-11-30 12:29:50 -05:00
Drew Tingen
b09b22266b fix: Fixed CrestronRuntimeEnvironment uses in utils 2022-11-30 12:29:50 -05:00
Drew Tingen
1c206c5539 doc:Fixed doc typo 2022-11-30 12:29:50 -05:00
Drew Tingen
56a48175c6 feat(IcdEnvironment):Updated environment to sperate Crestron platform for environment for SW on VC4 2022-11-30 12:29:41 -05:00
Drew Tingen
0acd0ae5d4 chore: Update Changelog, Increment assembly patch version 2022-07-11 18:54:12 -04:00
Drew Tingen
d2856c1983 fix: Fix Preprocessors and NetStandard package references 2022-07-11 14:48:10 -04:00
Drew Tingen
fa65a9de65 fix: Fix console command responses in Simpl runtime environment 2022-07-06 15:40:48 -04:00
Drew Tingen
e4b292f145 fix: Crestron apps aren't considered interactive console 2022-07-06 15:37:24 -04:00
Drew Tingen
11a5533fcd chore: Update changelog, increment assembly patch version 2022-07-01 12:02:45 -04:00
Drew Tingen
e0d0763306 chore: Update Crestron SDK to 2.18.96 2022-07-01 11:58:28 -04:00
Drew Tingen
7d42158bc7 chore: sqlite reference only for netstandard 2022-06-30 18:49:21 -04:00
Drew Tingen
92a28813e0 fix: Use Crestron SQLite for 4 series 2022-06-30 17:58:01 -04:00
Drew Tingen
3b313a442c fix: Fix preprocessors for netstandard vs simplsharp 2022-06-30 11:01:12 -04:00
Drew Tingen
16fb3683dd chore: Update changelog, increment assembly patch version 2022-06-23 15:07:50 -04:00
Drew Tingen
bb9bcf6cdf feat: TimeZone getting an invalid time zone now throws exception with time zone name 2022-06-23 15:05:45 -04:00
Drew Tingen
0490ffd572 chore: Update changelog, increment assembly patch version 2022-05-23 11:32:09 -04:00
Chris Cameron
3bf0aff0a0 feat: Added KeyedLock for async semaphore locking by some arbitrary key 2022-02-02 14:13:29 -05:00
Chris Cameron
e5b10a96f7 chore: Moved threading classes into Threading subdirectory 2022-02-02 13:49:03 -05:00
Chris Cameron
0c3c87308c chore: Updating AssemblyInfo year 2022-01-05 16:27:18 -05:00
Chris Cameron
68d1f1033a fix: AbstractGenericJsonConverter handles existing values properly 2021-12-21 16:11:17 -05:00
Chris Cameron
af66147648 Merge remote-tracking branch 'origin/Krang_v1.9' into Krang_v1.10 2021-10-28 11:15:02 -04:00
Chris Cameron
16067bca20 chore: Updating changelog, incrementing patch version 2021-10-28 11:13:30 -04:00
Austin Noska
57e64788c4 fix: Change sqlite connection strings for IcdCultureInfo & IcdTimeZoneInfo to work with SimplSharp 2021-10-27 18:06:29 -04:00
Chris Cameron
bc605bf8ae chore: Updating crestron nuget packages 2021-10-25 14:30:25 -04:00
Chris Cameron
be50aa3314 docs: Fixed typo 2021-10-20 10:11:50 -04:00
Drew Tingen
1b79bd21df Merge remote-tracking branch 'origin/Krang_v1.9' into Krang_v1.10 2021-10-05 10:17:58 -04:00
Drew Tingen
7d5ec0e636 chore: Update changelog, increment assembly major version 2021-10-04 12:21:41 -04:00
Drew Tingen
79c1c60d79 chore: changelog 2021-09-30 10:47:18 -04:00
Drew Tingen
033008616f remove: SafeCriticalSection - remove TryEnter method 2021-09-30 10:45:12 -04:00
Drew Tingen
84b5b636f6 remove: Removing RateLimitedEventQueue - Use ThreadedWorkerQueue instead 2021-09-30 10:45:12 -04:00
Drew Tingen
451cf08c0f fix: Fixing ThreadedWorkerQueue to not use TryEnter, and track the processing state with a bool 2021-09-30 10:45:12 -04:00
Drew Tingen
63d76d8cef feat: Adding IcdAutoResetEvent and IcdMaunalResetEvent 2021-09-30 10:44:30 -04:00
Drew Tingen
c08a6283b8 fix: Fixed EnumUtils.GetValuesExceptNone to work for non-flag enums 2021-09-30 10:44:01 -04:00
Austin Noska
df42a6674f fix: Uri builder builds relative path into valid URI 2021-09-18 16:12:42 -04:00
Chris Cameron
ae53812a98 fix: Fixed plugin loading for .net framework 2021-08-31 11:00:48 -04:00
Chris Cameron
74ff651798 fix: Fixed IcdEnvironment.Framework for .net framework 2021-08-30 17:23:34 -04:00
Chris Cameron
29013a2bf5 fix: Fixed a bug where plugins were not unzipping in .net framework 2021-08-30 17:09:04 -04:00
Chris Cameron
baa00f7b00 chore: S#, net framework and net standard all build to /bin 2021-08-30 14:05:42 -04:00
Chris Cameron
67a2b11ee6 chore: Added SimplSharp nuget packages, fixed SIMPLSHARP preprocessors 2021-08-30 13:39:43 -04:00
Chris Cameron
8f5fee2401 chore: Added net472 target 2021-08-25 16:21:56 -04:00
Drew Tingen
d00d2febf3 chore: Update Changelog, increment minor version 2021-08-18 10:45:49 -04:00
Austin Noska
af3d335079 feat: Add TryParse overload method to StringUtils, which tries to parse a GUID from a string 2021-08-17 16:04:52 -04:00
Drew Tingen
375e3154c6 chore: Update Changelog, increment minor version 2021-08-03 13:55:18 -04:00
Chris Cameron
37d799295c fix: Resolving warnings 2021-06-30 11:33:11 -04:00
Chris Cameron
06f40e5d22 fix: Resolving warning 2021-06-30 11:05:31 -04:00
Austin Noska
11c3e33fc1 fix: Use if statement instead of while when checking if SQL read was successful in IcdTimeZoneInfo 2021-06-30 10:09:58 -04:00
Chris Cameron
fc60e2b675 chore: Adding NotNull/CanBeNull attributes to StringUtils 2021-06-28 16:11:23 -04:00
Drew Tingen
531e9dfcf8 feat: Adding Equality Comparers to BiDictionary constructor 2021-06-24 11:48:52 -04:00
Drew Tingen
f601f9cda7 feat: SetFlags enum extension 2021-06-23 14:12:27 -04:00
Chris Cameron
841b692727 feat: Added Flush() method to ILoggerService 2021-06-23 14:11:37 -04:00
Chris Cameron
5f2c5e6dcb fix: Fixed a bug where EnumUtils.GetFlags, and overloads, would also return defined composites 2021-06-15 14:13:37 -04:00
Austin Noska
7ae9e86e1d fix: Fixed an issue where the SequenceComparer & the UndefinedVersionComparer were not handling null values properly 2021-06-07 14:43:53 -04:00
Chris Cameron
e6dec641f3 feat: Added ThreadingUtils overloads 2021-05-24 10:11:49 -04:00
Chris Cameron
8b848e5127 refactor: Tidying, resolving warnings 2021-05-20 17:09:12 -04:00
Chris Cameron
f328598f63 refactor: Tidying, resolving warnings 2021-05-20 14:45:49 -04:00
Chris Cameron
79db70211f fix: Removing "Microsoft" from windows model name 2021-05-20 09:55:05 -04:00
Chris Cameron
1390af967f feat: Logging when we intentionally restart the program or processor 2021-05-17 15:30:53 -04:00
Chris Cameron
28335ad99c chore: Updating changelog, incrementing major version 2021-05-14 10:59:54 -04:00
Chris Cameron
6967e9b013 fix: No longer automatically enabling ANSI color, must be enabled by the calling application 2021-05-14 10:17:01 -04:00
Chris Cameron
7784b99f75 fix: Logic for determining if there is a console output window, don't try to write to console if no console window 2021-05-14 10:17:01 -04:00
Chris Cameron
b3c1daaab5 fix: Fixed a bug where AbstractGenericXmlConverter was not reading out of empty elements 2021-05-04 17:26:01 -04:00
Austin Noska
ae10abd71e feat: Port open source CsvReader for CF 3.5 compatibility 2021-04-29 09:31:24 -04:00
Austin Noska
5aca963da0 feat: Expose wrapped stream for IcdStreamReader 2021-04-28 17:03:59 -04:00
Austin Noska
40330d0007 feat: Add method to IcdFile for getting the length in bytes of a specified file 2021-04-28 16:16:59 -04:00
Chris Cameron
a26783bd67 feat: IcdErrorLog traces to Visual Studio output 2021-04-22 14:26:54 -04:00
Chris Cameron
d58b8a9db9 feat: IcdConsole traces to Visual Studio output 2021-04-21 16:45:12 -04:00
Chris Cameron
5083f3d7ab fix: Fixing trailing whitespace in windows model name 2021-04-15 17:18:55 -04:00
Austin Noska
43fd348fae feat: Add enum extension method for cycling to next enum value 2021-04-13 15:59:37 -04:00
Chris Cameron
8cdddc94bc chore: Updating test projects to netcoreapp3.1 2021-04-13 11:46:10 -04:00
Chris Cameron
25d799fe7d fix: Fixed a bug where SafeTimer.Trigger would call the callback twice on NetStandard 2021-04-12 15:13:38 -04:00
Drew Tingen
f807db480e feat: added OnSystemDeviceAddedRemoved event to IcdEnviornment for NetStandard 2021-04-01 10:03:06 -04:00
Chris Cameron
985a81f961 refactor: Fixing warnings 2021-03-31 17:21:43 -04:00
Chris Cameron
0a9a382355 fix: GetProperty and CallMethod reflection utils use the FlattenHierarchy flag 2021-03-31 16:20:35 -04:00
Chris Cameron
86fabce6da feat: Initial commit of IcdOrderedDictionary 2021-03-30 13:43:59 -04:00
Chris Cameron
a7ab2ab3fe refactor: Renaming OrderedDictionary to SortedDictionary for consistency with .Net 2021-03-29 16:23:29 -04:00
Chris Cameron
370cadbaeb feat: Extension methods for joining enumerables of strings 2021-03-23 17:05:27 -04:00
Chris Cameron
e7bdcdfca5 feat: Adding methods for converting hex strings to byte arrays 2021-03-22 15:30:25 -04:00
Chris Cameron
cab1e237d5 chore: Updating nuget packages 2021-03-22 15:21:49 -04:00
Chris Cameron
da5e1d83a0 refactor: Tidying 2021-03-18 14:56:25 -04:00
Chris Cameron
63237b6fba feat: Added session change event to IcdEnvironment 2021-03-12 14:07:38 -05:00
Chris Cameron
a55480f8e5 fix: Preserve stack trace when rethrowing inner exceptions 2021-03-12 14:03:47 -05:00
Chris Cameron
d1f8096933 feat: Initial commit of RegistryExtensions 2021-03-03 16:45:19 -05:00
Chris Cameron
c41e61391e feat: Implementing ProcessorUtils for NetStandard 2021-03-03 13:50:28 -05:00
Chris Cameron
766e2da4e3 feat: Added property to ProgramUtils to determine if the application is running as admin 2021-03-02 11:44:36 -05:00
Chris Cameron
b83fba337f feat: Implementing RestartProgram and Reboot methods for windows 2021-03-01 09:46:10 -05:00
Austin Noska
766c265dac chore: Update changelog 2021-02-18 16:25:44 -05:00
Austin Noska
204d2a475d feat: Add MatchAny util method to RegexUtils that tries to match an input against a sequence of patterns. 2021-02-18 16:22:16 -05:00
Austin Noska
b5542fb0d2 feat: Add daylight time row entries & a new display name column to TimeZones.sqlite - Adjusted IcdTimeZoneInfo accordingly 2021-02-18 16:20:41 -05:00
Austin Noska
4ff4e9f3c6 feat: Add method to IcdEnvironment to get the name of the local time zone 2021-02-18 16:18:38 -05:00
Chris Cameron
456a8f74f9 feat: Adding shim for setting file attributes recursively 2021-02-11 11:43:18 -05:00
Chris Cameron
ffb00f960d feat: NetStandard krang plugins are archived to .kpz files 2021-02-10 14:21:31 -05:00
Chris Cameron
b44cbe0093 chore: Updating dependencies 2021-02-08 11:43:19 -05:00
Chris Cameron
cc79e88702 fix: Don't throw an ObjectDisposed exception when attempting to stop a disposed timer 2021-02-05 16:15:11 -05:00
Chris Cameron
1db7d4b45d Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9
# Conflicts:
#	CHANGELOG.md
2021-02-04 11:34:06 -05:00
Chris Cameron
5bf8fcc71f chore: Updating changelog, incrementing minor version 2021-02-04 10:57:12 -05:00
Drew Tingen
b05373157a feat: Changed ProcessorUtils Uptimes to StartTime 2021-02-03 16:43:36 -05:00
Chris Cameron
8427f2c0c6 Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9 2021-01-25 12:21:07 -05:00
Chris Cameron
572f069797 Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8 2021-01-25 12:20:18 -05:00
Chris Cameron
e44bd3e2cc Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2021-01-25 12:19:35 -05:00
Chris Cameron
419b070375 chore: Fixing unit test 2021-01-25 12:18:24 -05:00
Chris Cameron
8411a27173 fix: Fixing 2008 build 2021-01-25 12:04:08 -05:00
Chris Cameron
e0287cfc7b Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9 2021-01-21 10:42:51 -05:00
Chris Cameron
d7bee510c9 chore: Update changelog, increment minor version 2021-01-21 10:40:26 -05:00
Austin Noska
45a3c5c5a1 feat: Add overload to GuidUtils to combine an IEnumerable of guids into a new deterministic guid 2021-01-21 10:25:39 -05:00
Austin Noska
d090ab0085 fix: The SafeTimer constructor that executes the callback immediately now works as expected 2021-01-21 10:24:25 -05:00
Chris Cameron
e044c97af7 refactor: IcdUriBuilder.AppendPath returns itself for chaining 2021-01-18 15:22:46 -05:00
Chris Cameron
c7e8f09eeb feat: Added GetParentUri method to UriExtensions 2021-01-18 15:22:28 -05:00
Chris Cameron
9b53d77d6b feat: Added GetOrAddService method for lazy-loading services 2021-01-15 11:35:04 -05:00
Drew Tingen
c1b9426e32 chore: update changelog, increment assembly version 2021-01-14 23:41:26 -05:00
Drew Tingen
237b5d3e86 feat: Moving environment to 3 properties for Framework, Series, and Runtime 2021-01-12 16:25:09 -05:00
Drew Tingen
68365553ed feat: Adding SimplSharpMono environment (for non-pro 4-series environment)
fix: Fix error in console FixLineEndings
2021-01-12 16:25:09 -05:00
Drew Tingen
3abb792c5c fix: Replace \n with \r\n for 4series console writes, to help reabibility 2021-01-11 11:32:52 -05:00
Chris Cameron
342ac4ebca chore: Fixing warning on Net Standard 2021-01-07 12:09:21 -05:00
Chris Cameron
4174ed2e1f chore: Updating copyright year 2021-01-06 16:45:03 -05:00
Chris Cameron
41d86ecf48 perf: Adding faster breadth-first search for when paths are not important 2021-01-06 15:08:38 -05:00
Chris Cameron
6b28ae44d6 feat: Added program entry point to path utils 2021-01-06 15:08:34 -05:00
Drew Tingen
ca857a7aed chore: Changelog 2021-01-05 16:49:08 -05:00
Drew Tingen
ccb961fc2e feat: Add GetSystemStartTime, to pull the time the system started up 2021-01-05 16:49:08 -05:00
Drew Tingen
8fa1de991b refactor: Change Procesor model version to be a string, pull from CrestronEnvironment 2021-01-05 16:49:08 -05:00
Austin Noska
5acfed40ba Refactor: Rename SequenceEqual parameter 2021-01-05 16:09:58 -05:00
Austin Noska
6141a763ae Refactor: remove unused code in CsvWriter.cs 2021-01-05 10:04:41 -05:00
Drew Tingen
e61dcd8596 fix: Improved threadsafety for TypeExtensions 2021-01-04 11:18:31 -05:00
Drew Tingen
9819c44dcc fix: Fix threading issue in EnumUtils cache 2021-01-04 11:18:28 -05:00
Chris Cameron
f9a8be85d6 feat: Added TimeSpan extension methods to converting to/from universal time 2020-12-17 11:41:20 -05:00
Chris Cameron
3381977050 feat: Added ToStringUndefined method to EnumUtils for printing known flags of an undefined composite 2020-12-01 16:53:45 -05:00
Chris Cameron
81d1a97304 feat: Added DepthFirstSearch method to RecursionUtils 2020-12-01 16:53:10 -05:00
Chris Cameron
37d5468e20 feat: Enumerable.Consolidate extensions support predicates 2020-11-16 16:16:43 -05:00
Austin Noska
03be66983d feat: Support reading primitive type double in IcdXmlReader and XmlUtils 2020-11-06 10:03:01 -05:00
Drew Tingen
a13f1fd687 feat: Adding eDaysOfWeek enum 2020-11-02 12:08:15 -05:00
Chris Cameron
f0bcb3bbc9 chore: Added NotNull attributes to StringUtils methods 2020-10-28 16:45:24 -04:00
Chris Cameron
1e934c9877 fix: Added critical section to IcdConsole to clean up multi-threaded output 2020-10-28 14:42:30 -04:00
Chris Cameron
64cc662ee7 fix: Fixed bad Assembly path handling on 4-series 2020-10-28 11:00:28 -04:00
Chris Cameron
4aa76bd647 test: Added count to collection debugger displays 2020-10-26 11:48:24 -04:00
Chris Cameron
59f4585ae6 feat: Exposing RecursionUtils GetPath method 2020-10-23 16:52:12 -04:00
Chris Cameron
2235eeeb9d feat: Initial commit of INotifyCollectionChanged 2020-10-22 12:17:55 -04:00
Chris Cameron
2a7a051f1f chore: Added validation and [NotNull] attributes to ReflectionUtils 2020-10-22 12:17:37 -04:00
Chris Cameron
f174c32721 feat: Added breadth-first search method that returns the path from root for every node in the graph 2020-10-22 12:16:32 -04:00
Chris Cameron
c349757c00 refactor: Cleaned up SafeTimer, added duration validation to clarify bad values 2020-10-19 12:24:22 -04:00
Chris Cameron
8c70c8d534 refactor: ReflectionExtensions are generic, support for non-public members, reduced duplicate code 2020-10-15 14:30:46 -04:00
Chris Cameron
0d85fe8106 refactor: Using EqualityComparer instead of Comparer for equality... 2020-10-14 16:43:26 -04:00
Chris Cameron
c868034769 fix: Handling a Crestron bug where File.Exists throws an exception on 4-Series instead of returning false 2020-10-12 11:22:28 -04:00
Drew Tingen
55c0a7dc7e fix: Fixing Console for 4-series 2020-10-07 22:55:26 -04:00
Drew Tingen
4321ad6157 feat: Adding ThreadedWorkerQueue 2020-09-17 15:34:02 -04:00
Chris Cameron
9a68f0446d test: Moved version comparer tests from settings 2020-09-11 11:17:50 -04:00
Laura Gomez
0574314580 feat: Created extension class, comparer class and VersionSpan class to for Devices' version and model validation. 2020-09-10 17:01:24 -04:00
Chris Cameron
e5fc154858 feat: Add IcdTimeZoneInfo 2020-09-03 12:08:51 -04:00
Chris Cameron
7bc262b4e2 Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8
# Conflicts:
#	CHANGELOG.md
2020-09-03 12:05:09 -04:00
Austin Noska
1a0ea4119e feat: Add property to IcdEnvironment to determine if SSL communication is enabled 2020-08-21 12:09:16 -04:00
Chris Cameron
0ece381939 Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8 2020-08-21 12:06:54 -04:00
Chris Cameron
4e1ed312a5 Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8 2020-08-17 10:17:51 -04:00
Drew Tingen
c8edac0f14 feat: Add extensions to raise events for common event args 2020-08-03 17:59:41 -04:00
Laura Gomez
00a75c6d72 feat: Adding Extensions methods in order to handle the switching of the animations. 2020-08-03 16:15:05 -04:00
Chris Cameron
75404e8b20 Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8 2020-07-29 10:24:18 -04:00
Chris Cameron
7ece973c2f fix: Workaround for logged XML format exceptions when failing to parse floats 2020-07-24 13:42:11 -04:00
Chris Cameron
d7e890d6c3 refactor: Removing redundant method 2020-07-17 13:57:38 -04:00
Chris Cameron
816f8984be feat: Added Collection extensions for setting and adding ranges of items 2020-07-16 12:13:57 -04:00
Drew Tingen
449bd41cb3 feat: Adding GetPropertyInfo test 2020-07-14 14:38:03 -04:00
Drew Tingen
8b53700c58 refactor: Use new property get/set extensions 2020-07-14 14:16:46 -04:00
Drew Tingen
6d5eb07b1f chore: changelog 2020-07-14 14:09:12 -04:00
Drew Tingen
67445de8b9 feat: PropertyInfo extensions in 2008 to mimic overloads in NetStandard 2020-07-14 14:07:17 -04:00
Laura Gomez
e093a57172 refactor : supporting feature where sleeptime changes due to a meeting. 2020-07-14 13:29:46 -04:00
Laura Gomez
878784586f refactor : refactored Repeater for easier implementation in other classes. 2020-07-14 13:29:46 -04:00
150 changed files with 8100 additions and 1679 deletions

View File

@@ -6,6 +6,141 @@ 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
## [14.1.0] - 2021-01-21
### Added
- Added overload to GuidUtils that takes an enumerable of guids and combines them into a new deterministic guid
### Changed
- A SafeTimer constructor that executes the callback immediately now does this instead of waiting infinitely
## [14.0.0] - 2021-01-14
### Added
- Added Get and Set extensions to PropertyInfo in SIMPLSHARP to mimic overloads avaliable in NETSTANDARD
- Added Collection extensions for setting and adding ranges of items
- Added a method for getting the total number of seconds in a date
- Added extensions to raise events with common event args using the data directly
- Added property to IcdEnvironment to determine whether SSL communication is enabled
- Added IcdTimeZoneInfo, a very light implementation of System.TimeZoneInfo for the .NET Compact Framework
- Added ThreadedWorkerQueue - a threadsafe way to enqueue items and have a worker thread process them one at a time
- Added eDaysOfWeek flags enum
- Added support for reading the primitive type double to IcdXmlReader and XmlUtils
- Added ProcessorUtils.GetSystemStartTime() to get DateTime the system started instead of a TimeSpan
### Changed
- Repeater changed to use configured callbacks instead of a dumb event
- Scheduled action callbacks allow a TimeSpan to be returned to delay actions
- Handling a Crestron bug where File.Exists throws an exception on 4-Series instead of returning false
- Changed ProcessorUtils.ModelVersion to be a string, Crestron pulls model version from CrestronEnvironment
- For 4-series console outputs, replacing \n with \r\n to help console readability
- Changed RuntimeEnvironment to be 3 variables - Framework for Crestron vs Standard, CrestronSeries for 3 vs 4, and CrestronRuntimeEnvironment for Simpl vs SimplSharpPro vs Server
## [13.0.0] - 2020-09-03
### Added
- Added util methods for removing duplicate whitespace in strings

View 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));
}
}
}

View File

@@ -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
}
}

View 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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,18 @@
using System;
using ICD.Common.Utils.Comparers;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Comparers
{
[TestFixture]
public sealed class UndefinedVersionComparerTest
{
[Test]
public void CompareToTest()
{
Assert.AreEqual(0, UndefinedVersionComparer.Instance.Compare(new Version(0, 0), new Version(0, 0, 0, 0)));
Assert.AreEqual(-1, UndefinedVersionComparer.Instance.Compare(new Version(0, 0), new Version(1, 0, 0, 0)));
Assert.AreEqual(1, UndefinedVersionComparer.Instance.Compare(new Version(1, 0), new Version(0, 0, 0, 0)));
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using ICD.Common.Utils.Comparers;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Comparers
{
[TestFixture]
public sealed class UndefinedVersionEqualityComparerTest
{
[Test]
public void Equals()
{
Assert.IsTrue(UndefinedVersionEqualityComparer.Instance.Equals(new Version(0, 0), new Version(0, 0, 0, 0)));
Assert.IsFalse(UndefinedVersionEqualityComparer.Instance.Equals(new Version(0, 0), new Version(1, 0, 0, 0)));
}
}
}

View File

@@ -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()
{
@@ -290,5 +301,17 @@ namespace ICD.Common.Utils.Tests
}
#endregion
#region Formatting
[TestCase(eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | (eTestFlagsEnum)8,
"A, B, C, 8")]
public void ToStringUndefinedTest(eTestFlagsEnum value, string expected)
{
string toString = EnumUtils.ToStringUndefined(value);
Assert.AreEqual(expected, toString);
}
#endregion
}
}

View File

@@ -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());
}
}
}

View File

@@ -537,7 +537,7 @@ namespace ICD.Common.Utils.Tests.Extensions
[Test]
public void ConsolidateComparerTest()
{
string[] sequence = new[] {"A", "B", "B", "C"}.Consolidate(Comparer<string>.Default).ToArray();
string[] sequence = new[] {"A", "B", "B", "C"}.Consolidate(EqualityComparer<string>.Default).ToArray();
Assert.AreEqual(3, sequence.Length, StringUtils.ArrayFormat(sequence));
Assert.AreEqual("A", sequence[0]);

View File

@@ -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

View File

@@ -192,6 +192,41 @@ namespace ICD.Common.Utils.Tests.Extensions
}
[Test]
public void GetPropertyInfoTest()
{
var testClass = new PropertyTestClass();
object instance;
// Test GetPropertyInfo at various levels
Assert.AreEqual(testClass.GetType().GetProperty("PropertyString"),
testClass.GetPropertyInfo(out instance, "PropertyString"),
"First level property not expected value");
Assert.AreEqual(testClass, instance, "Unexpected property parent");
Assert.AreEqual(testClass.InternalClass.GetType().GetProperty("InternalPropertyString"),
testClass.GetPropertyInfo(out instance, "InternalClass", "InternalPropertyString"),
"Second level property not expected value");
Assert.AreEqual(testClass.InternalClass, instance, "Unexpected property parent");
Assert.AreEqual(testClass.InternalClass.DeepClass.GetType().GetProperty("DeepPropertyString"),
testClass.GetPropertyInfo(out instance, "InternalClass", "DeepClass", "DeepPropertyString"),
"Third level property not expected value");
Assert.AreEqual(testClass.InternalClass.DeepClass, instance, "Unexpected property parent");
// Property that doesn't exits should return null
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "DeepClass", "NonExistent"));
Assert.IsNull(instance);
Assert.IsNull(testClass.GetPropertyInfo(out instance, "FakeFirstLevel", "FakeSecondLevel" , "FakeThridLevel"));
Assert.IsNull(instance);
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "FakeSecondLevel", "ThirdLevelCanNotBeReal"));
Assert.IsNull(instance);
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "FakeSecondLevel"));
Assert.IsNull(instance);
Assert.IsNull(testClass.GetPropertyInfo(out instance, "FakeFirstLevel"));
Assert.IsNull(instance);
}
#endregion
}
}

View File

@@ -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());
}
}
}

View File

@@ -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>

View 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();
}
}
}

View 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();
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -250,5 +250,40 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(6, paths[62].ToArray()[1]);
Assert.AreEqual(62, paths[62].ToArray()[2]);
}
[Test]
public void BreadthFirstSearchPathsTest()
{
Dictionary<int, IEnumerable<int>> paths =
RecursionUtils.BreadthFirstSearchPaths(1, Graph)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
// Verify we visited all destinations
Assert.AreEqual(4, paths.Count);
Assert.IsTrue(paths.ContainsKey(1));
Assert.IsTrue(paths.ContainsKey(2));
Assert.IsTrue(paths.ContainsKey(3));
Assert.IsTrue(paths.ContainsKey(4));
// Verify path for destination 1
Assert.AreEqual(1, paths[1].ToArray().Length);
Assert.AreEqual(1, paths[1].ToArray()[0]);
// Verify path for destination 2
Assert.AreEqual(2, paths[2].ToArray().Length);
Assert.AreEqual(1, paths[2].ToArray()[0]);
Assert.AreEqual(2, paths[2].ToArray()[1]);
// Verify path for destination 3
Assert.AreEqual(2, paths[3].ToArray().Length);
Assert.AreEqual(1, paths[3].ToArray()[0]);
Assert.AreEqual(3, paths[3].ToArray()[1]);
// Verify path for destination 4
Assert.AreEqual(3, paths[4].ToArray().Length);
Assert.AreEqual(1, paths[4].ToArray()[0]);
Assert.AreEqual(2, paths[4].ToArray()[1]);
Assert.AreEqual(4, paths[4].ToArray()[2]);
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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")]

View 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
}
}

View File

@@ -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();
}

View 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);
}
}
}

View 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);
}
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,40 @@
using System.Linq;
using System.Text;
using ICD.Common.Properties;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronXmlLinq;
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
namespace ICD.Common.Utils
{
public static class AssemblyUtils
{
/// <summary>
/// Gets the process executable in the default application domain. In other application domains,
/// this is the first executable that was executed by ExecuteAssembly(String).
/// </summary>
/// <returns></returns>
[CanBeNull]
public static Assembly GetEntryAssembly()
{
#if SIMPLSHARP
string appDir = InitialParametersClass.ProgramDirectory.ToString();
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL)
return null;
string proginfo = IcdFile.ReadToEnd(IcdPath.Combine(appDir, "ProgramInfo.config"), Encoding.UTF8);
XDocument doc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n" + proginfo);
XElement entry = doc.Descendants("EntryPoint").FirstOrDefault();
return entry == null ? null : Assembly.Load(entry.Value);
#else
return Assembly.GetEntryAssembly();
#endif
}
}
}

View 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);
}
}
}

View File

@@ -2,6 +2,9 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
@@ -10,6 +13,9 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_KeyToValue;
@@ -32,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>
@@ -43,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

View File

@@ -0,0 +1,14 @@
using System;
namespace ICD.Common.Utils.Collections
{
// Poor-mans System.Collections.Specialized
// Delete when we finally drop Crestron 3-series.
public interface INotifyCollectionChanged
{
/// <summary>
/// Raised when the contents of the collection change, or the order changes.
/// </summary>
event EventHandler OnCollectionChanged;
}
}

View File

@@ -4,6 +4,9 @@ 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
{
@@ -11,6 +14,9 @@ namespace ICD.Common.Utils.Collections
/// A collection containing only unique items.
/// </summary>
/// <typeparam name="T"></typeparam>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class IcdHashSet<T> : ICollection<T>
{
private readonly Dictionary<T, object> m_Dict;

View File

@@ -1,207 +1,651 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
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);
}
}
}
}

View 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
}
}

View File

@@ -4,15 +4,21 @@ 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
{
/// <summary>
/// Provides a first-in first-out collection with enhanced insertion features.
/// </summary>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#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
@@ -40,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

View File

@@ -1,198 +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;
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
/// </summary>
public sealed class RateLimitedEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
{
/// <summary>
/// Raised to handle to the next item in the queue.
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
private readonly SafeTimer m_DequeueTimer;
private readonly Queue<T> m_Queue;
private readonly SafeCriticalSection m_QueueSection;
#region Properties
/// <summary>
/// Gets/sets the time between dequeues in milliseconds.
/// </summary>
public long BetweenMilliseconds { get; set; }
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
bool ICollection.IsSynchronized { get { return true; } }
[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
}
}

View 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
}
}

View File

@@ -2,6 +2,9 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
@@ -10,6 +13,9 @@ namespace ICD.Common.Utils.Collections
/// are removed as new items are added.
/// </summary>
/// <typeparam name="TContents"></typeparam>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class ScrollQueue<TContents> : IEnumerable<TContents>, ICollection
{
private readonly LinkedList<TContents> m_Collection;
@@ -32,7 +38,9 @@ namespace ICD.Common.Utils.Collections
m_MaxSize = value;
TContents unused;
Trim(out unused);
while (Trim(out unused))
{
}
}
}
@@ -143,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++;
}
}

View File

@@ -4,6 +4,9 @@ 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
{
@@ -58,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.
@@ -108,6 +111,9 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class WeakKeyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<WeakKeyReference<TKey>, TValue> m_Dictionary;

View File

@@ -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())
{

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Comparers
{
/// <summary>
/// Undefined Versions have a value of 0.0.-1.-1
/// This comparer Maxs Versions to 0.0.0.0
/// </summary>
public sealed class UndefinedVersionComparer : IComparer<Version>
{
private static UndefinedVersionComparer s_Instance;
public static UndefinedVersionComparer Instance
{
get { return s_Instance = s_Instance ?? new UndefinedVersionComparer(); }
}
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());
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Comparers
{
/// <summary>
/// Undefined Versions have a value of 0.0.-1.-1
/// This comparer Maxs Versions to 0.0.0.0
/// </summary>
public sealed class UndefinedVersionEqualityComparer : IEqualityComparer<Version>
{
private static UndefinedVersionEqualityComparer s_Instance;
public static UndefinedVersionEqualityComparer Instance
{
get { return s_Instance = s_Instance ?? new UndefinedVersionEqualityComparer(); }
}
public bool Equals(Version x, Version y)
{
return x.ClearUndefined()
.Equals(y.ClearUndefined());
}
public int GetHashCode(Version version)
{
return version.ClearUndefined()
.GetHashCode();
}
}
}

310
ICD.Common.Utils/Csv/Csv.cs Normal file
View 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&lt;string[]&gt;, 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
}
}

View 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
}
}

View 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'
};
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.IO;
@@ -12,6 +11,10 @@ namespace ICD.Common.Utils.Csv
private readonly IcdTextWriter m_Writer;
private readonly CsvWriterSettings m_Settings;
/// <summary>
/// Are we currently at the beginning of a new line?
/// </summary>
private bool m_NewLine;
#region Properties
@@ -27,7 +30,8 @@ namespace ICD.Common.Utils.Csv
/// <summary>
/// Constructor.
/// </summary>
public CsvWriter([NotNull] IcdTextWriter writer, [NotNull] CsvWriterSettings settings, [NotNull] params string[] header)
public CsvWriter([NotNull] IcdTextWriter writer,
[NotNull] CsvWriterSettings settings)
{
if (writer == null)
throw new ArgumentNullException("writer");
@@ -35,46 +39,9 @@ namespace ICD.Common.Utils.Csv
if (settings == null)
throw new ArgumentNullException("settings");
if (header == null)
throw new ArgumentNullException("header");
m_NewLine = true;
m_Writer = writer;
m_Settings = settings;
if (header.Any())
AppendRow(header);
}
/// <summary>
/// Deconstructor.
/// </summary>
~CsvWriter()
{
Dispose();
}
/// <summary>
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
/// </summary>
/// <param name="writer"></param>
/// <param name="settings"></param>
/// <param name="header"></param>
/// <returns></returns>
[PublicAPI]
public static CsvWriter Create([NotNull] IcdTextWriter writer, [NotNull] CsvWriterSettings settings,
[NotNull] params string[] header)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (settings == null)
throw new ArgumentNullException("settings");
if (header == null)
throw new ArgumentNullException("header");
return new CsvWriter(writer, settings, header);
}
#endregion
@@ -86,17 +53,6 @@ namespace ICD.Common.Utils.Csv
#region Methods
/// <summary>
/// Calls ToString() for each item and adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params object[] row)
{
foreach (object value in row)
AppendValue(value);
AppendNewline();
}
/// <summary>
/// Adds the row to the builder.

View File

@@ -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)

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
@@ -14,7 +15,10 @@ namespace ICD.Common.Utils
public static class EnumUtils
{
private static readonly Dictionary<Type, object[]> s_EnumValuesCache;
private static readonly SafeCriticalSection s_EnumValuesCacheSection;
private static readonly Dictionary<Type, Dictionary<object, object[]>> s_EnumFlagsCache;
private static readonly SafeCriticalSection s_EnumFlagsCacheSection;
/// <summary>
/// Static constructor.
@@ -22,7 +26,9 @@ namespace ICD.Common.Utils
static EnumUtils()
{
s_EnumValuesCache = new Dictionary<Type, object[]>();
s_EnumValuesCacheSection = new SafeCriticalSection();
s_EnumFlagsCache = new Dictionary<Type, Dictionary<object, object[]>>();
s_EnumFlagsCacheSection = new SafeCriticalSection();
}
#region Validation
@@ -58,6 +64,7 @@ namespace ICD.Common.Utils
/// <returns></returns>
public static bool IsEnum<T>(T value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
return value != null && IsEnumType(value.GetType());
}
@@ -167,7 +174,7 @@ namespace ICD.Common.Utils
throw new ArgumentNullException("type");
// Reflection is slow and this method is called a lot, so we cache the results.
return s_EnumValuesCache.GetOrAddNew(type, () => GetValuesUncached(type).ToArray());
return s_EnumValuesCacheSection.Execute(() => s_EnumValuesCache.GetOrAddNew(type, () => GetValuesUncached(type).ToArray()));
}
/// <summary>
@@ -191,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>
@@ -345,10 +352,11 @@ namespace ICD.Common.Utils
/// <returns></returns>
public static IEnumerable<object> GetFlags(Type type, object value)
{
return 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>
@@ -440,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>
@@ -756,5 +777,39 @@ namespace ICD.Common.Utils
}
#endregion
#region Formatting
/// <summary>
/// Builds a comma delimited string of the defined enum flags, followed by the numeric remainder.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static string ToStringUndefined<T>(T value)
where T : struct, IConvertible
{
if (!IsFlagsEnum<T>())
return value.ToString();
long remainder = (int)(object)value;
StringBuilder output = new StringBuilder();
string format = "{0}";
foreach (T flag in GetFlagsExceptNone(value))
{
output.AppendFormat(format, flag);
remainder -= (int)(object)flag;
format = ", {0}";
}
if (remainder != 0)
output.AppendFormat(", {0}", remainder);
return output.ToString();
}
#endregion
}
}

View File

@@ -1,4 +1,8 @@
namespace ICD.Common.Utils.EventArguments
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public sealed class BoolEventArgs : GenericEventArgs<bool>
{
@@ -20,4 +24,18 @@
{
}
}
public static class BoolEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<BoolEventArgs> extends, object sender, bool data)
{
extends.Raise(sender, new BoolEventArgs(data));
}
}
}

View File

@@ -1,4 +1,6 @@
using ICD.Common.Properties;
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -9,4 +11,18 @@ namespace ICD.Common.Utils.EventArguments
{
}
}
public static class CharEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<CharEventArgs> extends, object sender, char data)
{
extends.Raise(sender, new CharEventArgs(data));
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -13,4 +15,18 @@ namespace ICD.Common.Utils.EventArguments
{
}
}
public static class DateTimeEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<DateTimeEventArgs> extends, object sender, DateTime data)
{
extends.Raise(sender, new DateTimeEventArgs(data));
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -12,4 +14,18 @@ namespace ICD.Common.Utils.EventArguments
{
}
}
public static class DateTimeNullableEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<DateTimeNullableEventArgs> extends, object sender, DateTime? data)
{
extends.Raise(sender, new DateTimeNullableEventArgs(data));
}
}
}

View File

@@ -1,4 +1,8 @@
namespace ICD.Common.Utils.EventArguments
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public sealed class FloatEventArgs : GenericEventArgs<float>
{
@@ -11,4 +15,18 @@
{
}
}
public static class FloatEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<FloatEventArgs> extends, object sender, float data)
{
extends.Raise(sender, new FloatEventArgs(data));
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -25,4 +27,18 @@ namespace ICD.Common.Utils.EventArguments
m_Data = data;
}
}
public static class GenericEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise<T>([CanBeNull]this EventHandler<GenericEventArgs<T>> extends, object sender, T data)
{
extends.Raise(sender, new GenericEventArgs<T>(data));
}
}
}

View File

@@ -1,4 +1,8 @@
namespace ICD.Common.Utils.EventArguments
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public sealed class IntEventArgs : GenericEventArgs<int>
{
@@ -10,4 +14,18 @@
{
}
}
public static class IntEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<IntEventArgs> extends, object sender, int data)
{
extends.Raise(sender, new IntEventArgs(data));
}
}
}

View File

@@ -1,4 +1,8 @@
namespace ICD.Common.Utils.EventArguments
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public sealed class StringEventArgs : GenericEventArgs<string>
{
@@ -19,4 +23,18 @@
{
}
}
public static class StringEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<StringEventArgs> extends, object sender, string data)
{
extends.Raise(sender, new StringEventArgs(data));
}
}
}

View File

@@ -1,4 +1,6 @@
using ICD.Common.Properties;
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -13,4 +15,18 @@ namespace ICD.Common.Utils.EventArguments
{
}
}
public static class UShortEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="data"></param>
public static void Raise([CanBeNull]this EventHandler<UShortEventArgs> extends, object sender, ushort data)
{
extends.Raise(sender, new UShortEventArgs(data));
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
@@ -26,4 +27,19 @@ namespace ICD.Common.Utils.EventArguments
#endregion
}
public static class XmlRecursionEventArgsExtensions
{
/// <summary>
/// Raises the event safely. Simply skips if the handler is null.
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="outer"></param>
/// <param name="path"></param>
public static void Raise([CanBeNull]this EventHandler<XmlRecursionEventArgs> extends, object sender, string outer, string[] path)
{
extends.Raise(sender, new XmlRecursionEventArgs(outer, path));
}
}
}

View File

@@ -29,17 +29,16 @@ namespace ICD.Common.Utils.Extensions
#endif
.CodeBase;
#if !SIMPLSHARP
if (string.IsNullOrEmpty(path))
{
#if STANDARD
path = extends.Location;
#endif
}
else
const string prefix = @"file:/";
if (path != null && path.StartsWith(prefix))
{
const string prefix = @"file:///";
if (path.StartsWith(prefix))
path = path.Substring(prefix.Length);
Uri uri = new Uri(path);
path = uri.LocalPath;
}
return IcdFile.Exists(path) ? path : null;

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
public static class CollectionExtensions
{
/// <summary>
/// Clears the collection and adds the given range of items.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="range"></param>
public static void SetRange<T>([NotNull] this ICollection<T> extends, [NotNull] IEnumerable<T> range)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (range == null)
throw new ArgumentNullException("range");
extends.Clear();
extends.AddRange(range);
}
/// <summary>
/// Adds the given range of items to the collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="range"></param>
public static void AddRange<T>([NotNull] this ICollection<T> extends, [NotNull] IEnumerable<T> range)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (range == null)
throw new ArgumentNullException("range");
foreach (T item in range)
extends.Add(item);
}
}
}

View File

@@ -224,6 +224,15 @@ namespace ICD.Common.Utils.Extensions
minutes = MathUtils.Modulus(minutes + extends.Minute, 60);
return new DateTime(extends.Year, extends.Month, extends.Day, extends.Hour, minutes, extends.Second, extends.Millisecond);
}
/// <summary>
/// Returns the total number of seconds since DateTime.MinValue
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static double GetTotalSeconds(this DateTime extends)
{
return (extends - DateTime.MinValue).TotalSeconds;
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
@@ -60,7 +62,23 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Returns the enum value as a
/// 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>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
@@ -71,5 +89,37 @@ namespace ICD.Common.Utils.Extensions
{
return (ushort)(object)extends;
}
/// <summary>
/// Builds a comma delimited string of the defined enum flags, followed by the numeric remainder.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToStringUndefined<T>(this T extends)
where T : struct, IConvertible
{
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];
}
}
}

View File

@@ -234,10 +234,10 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="comparer"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static bool SequenceEqual<T>([NotNull] this IEnumerable<T> extends, [NotNull] IEnumerable<T> other,
[NotNull] IEqualityComparer<T> comparer)
[NotNull] Func<T, T, bool> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -245,31 +245,8 @@ namespace ICD.Common.Utils.Extensions
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
return extends.SequenceEqual(other, comparer.Equals);
}
/// <summary>
/// Compares the two sequences for identical values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static bool SequenceEqual<T>([NotNull] this IEnumerable<T> extends, [NotNull] IEnumerable<T> other,
[NotNull] Func<T, T, bool> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
if (predicate == null)
throw new ArgumentNullException("predicate");
// Simple count check
ICollection<T> a = extends as ICollection<T>;
@@ -286,7 +263,7 @@ namespace ICD.Common.Utils.Extensions
while (hasFirst && hasSecond)
{
if (!comparer(firstPos.Current, secondPos.Current))
if (!predicate(firstPos.Current, secondPos.Current))
return false;
hasFirst = firstPos.MoveNext();
@@ -1113,6 +1090,22 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
public static T Random<T>([NotNull] this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
Random random = new Random(Guid.NewGuid().GetHashCode());
return extends.Random(random);
}
/// <summary>
/// Returns a random item from the given sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="random"></param>
/// <returns></returns>
public static T Random<T>([NotNull] this IEnumerable<T> extends, [NotNull] Random random)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -1122,7 +1115,6 @@ namespace ICD.Common.Utils.Extensions
if (sequence.Count == 0)
throw new InvalidOperationException("Sequence is empty.");
Random random = new Random(Guid.NewGuid().GetHashCode());
int index = random.Next(0, sequence.Count);
return sequence[index];
@@ -1547,23 +1539,23 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return Consolidate(extends, Comparer<T>.Default);
return Consolidate(extends, EqualityComparer<T>.Default);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<T> Consolidate<T>([NotNull] this IEnumerable<T> extends,
[NotNull] IComparer<T> comparer)
[NotNull] IEqualityComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -1571,33 +1563,72 @@ namespace ICD.Common.Utils.Extensions
if (comparer == null)
throw new ArgumentNullException("comparer");
return ConsolidateIterator(extends, comparer);
return extends.Consolidate(e => e, comparer);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="predicate"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<TKey> Consolidate<TKey, TValue>([NotNull] this IEnumerable<TKey> extends,
[NotNull] Func<TKey, TValue> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
return extends.Consolidate(predicate, EqualityComparer<TValue>.Default);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="predicate"></param>
/// <param name="comparer"></param>
/// <returns></returns>
private static IEnumerable<T> ConsolidateIterator<T>([NotNull] IEnumerable<T> sequence,
[NotNull] IComparer<T> comparer)
[PublicAPI]
public static IEnumerable<TKey> Consolidate<TKey, TValue>([NotNull] this IEnumerable<TKey> extends,
[NotNull] Func<TKey, TValue> predicate,
[NotNull] IEqualityComparer<TValue> comparer)
{
bool first = true;
T last = default(T);
if (extends == null)
throw new ArgumentNullException("extends");
foreach (T item in sequence)
if (predicate == null)
throw new ArgumentNullException("predicate");
if (comparer == null)
throw new ArgumentNullException("comparer");
bool first = true;
TValue last = default(TValue);
foreach (TKey item in extends)
{
if (!first && comparer.Compare(last, item) == 0)
TValue value = predicate(item);
if (!first && comparer.Equals(last, value))
continue;
first = false;
last = item;
last = value;
yield return item;
}

View File

@@ -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);
}

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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>

View File

@@ -129,6 +129,38 @@ namespace ICD.Common.Utils.Extensions
}
#endif
#if SIMPLSHARP
/// <summary>
/// Returns the value of the property
/// Only needed for SIMPLSHARP since newer .Net contains this already
/// </summary>
/// <param name="extends"></param>
/// <param name="obj"></param>
/// <returns>The property value for the obj parameter.</returns>
public static object GetValue([NotNull] this PropertyInfo extends, object obj)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.GetValue(obj, null);
}
/// <summary>
/// Sets the value of the property
/// Only needed for SIMPLSHARP since newer .Net contains this already
/// </summary>
/// <param name="extends"></param>
/// <param name="obj"></param>
/// <param name="value"></param>
public static void SetValue([NotNull] this PropertyInfo extends, object obj, object value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.SetValue(obj, value, null);
}
#endif
/// <summary>
/// Sets the value of a property
/// Traverses the path to access properties nested in other properties
@@ -141,37 +173,17 @@ namespace ICD.Common.Utils.Extensions
{
if (extends == null)
throw new ArgumentNullException("extends");
if (path == null)
throw new ArgumentNullException("path");
object currentObject = extends;
object instance;
PropertyInfo property = extends.GetPropertyInfo(out instance, path);
//Grab property values until the last item in the path
for (int i = 0; i < path.Length - 1; i++)
{
PropertyInfo info =
currentObject.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetProperty(path[i]);
if (info == null)
return false;
currentObject = info.GetValue(currentObject, null);
}
//Set the property to the value
PropertyInfo finalPath =
currentObject.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetProperty(path[path.Length - 1]);
if (finalPath == null)
if (property == null)
return false;
finalPath.SetValue(currentObject, value, null);
property.SetValue(instance, value);
return true;
}
@@ -180,37 +192,59 @@ namespace ICD.Common.Utils.Extensions
/// Traverses the path to access properties nested in other properties
/// </summary>
/// <param name="extends"></param>
/// <param name="instance"></param>
/// <param name="path"></param>
/// <returns>true if property get was successful, false if the property was not found</returns>
/// <returns></returns>
[CanBeNull]
public static PropertyInfo GetPropertyInfo([NotNull] this object extends, [NotNull] params string[] path)
public static PropertyInfo GetPropertyInfo([NotNull] this object extends, out object instance, [NotNull] params string[] path)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (path == null)
throw new ArgumentNullException("path");
object currentObject = extends;
instance = extends;
//Grab property values until the last item in the path
// Grab properties until the last item in the path
for (int i = 0; i < path.Length - 1; i++)
{
PropertyInfo info = currentObject.GetType()
PropertyInfo info =
instance.GetType()
#if SIMPLSHARP
.GetCType()
.GetCType()
#endif
.GetProperty(path[i]);
.GetProperty(path[i],
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (info == null)
{
instance = null;
return null;
currentObject = info.GetValue(currentObject, null);
}
instance = info.GetValue(instance);
}
//Set the property to the value
return currentObject.GetType()
// Set the property to the value
PropertyInfo output =
instance.GetType()
#if SIMPLSHARP
.GetCType()
.GetCType()
#endif
.GetProperty(path[path.Length - 1]);
.GetProperty(path[path.Length - 1],
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (output == null)
instance = null;
return output;
}
/// <summary>
@@ -221,35 +255,23 @@ namespace ICD.Common.Utils.Extensions
/// <param name="value"></param>
/// <param name="path"></param>
/// <returns>true if property get was successful, false if the property was not found</returns>
public static bool GetProperty([NotNull] this object extends, [CanBeNull] out object value, [NotNull] params string[] path)
public static bool GetProperty<T>([NotNull] this object extends, [CanBeNull] out T value, [NotNull] params string[] path)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (path == null)
throw new ArgumentNullException("path");
value = null;
value = default(T);
object currentObject = extends;
object instance;
PropertyInfo property = extends.GetPropertyInfo(out instance, path);
//Grab property values
foreach (string node in path)
{
PropertyInfo info =
currentObject.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetProperty(node);
if (property == null)
return false;
if (info == null)
return false;
currentObject = info.GetValue(currentObject, null);
}
//set the last value and return
value = currentObject;
value = (T)property.GetValue(instance);
return true;
}
@@ -259,7 +281,7 @@ namespace ICD.Common.Utils.Extensions
return CallMethod(extends, methodName, out value, new object[] { });
}
public static bool CallMethod([NotNull] this object extends, string methodName, [CanBeNull] out object value)
public static bool CallMethod<T>([NotNull] this object extends, string methodName, [CanBeNull] out T value)
{
return CallMethod(extends, methodName, out value, new object[] { });
}
@@ -270,25 +292,31 @@ namespace ICD.Common.Utils.Extensions
return CallMethod(extends, methodName, out value, parameters);
}
public static bool CallMethod([NotNull] this object extends, string methodName, [CanBeNull] out object value, [NotNull] params object[] parameters)
public static bool CallMethod<T>([NotNull] this object extends, string methodName, [CanBeNull] out T value, [NotNull] params object[] parameters)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (parameters == null)
throw new ArgumentNullException("parameters");
value = false;
value = default(T);
MethodInfo method =
extends.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetMethod(methodName);
.GetMethod(methodName,
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (method == null)
return false;
value = method.Invoke(extends, parameters);
value = (T)method.Invoke(extends, parameters);
return true;
}
@@ -307,7 +335,7 @@ namespace ICD.Common.Utils.Extensions
return eventHandlerType == typeof(EventHandler)
? typeof(EventArgs)
: eventHandlerType.GetInnerGenericTypes(typeof(EventHandler<>))
.First();
.Single();
}
}
}

View 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

View File

@@ -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>

View File

@@ -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>
@@ -83,5 +110,29 @@ namespace ICD.Common.Utils.Extensions
minutes = MathUtils.Modulus(minutes + extends.Minutes, 60);
return new TimeSpan(extends.Days, extends.Hours, minutes, extends.Seconds, extends.Milliseconds);
}
/// <summary>
/// Adjusts the given timespan by the UTC offset.
/// e.g. EST is UTC-5, a TimeSpan of 9:00:00 would result in a universal TimeSpan of 14:00:00
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static TimeSpan ToUniversalTime(this TimeSpan extends)
{
TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset(IcdEnvironment.GetLocalTime());
return extends - offset;
}
/// <summary>
/// Adjusts the given timespan from the UTC offset.
/// e.g. EST is UTC-5, a TimeSpan of 9:00:00 would result in a local TimeSpan of 4:00:00
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static TimeSpan ToLocalTime(this TimeSpan extends)
{
TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset(IcdEnvironment.GetLocalTime());
return extends + offset;
}
}
}

View File

@@ -60,11 +60,17 @@ namespace ICD.Common.Utils.Extensions
};
private static readonly Dictionary<Type, Type[]> s_TypeAllTypes;
private static readonly SafeCriticalSection s_TypeAllTypesSection;
private static readonly Dictionary<Type, Type[]> s_TypeBaseTypes;
private static readonly SafeCriticalSection s_TypeBaseTypesSection;
private static readonly Dictionary<Type, Type[]> s_TypeImmediateInterfaces;
private static readonly SafeCriticalSection s_TypeImmediateInterfacesSection;
private static readonly Dictionary<Type, Type[]> s_TypeMinimalInterfaces;
private static readonly SafeCriticalSection s_TypeMinimalInterfacesSection;
private static readonly Dictionary<Type, string> s_TypeToMinimalName;
private static readonly SafeCriticalSection s_TypeToMinimalNameSection;
private static readonly Dictionary<Type, string> s_TypeToNameWithoutAssemblyDetails;
private static readonly SafeCriticalSection s_TypeToNameWithoutAssemblyDetailsSection;
/// <summary>
/// Static constructor.
@@ -72,11 +78,17 @@ namespace ICD.Common.Utils.Extensions
static TypeExtensions()
{
s_TypeAllTypes = new Dictionary<Type, Type[]>();
s_TypeAllTypesSection = new SafeCriticalSection();
s_TypeBaseTypes = new Dictionary<Type, Type[]>();
s_TypeBaseTypesSection = new SafeCriticalSection();
s_TypeImmediateInterfaces = new Dictionary<Type, Type[]>();
s_TypeImmediateInterfacesSection = new SafeCriticalSection();
s_TypeMinimalInterfaces = new Dictionary<Type, Type[]>();
s_TypeMinimalInterfacesSection = new SafeCriticalSection();
s_TypeToMinimalName = new Dictionary<Type, string>();
s_TypeToMinimalNameSection = new SafeCriticalSection();
s_TypeToNameWithoutAssemblyDetails = new Dictionary<Type, string>();
s_TypeToNameWithoutAssemblyDetailsSection = new SafeCriticalSection();
}
/// <summary>
@@ -246,16 +258,24 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
Type[] types;
if (!s_TypeAllTypes.TryGetValue(extends, out types))
s_TypeAllTypesSection.Enter();
try
{
types = extends.GetBaseTypes()
.Concat(extends.GetInterfaces())
.Prepend(extends)
.ToArray();
s_TypeAllTypes[extends] = types;
if (s_TypeAllTypes.TryGetValue(extends, out types))
return types;
}
finally
{
s_TypeAllTypesSection.Leave();
}
types = extends.GetBaseTypes()
.Concat(extends.GetInterfaces())
.Prepend(extends)
.ToArray();
s_TypeAllTypesSection.Execute(() => s_TypeAllTypes[extends] = types);
return types;
}
@@ -271,11 +291,20 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
Type[] types;
if (!s_TypeBaseTypes.TryGetValue(extends, out types))
s_TypeBaseTypesSection.Enter();
try
{
types = GetBaseTypesIterator(extends).ToArray();
s_TypeBaseTypes[extends] = types;
if (s_TypeBaseTypes.TryGetValue(extends, out types))
return types;
}
finally
{
s_TypeBaseTypesSection.Leave();
}
types = GetBaseTypesIterator(extends).ToArray();
s_TypeBaseTypesSection.Execute(() => s_TypeBaseTypes[extends] = types);
return types;
}
@@ -309,20 +338,30 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
Type[] immediateInterfaces;
if (!s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
s_TypeImmediateInterfacesSection.Enter();
try
{
IEnumerable<Type> allInterfaces = extends.GetInterfaces();
IEnumerable<Type> childInterfaces =
extends.GetAllTypes()
.Except(extends)
.SelectMany(t => t.GetImmediateInterfaces())
.Distinct();
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
s_TypeImmediateInterfaces[extends] = immediateInterfaces;
if (s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
return immediateInterfaces;
}
finally
{
s_TypeImmediateInterfacesSection.Leave();
}
IEnumerable<Type> allInterfaces = extends.GetInterfaces();
IEnumerable<Type> childInterfaces =
extends.GetAllTypes()
.Except(extends)
.SelectMany(t => t.GetImmediateInterfaces())
.Distinct();
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
s_TypeImmediateInterfacesSection.Execute(() => s_TypeImmediateInterfaces[extends] = immediateInterfaces);
return immediateInterfaces;
}
@@ -339,14 +378,24 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
Type[] minimalInterfaces;
if (!s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
{
Type[] allInterfaces = extends.GetInterfaces();
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
s_TypeMinimalInterfaces[extends] = minimalInterfaces;
s_TypeMinimalInterfacesSection.Enter();
try
{
if (s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
return minimalInterfaces;
}
finally
{
s_TypeMinimalInterfacesSection.Leave();
}
Type[] allInterfaces = extends.GetInterfaces();
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
s_TypeMinimalInterfacesSection.Execute(() => s_TypeMinimalInterfaces[extends] = minimalInterfaces);
return minimalInterfaces;
}
@@ -379,47 +428,44 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[NotNull]
public static string GetMinimalName([NotNull]this Type extends)
public static string GetMinimalName([NotNull] this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToMinimalName.TryGetValue(extends, out name))
s_TypeToMinimalNameSection.Enter();
try
{
// Generics are a pain
if (extends.IsGenericType)
{
string nameWithoutAssemblyDetails = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
int genericStart = nameWithoutAssemblyDetails.IndexOf('[');
if (genericStart < 0)
{
name = nameWithoutAssemblyDetails;
}
else
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = nameWithoutAssemblyDetails.LastIndexOf(']');
name = new StringBuilder().Append(nameWithoutAssemblyDetails, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(nameWithoutAssemblyDetails, genericEnd - 1,
nameWithoutAssemblyDetails.Length - genericEnd + 1)
.ToString();
}
}
else
{
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
}
s_TypeToMinimalName[extends] = name;
if (s_TypeToMinimalName.TryGetValue(extends, out name))
return name;
}
finally
{
s_TypeToMinimalNameSection.Leave();
}
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
// Generics are a pain
if (extends.IsGenericType)
{
int genericStart = name.IndexOf('[');
if (genericStart >= 0)
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = name.LastIndexOf(']');
name = new StringBuilder().Append(name, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(name, genericEnd - 1,
name.Length - genericEnd + 1)
.ToString();
}
}
s_TypeToMinimalNameSection.Execute(() => s_TypeToMinimalName[extends] = name);
return name;
}
@@ -436,11 +482,22 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
s_TypeToNameWithoutAssemblyDetailsSection.Enter();
try
{
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName);
s_TypeToNameWithoutAssemblyDetails[extends] = name;
if (s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
return name;
}
finally
{
s_TypeToNameWithoutAssemblyDetailsSection.Leave();
}
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName);
s_TypeToNameWithoutAssemblyDetailsSection.Execute(() => s_TypeToNameWithoutAssemblyDetails[extends] = name);
return name;
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
namespace ICD.Common.Utils.Extensions
{
public static class VersionExtensions
{
/// <summary>
/// Creates a new Version using 0 instead of -1 for omitted quadrants.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static Version ClearUndefined(this Version extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return new Version(Math.Max(0, extends.Major),
Math.Max(0, extends.Minor),
Math.Max(0, extends.Build),
Math.Max(0, extends.Revision));
}
/// <summary>
/// Formats the version to X.XXX.XXXX
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToCrestronString(this Version extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return string.Format("{0}.{1:D3}.{2:D4}", extends.Major, extends.Minor, extends.Build);
}
}
}

View File

@@ -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;
}
@@ -940,7 +938,11 @@ namespace ICD.Common.Utils.Globalization
return cultureInfo;
}
public static CultureInfo[] GetCultures(CultureTypes types)
public
#if !SIMPLSHARP
new
#endif
static CultureInfo[] GetCultures(CultureTypes types)
{
return s_DictAvailableCulturesByName.Where(de => (de.Value & types) != 0)
.Select(de => new IcdCultureInfo(de.Key))

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
@@ -30,5 +32,13 @@ namespace ICD.Common.Utils
return new Guid(aBytes);
}
/// <summary>
/// Combines the guids in the given order to make a new, deterministic guid.
/// </summary>
public static Guid Combine(IEnumerable<Guid> guids)
{
return guids.AggregateOrDefault(Combine, default(Guid));
}
}
}

View File

@@ -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,17 +30,24 @@
<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">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TimeZones.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -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>
@@ -75,19 +75,29 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AnsiUtils.cs" />
<Compile Include="AssemblyUtils.cs" />
<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" />
<Compile Include="Email\EmailClient.cs" />
<Compile Include="Email\eMailErrorCode.cs" />
<Compile Include="Email\EmailStringCollection.cs" />
@@ -109,7 +119,15 @@
<None Include="CultureInfo.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Compile Include="ObfuscationSettings.cs" />
<Compile Include="Extensions\CollectionExtensions.cs" />
<Compile Include="Extensions\RegistryExtensions.cs" />
<Compile Include="Extensions\VersionExtensions.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="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\CultureInfoExtensions.cs" />
@@ -200,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" />
@@ -216,14 +234,19 @@
<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" />
<Compile Include="Timers\SafeTimer.cs" />
<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" />
<Compile Include="Xml\AbstractGenericXmlConverter.cs" />
<Compile Include="Xml\AbstractXmlConverter.cs" />
<Compile Include="Xml\DefaultXmlConverter.cs" />
@@ -237,10 +260,9 @@
<Compile Include="Xml\XmlReaderExtensions.cs" />
<Compile Include="Xml\XmlUtils.cs" />
<None Include="Properties\ControlSystem.cfg" />
<None Include="TimeZones.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</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>

View File

@@ -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)

View File

@@ -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
}

View File

@@ -12,7 +12,7 @@ namespace ICD.Common.Utils.IO
public static class IcdFile
{
[PublicAPI]
public static string ReadToEnd(string path, Encoding encoding)
public static string ReadToEnd([NotNull] string path, [NotNull] Encoding encoding)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -28,7 +28,7 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static DateTime GetLastWriteTime(string path)
public static DateTime GetLastWriteTime([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -37,16 +37,25 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static bool Exists(string path)
public static bool Exists([CanBeNull] string path)
{
// Consistent with Net Standard
if (path == null)
throw new ArgumentNullException("path");
return false;
return File.Exists(path);
try
{
return File.Exists(path);
}
// Crestron's AdjustPathForMono method throws an exception that is inconsistent with Net Standard...
catch (Exception)
{
return false;
}
}
[PublicAPI]
public static void Copy(string pathFrom, string pathTo)
public static void Copy([NotNull] string pathFrom, [NotNull] string pathTo)
{
if (pathFrom == null)
throw new ArgumentNullException("pathFrom");
@@ -58,7 +67,7 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static void Delete(string path)
public static void Delete([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -89,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)
{
@@ -111,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
}
}

View File

@@ -15,7 +15,7 @@ namespace ICD.Common.Utils.IO
public static char AltDirectorySeparatorChar { get { return Path.AltDirectorySeparatorChar; } }
public static string GetFileName(string path)
public static string GetFileName([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -23,7 +23,7 @@ namespace ICD.Common.Utils.IO
return Path.GetFileName(path);
}
public static string GetFileNameWithoutExtension(string path)
public static string GetFileNameWithoutExtension([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -32,7 +32,7 @@ namespace ICD.Common.Utils.IO
}
[CanBeNull]
public static string GetDirectoryName(string path)
public static string GetDirectoryName([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -40,7 +40,7 @@ namespace ICD.Common.Utils.IO
return Path.GetDirectoryName(path);
}
public static string GetExtension(string path)
public static string GetExtension([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -48,7 +48,7 @@ namespace ICD.Common.Utils.IO
return Path.GetExtension(path);
}
public static string Combine(string a, string b)
public static string Combine([NotNull] string a, [NotNull] string b)
{
if (a == null)
throw new ArgumentNullException("a");
@@ -59,7 +59,7 @@ namespace ICD.Common.Utils.IO
return Path.Combine(a, b);
}
public static string ChangeExtension(string path, string ext)
public static string ChangeExtension([NotNull] string path, [NotNull] string ext)
{
if (path == null)
throw new ArgumentNullException("path");

View File

@@ -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();
}
}
}

View File

@@ -1,6 +1,6 @@
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
#else
using System.IO;
#endif

View File

@@ -1,9 +1,10 @@
using System;
using System.Linq;
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;
@@ -13,6 +14,7 @@ namespace ICD.Common.Utils
{
public static class IcdConsole
{
private const string NEWLINE = "\r\n";
public enum eAccessLevel
{
Operator = 0,
@@ -22,6 +24,57 @@ 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>
static IcdConsole()
{
s_Section = new SafeCriticalSection();
s_NewLineRegex = new Regex("(?!<\r)\n");
}
/// <summary>
/// Wraps CrestronConsole.ConsoleCommandResponse for S+ compatibility.
/// </summary>
@@ -30,7 +83,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void ConsoleCommandResponseLine(string message, params object[] args)
{
ConsoleCommandResponse(message + IcdEnvironment.NewLine, args);
ConsoleCommandResponse(message + NEWLINE, args);
}
/// <summary>
@@ -41,11 +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);
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
message = FixLineEndings(message);
#if !NETSTANDARD
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
{
try
{
@@ -55,22 +114,36 @@ namespace ICD.Common.Utils
{
PrintLine(message);
}
return;
return;
}
#endif
PrintLine(message);
PrintLine(message);
}
public static void PrintLine(string message)
{
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
CrestronConsole.PrintLine(message);
s_Section.Enter();
string fixedMessage = FixLineEndings(message);
try
{
#if !NETSTANDARD
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
CrestronConsole.PrintLine(fixedMessage);
#else
Console.WriteLine(message);
Trace.WriteLine(AnsiUtils.StripAnsi(fixedMessage));
if (IsConsoleApp)
Console.WriteLine(fixedMessage);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message + IcdEnvironment.NewLine));
}
finally
{
s_Section.Leave();
}
OnConsolePrint.Raise(null, new StringEventArgs(message + NEWLINE));
}
public static void PrintLine(string message, params object[] args)
@@ -93,13 +166,28 @@ namespace ICD.Common.Utils
public static void Print(string message)
{
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
CrestronConsole.Print(message);
s_Section.Enter();
string fixedMessage = FixLineEndings(message);
try
{
#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
OnConsolePrint.Raise(null, new StringEventArgs(message));
}
finally
{
s_Section.Leave();
}
OnConsolePrint.Raise(null, new StringEventArgs(fixedMessage));
}
public static void Print(string message, params object[] args)
@@ -122,9 +210,9 @@ namespace ICD.Common.Utils
public static bool SendControlSystemCommand(string command, ref string result)
{
#if SIMPLSHARP
// No console on VC4
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
#if !NETSTANDARD
// No console on Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
return CrestronConsole.SendControlSystemCommand(command, ref result);
@@ -135,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.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
return false;
// No console in Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
if (CrestronConsole.ConsoleRegistered)
@@ -149,5 +241,21 @@ namespace ICD.Common.Utils
return false;
#endif
}
/// <summary>
/// Code running on SimplSharpProMono uses \n for newline (due to linux environment),
/// Which causes console output to be unreadable on most SSH clients. This converts those
/// endings to \r\n, since Crestron's SSH server doesn't do it automatically.
/// This is a hack until Crestron fixes their SSH server.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string FixLineEndings(string input)
{
if (IcdEnvironment.CrestronSeries != IcdEnvironment.eCrestronSeries.FourSeries)
return input;
return s_NewLineRegex.Replace(input, NEWLINE);
}
}
}

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
@@ -14,14 +14,10 @@ namespace ICD.Common.Utils
/// </summary>
private const string INVALID_VALUE = "Invalid Value";
private static eRuntimeEnvironment s_RuntimeEnvironment;
#region Properties
public static string NewLine { get { return CrestronEnvironment.NewLine; } }
public static eRuntimeEnvironment RuntimeEnvironment { get { return s_RuntimeEnvironment; } }
/// <summary>
/// Gets the network address(es) of the processor.
/// </summary>
@@ -122,6 +118,39 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Gets the SSL state of the processor.
/// </summary>
[PublicAPI]
public static bool SslEnabled
{
get
{
const CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET param =
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_SSL_OFF_STATUS;
const EthernetAdapterType type = EthernetAdapterType.EthernetLANAdapter;
try
{
short id = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(type);
if (id >= InitialParametersClass.NumberOfEthernetInterfaces)
return false;
string status = CrestronEthernetHelper.GetEthernetParameter(param, id);
if (!string.IsNullOrEmpty(status) && !status.Equals(INVALID_VALUE))
// GET_SSL_OFF_STATUS return OFF for SSL is Enabled and ON for SSL is disabled
return status == "OFF";
return false;
}
catch (ArgumentException)
{
return false;
}
}
}
/// <summary>
/// Gets the hostname of the processor.
/// </summary>
@@ -164,12 +193,18 @@ namespace ICD.Common.Utils
static IcdEnvironment()
{
// Cache the runtime environment
s_RuntimeEnvironment =
CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? eRuntimeEnvironment.SimplSharpProServer
: Type.GetType("Mono.Runtime") == null
? GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment)
: eRuntimeEnvironment.SimplSharpProMono;
s_Framework = eFramework.Crestron;
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;
CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler;
@@ -177,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();
@@ -227,19 +271,6 @@ namespace ICD.Common.Utils
}
}
private static eRuntimeEnvironment GetRuntimeEnvironment(Crestron.SimplSharp.eRuntimeEnvironment runtimeEnvironment)
{
switch (runtimeEnvironment)
{
case Crestron.SimplSharp.eRuntimeEnvironment.SIMPL:
return eRuntimeEnvironment.SimplSharp;
case Crestron.SimplSharp.eRuntimeEnvironment.SimplSharpPro:
return eRuntimeEnvironment.SimplSharpPro;
default:
throw new ArgumentOutOfRangeException("runtimeEnvironment");
}
}
#endregion
#region Private Methods

View File

@@ -1,20 +1,37 @@
#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
{
public static string NewLine { get { return Environment.NewLine; } }
/// <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;
public static eRuntimeEnvironment RuntimeEnvironment { get { return eRuntimeEnvironment.Standard; } }
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>
/// Gets the network address(es) of the processor.
@@ -73,12 +90,40 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Exists for parity with SimplSharp processors which have the ability to enable/disable SSL communication.
/// </summary>
[PublicAPI]
public static bool SslEnabled
{
get { return true; }
}
/// <summary>
/// Gets the hostname of the processor.
/// </summary>
[PublicAPI]
public static IEnumerable<string> Hostnames { get { yield return Dns.GetHostName(); } }
static IcdEnvironment()
{
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()
{
return DateTime.Now;
@@ -105,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

View File

@@ -7,17 +7,42 @@ namespace ICD.Common.Utils
public static partial class IcdEnvironment
{
/// <summary>
/// Enumeration to define the various runtime environments a module can run in.
/// Enumeration to define the various frameworks a module can run in.
/// </summary>
public enum eRuntimeEnvironment
public enum eFramework
{
SimplSharp,
SimplSharpPro,
SimplSharpProMono,
SimplSharpProServer,
Crestron,
Framework,
Standard
}
/// <summary>
/// Enumeration to define the Crestron series a module can run it
/// </summary>
public enum eCrestronSeries
{
Na, //Non-Crestron
ThreeSeries,
FourSeries
}
/// <summary>
/// Enumeration to define the various Crestron runtime environments a module can run in
/// </summary>
public enum eCrestronRuntimeEnvironment
{
Na, //Non-Crestron
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>
/// Enum for the Program Event Types
/// </summary>
@@ -50,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>
@@ -68,9 +123,27 @@ namespace ICD.Common.Utils
/// </summary>
public static event EventHandler OnSystemDateTimeChanged;
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>
@@ -100,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();
}
}

View File

@@ -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
};
@@ -160,28 +199,26 @@ namespace ICD.Common.Utils
if (args.Length > 0)
message = string.Format(message, args);
switch (IcdEnvironment.RuntimeEnvironment)
if (IcdEnvironment.Framework == IcdEnvironment.eFramework.Standard)
{
case IcdEnvironment.eRuntimeEnvironment.Standard:
// Prepend the exception type, append the stack trace
if (exception != null)
message = string.Format("{0}: {1}{2}{3}",
exception.GetType().Name,
message,
IcdEnvironment.NewLine,
exception.StackTrace);
// Prepend the exception type, append the stack trace
if (exception != null)
message = string.Format("{0}: {1}{2}{3}",
exception.GetType().Name,
message,
IcdEnvironment.NewLine,
exception.StackTrace);
// Prefix severity and time
string fixedSeverity = severity.Substring(0, Math.Min(6, severity.Length));
fixedSeverity = string.Format("{0,-6}", fixedSeverity);
message = string.Format("{0} - {1} - {2}", fixedSeverity, IcdEnvironment.GetLocalTime(), message);
break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
// Add an extra newline
message += IcdEnvironment.NewLine;
break;
// Prefix severity and time
string fixedSeverity = severity.Substring(0, Math.Min(6, severity.Length));
fixedSeverity = string.Format("{0,-6}", fixedSeverity);
message = string.Format("{0} - {1} - {2}", fixedSeverity, IcdEnvironment.GetLocalTime(), message);
}
// Add an extra newline for 4-series to help formatting
else if(IcdEnvironment.CrestronSeries == IcdEnvironment.eCrestronSeries.FourSeries)
message += IcdEnvironment.NewLine;
// Color formatting
return s_SeverityToColor[severity].FormatAnsi(message);

View 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
{
}
}
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
}
}

View File

@@ -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)]

View File

@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
@@ -21,7 +26,7 @@ namespace ICD.Common.Utils
public static string RootPath {
get
{
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return IcdDirectory.GetApplicationRootDirectory();
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
@@ -34,6 +39,20 @@ namespace ICD.Common.Utils
[PublicAPI]
public static string ProgramPath { get { return IcdDirectory.GetApplicationDirectory(); } }
/// <summary>
/// Gets the path to the program entry point assembly (i.e. ICD.Connect.Core_SimplSharp.dll)
/// </summary>
[PublicAPI]
[CanBeNull]
public static string ProgramFilePath
{
get
{
Assembly entry = AssemblyUtils.GetEntryAssembly();
return entry == null ? null : entry.GetPath();
}
}
/// <summary>
/// Gets the path to the root config directory,
/// which contains common and program-specific config directories.
@@ -43,14 +62,15 @@ namespace ICD.Common.Utils
{
get
{
#if SIMPLSHARP
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
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
@@ -74,20 +94,11 @@ namespace ICD.Common.Utils
{
get
{
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard:
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
return "ProgramConfig";
default:
throw new ArgumentOutOfRangeException();
}
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return "ProgramConfig";
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
}
}
@@ -106,20 +117,11 @@ namespace ICD.Common.Utils
{
get
{
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard:
return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber);
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
return "ProgramData";
default:
throw new ArgumentOutOfRangeException();
}
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return "ProgramData";
return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber);
}
}
@@ -155,22 +157,11 @@ namespace ICD.Common.Utils
{
string directoryName;
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard:
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
directoryName = "ProgramLogs";
break;
default:
throw new ArgumentOutOfRangeException();
}
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
directoryName = "ProgramLogs";
else
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
return Join(RootConfigPath, directoryName);
}
@@ -185,28 +176,26 @@ namespace ICD.Common.Utils
{
get
{
switch (IcdEnvironment.RuntimeEnvironment)
if (IcdEnvironment.Framework != IcdEnvironment.eFramework.Standard)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
// 3-series
if (IcdEnvironment.CrestronSeries == IcdEnvironment.eCrestronSeries.ThreeSeries)
return Join(RootPath, "HTML");
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
return Join(RootPath, "html");
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
// 4-series server (because Crestron)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return Join(RootPath, "Html");
case IcdEnvironment.eRuntimeEnvironment.Standard:
#if LINUX
return Join(RootPath, "var", "www", "html");
#else
return "C:\\INetPub";
#endif
default:
throw new ArgumentOutOfRangeException();
// 4-series non-server (because Crestron)
return Join(RootPath, "html");
}
#if LINUX
return Join(RootPath, "var", "www", "html");
#else
return "C:\\INetPub";
#endif
}
}

Some files were not shown because too many files have changed in this diff Show More