Compare commits

..

390 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
Chris Cameron
867338eca2 chore: Updating changelog, incrementing major version 2020-09-03 11:59:46 -04:00
Chris Cameron
8625f0a3a4 Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2020-09-03 11:58:28 -04:00
Chris Cameron
09516d2f11 chore: Updating changelog 2020-09-03 11:57:58 -04:00
Laura Gomez
76591155d3 test: Fixed ScrollQueueTest after fixing deadlock bug 2020-09-03 11:54:30 -04:00
Chris Cameron
caa4878a37 feat: Adding dequeue overload to ScrollSqueue 2020-09-01 12:06:29 -04:00
Chris Cameron
a5d3a7c19d fix: Simplifying IcdErrorLog, fixed formatting on 4-Series processors 2020-08-31 16:43:44 -04:00
Chris Cameron
55457253b2 fix: Splitting runtime environment SimplSharpProMono into SimplSharpProServer and fixing usages 2020-08-31 16:43:13 -04:00
Chris Cameron
b8722af752 fix: Fixed 4-series version string date parsing error 2020-08-31 14:15:40 -04:00
Chris Cameron
bf8c320d66 feat: Added util methods for removing duplicate whitespace 2020-08-31 13:37:38 -04:00
Chris Cameron
28f4818ca3 fix: Fixed "version" regex for 4-series 2020-08-31 12:03:51 -04:00
Chris Cameron
00c876be7a feat: Replaced Crestron Unzip with Yallie Unzip 2020-08-31 12:03:46 -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
ffeb313456 Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-08-21 12:06:14 -04:00
Chris Cameron
aeba163a0f chore: Updating changelog, incrementing patch version 2020-08-21 12:04:48 -04:00
Chris Cameron
49a154ebed fix: Removed the OnItemTrimmed event from the ScrollQueue due to deadlocks 2020-08-21 12:04:35 -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
Chris Cameron
3316ef9343 Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2020-08-17 10:16:58 -04:00
Chris Cameron
5ca76f7e7e Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6 2020-08-17 10:16:05 -04:00
Chris Cameron
66b8107233 Merge remote-tracking branch 'origin/ConnectPro_v1.4' into ConnectPro_v1.5 2020-08-17 10:15:39 -04:00
Chris Cameron
dd6dfa191e Merge remote-tracking branch 'origin/ConnectPro_v1.3' into ConnectPro_v1.4 2020-08-17 10:15:06 -04:00
Chris Cameron
5aa8b15472 Merge remote-tracking branch 'origin/ConnectPro_v1.2' into ConnectPro_v1.3
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-08-17 10:12:13 -04:00
Chris Cameron
c6cfdd4d72 chore: Updating changelog, incrementing patch version 2020-08-17 10:09:58 -04:00
Chris Cameron
ab699406a6 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-08-17 10:08:10 -04:00
Chris Cameron
15330d7dd0 chore: Updating changelog, incrementing patch version 2020-08-17 10:03:30 -04:00
Chris Cameron
34cfe8c7a3 fix: Workaround for logged XML format exceptions when failing to parse floats 2020-08-17 10:01:58 -04:00
Drew Tingen
6d93911766 chore:changelog 2020-08-13 22:44:44 -04:00
Drew Tingen
8dd8d48a8b fix: Fixed cultureinfo sqlite connection for 4 series compatability 2020-08-13 22:41:31 -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
a9f1c57d1e Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2020-07-29 10:21:55 -04:00
Chris Cameron
27bb1e3482 Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6 2020-07-29 10:17:41 -04:00
Chris Cameron
6173be0a1d Merge remote-tracking branch 'origin/ConnectPro_v1.4' into ConnectPro_v1.5 2020-07-29 10:17:03 -04:00
Chris Cameron
66502d3617 Merge remote-tracking branch 'origin/ConnectPro_v1.3' into ConnectPro_v1.4 2020-07-29 10:16:24 -04:00
Chris Cameron
0db03f7fdf Merge remote-tracking branch 'origin/ConnectPro_v1.2' into ConnectPro_v1.3
# Conflicts:
#	CHANGELOG.md
2020-07-29 10:15:58 -04:00
Chris Cameron
a74cdc5614 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-07-29 10:14:02 -04:00
Drew Tingen
7f306801b3 chore: Updating changelog, incremeting patch version 2020-07-29 10:01:43 -04:00
Austin Noska
51ec3dd8ff docs: Update changelog 2020-07-28 18:09:00 -04:00
Austin Noska
7cc8359284 fix: Check if any & all characters in a string is a digit instead of just all 2020-07-28 17:31:41 -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
Drew Tingen
c6b2cd78cc fix: fixing 2008 compatibiltiy issues 2020-07-14 13:27:03 -04:00
Chris Cameron
5d9369b582 chore: Updating changelog, incrementing minor version 2020-07-14 13:21:37 -04:00
Drew Tingen
a822bc406b feat: Tests for GetProperty and SetProperty 2020-07-14 13:18:08 -04:00
Drew Tingen
c6a755f080 feat: Added GetPropertyInfo for properties at paths 2020-07-14 13:17:38 -04:00
Drew Tingen
21c68905d0 fix: Fix get/set property extensions to work with deep properties 2020-07-14 13:16:54 -04:00
Chris Cameron
4bbf0aec37 refactor: Reflection extension methods work in S# 2020-07-09 13:20:21 -04:00
Drew Tingen
0a87c4efe7 chore: update changelog 2020-07-07 21:31:34 -04:00
Drew Tingen
9882febede feat: Adding CallMethod reflection extensions 2020-07-07 21:31:33 -04:00
Drew Tingen
80b1412632 chore: changelog 2020-07-07 21:31:32 -04:00
Drew Tingen
a1d480d24b chore: documentation update 2020-07-07 21:31:32 -04:00
Drew Tingen
9c0d6a87df feat: Get and Set property extensions using reflection - NetStandard only (for now) 2020-07-07 21:31:31 -04:00
Chris Cameron
0da0c41fe2 chore: ObfuscationSettings are compiled 2020-07-06 15:52:17 -04:00
Chris Cameron
f8030a45f4 fix: Obfuscation attributes NEED to be in the System.Reflection namespace to work 2020-07-06 15:52:00 -04:00
Chris Cameron
48ff54de82 test: Adding test case for minimal interfaces 2020-07-02 14:32:11 -04:00
Chris Cameron
99945b41a4 fix: Fixing DHCP status return type and Hostnames property name 2020-07-02 13:42:52 -04:00
Chris Cameron
d31b698cea fix: Fixing error message 2020-06-30 17:08:36 -04:00
Austin Noska
5212cb9a7a feat: Add overload methods for MaxOrDefault & MinOrDefault that take a selector function as an argument 2020-06-29 11:51:58 -04:00
Austin Noska
ee670487dd feat: Added AggregateOrDefault extension method 2020-06-29 11:51:46 -04:00
Chris Cameron
4f34b3da7c feat: Added attributes for controlling obfuscation 2020-06-26 14:06:58 -04:00
Chris Cameron
9bf0a9b8a5 chore: Updating changelog, incrementing major version 2020-06-18 13:57:39 -04:00
Chris Cameron
f7b35e64ec feat: Added extension method for getting the EventArgs type for a given EventInfo 2020-06-05 12:26:25 -04:00
Chris Cameron
bfab3c77b0 feat: Adding extesion methods for reading a JSON token as a float or a double 2020-06-05 12:25:52 -04:00
Chris Cameron
4a68c0caad refactor: Tidying ILoggerService interface 2020-06-04 14:12:55 -04:00
Chris Cameron
07ee6ce586 fix: Deadlock detection works better for false positives 2020-06-04 14:11:48 -04:00
Chris Cameron
6f5deaf1ea feat: Added dictionary extension for removing a key and outputting the value 2020-06-04 13:23:14 -04:00
Chris Cameron
3da9b23e12 feat: Adding simple IGenericEventArgs interface 2020-06-03 16:14:43 -04:00
Chris Cameron
9deafaec9b feat: JSON conversion improvements for log items 2020-06-01 11:35:41 -04:00
Chris Cameron
28ea05e2f6 feat: Standardizing JSON Type conversion 2020-06-01 10:27:42 -04:00
Chris Cameron
f53d05936c fix: Better NiceName implementation, handles more cases of syntax, whitespace and punctuation 2020-05-28 16:21:12 -04:00
Chris Cameron
4339f02698 refactor: JSON DateTime cleanup 2020-05-28 13:46:49 -04:00
Chris Cameron
25fb5a0ad8 fix: Another round of JSON DateTime fixes 2020-05-27 19:38:04 -04:00
Chris Cameron
6240d3fccf Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2020-05-27 16:04:13 -04:00
Chris Cameron
88ff67b356 Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6 2020-05-27 16:02:50 -04:00
Chris Cameron
1d0ba453e3 Merge branch 'ConnectPro_v1.4' into ConnectPro_v1.5 2020-05-27 15:59:53 -04:00
Chris Cameron
121d267aad Merge branch 'ConnectPro_v1.3' into ConnectPro_v1.4 2020-05-27 15:59:40 -04:00
Chris Cameron
f23b051c3a Merge branch 'ConnectPro_v1.2' into ConnectPro_v1.3 2020-05-27 15:59:27 -04:00
Chris Cameron
b5ec51d9a4 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-05-27 15:59:12 -04:00
Chris Cameron
62f54f1213 chore: Updating changelog, incrementing patch version 2020-05-27 15:56:22 -04:00
Drew Tingen
dd3c0f530b chore: Changelog 2020-05-27 11:28:12 -04:00
Drew Tingen
fc74865c5b feat: Use CrestronEnvironment.SystemInfo.SerialNumber to retrieve the serial number, instead of trying to convert TSID from console. 2020-05-27 11:25:14 -04:00
Drew Tingen
4de7944286 fix: temp fix for 4-series shenanagans 2020-05-26 11:22:47 -04:00
Chris Cameron
dbad18925f feat: Clarifying which culture failed to load when throwing an exception 2020-05-26 11:18:58 -04:00
Chris Cameron
1a027bcea0 chore: Updating changelog 2020-05-26 10:25:22 -04:00
Chris Cameron
7053faede1 feat: Serializing JSON DateTimes to ISO-8601 2020-05-22 17:55:53 -04:00
Chris Cameron
582cbe9157 fix: Temp fix for annoying CompiledOn DateTime parsing bug 2020-05-21 13:59:09 -04:00
Chris Cameron
00803fcd66 Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7
# Conflicts:
#	CHANGELOG.md
2020-05-19 10:50:55 -04:00
Chris Cameron
9912097a33 chore: Updating changelog, incrementing minor version 2020-05-19 10:48:55 -04:00
Drew Tingen
cad3cf60cf chore: changelog 2020-05-18 15:27:51 -04:00
Drew Tingen
0431b5afe4 feat: DateTimeNullableEventArgs 2020-05-18 15:27:01 -04:00
Chris Cameron
d1c21ae7b4 fix: TableBuilder no longer draws redundant separators 2020-05-12 10:24:42 -04:00
Chris Cameron
f34e307a93 feat: Added method for combining GUIDs to make deterministic GUIDs 2020-05-11 10:11:56 -04:00
Drew Tingen
2ad9adf86c feat: ScrollQueue - added OnItemTrimmed event 2020-05-07 18:16:54 -04:00
Chris Cameron
bbb09666dd fix: Hack to catch System.Reflection constructor invocation exceptions on crestron 2020-05-04 18:36:59 -04:00
Chris Cameron
dd3c6b13fd Merge remote-tracking branch 'origin/ConnectPro_v1.6' into ConnectPro_v1.7 2020-05-01 13:30:26 -04:00
Chris Cameron
f9dccd30e5 Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6 2020-04-30 21:49:38 -04:00
Chris Cameron
db0029bbc1 Merge remote-tracking branch 'origin/ConnectPro_v1.4' into ConnectPro_v1.5 2020-04-30 21:29:50 -04:00
Chris Cameron
a5289e01f7 Merge remote-tracking branch 'origin/ConnectPro_v1.3' into ConnectPro_v1.4 2020-04-30 19:38:47 -04:00
Chris Cameron
9de5acfbe7 Merge remote-tracking branch 'origin/ConnectPro_v1.2' into ConnectPro_v1.3
# Conflicts:
#	ICD.Common.Utils/ProgramUtils.SimplSharp.cs
2020-04-30 18:49:14 -04:00
Chris Cameron
a22c3626ab Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-04-30 15:16:23 -04:00
Chris Cameron
d90c60126e chore: Updating changelog, incrementing minor version 2020-04-30 13:02:14 -04:00
Drew Tingen
49d12d454f fix: Program and Processor utils actually return DateTimes for date properties 2020-04-30 12:58:23 -04:00
Chris Cameron
760ab259fa feat: Adding MathUtils.Clamp overload for bytes 2020-04-23 10:20:02 -04:00
Chris Cameron
40e00e0ab7 feat: Adding methods for reading DateTimes from XML 2020-04-20 16:09:20 -04:00
Chris Cameron
64405a1cd6 feat: Adding overloads for reading XML attributes as GUIDs 2020-04-18 18:57:13 -04:00
Chris Cameron
ffb217839c fix: Cleaning up TimeSpan.ToReadableString() output 2020-04-18 12:29:13 -04:00
Chris Cameron
0d8dbf4a30 feat: Added methods for getting user data paths 2020-04-14 16:13:13 -04:00
Chris Cameron
79344a3667 refactor: Reworking EnumUtils to rely less on casting back and forth to int, added method for getting enum names 2020-04-13 14:50:29 -04:00
Chris Cameron
5e61098e71 feat: Added extension method for dynamically converting a sequence to a generic list of the given item type 2020-04-13 14:50:29 -04:00
Jack Kanarish
e0663bdbfd core telemetry initial commit 2020-04-09 14:46:28 -04:00
Chris Cameron
96946d6323 refactor: Rewrote JsonItemWrapper serialization for JsonConvert friendliness 2020-04-09 12:21:42 -04:00
Chris Cameron
5067f5fdb3 feat: Added Type extension method for getting inner generic types 2020-04-06 13:11:05 -04:00
Chris Cameron
d002dda568 fix: Fixing null parameter exception in TableBuilder 2020-04-06 13:09:34 -04:00
Chris Cameron
08aeba756a feat: Added extension method for determining if a type is Anonymous 2020-04-01 13:58:03 -04:00
Chris Cameron
b29ae4c8c8 perf: Potential optimization when searching for Constructors/Methods 2020-03-27 14:50:02 -04:00
Chris Cameron
c1418ae4e2 feat: Adding Move method to IcdFile 2020-03-27 14:47:41 -04:00
Chris Cameron
5978d492cf feat: Added eIcdFileMode for IO platform agnosticism 2020-03-27 13:54:29 -04:00
Chris Cameron
a1775b281f feat: TableBuilder supports multi-line content 2020-03-26 14:16:40 -04:00
Chris Cameron
75ddc37cf1 feat: Added ToCollection extension method for copying an enumerable to a new collection 2020-03-23 16:35:16 -04:00
Chris Cameron
d07373b69e chore: Updating changelog, incrementing major version 2020-03-20 13:04:11 -04:00
Laura Gomez
3ebfa76970 feat : Added ToPrivateString Method to convert password into special characters for privacy. 2020-03-17 18:47:46 -04:00
Chris Cameron
ac5da50e98 feat: Added IsDistinct extension method 2020-03-16 10:07:15 -04:00
Chris Cameron
6c6d272511 feat: Added MinOrDefault extension method 2020-03-16 10:04:23 -04:00
Chris Cameron
022e625143 fix: Fixed a bug where table width calculations were not considering unprintable characters 2020-03-15 15:51:05 -04:00
Chris Cameron
b496594cd6 test: Using UTC in unit tests 2020-03-11 15:36:21 -04:00
Chris Cameron
e99a7f313f fix: Using UTC for tracking durations 2020-03-11 13:02:34 -04:00
Chris Cameron
ed2cf84a7e fix: Using UTC for tracking scheduled events 2020-03-10 13:39:38 -04:00
Chris Cameron
91684f25f2 refactor: Tidying 2020-03-04 14:50:38 -05:00
Chris Cameron
be5024271f chore: Fixing comments 2020-03-03 17:41:02 -05:00
Chris Cameron
dd90227ca1 fix: Resolving warning 2020-03-03 17:31:56 -05:00
Chris Cameron
2fa6ff3de6 fix: Fixing TableBuilder spacing for Net Standard 2020-03-03 15:57:57 -05:00
Chris Cameron
311d63204f refactor: Tidying 2020-02-28 12:24:04 -05:00
Chris Cameron
82d0598352 feat: AnsiUtils exposes ANSI codes 2020-02-28 11:27:39 -05:00
Chris Cameron
4229a6a73f chore: Exluding build directory from test project 2020-02-28 11:27:09 -05:00
Chris Cameron
f5c6b64ab7 fix: Fixing bugs in AnsiUtils color conversion 2020-02-26 12:25:25 -05:00
Chris Cameron
3f62e7e929 refactor: Splitting ANSI into codes and texts, instead of converting straight to HTML 2020-02-25 19:06:34 -05:00
Chris Cameron
a82645e902 feat: Console uses unicode for table drawing on Net Standard 2020-02-24 17:57:43 -05:00
Chris Cameron
985578985a feat: Simplifying ANSI color methods, better cross-platform color support 2020-02-21 21:53:27 -05:00
Chris Cameron
c453717998 feat: First attempt at an ANSI to HTML converter 2020-02-19 16:52:21 -05:00
Chris Cameron
fde3106389 refactor: Tidying 2020-02-19 13:48:04 -05:00
Chris Cameron
94265b1915 Merge branch 'ConnectPro_v1.5' into ConnectPro_v1.6 2020-02-18 13:01:11 -05:00
Chris Cameron
9b3294382f Merge branch 'ConnectPro_v1.4' into ConnectPro_v1.5
# Conflicts:
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-02-18 13:00:59 -05:00
Chris Cameron
d59eb00d6b Merge branch 'ConnectPro_v1.3' into ConnectPro_v1.4
# Conflicts:
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-02-18 13:00:27 -05:00
Chris Cameron
d051ddb098 Merge branch 'ConnectPro_v1.2' into ConnectPro_v1.3
# Conflicts:
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-02-18 12:59:56 -05:00
Chris Cameron
a53ad74811 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-02-18 12:59:18 -05:00
Chris Cameron
69eb4b3d34 chore: Updating changelog, incrementing patch version 2020-02-18 12:13:00 -05:00
Drew Tingen
8e5486e1ef chore: Changelog 2020-02-14 23:56:56 -05:00
Drew Tingen
ecf7e626f3 fix: IcdTimer fix issue that prevents OnElapsed callback from firing when Length is less than (or close to) Heartbeat Interval 2020-02-14 23:55:32 -05:00
Chris Cameron
3b5c9ed51b fix: Fixed a bug where color formatted console output on Net Standard was not raising the OnConsolePrint event 2020-02-12 16:59:40 -05:00
Jeffery Thompson
e2646194b8 chore: changelog 2020-02-11 11:33:12 -05:00
Jeffery Thompson
0adad154c6 feat: add extensions for getting JsonReader value as long or ulong 2020-02-11 11:32:03 -05:00
Jeffery Thompson
0b8eb82e3b feat: add methods for creating a DateTime from epoch seconds or millis 2020-02-11 11:31:42 -05:00
Chris Cameron
13d1072d10 feat: Adding shims for converting DateTimes to/from XML 2020-02-03 16:32:04 -05:00
Chris Cameron
8d2321c350 refactor: Removing redundant code 2020-02-03 16:32:02 -05:00
Chris Cameron
55f494e2f5 Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6 2020-02-03 16:31:43 -05:00
Chris Cameron
859a2b0830 Merge remote-tracking branch 'origin/ConnectPro_v1.4' into ConnectPro_v1.5 2020-02-03 16:31:03 -05:00
Chris Cameron
9d5dfb3b64 Merge branch 'ConnectPro_v1.3' into ConnectPro_v1.4
# Conflicts:
#	ICD.Common.Utils/Extensions/DictionaryExtensions.cs
2020-02-03 16:30:18 -05:00
Chris Cameron
c60acf52ba Merge remote-tracking branch 'origin/ConnectPro_v1.2' into ConnectPro_v1.3 2020-02-03 16:27:54 -05:00
Chris Cameron
311bf683e0 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Extensions/DictionaryExtensions.cs
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-02-03 16:12:26 -05:00
Chris Cameron
d025946948 chore: Updating changelog, incrementing minor version 2020-01-23 12:46:06 -05:00
Chris Cameron
b74d2c9d60 fix: Adding PriorityQueue EnqueueRemove overload for backwards compat 2020-01-23 12:44:08 -05:00
Jack Kanarish
ac4c0eccc9 feat: add ability to select when de-duplication should queue at the end of the queue, or at the first occurance
# Conflicts:
#	ICD.Common.Utils/Collections/PriorityQueue.cs
2020-01-23 11:44:31 -05:00
Jeffery Thompson
0cae292899 chore: changelog 2020-01-22 14:45:13 -05:00
Jeffery Thompson
9cdc8b26a2 fix: catch PlatformNotSupportedException for linux systems 2020-01-22 14:43:27 -05:00
Chris Cameron
80786dd84c Merge remote-tracking branch 'origin/ConnectPro_v1.5' into ConnectPro_v1.6
# Conflicts:
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2020-01-21 11:15:18 -05:00
Chris Cameron
bf8061046b chore: Updating changelog, incrementing minor version 2020-01-20 11:30:45 -05:00
Chris Cameron
201639b30b fix: Skip over invalid adapter ids when getting network information 2020-01-17 11:11:32 -05:00
Chris Cameron
89f0214cce fix: Ignoring Crestron ethernet parameters that say "Invalid Value" 2020-01-16 15:51:05 -05:00
Chris Cameron
49f8f116fd feat: Simplifying network interfaces, enumerating all adapter types
# Conflicts:
#	CHANGELOG.md
2020-01-16 15:49:40 -05:00
Laura Gomez
7598a451cc chore: XmlConverterAttribute is decorated as implicit 2020-01-14 14:08:32 -05:00
Laura Gomez
f03f49b376 fix: IcdXmlConvert supports serializing arrays of different items 2020-01-14 14:08:06 -05:00
Jack Kanarish
da4415a11c chore: changelog 2020-01-09 14:00:19 -05:00
Jack Kanarish
67c07b8833 feat: make get values public 2020-01-09 14:00:18 -05:00
Austin Noska
11c4d10a51 feat: added not null tag to ICDUriBuilder constructor that takes a Uri as an argument. 2020-01-06 12:54:02 -05:00
Chris Cameron
d1ec8a45d1 Merge branch 'ConnectPro_v1.5' into ConnectPro_v1.6 2020-01-06 10:30:00 -05:00
Chris Cameron
352121e25c Merge branch 'ConnectPro_v1.4' into ConnectPro_v1.5 2020-01-06 10:29:45 -05:00
Chris Cameron
0d5ee31d61 Merge branch 'ConnectPro_v1.3' into ConnectPro_v1.4 2020-01-06 10:28:48 -05:00
Chris Cameron
ec7004e053 Merge branch 'ConnectPro_v1.2' into ConnectPro_v1.3 2020-01-06 10:28:39 -05:00
Chris Cameron
ea0bcec2c3 Merge branch 'ConnectPro_v1.1' into ConnectPro_v1.2 2020-01-06 10:28:31 -05:00
Chris Cameron
55bf458a2b test: Fixing PriorityQueue unit tests 2020-01-06 10:28:03 -05:00
Chris Cameron
7562dddeff chore: Updating changelog 2020-01-06 09:50:08 -05:00
Laura Gomez
f66ddc7e03 refactor: cleaned up code. 2020-01-03 15:28:16 -05:00
Laura Gomez
b5fc25de7c feat: Added WriteAllByte method to IcdFile class. 2020-01-03 15:27:48 -05:00
Chris Cameron
d4ef4c3e1a feat: Added utils for building paths in the HTML directory 2020-01-03 14:17:13 -05:00
Chris Cameron
2a8a936b6f chore: Updating AssemblyInfo 2020-01-03 10:28:27 -05:00
Chris Cameron
bbe596c116 feat: Added methods for serializing an XML array 2019-12-13 12:21:41 -05:00
Laura Gomez
7689ffc3c8 feat: creating deserializItem method. Making xml changes. 2019-12-12 15:39:27 -05:00
Jeffery Thompson
1ab247f204 chore: changelog 2019-12-12 15:27:05 -05:00
Jeffery Thompson
5b5ee258ca chore: comments 2019-12-12 15:23:44 -05:00
Jeffery Thompson
4695a80575 feat: add DateTime extensions for add and wrap, like TimeSpan 2019-12-12 14:58:00 -05:00
Chris Cameron
79a98d8c10 feat: Added enum extensions for finding the inclusion and exclusion of enum flags 2019-12-05 15:31:20 -05:00
Chris Cameron
b68c5f1c97 feat: Added MathUtils methods for converting to and from percentages 2019-12-04 11:53:44 -05:00
Chris Cameron
3deec98f44 chore: Updating changelog, incrementing minor version 2019-12-04 11:51:18 -05:00
Chris Cameron
eb52b164bf feat: Added shim methods for finding closest DateTimes from a sequence 2019-11-19 17:08:21 -05:00
Chris Cameron
fb8abe2aa4 chore: Updating changelog, incrementing minor version 2019-11-18 14:17:31 -05:00
Chris Cameron
f61305856f refactor: Simplifying reflection code, potential micro-optimization 2019-11-14 11:24:10 -05:00
Chris Cameron
de00acbf79 feat: Adding extension method for getting max value from a sequence, otherwise a default value 2019-11-12 17:12:20 -05:00
Chris Cameron
5ffca5e7a3 feat: Adding overload for determining if a sorted list contains an item by a given predicate 2019-11-08 14:57:27 -05:00
Chris Cameron
2037c990b2 feat: Method for determining if a URI is default 2019-11-07 19:22:52 -05:00
Chris Cameron
13a2493f4c feat: Adding methods to get the data directory for a given room 2019-11-06 14:34:47 -05:00
Chris Cameron
09e0a20443 fix: IcdSqliteConnection CreateFile method will make directories recursively 2019-11-05 16:19:32 -05:00
Chris Cameron
7870d2c375 feat: Added method for getting a path in the ProgramData directory 2019-11-05 16:18:24 -05:00
Laura Gomez
7cf3d18127 feat: Adding CompareTo method wich helped with validation cheking on DeploymentTool. 2019-10-30 10:13:15 -04:00
Chris Cameron
f39b3897fc Merge branch 'ConnectPro_v1.3' into ConnectPro_v1.4 2019-10-29 14:58:27 -04:00
Chris Cameron
ab2fab0b3a Merge remote-tracking branch 'origin/ConnectPro_v1.2' into ConnectPro_v1.3 2019-10-29 14:58:19 -04:00
Chris Cameron
9b69b2a712 Merge remote-tracking branch 'origin/ConnectPro_v1.1' into ConnectPro_v1.2
# Conflicts:
#	CHANGELOG.md
#	ICD.Common.Utils/Properties/AssemblyInfo.cs
2019-10-29 14:57:38 -04:00
Chris Cameron
0a1f637b45 chore: Updating changelog, incrementing patch version 2019-10-29 14:52:36 -04:00
Chris Cameron
e0ace12ef9 Merge pull request #43 from Utils fix/SerialQueueFix
Fix/SerialQueueFix
2019-10-29 14:33:28 -04:00
Jack Kanarish
9f1541f843 chore: update changelog 2019-10-29 14:19:58 -04:00
Jack Kanarish
b9f5349055 fix: fix an issue where commands would collapse to the point of the last deleted command instead of the position in queue where the new command was going 2019-10-29 14:18:45 -04:00
Chris Cameron
0c7f82835c fix: Fixing Net Standard build 2019-10-15 14:06:37 -04:00
Chris Cameron
41755cb472 refactor: Adding NotNull attributes to collections 2019-10-09 10:47:29 -04:00
Chris Cameron
238d6518ab refactor: Adding NotNull attributes to RangeAttribute 2019-10-09 10:15:47 -04:00
Chris Cameron
89cde5c9a0 chore: Decorating extension methods with NotNull and CanBeNull attributes 2019-10-07 21:59:21 -04:00
181 changed files with 12438 additions and 3060 deletions

View File

@@ -6,6 +6,252 @@ 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
- Added dequeue overload to ScrollQueue
### Changed
- Replaced Crestron Unzip with Yallie Unzip
- Fixed "version" regex for 4-series
- Fixed date parsing error for 4-series
- Split SimplSharpProMono runtime environment into SimplSharpProSever
- Fixed log formatting on 4-series
## [12.1.0] - 2020-07-14
### Added
- ReflectionExtensions : GetProperty, SetProperty, CallMethod extensions for NetStandard
- Added attributes for controlling obfuscation
- Added AggregateOrDefault extension method for applying an accumulator function over a sequence that returns a default value if the sequence is empty
### Changed
- DHCP status is a boolean
- Changed Hostname property to Hostnames
## [12.0.0] - 2020-06-18
### Added
- Added ToCollection extension method for copying an enumerable to a new collection
- TableBuilder supports multi-line content
- Added eIcdFileMode for IO platform agnosticism
- Extension method for determining if a Type is anonymous
- Extension method for getting inner generic Types
- Added extension method for dynamically converting a sequence to a generic list of the given item type
- Added methods for getting UserData paths
- Added methods for reading GUIDs from XML
- Added methods for reading DateTimes from XML
- Added method for combining GUIDs
- Added method for getting the EventArgs type for an EventHandler
- Added methods for getting a JSON value as a float or double
- Added dictionary Remove method for outputting the removed value
- Added IGenericEventArgs interface
- Added MinimalTypeConverter for serializing Types to JSON
- Added common JSON serializer settings for common, platform agnostic DateTime and Type conversion
### Changed
- Rewrote JsonItemWrapper serialization for JsonConvert friendliness
- Reflection optimizations
- Fixed NullParameterException in TableBuilder
- Improvements to EnumUtils, less reliance on casting to/from int
- Cleaned up TimeSpan.ToReadableString() output
- Fixed a bug where System.Reflection exceptions can't be caught in S#
- TableBuilder no longer draws redundant separators
- Fixed a bug where CompiledOn date was not being parsed correctly due to culture
- S# DateTimes are serialized to JSON in ISO-8601 format
- Deadlock detection works better for false positives
- Improved LogItem JSON serialization
- Improved NiceName method to better handle syntax, whitespace and punctuation
- Fixed a bug where IcdCultureInfo would fail to load on Crestron 4-series processors
- Clarifying which culture failed to load when IcdCultureInfo throws an exception
## [11.1.1] - 2020-08-21
### Removed
- Removed the OnItemTrimmed event from the ScrollQueue due to deadlocks
## [11.1.0] - 2020-05-19
### Added
- ScrollQueue - Added OnItemTrimmed event
- Added DateTimeNullableEventArgs
## [11.0.0] - 2020-03-20
### Added
- Added Not null tag for ICDUriBuilder Constructor that takes a URI as an argument.
- Added MathUtils methods for converting to and from percentages
- Added enum extensions for finding the inclusion and exclusion of enum flags
- Added DateTime extensions for adding years, months, days, hours, minutes and wrapping without modifying other values
- Added shims for deserializing an XML array using a callback for each item
- Added methods for serializing an XML array
- Added WriteAllByte method on IcdFile.
- Added PathUtils for building paths in the HTML directory
- Added public access to GetValues enumeration extension
- Added extensions for getting JsonReader values as long or ulong
- Added DateTimeUtils methods for creating DateTimes from epoch seconds or milliseconds
- Added utils for splitting ANSI into spans for conversion to XAML, HTML, etc
### Changed
- Fixed exception trying to get DHCP status of network interfaces on Linux
- Fixed a bug where color formatted console output on Net Standard was not raising the OnConsolePrint event
- Simplifying ANSI color methods, better cross-platform color support
- Console uses unicode for table drawing on Net Standard
- Using UTC for tracking scheduled events, fixes issues with DST
- Using UTC for tracking durations
- Fixed a bug where table width calculations were not considering unprintable characters
## [10.3.0] - 2020-01-20
### Changed
- Network/MAC/DNS address utils are now enumerating all adapter types
- Ignoring Crestron ethernet parameters that say "Invalid Value"
- Skipping network interfaces with an invalid adapter id
## [10.2.0] - 2019-12-04
### Added
- Added shim methods for finding closest DateTimes from a sequence
## [10.1.0] - 2019-11-18
### Added
- Added PathUtils methods for getting ProgramData paths
- Added a method for determining if a URI is defaults
- Added MaxOrDefault extension method for enumerables
- Added a method for finding an item in a sorted list by a given predicate
### Changed
- NullObject implements IComparable, fixes issues with null keys in ordered dictionaries
- IcdSqliteConnection CreateFile method will create directories recursively
## [10.0.0] - 2019-10-07
### Added
- IcdEnvironment.GetUtcTime() to get UTC representaiton of current time.
@@ -43,17 +289,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added methods for deserializing an XML array
### Changed
- Fixed a bug where ANSI color encoded strings with percentages were being scrambled
- Improvements to JSON DateTime parsing, particularly in Net Standard
## [9.7.1] - 2019-08-17
### Changed
- Fixed CultureInfo SQLite conection string for 4-series compatibility
## [9.7.0] - 2019-08-15
### Added
- Added logger timestamps to non simplsharp programs
- Added Net Standard Support for JSON DateTime formats
- Added EmailValidation class
### Changed
- JSON dict serialization serializes keys instead of converting to property name
- Fixed a bug where ANSI color encoded strings with percentages were being scrambled
## [9.6.0] - 2019-07-03
### Added
@@ -128,6 +378,34 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Better VC-4 support for IcdConsole
- JSON refactoring for simpler deserialization
## [8.9.3] - 2020-08-17
### Changed
- Workaround for logged XML format exceptions when failing to parse floats
## [8.9.2] - 2020-07-28
### Changed
- StringExtensions - fixed an issue with IsNumeric where empty strings would return true
## [8.9.1] - 2020-05-27
### Changed
- Changed ProcessorUtils to use CrestronEnvironment to retrive serial number - this fixes issues with new serial numbers that aren't deciaml TSIDs
## [8.9.0] - 2020-04-30
### Changed
- ProgramUtils and ProcessorUtils return dates instead of strings for date properties
## [8.8.1] - 2020-02-18
### Changed
- IcdTimer - fixed issue that prevented OnElapsed event from firing when Length is less than (or close to) Heartbeat Interval
## [8.8.0] - 2020-01-23
### Added
- Added an overload to PriorityQueue for determing the de-duplication behaviour
## [8.7.2] - 2019-10-29
### Changed
- Fixed a bug with PriorityQueue de-duplication where a new command would be inserted in the wrong position
## [8.7.1] - 2019-08-22
### Changed
- Fixed a bug with the IcdOrderedDict index setter that was creating additional values
@@ -234,7 +512,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- New XML conversion framework for performance improvements
### Changed
- XmlUtils is now using the improved XML conversion framework
- XmlUtils is now using the improved XML conversion framework
- Better implementation of DictionaryExtensions.ToInverse
## [5.0.0] - 2018-09-14
@@ -307,8 +585,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Adding extension method for getting Informational Version from an Assembly
- Adding WeakKeyDictionary for caching
- Reflection util methods
### Changed
- JSON serialization/deserialization features moved into base converter
- Removed suffix from assembly name

View File

@@ -0,0 +1,27 @@
using System.Linq;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class AnsiUtilsTest
{
[Test]
public void ToSpansTest()
{
string ansi = "\x1b[30mblack\x1b[37mwhite\x1b[0mdefault";
AnsiSpan[] spans = AnsiUtils.ToSpans(ansi).ToArray();
Assert.AreEqual(3, spans.Length);
Assert.AreEqual("black", spans[0].Text);
Assert.AreEqual("30", spans[0].Code);
Assert.AreEqual("white", spans[1].Text);
Assert.AreEqual("37", spans[1].Code);
Assert.AreEqual("default", spans[2].Text);
Assert.AreEqual("0", spans[2].Code);
}
}
}

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

@@ -133,8 +133,8 @@ namespace ICD.Common.Utils.Tests.Collections
};
Assert.AreEqual(1, dequeue[0]);
Assert.AreEqual(4, dequeue[1]);
Assert.AreEqual(3, dequeue[2]);
Assert.AreEqual(3, dequeue[1]);
Assert.AreEqual(4, dequeue[2]);
}
[Test]

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

@@ -11,12 +11,14 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly]
public void MaxSizeTest()
{
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0);
test.Enqueue(1);
test.Enqueue(2);
test.Enqueue(3);
test.Enqueue(4);
test.Enqueue(0, out unused);
test.Enqueue(1, out unused);
test.Enqueue(2, out unused);
test.Enqueue(3, out unused);
test.Enqueue(4, out unused);
Assert.AreEqual(5, test.Count);
@@ -25,7 +27,7 @@ namespace ICD.Common.Utils.Tests.Collections
Assert.AreEqual(3, test.Count);
Assert.AreEqual(2, test.Peek());
test.Enqueue(0);
test.Enqueue(0, out unused);
Assert.AreEqual(3, test.Count);
Assert.AreEqual(3, test.Peek());
@@ -34,8 +36,10 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly]
public void ClearTest()
{
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0);
test.Enqueue(0, out unused);
test.Clear();
Assert.AreEqual(0, test.Count);
@@ -44,9 +48,11 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly]
public void EnqueueTest()
{
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0);
test.Enqueue(1);
test.Enqueue(0, out unused);
test.Enqueue(1, out unused);
Assert.AreEqual(2, test.Count);
@@ -59,9 +65,11 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly]
public void DequeueTest()
{
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0);
test.Enqueue(1);
test.Enqueue(0, out unused);
test.Enqueue(1, out unused);
Assert.AreEqual(0, test.Dequeue());
Assert.AreEqual(1, test.Count);
@@ -70,9 +78,11 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly]
public void PeekTest()
{
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0);
test.Enqueue(1);
test.Enqueue(0, out unused);
test.Enqueue(1, out unused);
Assert.AreEqual(0, test.Peek());
}

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

@@ -5,12 +5,12 @@ using System.Linq;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class EnumUtilsTest
{
public sealed class EnumUtilsTest
{
public enum eTestEnum
{
None = 0,
A = 1,
A = 1,
B = 2,
C = 3,
}
@@ -22,9 +22,10 @@ namespace ICD.Common.Utils.Tests
A = 1,
B = 2,
C = 4,
D = 32
D = 32,
BandC = B | C
}
[Test]
public void GetValuesTest()
{
@@ -36,7 +37,7 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(eTestEnum.B, values[2]);
Assert.AreEqual(eTestEnum.C, values[3]);
}
[Test]
public void IsEnumTypeGenericTest()
{
@@ -57,17 +58,29 @@ namespace ICD.Common.Utils.Tests
Assert.IsFalse(EnumUtils.IsEnum(""));
}
[Test]
public void IsDefinedTest()
{
Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20));
Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2));
[Test]
public void IsDefinedTest()
{
Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20));
Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2));
Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8));
Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3));
}
Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8));
Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3));
}
#region Values
#region Values
[Test]
public void GetNamesTest()
{
string[] names = EnumUtils.GetNames<eTestEnum>().ToArray();
Assert.AreEqual(4, names.Length);
Assert.IsTrue(names.Contains("None"));
Assert.IsTrue(names.Contains("A"));
Assert.IsTrue(names.Contains("B"));
Assert.IsTrue(names.Contains("C"));
}
[Test]
public void GetValuesGenericTest()
@@ -107,7 +120,9 @@ namespace ICD.Common.Utils.Tests
[Test]
public void GetFlagsIntersectionGenericTest()
{
Assert.AreEqual(eTestFlagsEnum.B, EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.B | eTestFlagsEnum.C));
Assert.AreEqual(eTestFlagsEnum.B,
EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B,
eTestFlagsEnum.B | eTestFlagsEnum.C));
}
[Test]
@@ -138,11 +153,11 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(aValues.Contains(eTestFlagsEnum.D));
}
[Test]
public void GetAllFlagCombinationsExceptNoneGenericTest()
{
eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D;
eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray();
[Test]
public void GetAllFlagCombinationsExceptNoneGenericTest()
{
eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D;
eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray();
Assert.AreEqual(15, aValues.Length);
Assert.IsFalse(aValues.Contains(eTestFlagsEnum.None));
@@ -167,7 +182,18 @@ namespace ICD.Common.Utils.Tests
public void GetFlagsAllValueGenericTest()
{
eTestFlagsEnum value = EnumUtils.GetFlagsAllValue<eTestFlagsEnum>();
Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, value);
Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D,
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]
@@ -181,7 +207,8 @@ namespace ICD.Common.Utils.Tests
public void HasFlagsGenericTest()
{
Assert.IsTrue(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.B));
Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.C));
Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B,
eTestFlagsEnum.A | eTestFlagsEnum.C));
}
[Test]
@@ -200,28 +227,28 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(EnumUtils.HasMultipleFlags(eTestFlagsEnum.A | eTestFlagsEnum.B));
}
[TestCase(false, eTestFlagsEnum.None)]
[TestCase(true, eTestFlagsEnum.B)]
[TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)]
public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value)
{
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value));
}
[TestCase(false, eTestFlagsEnum.None)]
[TestCase(true, eTestFlagsEnum.B)]
[TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)]
public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value)
{
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value));
}
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)]
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)]
[TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)]
[TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)]
public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other)
{
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other));
}
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)]
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)]
[TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)]
[TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)]
public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other)
{
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other));
}
#endregion
#endregion
#region Conversion
#region Conversion
[Test]
[Test]
public void ParseGenericTest()
{
Assert.AreEqual(eTestEnum.A, EnumUtils.Parse<eTestEnum>("A", false));
@@ -243,35 +270,47 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(default(eTestEnum), output);
}
[Test]
public void ParseStrictGenericTest()
{
Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict<eTestEnum>("1", false));
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestEnum>("4", false));
[Test]
public void ParseStrictGenericTest()
{
Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict<eTestEnum>("1", false));
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestEnum>("4", false));
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict<eTestFlagsEnum>("3", false));
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestFlagsEnum>("8", false));
}
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict<eTestFlagsEnum>("3", false));
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestFlagsEnum>("8", false));
}
[Test]
public void TryParseStrictGenericTest()
{
eTestEnum outputA;
[Test]
public void TryParseStrictGenericTest()
{
eTestEnum outputA;
Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA));
Assert.AreEqual(eTestEnum.A, outputA);
Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA));
Assert.AreEqual(eTestEnum.A, outputA);
Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA));
Assert.AreEqual(eTestEnum.None, outputA);
Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA));
Assert.AreEqual(eTestEnum.None, outputA);
eTestFlagsEnum outputB;
eTestFlagsEnum outputB;
Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB));
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB);
Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB));
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB);
Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB));
Assert.AreEqual(eTestFlagsEnum.None, outputB);
}
Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB));
Assert.AreEqual(eTestFlagsEnum.None, outputB);
}
#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

@@ -1,4 +1,6 @@
using NUnit.Framework;
using System;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Extensions
{
@@ -10,5 +12,221 @@ namespace ICD.Common.Utils.Tests.Extensions
{
Assert.Inconclusive();
}
#region Get/Set Property Tests
private sealed class PropertyTestClass
{
private readonly InternalPropertyTestClass m_InternalClass;
public string PropertyString { get; set; }
public int PropertyInt { get; set; }
public DateTime PropertyDateTime { get; set; }
public InternalPropertyTestClass InternalClass { get { return m_InternalClass; } }
public PropertyTestClass()
{
m_InternalClass = new InternalPropertyTestClass();
}
internal sealed class InternalPropertyTestClass
{
private readonly DeepPropertyTestClass m_DeepClass;
public string InternalPropertyString { get; set; }
public int InternalPropertyInt { get; set; }
public DateTime InternalPropertyDateTime { get; set; }
public DeepPropertyTestClass DeepClass { get { return m_DeepClass; } }
public InternalPropertyTestClass()
{
m_DeepClass = new DeepPropertyTestClass();
}
internal sealed class DeepPropertyTestClass
{
public string DeepPropertyString { get; set; }
public int DeepPropertyInt { get; set; }
public DateTime DeepPropertyDateTime { get; set; }
}
}
}
[Test]
public void GetPropertyTest()
{
var testClass = new PropertyTestClass();
int testInt = 15;
string testString = "TestString";
var testDateTime = DateTime.Parse("1987-08-27");
testClass.PropertyInt = testInt;
testClass.PropertyString = testString;
testClass.PropertyDateTime = testDateTime;
int internalTestInt = 34;
string internalTestString = "InternalTestString";
var internalTestDateTime = DateTime.Parse("2011-12-05");
testClass.InternalClass.InternalPropertyInt = internalTestInt;
testClass.InternalClass.InternalPropertyString = internalTestString;
testClass.InternalClass.InternalPropertyDateTime = internalTestDateTime;
int deepTestInt = int.MaxValue;
string deepTestString = "HelloThere";
var deepTestDateTime = DateTime.Parse("1776-07-04");
testClass.InternalClass.DeepClass.DeepPropertyInt = deepTestInt;
testClass.InternalClass.DeepClass.DeepPropertyString = deepTestString;
testClass.InternalClass.DeepClass.DeepPropertyDateTime = deepTestDateTime;
object propertyInt;
object propertyString;
object propertyDateTime;
object fakeObject;
// Test at first level
Assert.True(testClass.GetProperty(out propertyInt, "PropertyInt"), "Couldn't get PropertyInt");
Assert.AreEqual(testInt, propertyInt, "PropertyInt wasn't expected value");
Assert.True(testClass.GetProperty(out propertyString, "PropertyString"), "Couldn't Get PropertyString");
Assert.AreEqual(testString, propertyString, "PropertyString wasn't expected value");
Assert.True(testClass.GetProperty(out propertyDateTime, "PropertyDateTime"), "Couldn't get PropertyDateTime");
Assert.AreEqual(testDateTime, propertyDateTime, "PropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.GetProperty(out fakeObject, "FakePropertyName"));
// Test at second level
Assert.True(testClass.GetProperty(out propertyInt, "InternalClass", "InternalPropertyInt"), "Couldn't get InternalPropertyInt");
Assert.AreEqual(internalTestInt, propertyInt, "InternalPropertyInt wasn't expected value");
Assert.True(testClass.GetProperty(out propertyString, "InternalClass", "InternalPropertyString"), "Couldn't Get InternalPropertyString");
Assert.AreEqual(internalTestString, propertyString, "InternalPropertyString wasn't expected value");
Assert.True(testClass.GetProperty(out propertyDateTime, "InternalClass", "InternalPropertyDateTime"), "Couldn't get InternalPropertyDateTime");
Assert.AreEqual(internalTestDateTime, propertyDateTime, "InternalPropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.GetProperty(out fakeObject, "InternalClass", "FakePropertyName"));
// Test at third level
Assert.True(testClass.GetProperty(out propertyInt, "InternalClass", "DeepClass", "DeepPropertyInt"), "Couldn't get DeepPropertyInt");
Assert.AreEqual(deepTestInt, propertyInt, "DeepPropertyInt wasn't expected value");
Assert.True(testClass.GetProperty(out propertyString, "InternalClass", "DeepClass", "DeepPropertyString"), "Couldn't Get DeepPropertyString");
Assert.AreEqual(deepTestString, propertyString, "DeepPropertyString wasn't expected value");
Assert.True(testClass.GetProperty(out propertyDateTime, "InternalClass", "DeepClass", "DeepPropertyDateTime"), "Couldn't get DeepPropertyDateTime");
Assert.AreEqual(deepTestDateTime, propertyDateTime, "DeepPropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.GetProperty(out fakeObject, "InternalClass", "DeepClass", "FakePropertyName"));
// Test Default/Null Cases
var emptyTestClass = new PropertyTestClass();
Assert.True(emptyTestClass.GetProperty(out propertyInt, "PropertyInt"), "Couldn't get empty PropertyInt");
Assert.AreEqual(default(int), propertyInt, "Empty PropertyInt wasn't expected value");
Assert.True(emptyTestClass.GetProperty(out propertyString, "PropertyString"), "Couldn't Get empty PropertyString");
Assert.AreEqual(default(string), propertyString, "Empty PropertyString wasn't expected value");
Assert.True(emptyTestClass.GetProperty(out propertyDateTime, "PropertyDateTime"), "Couldn't get empty PropertyDateTime");
Assert.AreEqual(default(DateTime), propertyDateTime, "Empty PropertyDateTime wasn't expected value");
}
[Test]
public void SetPropertyTest()
{
var testClass = new PropertyTestClass();
int testInt = 15;
string testString = "TestString";
var testDateTime = DateTime.Parse("1987-08-27");
int internalTestInt = 34;
string internalTestString = "InternalTestString";
var internalTestDateTime = DateTime.Parse("2011-12-05");
int deepTestInt = int.MaxValue;
string deepTestString = "HelloThere";
var deepTestDateTime = DateTime.Parse("1776-07-04");
// Test at first level
Assert.True(testClass.SetProperty(testInt, "PropertyInt"), "Couldn't get PropertyInt");
Assert.AreEqual(testInt, testClass.PropertyInt, "PropertyInt wasn't expected value");
Assert.True(testClass.SetProperty(testString, "PropertyString"), "Couldn't Get PropertyString");
Assert.AreEqual(testString, testClass.PropertyString, "PropertyString wasn't expected value");
Assert.True(testClass.SetProperty(testDateTime, "PropertyDateTime"), "Couldn't get PropertyDateTime");
Assert.AreEqual(testDateTime, testClass.PropertyDateTime, "PropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.SetProperty("SomeObject", "FakePropertyName"));
// Test at second level
Assert.True(testClass.SetProperty(internalTestInt, "InternalClass", "InternalPropertyInt"), "Couldn't get InternalPropertyInt");
Assert.AreEqual(internalTestInt, testClass.InternalClass.InternalPropertyInt, "InternalPropertyInt wasn't expected value");
Assert.True(testClass.SetProperty(internalTestString, "InternalClass", "InternalPropertyString"), "Couldn't Get InternalPropertyString");
Assert.AreEqual(internalTestString, testClass.InternalClass.InternalPropertyString, "InternalPropertyString wasn't expected value");
Assert.True(testClass.SetProperty(internalTestDateTime, "InternalClass", "InternalPropertyDateTime"), "Couldn't get InternalPropertyDateTime");
Assert.AreEqual(internalTestDateTime, testClass.InternalClass.InternalPropertyDateTime, "InternalPropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.SetProperty("SomeAdditionalObject", "InternalClass", "FakePropertyName"));
// Test at third level
Assert.True(testClass.SetProperty(deepTestInt, "InternalClass", "DeepClass", "DeepPropertyInt"), "Couldn't get DeepPropertyInt");
Assert.AreEqual(deepTestInt, testClass.InternalClass.DeepClass.DeepPropertyInt, "DeepPropertyInt wasn't expected value");
Assert.True(testClass.SetProperty(deepTestString, "InternalClass", "DeepClass", "DeepPropertyString"), "Couldn't Get DeepPropertyString");
Assert.AreEqual(deepTestString, testClass.InternalClass.DeepClass.DeepPropertyString, "DeepPropertyString wasn't expected value");
Assert.True(testClass.SetProperty(deepTestDateTime, "InternalClass", "DeepClass", "DeepPropertyDateTime"), "Couldn't get DeepPropertyDateTime");
Assert.AreEqual(deepTestDateTime, testClass.InternalClass.DeepClass.DeepPropertyDateTime, "DeepPropertyDateTime wasn't expected value");
// Test properties that don't exist
Assert.False(testClass.SetProperty("ThisIsAnObjectToo", "InternalClass", "DeepClass", "FakePropertyName"));
// Test Set Default/Null
Assert.True(testClass.SetProperty(default(int), "PropertyInt"), "Couldn't get empty PropertyInt");
Assert.AreEqual(default(int), testClass.PropertyInt, "Empty PropertyInt wasn't expected value");
Assert.True(testClass.SetProperty(default(string), "PropertyString"), "Couldn't Get empty PropertyString");
Assert.AreEqual(default(string), testClass.PropertyString, "Empty PropertyString wasn't expected value");
Assert.True(testClass.SetProperty(default(DateTime), "PropertyDateTime"), "Couldn't get empty PropertyDateTime");
Assert.AreEqual(default(DateTime), testClass.PropertyDateTime, "Empty PropertyDateTime wasn't expected value");
}
[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

@@ -145,7 +145,15 @@ namespace ICD.Common.Utils.Tests.Extensions
[Test]
public void GetMinimalInterfacesTest()
{
Assert.Inconclusive();
Type[] interfaces = typeof(ICollection<int>).GetMinimalInterfaces().ToArray();
Assert.AreEqual(1, interfaces.Length);
Assert.AreEqual(typeof(IEnumerable<int>), interfaces[0]);
interfaces = typeof(IEnumerable<int>).GetMinimalInterfaces().ToArray();
Assert.AreEqual(1, interfaces.Length);
Assert.AreEqual(typeof(IEnumerable), interfaces[0]);
}
[TestCase(typeof(int), "Int32")]

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,18 +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>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<Compile Remove="bin\**" />
<EmbeddedResource Remove="bin\**" />
<None Remove="bin\**" />
</ItemGroup>
<ItemGroup>
<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

@@ -0,0 +1,46 @@
#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
{
[TestFixture]
public sealed class DateTimeIsoConverterTest : AbstractGenericJsonConverterTest
{
[Test]
public override void WriteJsonTest()
{
JsonSerializerSettings settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
};
settings.Converters.Add(new DateTimeIsoConverter());
DateTime dateTime = new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc);
string serial = JsonConvert.SerializeObject(dateTime, settings);
Assert.AreEqual("\"2020-01-02T03:04:05Z\"", serial);
}
[Test]
public override void ReadJsonTest()
{
JsonSerializerSettings settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
};
settings.Converters.Add(new DateTimeIsoConverter());
string serial = "\"2020-01-02T03:04:05Z\"";
DateTime dateTime = JsonConvert.DeserializeObject<DateTime>(serial, settings);
Assert.AreEqual(new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc), dateTime);
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Json
@@ -14,7 +14,7 @@ namespace ICD.Common.Utils.Tests.Json
[TestCase(1, typeof(int))]
public void ItemTypeTest(object item, Type expected)
{
Assert.AreEqual(expected, new JsonItemWrapper(item).ItemType);
Assert.AreEqual(expected, new JsonItemWrapper(item).Type);
}
[TestCase("")]
@@ -27,19 +27,21 @@ namespace ICD.Common.Utils.Tests.Json
[Test]
public void WriteTest()
{
JsonItemWrapper wrapper = new JsonItemWrapper(new List<int> {1, 2, 3});
string json = JsonUtils.Serialize(wrapper.Write);
const string expected = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}";
Assert.Inconclusive();
JsonItemWrapper wrapper = new JsonItemWrapper(new List<int> {1, 2, 3});
string json = JsonConvert.SerializeObject(wrapper);
Assert.AreEqual(expected, json);
}
[Test]
public void ReadTest()
{
const string json = "{\"t\":\"System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]\",\"i\":\"[1,2,3]\"}";
const string json = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}";
JObject jObject = JObject.Parse(json);
List<int> wrappedObject = JsonItemWrapper.ReadToObject(jObject) as List<int>;
JsonItemWrapper wrapper = JsonConvert.DeserializeObject<JsonItemWrapper>(json);
List<int> wrappedObject = wrapper.Item as List<int>;
Assert.NotNull(wrappedObject);
Assert.AreEqual(3, wrappedObject.Count);

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

@@ -12,7 +12,7 @@ namespace ICD.Common.Utils.Tests.Services.Logging
public void TimestampTest()
{
LogItem item = new LogItem(eSeverity.Critical, null);
DateTime time = IcdEnvironment.GetLocalTime();
DateTime time = IcdEnvironment.GetUtcTime();
Assert.IsTrue((time - item.Timestamp).TotalSeconds <= 1);
}

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,11 +25,33 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual("\x08\x22\x00\x00\x00\x02", output);
}
[Test, UsedImplicitly]
public void NiceNameTest()
[TestCase("FF", new byte[] {0xFF})]
[TestCase("01FF", new byte[] { 0x01, 0xFF })]
public void HexToBytes(string value, byte[] expected)
{
string output = StringUtils.NiceName("TodayILiveInTheUSAWithSimon");
Assert.AreEqual("Today I Live In The USA With Simon", output);
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")]
[TestCase("CONST_VALUE", "CONST VALUE")]
[TestCase("m_PrivateMember", "Private Member")]
[TestCase("variableName", "Variable Name")]
[TestCase("Comma, Delimited", "Comma, Delimited")]
[TestCase("Comma,Delimited", "Comma, Delimited")]
public void NiceNameTest(string input, string expected)
{
string output = StringUtils.NiceName(input);
Assert.AreEqual(expected, output);
}
[Test, UsedImplicitly]

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

@@ -13,7 +13,7 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(ThreadingUtils.Wait(() => true, 100));
bool complete = false;
DateTime time = IcdEnvironment.GetLocalTime();
DateTime time = IcdEnvironment.GetUtcTime();
ThreadingUtils.SafeInvoke(() =>
{
@@ -22,15 +22,15 @@ namespace ICD.Common.Utils.Tests
});
Assert.IsTrue(ThreadingUtils.Wait(() => complete, 200));
Assert.AreEqual(100, (IcdEnvironment.GetLocalTime() - time).TotalMilliseconds, 20);
Assert.AreEqual(100, (IcdEnvironment.GetUtcTime() - time).TotalMilliseconds, 20);
}
[Test]
public void Sleep()
{
DateTime now = IcdEnvironment.GetLocalTime();
DateTime now = IcdEnvironment.GetUtcTime();
ThreadingUtils.Sleep(1000);
DateTime now2 = IcdEnvironment.GetLocalTime();
DateTime now2 = IcdEnvironment.GetUtcTime();
Assert.AreEqual(1000, (now2 - now).TotalMilliseconds, 100);
}

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

@@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace ICD.Common.Utils
{
public static class AnsiUtils
{
public const string COLOR_RED = "\x1b[31;1m";
public const string COLOR_GREEN = "\x1b[32;1m";
public const string COLOR_YELLOW = "\x1b[33;1m";
public const string COLOR_BLUE = "\x1b[34;1m";
public const string COLOR_MAGENTA = "\x1b[35;1m";
public const string COLOR_CYAN = "\x1b[36;1m";
public const string COLOR_WHITE = "\x1b[37;1m";
public const string COLOR_YELLOW_ON_RED_BACKGROUND = "\x1b[93;41m";
public const string ANSI_RESET = "\x1b[0m";
public const string CODE_BLACK = "30";
public const string CODE_RED = "31";
public const string CODE_GREEN = "32";
public const string CODE_YELLOW = "33";
public const string CODE_BLUE = "34";
public const string CODE_MAGENTA = "35";
public const string CODE_CYAN = "36";
public const string CODE_WHITE = "37";
public const string CODE_BRIGHT_BLACK = "30;1";
public const string CODE_BRIGHT_RED = "31;1";
public const string CODE_BRIGHT_GREEN = "32;1";
public const string CODE_BRIGHT_YELLOW = "33;1";
public const string CODE_BRIGHT_BLUE = "34;1";
public const string CODE_BRIGHT_MAGENTA = "35;1";
public const string CODE_BRIGHT_CYAN = "36;1";
public const string CODE_BRIGHT_WHITE = "37;1";
/// <summary>
/// Matches ANSI escape codes, e.g. \x1b[31m and \x1b[30;1m
/// </summary>
public const string ANSI_REGEX = "(?'match'\x01b\\[(?'code'[\\d;]+)m)";
/// <summary>
/// Matches ANSI escape codes to HTML styles.
/// Color values are taken from PuTTY.
/// </summary>
private static readonly Dictionary<string, string> s_PuttyColors =
new Dictionary<string, string>
{
{CODE_BLACK, "#000000"},
{CODE_RED, "#BB0000"},
{CODE_GREEN, "#00BB00"},
{CODE_YELLOW, "#BBBB00"},
{CODE_BLUE, "#0000BB"},
{CODE_MAGENTA, "#BB00BB"},
{CODE_CYAN, "#00BBBB"},
{CODE_WHITE, "#BBBBBB"},
{CODE_BRIGHT_BLACK, "#555555"},
{CODE_BRIGHT_RED, "#FF5555"},
{CODE_BRIGHT_GREEN, "#55FF55"},
{CODE_BRIGHT_YELLOW, "#FFFF55"},
{CODE_BRIGHT_BLUE, "#5555FF"},
{CODE_BRIGHT_MAGENTA, "#FF55FF"},
{CODE_BRIGHT_CYAN, "#55FFFF"},
{CODE_BRIGHT_WHITE, "#FFFFFF"}
};
/// <summary>
/// Gets the color map matching PuTTY.
/// </summary>
public static IDictionary<string, string> PuttyColors { get { return s_PuttyColors; } }
#if NETSTANDARD
/// <summary>
/// Enables ANSI color in the console.
/// </summary>
public static void EnableAnsiColor()
{
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>
/// Prefixes the given data with an ANSI control sequence and suffixes with a reset sequence.
/// </summary>
/// <param name="data"></param>
/// <param name="ansiSequence"></param>
/// <returns></returns>
public static string Format(string data, string ansiSequence)
{
// % needs to be escaped or weird things happen
data = string.IsNullOrEmpty(data) ? data : data.Replace("%", "%%");
return string.Format("{0}{1}{2}", ansiSequence, data, ANSI_RESET);
}
/// <summary>
/// Splits the given ANSI string into spans.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static IEnumerable<AnsiSpan> ToSpans(string data)
{
if (string.IsNullOrEmpty(data))
yield break;
Regex regex = new Regex(ANSI_REGEX);
Match match = regex.Match(data);
// No matches
if (!match.Success)
yield return new AnsiSpan {Text = data};
// Find the spans
while (match.Success)
{
// Get the code
string code = match.Groups["code"].Value;
// Get the text
Match next = match.NextMatch();
int startIndex = match.Index + match.Length;
int endIndex = next.Success ? next.Index : data.Length;
string text = data.Substring(startIndex, endIndex - startIndex);
// Build the span
if (text.Length > 0)
yield return new AnsiSpan { Code = code, Text = text };
// Loop
match = next;
}
}
/// <summary>
/// Removes the bright suffix from the code if present, otherwise appends a bright suffix.
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static string InvertBright(string code)
{
return code.EndsWith(";1")
? code.Substring(0, code.Length - 2)
: code + ";1";
}
}
public sealed class AnsiSpan
{
public string Code { get; set; }
public string Text { get; set; }
/// <summary>
/// Gets the color value for the code.
/// </summary>
/// <param name="colors"></param>
/// <param name="invertBright"></param>
/// <returns></returns>
public string GetColor(IDictionary<string, string> colors, bool invertBright)
{
if (colors == null)
throw new ArgumentNullException("colors");
return GetColor<string>(colors, invertBright);
}
/// <summary>
/// Gets the color value for the code.
/// </summary>
/// <param name="colors"></param>
/// <param name="invertBright"></param>
/// <returns></returns>
public T GetColor<T>(IDictionary<string, T> colors, bool invertBright)
{
if (colors == null)
throw new ArgumentNullException("colors");
string code = invertBright ? AnsiUtils.InvertBright(Code) : Code;
return colors[code];
}
}
}

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

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Attributes
@@ -285,12 +286,37 @@ namespace ICD.Common.Utils.Attributes
#region Methods
/// <summary>
/// Remaps the numeric value into the min-max range of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
[NotNull]
public static object Remap([NotNull] object value, [NotNull] Type type)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double intermediate = RemapToDouble(value);
return RemapFromDouble(intermediate, type);
}
/// <summary>
/// Remaps the given numeric value from its min/max range into double min/max range.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double RemapToDouble(object value)
public static double RemapToDouble([NotNull] object value)
{
if (value == null)
throw new ArgumentNullException("value");
@@ -308,7 +334,8 @@ namespace ICD.Common.Utils.Attributes
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object RemapFromDouble(double value, Type type)
[NotNull]
public static object RemapFromDouble(double value, [NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -320,13 +347,41 @@ namespace ICD.Common.Utils.Attributes
return remap(value);
}
/// <summary>
/// Remaps the given numeric value to the defined min/max.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NotNull]
public object RemapMinMax([NotNull] object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double sourceMin = GetMinAsDouble(value.GetType());
double sourceMax = GetMaxAsDouble(value.GetType());
double targetMin = Convert.ToDouble(Min);
double targetMax = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
/// <summary>
/// Clamps the given numeric value into the valid ranges of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Clamp(object value, Type type)
[NotNull]
public static object Clamp([NotNull] object value, [NotNull] Type type)
{
if (value == null)
throw new ArgumentNullException("value");
@@ -352,7 +407,7 @@ namespace ICD.Common.Utils.Attributes
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static double Clamp(double value, Type type)
public static double Clamp(double value, [NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -364,37 +419,14 @@ namespace ICD.Common.Utils.Attributes
return clamp(value);
}
/// <summary>
/// Remaps the numeric value into the min-max range of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Remap(object value, Type type)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double intermediate = RemapToDouble(value);
return RemapFromDouble(intermediate, type);
}
/// <summary>
/// Clamps the given numeric value to the defined min/max then remaps to the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public object ClampMinMaxThenRemap(object value, Type type)
[NotNull]
public object ClampMinMaxThenRemap([NotNull] object value, [NotNull] Type type)
{
if (value == null)
throw new ArgumentNullException("value");
@@ -418,33 +450,12 @@ namespace ICD.Common.Utils.Attributes
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
/// <summary>
/// Remaps the given numeric value to the defined min/max.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public object RemapMinMax(object value)
{
if (value == null)
throw new ArgumentNullException("value");
#endregion
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
#region Private Methods
double sourceMin = GetMinAsDouble(value.GetType());
double sourceMax = GetMaxAsDouble(value.GetType());
double targetMin = Convert.ToDouble(Min);
double targetMax = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
private object RemapMinMax(object value, Type type)
[NotNull]
private object RemapMinMax([NotNull] object value, [NotNull] Type type)
{
if (value == null)
throw new ArgumentNullException("value");
@@ -471,16 +482,12 @@ namespace ICD.Common.Utils.Attributes
return Convert.ChangeType(remapped, type, CultureInfo.InvariantCulture);
}
#endregion
#region Private Methods
/// <summary>
/// Gets the min value for the given numeric type as a double.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMinAsDouble(Type type)
private static double GetMinAsDouble([NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -500,7 +507,7 @@ namespace ICD.Common.Utils.Attributes
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMaxAsDouble(Type type)
private static double GetMaxAsDouble([NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");

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

@@ -1,6 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
@@ -9,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;
@@ -20,8 +27,10 @@ namespace ICD.Common.Utils.Collections
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValueToKey.Keys; } }
#endregion
@@ -29,27 +38,47 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Constructor.
/// </summary>
public BiDictionary()
: this(null)
public BiDictionary() :
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
/// <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>
public BiDictionary(Dictionary<TKey, TValue> dict)
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
: this(keyComparer, valueComparer)
{
m_KeyToValue = new Dictionary<TKey, TValue>();
m_ValueToKey = new Dictionary<TValue, TKey>();
if (dict == null)
return;
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict)
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
#region Methods
public void Clear()
@@ -58,24 +87,31 @@ namespace ICD.Common.Utils.Collections
m_ValueToKey.Clear();
}
public bool ContainsKey(TKey key)
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(TValue value)
public bool ContainsValue([NotNull] TValue value)
{
return m_ValueToKey.ContainsKey(value);
}
public void Add(TKey key, TValue value)
public void Add([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (ContainsKey(key))
@@ -88,14 +124,16 @@ namespace ICD.Common.Utils.Collections
m_ValueToKey.Add(value, key);
}
public void Set(TKey key, TValue value)
public void Set([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
RemoveKey(key);
@@ -104,18 +142,35 @@ namespace ICD.Common.Utils.Collections
Add(key, value);
}
public TKey GetKey(TValue value)
[NotNull]
public TKey GetKey([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
return m_ValueToKey[value];
}
public TValue GetValue(TKey key)
[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(TKey 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;
@@ -127,8 +182,13 @@ namespace ICD.Common.Utils.Collections
return true;
}
public bool RemoveValue(TValue value)
public bool RemoveValue([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (!ContainsValue(value))
return false;
@@ -137,13 +197,23 @@ namespace ICD.Common.Utils.Collections
return RemoveKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
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 TryGetKey(TValue value, out TKey key)
public bool TryGetKey([NotNull] TValue value, out TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
return m_ValueToKey.TryGetValue(value, out key);
}
@@ -151,9 +221,10 @@ namespace ICD.Common.Utils.Collections
#region IDictionary
TValue IDictionary<TKey, TValue>.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
[NotNull]
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
bool IDictionary<TKey, TValue>.Remove(TKey key)
bool IDictionary<TKey, TValue>.Remove([NotNull] TKey key)
{
return RemoveKey(key);
}
@@ -177,7 +248,7 @@ namespace ICD.Common.Utils.Collections
return RemoveKey(item.Key);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
}
@@ -186,11 +257,13 @@ namespace ICD.Common.Utils.Collections
#region IEnumerable
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_KeyToValue.GetEnumerator();
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();

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;
@@ -49,7 +55,7 @@ namespace ICD.Common.Utils.Collections
/// Constructor.
/// </summary>
/// <param name="items"></param>
public IcdHashSet(IEnumerable<T> items)
public IcdHashSet([NotNull] IEnumerable<T> items)
: this(EqualityComparer<T>.Default, items)
{
}
@@ -58,7 +64,7 @@ namespace ICD.Common.Utils.Collections
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdHashSet(IEqualityComparer<T> comparer)
public IcdHashSet([NotNull] IEqualityComparer<T> comparer)
: this(comparer, Enumerable.Empty<T>())
{
}
@@ -68,7 +74,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="comparer"></param>
/// <param name="items"></param>
public IcdHashSet(IEqualityComparer<T> comparer, IEnumerable<T> items)
public IcdHashSet([NotNull] IEqualityComparer<T> comparer, [NotNull] IEnumerable<T> items)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
@@ -91,7 +97,8 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Union(IEnumerable<T> set)
[NotNull]
public IcdHashSet<T> Union([NotNull] IEnumerable<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -108,7 +115,8 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Subtract(IEnumerable<T> set)
[NotNull]
public IcdHashSet<T> Subtract([NotNull] IEnumerable<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -127,7 +135,8 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Intersection(IEnumerable<T> set)
[NotNull]
public IcdHashSet<T> Intersection([NotNull] IEnumerable<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -146,7 +155,8 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> NonIntersection(IEnumerable<T> set)
[NotNull]
public IcdHashSet<T> NonIntersection([NotNull] IEnumerable<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -170,7 +180,7 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsSubsetOf(IcdHashSet<T> set)
public bool IsSubsetOf([NotNull] IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -184,7 +194,7 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsProperSubsetOf(IcdHashSet<T> set)
public bool IsProperSubsetOf([NotNull] IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -198,7 +208,7 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsSupersetOf(IcdHashSet<T> set)
public bool IsSupersetOf([NotNull] IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -212,7 +222,7 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsProperSupersetOf(IcdHashSet<T> set)
public bool IsProperSupersetOf([NotNull] IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -226,7 +236,7 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool SetEquals(IcdHashSet<T> set)
public bool SetEquals([NotNull] IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
@@ -243,7 +253,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Add(T item)
public bool Add([NotNull] T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
@@ -261,7 +271,7 @@ namespace ICD.Common.Utils.Collections
/// Adds the item to the collection.
/// </summary>
/// <param name="item"></param>
void ICollection<T>.Add(T item)
void ICollection<T>.Add([NotNull] T item)
{
Add(item);
}
@@ -270,7 +280,7 @@ namespace ICD.Common.Utils.Collections
/// Adds each of the items in the sequence to the collection.
/// </summary>
/// <param name="items"></param>
public void AddRange(IEnumerable<T> items)
public void AddRange([NotNull] IEnumerable<T> items)
{
if (items == null)
throw new ArgumentNullException("items");
@@ -293,7 +303,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(T item)
public bool Contains([NotNull] T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
@@ -307,8 +317,11 @@ namespace ICD.Common.Utils.Collections
/// Copies the items of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the items copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of items in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type T cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
public void CopyTo(T[] array, int arrayIndex)
public void CopyTo([NotNull] T[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException("array");
m_Dict.Keys.CopyTo(array, arrayIndex);
}
@@ -319,7 +332,7 @@ namespace ICD.Common.Utils.Collections
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public bool Remove(T item)
public bool Remove([NotNull] T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
@@ -333,7 +346,7 @@ namespace ICD.Common.Utils.Collections
/// Removes each of the items in the sequence from the collection.
/// </summary>
/// <param name="items"></param>
public void RemoveRange(IEnumerable<T> items)
public void RemoveRange([NotNull] IEnumerable<T> items)
{
if (items == null)
throw new ArgumentNullException("items");
@@ -352,6 +365,7 @@ namespace ICD.Common.Utils.Collections
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
[NotNull]
public IEnumerator<T> GetEnumerator()
{
return m_Dict.Keys.GetEnumerator();
@@ -364,6 +378,7 @@ namespace ICD.Common.Utils.Collections
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();

View File

@@ -1,186 +1,651 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
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; } }
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
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(IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdOrderedDictionary(IComparer<TKey> comparer, IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
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
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(TKey key, TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
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 Contains(KeyValuePair<TKey, TValue> item)
{
if (item.Key == null || !m_PrivateDictionary.Contains(item.Key))
return false;
return m_PrivateDictionary[item.Key].Equals(item.Value);
}
public bool ContainsKey(TKey key)
{
return m_Dictionary.ContainsKey(key);
}
public bool Remove(TKey key)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
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;
return m_PrivateDictionary.Contains(key);
}
public bool TryGetValue(TKey key, out TValue value)
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
return m_Dictionary.TryGetValue(key, out value);
if (array == null)
{
throw new ArgumentNullException("array");
}
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++;
}
}
#endregion
#region Private Methods
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in m_PrivateDictionary)
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
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(KeyValuePair<TKey, TValue>[] array, int index)
{
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
@@ -30,6 +36,7 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Gets a reference for locking.
/// </summary>
[NotNull]
public object SyncRoot { get { return this; } }
#endregion
@@ -39,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
@@ -59,7 +66,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void Enqueue(T item)
public void Enqueue([CanBeNull] T item)
{
Enqueue(item, int.MaxValue);
}
@@ -71,16 +78,24 @@ namespace ICD.Common.Utils.Collections
/// <param name="item"></param>
/// <param name="priority"></param>
[PublicAPI]
public void Enqueue(T item, int priority)
public void Enqueue([CanBeNull] T item, int priority)
{
List<T> queue;
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
{
queue = new List<T>();
m_PriorityToQueue.Add(priority, queue);
}
m_PriorityToQueue.GetOrAddNew(priority, () => new List<T>())
.Add(item);
m_Count++;
}
queue.Add(item);
/// <summary>
/// Adds the item to the queue with the given priority at the given index.
/// </summary>
/// <param name="item"></param>
/// <param name="priority"></param>
/// <param name="position"></param>
[PublicAPI]
public void Enqueue([CanBeNull] T item, int priority, int position)
{
m_PriorityToQueue.GetOrAddNew(priority, ()=> new List<T>())
.Insert(position, item);
m_Count++;
}
@@ -89,30 +104,22 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void EnqueueFirst(T item)
public void EnqueueFirst([CanBeNull] T item)
{
const int priority = int.MinValue;
List<T> queue;
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
{
queue = new List<T>();
m_PriorityToQueue.Add(priority, queue);
}
queue.Insert(0, item);
m_PriorityToQueue.GetOrAddNew(int.MinValue, () => new List<T>())
.Insert(0, item);
m_Count++;
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
/// This is useful for reducing duplication, or replacing items with something more pertinant.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
[PublicAPI]
public void EnqueueRemove(T item, Func<T, bool> remove)
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove)
{
if (remove == null)
throw new ArgumentNullException("remove");
@@ -122,19 +129,38 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
/// This is useful for reducing duplication, or replacing items with something more pertinant.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
[PublicAPI]
public void EnqueueRemove(T item, Func<T, bool> remove, int priority)
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority)
{
if (remove == null)
throw new ArgumentNullException("remove");
bool inserted = false;
EnqueueRemove(item, remove, priority, false);
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
/// <param name="deDuplicateToEndOfQueue"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority, bool deDuplicateToEndOfQueue)
{
if (remove == null)
throw new ArgumentNullException("remove");
int lowestMatchingPriority = int.MaxValue;
int? firstMatchingIndex = null;
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue.ToArray())
{
@@ -144,8 +170,11 @@ namespace ICD.Common.Utils.Collections
.Reverse()
.ToArray();
if (removeIndices.Length == 0)
continue;
if (removeIndices.Any() && kvp.Key < lowestMatchingPriority )
{
lowestMatchingPriority = kvp.Key;
firstMatchingIndex = removeIndices.Last();
}
foreach (int removeIndex in removeIndices)
{
@@ -153,26 +182,20 @@ namespace ICD.Common.Utils.Collections
m_Count--;
}
if (!inserted)
{
int insertIndex = removeIndices[0];
if (insertIndex >= kvp.Value.Count)
kvp.Value.Add(item);
else
kvp.Value.Insert(insertIndex, item);
m_Count++;
inserted = true;
}
if (kvp.Value.Count == 0)
m_PriorityToQueue.Remove(kvp.Key);
}
if (!inserted)
if(deDuplicateToEndOfQueue)
Enqueue(item, priority);
else
{
if(firstMatchingIndex == null)
Enqueue(item, lowestMatchingPriority);
else
Enqueue(item, lowestMatchingPriority, firstMatchingIndex.Value);
}
}
/// <summary>
@@ -180,6 +203,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <returns></returns>
[PublicAPI]
[CanBeNull]
public T Dequeue()
{
T output;
@@ -227,6 +251,7 @@ namespace ICD.Common.Utils.Collections
/// Gets an enumerator for the items.
/// </summary>
/// <returns></returns>
[NotNull]
public IEnumerator<T> GetEnumerator()
{
return m_PriorityToQueue.Values
@@ -239,8 +264,11 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="array"></param>
/// <param name="index"></param>
public void CopyTo(Array array, int index)
public void CopyTo([NotNull] Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
foreach (T item in this)
array.SetValue(item, index++);
}
@@ -253,6 +281,7 @@ namespace ICD.Common.Utils.Collections
/// Gets an enumerator for the items.
/// </summary>
/// <returns></returns>
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();

View File

@@ -1,191 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.Timers;
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
/// </summary>
public sealed class RateLimitedEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
{
/// <summary>
/// Raised to handle to the next item in the queue.
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
private readonly SafeTimer m_DequeueTimer;
private readonly Queue<T> m_Queue;
private readonly SafeCriticalSection m_QueueSection;
#region Properties
/// <summary>
/// Gets/sets the time between dequeues in milliseconds.
/// </summary>
public long BetweenMilliseconds { get; set; }
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
bool ICollection.IsSynchronized { get { return true; } }
object ICollection.SyncRoot { get { return this; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public RateLimitedEventQueue()
{
m_Queue = new Queue<T>();
m_QueueSection = new SafeCriticalSection();
m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback);
}
#region Methods
/// <summary>
/// Release resources.
/// </summary>
public void Dispose()
{
OnItemDequeued = null;
m_QueueSection.Enter();
try
{
m_DequeueTimer.Dispose();
}
finally
{
m_QueueSection.Leave();
}
}
/// <summary>
/// Enqueues the given item.
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
m_QueueSection.Enter();
try
{
m_Queue.Enqueue(item);
if (m_Queue.Count == 1)
SendNext();
}
finally
{
m_QueueSection.Leave();
}
}
/// <summary>
/// Clears the queued items.
/// </summary>
public void Clear()
{
m_QueueSection.Enter();
try
{
m_DequeueTimer.Stop();
m_Queue.Clear();
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
#region Private Methods
/// <summary>
/// Sends the next pulse in the queue.
/// </summary>
private void SendNext()
{
if (!m_QueueSection.TryEnter())
return;
try
{
if (m_Queue.Count == 0)
return;
T item = m_Queue.Peek();
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
m_DequeueTimer.Reset(BetweenMilliseconds);
}
finally
{
m_QueueSection.Leave();
}
}
/// <summary>
/// Called when the dequeue timer elapses.
/// </summary>
private void DequeueTimerCallback()
{
m_QueueSection.Enter();
try
{
m_Queue.Dequeue();
SendNext();
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
#region IEnumerable/ICollection
public IEnumerator<T> GetEnumerator()
{
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
m_QueueSection.Enter();
try
{
foreach (T item in this)
{
array.SetValue(item, index);
index++;
}
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
}
}

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;
@@ -31,7 +37,10 @@ namespace ICD.Common.Utils.Collections
m_MaxSize = value;
Trim();
TContents unused;
while (Trim(out unused))
{
}
}
}
@@ -79,11 +88,13 @@ namespace ICD.Common.Utils.Collections
/// Appends the item to the queue, trims old items that exceed max length.
/// </summary>
/// <param name="item"></param>
/// <param name="removed"></param>
/// <returns>Returns true if an item was dequeued.</returns>
[PublicAPI]
public void Enqueue(TContents item)
public bool Enqueue(TContents item, out TContents removed)
{
m_Collection.AddLast(item);
Trim();
return Trim(out removed);
}
/// <summary>
@@ -98,6 +109,24 @@ namespace ICD.Common.Utils.Collections
return output;
}
/// <summary>
/// Dequeues the next item in the queue. Returns false if the queue is empty.
/// </summary>
/// <typeparam name="TContents"></typeparam>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public bool Dequeue(out TContents item)
{
item = default(TContents);
if (Count == 0)
return false;
item = Dequeue();
return true;
}
/// <summary>
/// Returns the oldest item in the queue.
/// </summary>
@@ -122,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++;
}
}
@@ -138,10 +167,17 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Removes items that fall outside of the max size.
/// </summary>
private void Trim()
private bool Trim(out TContents removed)
{
while (Count > MaxSize)
m_Collection.RemoveFirst();
removed = default(TContents);
if (Count <= MaxSize)
return false;
removed = m_Collection.First.Value;
m_Collection.RemoveFirst();
return true;
}
#endregion

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

View File

@@ -1,111 +0,0 @@
using System;
namespace ICD.Common.Utils
{
public enum eConsoleColor
{
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
YellowOnRed
}
public static class ConsoleColorExtensions
{
public const string CONSOLE_RED = "\x1B[31;1m";
public const string CONSOLE_GREEN = "\x1B[32;1m";
public const string CONSOLE_YELLOW = "\x1B[33;1m";
public const string CONSOLE_BLUE = "\x1B[34;1m";
public const string CONSOLE_MAGENTA = "\x1B[35;1m";
public const string CONSOLE_CYAN = "\x1B[36;1m";
public const string CONSOLE_WHITE = "\x1B[37;1m";
public const string CONSOLE_YELLOW_ON_RED_BACKGROUND = "\x1B[93;41m";
public const string CONSOLE_RESET = "\x1B[0m";
public static string FormatAnsi(this eConsoleColor extends, string data)
{
// % needs to be escaped or weird things happen
data = string.IsNullOrEmpty(data) ? data : data.Replace("%", "%%");
return string.Format("{0}{1}{2}", extends.ToAnsiPrefix(), data, CONSOLE_RESET);
}
public static string ToAnsiPrefix(this eConsoleColor extends)
{
switch (extends)
{
case eConsoleColor.Red:
return CONSOLE_RED;
case eConsoleColor.Green:
return CONSOLE_GREEN;
case eConsoleColor.Yellow:
return CONSOLE_YELLOW;
case eConsoleColor.Blue:
return CONSOLE_BLUE;
case eConsoleColor.Magenta:
return CONSOLE_MAGENTA;
case eConsoleColor.Cyan:
return CONSOLE_CYAN;
case eConsoleColor.White:
return CONSOLE_WHITE;
case eConsoleColor.YellowOnRed:
return CONSOLE_YELLOW_ON_RED_BACKGROUND;
default:
throw new ArgumentOutOfRangeException("extends");
}
}
#if STANDARD
public static ConsoleColor ToForegroundConsoleColor(this eConsoleColor extends)
{
switch (extends)
{
case eConsoleColor.Red:
return ConsoleColor.Red;
case eConsoleColor.Green:
return ConsoleColor.Green;
case eConsoleColor.Yellow:
case eConsoleColor.YellowOnRed:
return ConsoleColor.Yellow;
case eConsoleColor.Blue:
return ConsoleColor.Blue;
case eConsoleColor.Magenta:
return ConsoleColor.Magenta;
case eConsoleColor.Cyan:
return ConsoleColor.Cyan;
case eConsoleColor.White:
return ConsoleColor.White;
default:
throw new ArgumentOutOfRangeException("extends");
}
}
public static ConsoleColor ToBackgroundConsoleColor(this eConsoleColor extends)
{
switch (extends)
{
case eConsoleColor.Red:
case eConsoleColor.Green:
case eConsoleColor.Yellow:
case eConsoleColor.Blue:
case eConsoleColor.Magenta:
case eConsoleColor.Cyan:
case eConsoleColor.White:
return ConsoleColor.Black;
case eConsoleColor.YellowOnRed:
return ConsoleColor.Red;
default:
throw new ArgumentOutOfRangeException("extends");
}
}
#endif
}
}

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;
@@ -11,44 +10,49 @@ namespace ICD.Common.Utils.Csv
private const string DOUBLE_QUOTE_MARK = "\"\"";
private readonly IcdTextWriter m_Writer;
private readonly CsvWriterSettings m_Settings;
private readonly string m_Seperator;
private readonly string m_LineTerminator;
private readonly bool m_AlwaysEscape;
/// <summary>
/// Are we currently at the beginning of a new line?
/// </summary>
private bool m_NewLine;
#region Properties
private string Separator { get { return m_Settings.InsertSpaceAfterComma ? ", " : ","; } }
private string LineTerminator { get { return m_Settings.NewLineSequence; } }
#endregion
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
public CsvWriter(IcdTextWriter writer, bool spaceAfterComma, bool alwaysEscape, string newline, params string[] header)
public CsvWriter([NotNull] IcdTextWriter writer,
[NotNull] CsvWriterSettings settings)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (settings == null)
throw new ArgumentNullException("settings");
m_NewLine = true;
m_Writer = writer;
m_Seperator = spaceAfterComma ? ", " : ",";
m_AlwaysEscape = alwaysEscape;
m_LineTerminator = newline;
if(header.Any())
AppendRow(header);
m_Settings = settings;
}
~CsvWriter()
#endregion
public void Dispose()
{
Dispose();
m_Writer.Dispose();
}
/// <summary>
/// Calls ToString() for each item and adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params object[] row)
{
foreach (object value in row)
AppendValue(value);
AppendNewline();
}
#region Methods
/// <summary>
/// Adds the row to the builder.
@@ -82,9 +86,9 @@ namespace ICD.Common.Utils.Csv
value = value ?? string.Empty;
if (!m_NewLine)
m_Writer.WrappedTextWriter.Write(m_Seperator);
m_Writer.WrappedTextWriter.Write(Separator);
if (m_AlwaysEscape || value.Contains(","))
if (m_Settings.AlwaysEscapeEveryValue || value.Contains(","))
{
value = value.Replace(QUOTATION_MARK, DOUBLE_QUOTE_MARK);
@@ -107,31 +111,11 @@ namespace ICD.Common.Utils.Csv
[PublicAPI]
public void AppendNewline()
{
m_Writer.WrappedTextWriter.Write(m_LineTerminator);
m_Writer.WrappedTextWriter.Write(LineTerminator);
m_NewLine = true;
}
public void Dispose()
{
m_Writer.Dispose();
}
/// <summary>
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
/// </summary>
/// <param name="writer"></param>
/// <param name="settings"></param>
/// <param name="header"></param>
/// <returns></returns>
[PublicAPI]
public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header)
{
return new CsvWriter(writer,
settings.InsertSpaceAfterComma,
settings.AlwaysEscapeEveryValue,
settings.NewLineSequence,
header);
}
#endregion
}
}
}

View File

@@ -1,4 +1,7 @@
namespace ICD.Common.Utils
using System;
using System.Globalization;
namespace ICD.Common.Utils
{
public static class DateTimeUtils
{
@@ -11,5 +14,35 @@
{
return MathUtils.Modulus(hour + 11, 12) + 1;
}
/// <summary>
/// Creates a DateTime from the given number of milliseconds since the epoch (1970-01-01T00:00:00Z)
/// </summary>
/// <param name="milliseconds">milliseconds since the epoch</param>
/// <returns></returns>
public static DateTime FromEpochMilliseconds(long milliseconds)
{
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds);
}
/// <summary>
/// Creates a DateTime from the given number of seconds since the epoch (1970-01-01T00:00:00Z)
/// </summary>
/// <param name="seconds">seconds since the epoch</param>
/// <returns></returns>
public static DateTime FromEpochSeconds(long seconds)
{
return FromEpochMilliseconds(seconds * 1000);
}
/// <summary>
/// Returns a DateTime for the given ISO-8601 string.
/// </summary>
/// <param name="iso"></param>
/// <returns></returns>
public static DateTime FromIso8601(string iso)
{
return DateTime.Parse(iso, null, DateTimeStyles.RoundtripKind);
}
}
}

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
@@ -13,16 +14,21 @@ namespace ICD.Common.Utils
{
public static class EnumUtils
{
private static readonly Dictionary<Type, object> s_EnumValuesCache;
private static readonly Dictionary<Type, Dictionary<int, object>> s_EnumFlagsCache;
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.
/// </summary>
static EnumUtils()
{
s_EnumValuesCache = new Dictionary<Type, object>();
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
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,7 +64,7 @@ namespace ICD.Common.Utils
/// <returns></returns>
public static bool IsEnum<T>(T value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable once CompareNonConstrainedGenericWithNull
return value != null && IsEnumType(value.GetType());
}
@@ -123,6 +129,30 @@ namespace ICD.Common.Utils
#region Values
/// <summary>
/// Gets the names of the values in the enumeration.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<string> GetNames<T>()
where T : struct, IConvertible
{
return GetNames(typeof(T));
}
/// <summary>
/// Gets the names of the values in the enumeration.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<string> GetNames([NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValues(type).Select(v => v.ToString());
}
/// <summary>
/// Gets the values from an enumeration.
/// </summary>
@@ -131,46 +161,51 @@ namespace ICD.Common.Utils
public static IEnumerable<T> GetValues<T>()
where T : struct, IConvertible
{
Type type = typeof(T);
// Reflection is slow and this method is called a lot, so we cache the results.
object cache;
if (!s_EnumValuesCache.TryGetValue(type, out cache))
{
cache = GetValuesUncached<T>().ToArray();
s_EnumValuesCache[type] = cache;
}
return cache as T[];
return GetValues(typeof(T)).Cast<T>();
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// Gets the values from an enumeration.
/// </summary>
/// <returns></returns>
private static IEnumerable<int> GetValues(Type type)
public static IEnumerable<object> GetValues(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValuesUncached(type);
// Reflection is slow and this method is called a lot, so we cache the results.
return s_EnumValuesCacheSection.Execute(() => s_EnumValuesCache.GetOrAddNew(type, () => GetValuesUncached(type).ToArray()));
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// Gets the values from an enumeration except the 0 value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
private static IEnumerable<T> GetValuesUncached<T>()
public static IEnumerable<T> GetValuesExceptNone<T>()
where T : struct, IConvertible
{
return GetValuesUncached(typeof(T)).Select(i => (T)(object)i);
return GetValuesExceptNone(typeof(T)).Cast<T>();
}
/// <summary>
/// Gets the values from an enumeration except the 0 value.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<object> GetValuesExceptNone([NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValues(type).Where(v => (int)v != 0);
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// </summary>
/// <returns></returns>
private static IEnumerable<int> GetValuesUncached(Type type)
private static IEnumerable<object> GetValuesUncached(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -182,34 +217,10 @@ namespace ICD.Common.Utils
#if SIMPLSHARP
.GetCType()
#else
.GetTypeInfo()
.GetTypeInfo()
#endif
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Select(x => x.GetValue(null))
.Cast<int>();
}
/// <summary>
/// Gets the values from an enumeration except the 0 value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetValuesExceptNone<T>()
where T : struct, IConvertible
{
return GetFlagsExceptNone<T>();
}
/// <summary>
/// Gets the values from an enumeration except the 0 value without performing any caching. This is slow because of reflection.
/// </summary>
/// <returns></returns>
public static IEnumerable<int> GetValuesExceptNone(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValues(type).Except(0);
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Select(x => x.GetValue(null));
}
#endregion
@@ -330,24 +341,22 @@ namespace ICD.Common.Utils
public static IEnumerable<T> GetFlags<T>(T value)
where T : struct, IConvertible
{
Type type = typeof(T);
int valueInt = (int)(object)value;
return GetFlags(typeof(T), value).Cast<T>();
}
Dictionary<int, object> cache;
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
{
cache = new Dictionary<int, object>();
s_EnumFlagsCache[type] = cache;
}
object flags;
if (!cache.TryGetValue(valueInt, out flags))
{
flags = GetValues<T>().Where(e => HasFlag(value, e)).ToArray();
cache[valueInt] = flags;
}
return flags as T[];
/// <summary>
/// Gets all of the set flags on the given enum.
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<object> GetFlags(Type type, object value)
{
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache
.GetOrAddNew(type, () => new Dictionary<object, object[]>())
.GetOrAddNew(value, () => GetValues(type)
.Where(f => !HasMultipleFlags((int)f) && HasFlag(value, f))
.ToArray()));
}
/// <summary>
@@ -358,8 +367,7 @@ namespace ICD.Common.Utils
public static IEnumerable<T> GetFlagsExceptNone<T>()
where T : struct, IConvertible
{
T allValue = GetFlagsAllValue<T>();
return GetFlagsExceptNone(allValue);
return GetFlagsExceptNone(typeof(T)).Cast<T>();
}
/// <summary>
@@ -371,7 +379,35 @@ namespace ICD.Common.Utils
public static IEnumerable<T> GetFlagsExceptNone<T>(T value)
where T : struct, IConvertible
{
return GetFlags(value).Except(default(T));
return GetFlagsExceptNone(typeof(T), value).Cast<T>();
}
/// <summary>
/// Gets all of the set flags on the given enum type except 0.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<object> GetFlagsExceptNone([NotNull] Type type)
{
if (type == null)
throw new ArgumentNullException("type");
object allValue = GetFlagsAllValue(type);
return GetFlagsExceptNone(type, allValue);
}
/// <summary>
/// Gets all of the set flags on the given enum except 0.
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<object> GetFlagsExceptNone([NotNull] Type type, object value)
{
if (type == null)
throw new ArgumentNullException("type");
return GetFlags(type, value).Where(f => (int)f != 0);
}
/// <summary>
@@ -412,7 +448,20 @@ namespace ICD.Common.Utils
if (type == null)
throw new ArgumentNullException("type");
return GetValuesUncached(type).Aggregate(0, (current, value) => current | 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>
@@ -428,6 +477,17 @@ namespace ICD.Common.Utils
return HasFlags(value, flag);
}
/// <summary>
/// Returns true if the enum contains the given flag.
/// </summary>
/// <param name="value"></param>
/// <param name="flag"></param>
/// <returns></returns>
public static bool HasFlag(object value, object flag)
{
return HasFlag((int)value, (int)flag);
}
/// <summary>
/// Returns true if the enum contains the given flag.
/// </summary>
@@ -717,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

@@ -0,0 +1,31 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public sealed class DateTimeNullableEventArgs : GenericEventArgs<DateTime?>
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="data"></param>
public DateTimeNullableEventArgs(DateTime? data) : base(data)
{
}
}
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,13 +1,22 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.EventArguments
{
public class GenericEventArgs<T> : EventArgs, IGenericEventArgs<T>
{
private readonly T m_Data;
/// <summary>
/// Gets the wrapped data associated with the event.
/// </summary>
public T Data { get; private set; }
object IGenericEventArgs.Data { get { return Data; } }
/// <summary>
/// Gets the wrapped data associated with the event.
/// </summary>
public T Data { get { return m_Data; } }
/// <summary>
/// Constructor.
@@ -15,7 +24,21 @@ namespace ICD.Common.Utils.EventArguments
/// <param name="data"></param>
public GenericEventArgs(T data)
{
Data = data;
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,10 +1,18 @@
namespace ICD.Common.Utils.EventArguments
{
public interface IGenericEventArgs<T>
public interface IGenericEventArgs
{
/// <summary>
/// Gets the wrapped data associated with the event.
/// </summary>
T Data { get; }
object Data { get; }
}
public interface IGenericEventArgs<T> : IGenericEventArgs
{
/// <summary>
/// Gets the wrapped data associated with the event.
/// </summary>
new T Data { get; }
}
}

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

@@ -18,7 +18,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[CanBeNull]
public static string GetPath(this Assembly extends)
public static string GetPath([NotNull]this Assembly extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -29,17 +29,16 @@ namespace ICD.Common.Utils.Extensions
#endif
.CodeBase;
if (path == null)
{
#if STANDARD
#if !SIMPLSHARP
if (string.IsNullOrEmpty(path))
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;
@@ -51,7 +50,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static DateTime GetCreationTime(this Assembly extends)
public static DateTime GetCreationTime([NotNull]this Assembly extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -66,7 +65,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static string GetInformationalVersion(this Assembly extends)
public static string GetInformationalVersion([NotNull]this Assembly extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -85,7 +84,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="version"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryGetInformationalVersion(this Assembly extends, out string version)
public static bool TryGetInformationalVersion([NotNull]this Assembly extends, out string version)
{
if (extends == null)
throw new ArgumentNullException("extends");

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

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -10,7 +11,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool Uses24HourFormat(this CultureInfo extends)
public static bool Uses24HourFormat([NotNull]this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -22,7 +23,7 @@ namespace ICD.Common.Utils.Extensions
/// Updates the time patterns for the given culture to use 12 hour time.
/// </summary>
/// <param name="extends"></param>
public static void ConvertTo12HourCulture(this CultureInfo extends)
public static void ConvertTo12HourCulture([NotNull]this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -41,7 +42,7 @@ namespace ICD.Common.Utils.Extensions
/// Updates the time patterns for the given culture to use 24 hour time.
/// </summary>
/// <param name="extends"></param>
public static void ConvertTo24HourCulture(this CultureInfo extends)
public static void ConvertTo24HourCulture([NotNull]this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ICD.Common.Properties;
@@ -38,14 +39,29 @@ namespace ICD.Common.Utils.Extensions
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, params DateTime[] times)
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, [NotNull] params DateTime[] times)
{
if (times == null)
throw new ArgumentNullException("times");
return target.NextEarliestTime(inclusive, (IEnumerable<DateTime>)times);
}
/// <summary>
/// Returns the closest DateTime to the target time that is greater than the target time
/// </summary>
/// <param name="target"></param>
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, [NotNull] IEnumerable<DateTime> times)
{
if (times == null)
throw new ArgumentNullException("times");
DateTime earliestTime;
bool success = times.OrderBy(dt => dt).TryFirst(dt => inclusive ? target <= dt : target < dt, out earliestTime);
return success ? earliestTime : (DateTime?) null;
return success ? earliestTime : (DateTime?)null;
}
/// <summary>
@@ -55,14 +71,29 @@ namespace ICD.Common.Utils.Extensions
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, params DateTime[] times)
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, [NotNull] params DateTime[] times)
{
if (times == null)
throw new ArgumentNullException("null");
throw new ArgumentNullException("times");
return target.PreviousLatestTime(inclusive, (IEnumerable<DateTime>)times);
}
/// <summary>
/// Returns the closest DateTime to the target time that is less than the target time
/// </summary>
/// <param name="target"></param>
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, [NotNull] IEnumerable<DateTime> times)
{
if (times == null)
throw new ArgumentNullException("times");
DateTime latestTime;
bool success = times.OrderByDescending(dt => dt).TryFirst(dt => inclusive ? target >= dt : target > dt, out latestTime);
return success ? latestTime : (DateTime?) null;
return success ? latestTime : (DateTime?)null;
}
/// <summary>
@@ -85,6 +116,21 @@ namespace ICD.Common.Utils.Extensions
return extends.StartOfDay() + new TimeSpan(24, 0, 0);
}
/// <summary>
/// Returns the give date in ISO-8601 format.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToIso(this DateTime extends)
{
return extends.ToString("o");
}
/// <summary>
/// Returns the given date in unix timestamp format.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static double ToUnixTimestamp(this DateTime extends)
{
@@ -92,5 +138,101 @@ namespace ICD.Common.Utils.Extensions
TimeSpan diff = extends.ToUniversalTime() - origin;
return Math.Floor(diff.TotalSeconds);
}
/// <summary>
/// Adds the given number of years to the date, and checks if the day is still valid (basically only for leap days).
/// </summary>
/// <param name="extends"></param>
/// <param name="years"></param>
/// <returns></returns>
public static DateTime AddYearsAndWrap(this DateTime extends, int years)
{
years += extends.Year;
// need to check days in month due to leap year
int daysInMonth = DateTime.DaysInMonth(years, extends.Month);
int day = daysInMonth < extends.Day ? daysInMonth : extends.Day;
return new DateTime(years, extends.Month, day, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
}
/// <summary>
/// Adds the given number of months to the date, wrapping every 12 months, and lowering the day in the month is not valid.
/// </summary>
/// <param name="extends"></param>
/// <param name="months"></param>
/// <returns></returns>
public static DateTime AddMonthsAndWrap(this DateTime extends, int months)
{
months = MathUtils.Modulus(months + extends.Month - 1, 12) + 1;
int daysInMonth = DateTime.DaysInMonth(extends.Year, months);
int day = daysInMonth < extends.Day ? daysInMonth : extends.Day;
return new DateTime(extends.Year, months, day, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
}
/// <summary>
/// Adds the given number of days to the date, wrapping when the number of days exceeds the days in a month.
/// </summary>
/// <param name="extends"></param>
/// <param name="days"></param>
/// <returns></returns>
public static DateTime AddDaysAndWrap(this DateTime extends, int days)
{
days = MathUtils.Modulus(days + extends.Day - 1, DateTime.DaysInMonth(extends.Year, extends.Month)) + 1;
return new DateTime(extends.Year, extends.Month, days, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
}
/// <summary>
/// Adds the given number of hours to the time, wrapping every 24 hours without modifying the day.
/// </summary>
/// <param name="extends"></param>
/// <param name="hours"></param>
/// <returns></returns>
public static DateTime AddHoursAndWrap(this DateTime extends, int hours)
{
hours = MathUtils.Modulus(hours + extends.Hour, 24);
return new DateTime(extends.Year, extends.Month, extends.Day, hours, extends.Minute, extends.Second, extends.Millisecond);
}
/// <summary>
/// Adds the given number of hours to the time, wrapping within the current 12 hour span, without modifying the day.
/// </summary>
/// <param name="extends"></param>
/// <param name="hours"></param>
/// <returns></returns>
public static DateTime AddHoursAndWrap12Hour(this DateTime extends, int hours)
{
int currentHour = extends.Hour;
bool am = extends.Hour < 12;
int current12Hour = MathUtils.Modulus(currentHour, 12);
int new12Hour = MathUtils.Modulus(current12Hour + hours, 12);
return am
? new DateTime(extends.Year, extends.Month, extends.Day, new12Hour, extends.Minute, extends.Second, extends.Millisecond)
: new DateTime(extends.Year, extends.Month, extends.Day, new12Hour + 12, extends.Minute, extends.Second, extends.Millisecond);
}
/// <summary>
/// Adds the given number of minutes to the time, wrapping every 60 minutes without modifying the hour.
/// </summary>
/// <param name="extends"></param>
/// <param name="minutes"></param>
/// <returns></returns>
public static DateTime AddMinutesAndWrap(this DateTime extends, int minutes)
{
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

@@ -7,6 +7,29 @@ namespace ICD.Common.Utils.Extensions
{
public static class DictionaryExtensions
{
/// <summary>
/// Removes the key from the dictionary, outputting the value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool Remove<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key, out TValue value)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return extends.TryGetValue(key, out value) && extends.Remove(key);
}
/// <summary>
/// Removes all of the given keys from the dictionary.
/// </summary>
@@ -14,7 +37,8 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="keys"></param>
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TKey> keys)
public static void RemoveAll<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<TKey> keys)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -35,7 +59,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="value"></param>
/// <returns>False if value is not found in the dictionary.</returns>
[PublicAPI]
public static bool RemoveValue<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
public static bool RemoveValue<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -52,7 +76,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="value"></param>
[PublicAPI]
public static void RemoveAllValues<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
public static void RemoveAllValues<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -71,13 +95,15 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[CanBeNull]
[PublicAPI]
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
public static TValue GetDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return extends.GetDefault(key, default(TValue));
@@ -93,13 +119,16 @@ namespace ICD.Common.Utils.Extensions
/// <param name="defaultValue"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key, TValue defaultValue)
public static TValue GetDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key,
TValue defaultValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
TValue value;
@@ -116,14 +145,15 @@ namespace ICD.Common.Utils.Extensions
/// <param name="defaultValue"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key,
TValue defaultValue)
public static TValue GetOrAddDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key, TValue defaultValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable once CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
TValue value = extends.GetDefault(key, defaultValue);
@@ -141,15 +171,16 @@ namespace ICD.Common.Utils.Extensions
/// <param name="key"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key)
where TValue : new()
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return extends.GetOrAddNew(key, () => ReflectionUtils.CreateInstance<TValue>());
@@ -165,16 +196,21 @@ namespace ICD.Common.Utils.Extensions
/// <param name="valueFunc"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key, Func<TValue> valueFunc)
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key,
[NotNull] Func<TValue> valueFunc)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (valueFunc == null)
throw new ArgumentNullException("valueFunc");
TValue value;
if (!extends.TryGetValue(key, out value))
{
@@ -195,7 +231,7 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException">The value does not exist in the dictionary.</exception>
[PublicAPI]
public static TKey GetKey<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
public static TKey GetKey<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -218,7 +254,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="key"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryGetKey<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value, out TKey key)
public static bool TryGetKey<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value,
out TKey key)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -235,7 +272,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<TKey> GetKeys<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
public static IEnumerable<TKey> GetKeys<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
TValue value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -253,8 +291,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="other"></param>
/// <returns></returns>
[PublicAPI]
public static bool Update<TKey, TValue>(this IDictionary<TKey, TValue> extends,
IEnumerable<KeyValuePair<TKey, TValue>> other)
public static bool Update<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> other)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -275,9 +313,9 @@ namespace ICD.Common.Utils.Extensions
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool Update<TKey, TValue>(this IDictionary<TKey, TValue> extends,
IEnumerable<KeyValuePair<TKey, TValue>> other,
IEqualityComparer<TValue> comparer)
public static bool Update<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> other,
[NotNull] IEqualityComparer<TValue> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -285,6 +323,9 @@ namespace ICD.Common.Utils.Extensions
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
bool change = false;
foreach (KeyValuePair<TKey, TValue> pair in other)
@@ -308,7 +349,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="items"></param>
[PublicAPI]
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<KeyValuePair<TKey, TValue>> items)
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -329,8 +371,9 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param>
/// <param name="getKey"></param>
[PublicAPI]
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TValue> items,
Func<TValue, TKey> getKey)
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<TValue> items,
[NotNull] Func<TValue, TKey> getKey)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -354,8 +397,9 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param>
/// <param name="getValue"></param>
[PublicAPI]
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TKey> items,
Func<TKey, TValue> getValue)
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IEnumerable<TKey> items,
[NotNull] Func<TKey, TValue> getValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -378,8 +422,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="items"></param>
[PublicAPI]
public static void AddRange<TKey, TValue>(this Dictionary<TKey, TValue> extends,
IEnumerable<KeyValuePair<TKey, TValue>> items)
public static void AddRange<TKey, TValue>([NotNull] this Dictionary<TKey, TValue> extends,
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -400,12 +444,15 @@ namespace ICD.Common.Utils.Extensions
/// <param name="other"></param>
/// <returns></returns>
[PublicAPI]
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
IDictionary<TKey, TValue> other)
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IDictionary<TKey, TValue> other)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
return extends.DictionaryEqual(other, EqualityComparer<TValue>.Default);
}
@@ -419,13 +466,16 @@ namespace ICD.Common.Utils.Extensions
/// <param name="valueComparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
IDictionary<TKey, TValue> other,
IEqualityComparer<TValue> valueComparer)
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IDictionary<TKey, TValue> other,
[NotNull] IEqualityComparer<TValue> valueComparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (valueComparer == null)
throw new ArgumentNullException("valueComparer");
@@ -442,20 +492,21 @@ namespace ICD.Common.Utils.Extensions
/// <param name="valueComparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
IDictionary<TKey, TValue> other,
Func<TValue, TValue, bool> valueComparer)
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] IDictionary<TKey, TValue> other,
[NotNull] Func<TValue, TValue, bool> valueComparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (valueComparer == null)
throw new ArgumentNullException("valueComparer");
if (extends == other)
return true;
if (other == null)
return false;
if (extends.Count != other.Count)
return false;
@@ -467,6 +518,7 @@ namespace ICD.Common.Utils.Extensions
if (!valueComparer(kvp.Value, secondValue))
return false;
}
return true;
}
@@ -479,7 +531,7 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[PublicAPI]
public static IEnumerable<KeyValuePair<TKey, TValue>> OrderByKey<TKey, TValue>(
this IEnumerable<KeyValuePair<TKey, TValue>> extends)
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -495,7 +547,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<TValue> OrderValuesByKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
public static IEnumerable<TValue> OrderValuesByKey<TKey, TValue>(
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -511,7 +564,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -532,5 +586,22 @@ namespace ICD.Common.Utils.Extensions
return output;
}
/// <summary>
/// Turns an enumerable of KeyValuePairs back into a dictionary
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.ToDictionary(x => x.Key, x => x.Value);
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
@@ -32,7 +34,51 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Returns the enum value as a
/// Returns these enum flags, excluding the other enum flags.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <returns></returns>
[PublicAPI]
public static T ExcludeFlags<T>(this T extends, T other)
where T : struct, IConvertible
{
return EnumUtils.ExcludeFlags(extends, other);
}
/// <summary>
/// Returns these enum flags, including the other enum flags.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <returns></returns>
[PublicAPI]
public static T IncludeFlags<T>(this T extends, T other)
where T : struct, IConvertible
{
return EnumUtils.IncludeFlags(extends, other);
}
/// <summary>
/// Returns the enum, with the other flags set or unset
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="set"></param>
/// <returns></returns>
public static T SetFlags<T>(this T extends, T other, bool set)
where T : struct, IConvertible
{
return set ?
EnumUtils.IncludeFlags(extends, other) :
EnumUtils.ExcludeFlags(extends, other);
}
/// <summary>
/// Returns the enum value as a ushort.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
@@ -43,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];
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,5 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -17,7 +13,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <param name="sender"></param>
public static void Raise(this EventHandler extends, object sender)
public static void Raise([CanBeNull] this EventHandler extends, object sender)
{
if (extends != null)
extends(sender, EventArgs.Empty);
@@ -30,7 +26,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="sender"></param>
/// <param name="args"></param>
public static void Raise<T>(this EventHandler<T> extends, object sender, T args)
public static void Raise<T>([CanBeNull]this EventHandler<T> extends, object sender, [NotNull]T args)
where T : EventArgs
{
if (args == null)
@@ -39,22 +35,5 @@ namespace ICD.Common.Utils.Extensions
if (extends != null)
extends(sender, args);
}
/// <summary>
/// Cross-platform shim for getting MethodInfo for the delegate.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static MethodInfo GetMethodInfo(this Delegate extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
#if SIMPLSHARP
return extends.GetMethod();
#else
return extends.Method;
#endif
}
}
}

View File

@@ -1,6 +1,12 @@
using System;
using ICD.Common.Properties;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -15,7 +21,7 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends)
public static T ReadAsObject<T>([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -26,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>(this JsonReader extends, JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (serializer == null)
throw new ArgumentNullException("serializer");
return serializer.Deserialize<T>(extends);
}
@@ -53,8 +41,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="serializer"></param>
/// <param name="readPropertyValue"></param>
public static void ReadObject(this JsonReader extends, JsonSerializer serializer,
Action<string, JsonReader, JsonSerializer> readPropertyValue)
public static void ReadObject([NotNull] this JsonReader extends, [NotNull] JsonSerializer serializer,
[NotNull] Action<string, JsonReader, JsonSerializer> readPropertyValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -69,7 +57,8 @@ namespace ICD.Common.Utils.Extensions
return;
if (extends.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, extends.TokenType));
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject,
extends.TokenType));
while (extends.Read())
{
@@ -94,11 +83,15 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Type GetValueAsType(this JsonReader extends)
[CanBeNull]
public static Type GetValueAsType([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Null)
return null;
string value = extends.GetValueAsString();
return Type.GetType(value);
}
@@ -109,7 +102,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static uint GetValueAsUInt(this JsonReader extends)
public static uint GetValueAsUInt([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -117,7 +110,8 @@ namespace ICD.Common.Utils.Extensions
if (extends.TokenType == JsonToken.Integer)
return (uint)(long)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
@@ -127,7 +121,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static int GetValueAsInt(this JsonReader extends)
public static int GetValueAsInt([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -135,7 +129,84 @@ namespace ICD.Common.Utils.Extensions
if (extends.TokenType == JsonToken.Integer)
return (int)(long)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a long.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static long GetValueAsLong([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Integer)
return (long)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as an unsigned long.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static ulong GetValueAsULong([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Integer)
return (ulong)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a float.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static float GetValueAsFloat([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Float || extends.TokenType == JsonToken.Integer)
return (float)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
/// <summary>
/// Gets the current value as a double.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static double GetValueAsDouble([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.Float || extends.TokenType == JsonToken.Integer)
return (double)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Integer);
throw new InvalidCastException(message);
}
@@ -145,7 +216,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static string GetValueAsString(this JsonReader extends)
public static string GetValueAsString([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -168,7 +239,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static bool GetValueAsBool(this JsonReader extends)
public static bool GetValueAsBool([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -176,7 +247,8 @@ namespace ICD.Common.Utils.Extensions
if (extends.TokenType == JsonToken.Boolean)
return (bool)extends.Value;
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Boolean);
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
JsonToken.Boolean);
throw new InvalidCastException(message);
}
@@ -186,7 +258,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static T GetValueAsEnum<T>(this JsonReader extends)
public static T GetValueAsEnum<T>([NotNull] this JsonReader extends)
where T : struct, IConvertible
{
if (extends == null)
@@ -204,7 +276,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Guid GetValueAsGuid(this JsonReader extends)
public static Guid GetValueAsGuid([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -218,7 +290,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static DateTime GetValueAsDateTime(this JsonReader extends)
public static DateTime GetValueAsDateTime([NotNull] this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -228,31 +300,29 @@ namespace ICD.Common.Utils.Extensions
if (extends.DateParseHandling != DateParseHandling.None)
return (DateTime)extends.Value;
#endif
/*
"\"\\/Date(1335205592410)\\/\"" .NET JavaScriptSerializer
"\"\\/Date(1335205592410-0500)\\/\"" .NET DataContractJsonSerializer
"2012-04-23T18:25:43.511Z" JavaScript built-in JSON object
"2012-04-21T18:25:43-05:00" ISO 8601
*/
string stringValue = extends.GetValueAsString();
return DateTime.Parse(stringValue);
}
string serial = extends.GetValueAsString();
/// <summary>
/// Gets the current value as a date.
/// </summary>
/// <param name="extends"></param>
/// <param name="format"></param>
/// <param name="provider"></param>
/// <returns></returns>
public static DateTime GetValueAsDateTimeExact(this JsonReader extends, string format, IFormatProvider provider)
{
if (extends == null)
throw new ArgumentNullException("extends");
Match match;
if (RegexUtils.Matches(serial, @"Date\((?'date'\d+)(?'zone'(-|\+)\d+)?\)", out match))
{
long ms = long.Parse(match.Groups["date"].Value);
DateTime dateTime = DateTimeUtils.FromEpochMilliseconds(ms);
if (!match.Groups["zone"].Success)
return dateTime;
#if !SIMPLSHARP
// Newer NewtonSoft tries to be helpful by assuming that anything that looks like a DateTime must be a date.
if (extends.DateParseHandling != DateParseHandling.None)
throw new InvalidOperationException("DateParseHandling needs to be set to None");
#endif
// No TimeZoneInfo in CF, so now things get gross
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
serial = dateTime.ToIso() + match.Groups["zone"].Value;
}
string stringValue = extends.GetValueAsString();
return DateTime.ParseExact(stringValue, format, provider);
return DateTimeUtils.FromIso8601(serial);
}
}
}

View File

@@ -1,7 +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 Newtonsoft.Json;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -16,8 +22,16 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader)
[CanBeNull]
public static IEnumerable<TItem> DeserializeArray<TItem>([NotNull] this JsonSerializer extends,
[NotNull] JsonReader reader)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
return extends.DeserializeArray(reader, (s, r) => extends.Deserialize<TItem>(reader));
}
@@ -28,8 +42,10 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
[CanBeNull]
public static IEnumerable<TItem> DeserializeArray<TItem>([NotNull] this JsonSerializer extends,
[NotNull] JsonReader reader,
[NotNull] Func<JsonSerializer, JsonReader, TItem> read)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -41,10 +57,11 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("read");
if (reader.TokenType == JsonToken.Null)
return Enumerable.Empty<TItem>();
return null;
if (reader.TokenType != JsonToken.StartArray)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray,
reader.TokenType));
// ToArray to ensure everything gets read before moving onto the next token
return DeserializeArrayIterator(extends, reader, read).ToArray();
@@ -57,8 +74,10 @@ namespace ICD.Common.Utils.Extensions
/// <param name="serializer"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
[NotNull]
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(
[NotNull] JsonSerializer serializer, [NotNull] JsonReader reader,
[NotNull] Func<JsonSerializer, JsonReader, TItem> read)
{
// Step into the first value
reader.Read();
@@ -80,8 +99,10 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(this JsonSerializer extends,
JsonReader reader)
[CanBeNull]
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(
[NotNull] this JsonSerializer extends,
[NotNull] JsonReader reader)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -103,12 +124,12 @@ namespace ICD.Common.Utils.Extensions
/// <param name="reader"></param>
/// <param name="readKey"></param>
/// <param name="readValue"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(this JsonSerializer extends,
JsonReader reader,
Func<JsonSerializer, JsonReader,
TKey> readKey,
Func<JsonSerializer, JsonReader,
TValue> readValue)
[CanBeNull]
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(
[NotNull] this JsonSerializer extends,
[NotNull] JsonReader reader,
[NotNull] Func<JsonSerializer, JsonReader, TKey> readKey,
[NotNull] Func<JsonSerializer, JsonReader, TValue> readValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -125,11 +146,11 @@ namespace ICD.Common.Utils.Extensions
return extends.DeserializeArray(reader, (s, r) => s.DeserializeKeyValuePair(r, readKey, readValue));
}
public static KeyValuePair<TKey, TValue> DeserializeKeyValuePair<TKey, TValue>(this JsonSerializer extends, JsonReader reader,
Func<JsonSerializer, JsonReader,
TKey> readKey,
Func<JsonSerializer, JsonReader,
TValue> readValue)
public static KeyValuePair<TKey, TValue> DeserializeKeyValuePair<TKey, TValue>(
[NotNull] this JsonSerializer extends,
[NotNull] JsonReader reader,
[NotNull] Func<JsonSerializer, JsonReader, TKey> readKey,
[NotNull] Func<JsonSerializer, JsonReader, TValue> readValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -144,7 +165,8 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("readValue");
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject, reader.TokenType));
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject,
reader.TokenType));
TKey key = default(TKey);
TValue value = default(TValue);
@@ -155,7 +177,8 @@ namespace ICD.Common.Utils.Extensions
while (reader.TokenType != JsonToken.EndObject)
{
if (reader.TokenType != JsonToken.PropertyName)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.PropertyName, reader.TokenType));
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.PropertyName,
reader.TokenType));
string propertyName = (string)reader.Value;
@@ -190,8 +213,15 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items)
public static void SerializeArray<TItem>([NotNull] this JsonSerializer extends, [NotNull] JsonWriter writer,
[CanBeNull] IEnumerable<TItem> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
}
@@ -203,8 +233,9 @@ namespace ICD.Common.Utils.Extensions
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="write"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items,
Action<JsonSerializer, JsonWriter, TItem> write)
public static void SerializeArray<TItem>([NotNull] this JsonSerializer extends, [NotNull] JsonWriter writer,
[CanBeNull] IEnumerable<TItem> items,
[NotNull] Action<JsonSerializer, JsonWriter, TItem> write)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -237,8 +268,9 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeDict<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items)
public static void SerializeDict<TKey, TValue>([NotNull] this JsonSerializer extends,
[NotNull] JsonWriter writer,
[CanBeNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
{
extends.SerializeDict(writer, items,
(s, w, k) => s.Serialize(w, k),
@@ -255,10 +287,11 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param>
/// <param name="writeKey"></param>
/// <param name="writeValue"></param>
public static void SerializeDict<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items,
Action<JsonSerializer, JsonWriter, TKey> writeKey,
Action<JsonSerializer, JsonWriter, TValue> writeValue)
public static void SerializeDict<TKey, TValue>([NotNull] this JsonSerializer extends,
[NotNull] JsonWriter writer,
[CanBeNull] IEnumerable<KeyValuePair<TKey, TValue>> items,
[NotNull] Action<JsonSerializer, JsonWriter, TKey> writeKey,
[NotNull] Action<JsonSerializer, JsonWriter, TValue> writeValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -275,10 +308,11 @@ namespace ICD.Common.Utils.Extensions
extends.SerializeArray(writer, items, (s, w, kvp) => s.SerializeKeyValuePair(w, kvp, writeKey, writeValue));
}
public static void SerializeKeyValuePair<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
KeyValuePair<TKey, TValue> kvp,
Action<JsonSerializer, JsonWriter, TKey> writeKey,
Action<JsonSerializer, JsonWriter, TValue> writeValue)
public static void SerializeKeyValuePair<TKey, TValue>(
[NotNull] this JsonSerializer extends,
[NotNull] JsonWriter writer, KeyValuePair<TKey, TValue> kvp,
[NotNull] Action<JsonSerializer, JsonWriter, TKey> writeKey,
[NotNull] Action<JsonSerializer, JsonWriter, TValue> writeValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -303,4 +337,4 @@ namespace ICD.Common.Utils.Extensions
writer.WriteEndObject();
}
}
}
}

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,18 +1,41 @@
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
{
public static class JsonWriterExtensions
{
/// <summary>
/// Writes the DateTime as an ISO-8601 string.
/// </summary>
/// <param name="extends"></param>
/// <param name="dateTime"></param>
public static void WriteDateTime([NotNull] this JsonWriter extends, DateTime dateTime)
{
if (extends == null)
throw new ArgumentNullException("extends");
string iso = dateTime.ToIso();
// Remove redundant ms
iso = iso.Replace(".0000000", "");
extends.WriteValue(iso);
}
/// <summary>
/// Writes the type value.
/// </summary>
/// <param name="extends"></param>
/// <param name="type"></param>
[PublicAPI]
public static void WriteType(this JsonWriter extends, Type type)
public static void WriteType([NotNull] this JsonWriter extends, [CanBeNull] Type type)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -33,7 +56,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, object value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, object value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -48,7 +71,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, string value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, string value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -63,7 +86,22 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, DateTime value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, DateTime value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteDateTime(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, bool value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -78,7 +116,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, bool value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, int value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -93,7 +131,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, int value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, Guid value)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -108,22 +146,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, Guid value)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
}
/// <summary>
/// Writes the property name and value to the writer.
/// </summary>
/// <param name="extends"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void WriteProperty(this JsonWriter extends, string propertyName, Type value)
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, Type value)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -21,7 +21,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public static bool AddSorted<T>(this IList<T> extends, T item)
public static bool AddSorted<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -39,7 +39,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static bool AddSorted<T, TProp>(this IList<T> extends, T item, Func<T, TProp> predicate)
public static bool AddSorted<T, TProp>([NotNull] this IList<T> extends, T item,
[NotNull] Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -61,7 +62,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool AddSorted<T>(this IList<T> extends, T item, IComparer<T> comparer)
public static bool AddSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -92,7 +93,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public static bool RemoveSorted<T>(this IList<T> extends, T item)
public static bool RemoveSorted<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -110,7 +111,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static bool RemoveSorted<T, TProp>(this IList<T> extends, T item, Func<T, TProp> predicate)
public static bool RemoveSorted<T, TProp>([NotNull] this IList<T> extends, T item,
[NotNull] Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -132,7 +134,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool RemoveSorted<T>(this IList<T> extends, T item, IComparer<T> comparer)
public static bool RemoveSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -160,7 +162,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="items"></param>
[PublicAPI]
public static void InsertSorted<T>(this IList<T> extends, IEnumerable<T> items)
public static void InsertSorted<T>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -179,7 +181,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param>
/// <param name="comparer"></param>
[PublicAPI]
public static void InsertSorted<T>(this IList<T> extends, IEnumerable<T> items, IComparer<T> comparer)
public static void InsertSorted<T>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items,
[NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -202,7 +205,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static void InsertSorted<T, TProp>(this IList<T> extends, IEnumerable<T> items, Func<T, TProp> predicate)
public static void InsertSorted<T, TProp>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items,
[NotNull] Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -224,7 +228,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="item"></param>
[PublicAPI]
public static int InsertSorted<T>(this IList<T> extends, T item)
public static int InsertSorted<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -241,7 +245,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static int InsertSorted<T, TProp>(this IList<T> extends, T item, Func<T, TProp> predicate)
public static int InsertSorted<T, TProp>([NotNull] this IList<T> extends, T item,
[NotNull] Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -261,7 +266,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="comparer"></param>
[PublicAPI]
public static int InsertSorted<T>(this IList<T> extends, T item, IComparer<T> comparer)
public static int InsertSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -281,7 +286,7 @@ namespace ICD.Common.Utils.Extensions
#endregion
#region Contains Sorted
/// <summary>
/// Returns true if the sorted list contains the given item.
/// </summary>
@@ -290,7 +295,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public static bool ContainsSorted<T>(this IList<T> extends, T item)
public static bool ContainsSorted<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -307,7 +312,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static bool ContainsSorted<T>(this IList<T> extends, T item, IComparer<T> comparer)
public static bool ContainsSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -318,10 +323,48 @@ namespace ICD.Common.Utils.Extensions
return extends.BinarySearch(item, comparer) >= 0;
}
/// <summary>
/// Returns true if the sorted list contains the given item.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TProp"></typeparam>
/// <param name="extends"></param>
/// <param name="item"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static bool ContainsSorted<T, TProp>([NotNull] this IList<T> extends, T item,
[NotNull] Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
return extends.ContainsSorted(item, comparer);
}
#endregion
#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>
@@ -331,7 +374,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static int BinarySearch<T>(this IList<T> extends, T item, IComparer<T> comparer)
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,5 +1,6 @@
using System;
using System.Text;
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -15,8 +16,11 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="method">The Method</param>
/// <returns>Method signature</returns>
public static string GetSignature(this MethodInfo method)
public static string GetSignature([NotNull]this MethodInfo method)
{
if (method == null)
throw new ArgumentNullException("method");
return method.GetSignature(false);
}
@@ -26,8 +30,11 @@ namespace ICD.Common.Utils.Extensions
/// <param name="method">The Method</param>
/// <param name="callable">Return as a callable string(public void a(string b) would return a(b))</param>
/// <returns>Method signature</returns>
public static string GetSignature(this MethodInfo method, bool callable)
public static string GetSignature([NotNull]this MethodInfo method, bool callable)
{
if (method == null)
throw new ArgumentNullException("method");
bool firstParam = true;
StringBuilder sigBuilder = new StringBuilder();
@@ -123,5 +130,22 @@ namespace ICD.Common.Utils.Extensions
return sigBuilder.ToString();
}
/// <summary>
/// Cross-platform shim for getting MethodInfo for the delegate.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static MethodInfo GetMethodInfo([NotNull]this Delegate extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
#if SIMPLSHARP
return extends.GetMethod();
#else
return extends.Method;
#endif
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -14,7 +15,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool GetIsOut(this ParameterInfo extends)
public static bool GetIsOut([NotNull] this ParameterInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -11,7 +12,7 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="items"></param>
public static void EnqueueRange<T>(this Queue<T> extends, IEnumerable<T> items)
public static void EnqueueRange<T>([NotNull] this Queue<T> extends, [NotNull] IEnumerable<T> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -30,7 +31,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="item"></param>
/// <returns></returns>
public static bool Dequeue<T>(this Queue<T> extends, out T item)
public static bool Dequeue<T>([NotNull] this Queue<T> extends, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -51,7 +52,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="item"></param>
/// <returns></returns>
public static bool Peek<T>(this Queue<T> extends, out T item)
public static bool Peek<T>([NotNull] this Queue<T> extends, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -20,7 +21,7 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributes<T>(this ICustomAttributeProvider extends)
public static IEnumerable<T> GetCustomAttributes<T>([NotNull] this ICustomAttributeProvider extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -35,7 +36,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributes<T>(this ICustomAttributeProvider extends, bool inherits)
public static IEnumerable<T> GetCustomAttributes<T>([NotNull] this ICustomAttributeProvider extends,
bool inherits)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -57,7 +59,7 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T GetCustomAttribute<T>(this ICustomAttributeProvider extends)
public static T GetCustomAttribute<T>([NotNull] this ICustomAttributeProvider extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -72,7 +74,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static T GetCustomAttribute<T>(this ICustomAttributeProvider extends, bool inherits)
public static T GetCustomAttribute<T>([NotNull] this ICustomAttributeProvider extends, bool inherits)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -86,16 +88,18 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this Type extends)
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>([NotNull] this Type extends)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.GetCustomAttributes<T>(true)
.Union(extends.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetCustomAttributes<T>(true)))
.Distinct();
.Union(extends.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetCustomAttributes<T>(true)))
.Distinct();
}
/// <summary>
@@ -103,24 +107,235 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this MemberInfo extends)
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>([NotNull] this MemberInfo extends)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.GetCustomAttributes<T>(true)
.Union(extends.DeclaringType?
.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetMember(
extends.Name,
extends.MemberType,
BindingFlags.Instance)
.FirstOrDefault()?
.GetCustomAttributes<T>(true) ?? Enumerable.Empty<T>())?
.Except(null) ?? Enumerable.Empty<T>())
.Distinct();
.Union(extends.DeclaringType?
.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetMember(extends.Name,
extends.MemberType,
BindingFlags.Instance)
.FirstOrDefault()?
.GetCustomAttributes<T>(true) ??
Enumerable.Empty<T>())?
.Except(null) ?? Enumerable.Empty<T>())
.Distinct();
}
#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
/// </summary>
/// <param name="extends"></param>
/// <param name="value"></param>
/// <param name="path"></param>
/// <returns>true if property was set, false if property was not found</returns>
public static bool SetProperty([NotNull] this object extends, [CanBeNull] object value, [NotNull] params string[] path)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (path == null)
throw new ArgumentNullException("path");
object instance;
PropertyInfo property = extends.GetPropertyInfo(out instance, path);
if (property == null)
return false;
property.SetValue(instance, value);
return true;
}
/// <summary>
/// Gets the PropertyInfo of the specified property
/// Traverses the path to access properties nested in other properties
/// </summary>
/// <param name="extends"></param>
/// <param name="instance"></param>
/// <param name="path"></param>
/// <returns></returns>
[CanBeNull]
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");
instance = extends;
// Grab properties until the last item in the path
for (int i = 0; i < path.Length - 1; i++)
{
PropertyInfo info =
instance.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetProperty(path[i],
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (info == null)
{
instance = null;
return null;
}
instance = info.GetValue(instance);
}
// Set the property to the value
PropertyInfo output =
instance.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetProperty(path[path.Length - 1],
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (output == null)
instance = null;
return output;
}
/// <summary>
/// Gets the value of a property
/// Traverses the path to access properties nested in other properties
/// </summary>
/// <param name="extends"></param>
/// <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<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 = default(T);
object instance;
PropertyInfo property = extends.GetPropertyInfo(out instance, path);
if (property == null)
return false;
value = (T)property.GetValue(instance);
return true;
}
public static bool CallMethod([NotNull] this object extends, string methodName)
{
object value;
return CallMethod(extends, methodName, out value, new object[] { });
}
public static bool CallMethod<T>([NotNull] this object extends, string methodName, [CanBeNull] out T value)
{
return CallMethod(extends, methodName, out value, new object[] { });
}
public static bool CallMethod([NotNull] this object extends, string methodName, [NotNull] params object[] parameters)
{
object value;
return CallMethod(extends, methodName, out value, 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 = default(T);
MethodInfo method =
extends.GetType()
#if SIMPLSHARP
.GetCType()
#endif
.GetMethod(methodName,
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (method == null)
return false;
value = (T)method.Invoke(extends, parameters);
return true;
}
/// <summary>
/// Gets the EventArgs Type for the given event.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static Type GetEventArgsType([NotNull] this EventInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
Type eventHandlerType = extends.EventHandlerType;
return eventHandlerType == typeof(EventHandler)
? typeof(EventArgs)
: eventHandlerType.GetInnerGenericTypes(typeof(EventHandler<>))
.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,5 +1,6 @@
using System;
using System.Text;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -9,7 +10,7 @@ namespace ICD.Common.Utils.Extensions
/// Empties the StringBuilder.
/// </summary>
/// <param name="extends"></param>
public static void Clear(this StringBuilder extends)
public static void Clear([NotNull] this StringBuilder extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -23,7 +24,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string Pop(this StringBuilder extends)
public static string Pop([NotNull] this StringBuilder extends)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
@@ -15,7 +17,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="first"></param>
/// <returns></returns>
[PublicAPI]
public static int IndexOf(this string extends, IEnumerable<string> items, out string first)
public static int IndexOf([NotNull] this string extends, [NotNull] IEnumerable<string> items, out string first)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -52,7 +54,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="character"></param>
/// <returns></returns>
[PublicAPI]
public static bool StartsWith(this string extends, char character)
public static bool StartsWith([NotNull] this string extends, char character)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -67,7 +69,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="character"></param>
/// <returns></returns>
[PublicAPI]
public static bool EndsWith(this string extends, char character)
public static bool EndsWith([NotNull] this string extends, char character)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -83,7 +85,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="delimeter"></param>
/// <param name="count"></param>
/// <returns></returns>
public static IEnumerable<string> Split(this string extends, char delimeter, int count)
[NotNull]
public static IEnumerable<string> Split([NotNull] this string extends, char delimeter, int count)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -102,7 +105,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="delimeter"></param>
/// <param name="count"></param>
/// <returns></returns>
private static IEnumerable<string> SplitIterator(string value, char delimeter, int count)
[NotNull]
private static IEnumerable<string> SplitIterator([NotNull] string value, char delimeter, int count)
{
while (count > 1)
{
@@ -125,7 +129,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="chunkSize"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<string> Split(this string extends, int chunkSize)
[NotNull]
public static IEnumerable<string> Split([NotNull] this string extends, int chunkSize)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -134,7 +139,48 @@ namespace ICD.Common.Utils.Extensions
throw new InvalidOperationException("chunkSize must be greater than 0");
return Enumerable.Range(0, (int)Math.Ceiling(extends.Length / (double)chunkSize))
.Select(i => extends.Substring(i * chunkSize, Math.Min(chunkSize, extends.Length - (i * chunkSize))));
.Select(i => extends.Substring(i * chunkSize,
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>
@@ -143,7 +189,8 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static string RemoveWhitespace(this string extends)
[NotNull]
public static string RemoveWhitespace([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -152,12 +199,28 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Removes all occurances of the given string.
/// Replaces spans of whitespace with a single space.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
[NotNull]
public static string RemoveDuplicateWhitespace([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return Regex.Replace(extends, @"\s+", " ");
}
/// <summary>
/// Removes all occurrences of the given string.
/// </summary>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <returns></returns>
public static string Remove(this string extends, string other)
[NotNull]
public static string Remove([NotNull] this string extends, [NotNull] string other)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -176,19 +239,21 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Removes all occurances the given characters from the string.
/// Removes all occurrences the given characters from the string.
/// </summary>
/// <param name="extends"></param>
/// <param name="characters"></param>
/// <returns></returns>
[PublicAPI]
public static string Remove(this string extends, IEnumerable<char> characters)
[NotNull]
public static string Remove([NotNull] this string extends, IEnumerable<char> characters)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (characters == null)
throw new ArgumentNullException("characters");
var cSet = characters.ToIcdHashSet();
return new string(extends.Where(c => !cSet.Contains(c)).ToArray());
@@ -200,12 +265,12 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static bool IsNumeric(this string extends)
public static bool IsNumeric([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.All(char.IsDigit);
return extends.AnyAndAll(char.IsDigit);
}
/// <summary>
@@ -214,7 +279,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="character"></param>
/// <returns></returns>
public static bool Contains(this string extends, char character)
public static bool Contains([NotNull] this string extends, char character)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -227,8 +292,11 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static int GetStableHashCode(this string extends)
public static int GetStableHashCode([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
unchecked
{
int hash1 = 5381;
@@ -245,5 +313,55 @@ namespace ICD.Common.Utils.Extensions
return hash1 + (hash2 * 1566083941);
}
}
/// <summary>
/// Strips all of the non-printable characters and control codes from the string.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToPrintableCharacters([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
// Strip ANSI escape sequences
extends = Regex.Replace(extends, AnsiUtils.ANSI_REGEX, string.Empty);
// Strip control characters
extends = Regex.Replace(extends, @"\p{C}+", string.Empty);
return extends;
}
/// <summary>
/// Gets the printable length of the string.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static int GetPrintableLength([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.ToPrintableCharacters().Length;
}
/// <summary>
/// Pads the string to the number of printable characters.
/// </summary>
/// <param name="extends"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string PadRightPrintable([NotNull] this string extends, int length)
{
if (extends == null)
throw new ArgumentNullException("extends");
int printableLength = extends.GetPrintableLength();
int actualLength = extends.Length;
int delta = actualLength - printableLength;
return extends.PadRight(length + delta);
}
}
}

View File

@@ -5,27 +5,69 @@ namespace ICD.Common.Utils.Extensions
{
public static class TimeSpanExtensions
{
/// <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.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToReadableString(this TimeSpan extends)
{
StringBuilder builder = new StringBuilder();
if (extends.Days > 0)
builder.AppendFormat("{0} days, ", extends.Days);
if (extends.Hours > 0)
builder.AppendFormat("{0} hours, ", extends.Hours);
if (extends.Minutes > 0)
builder.AppendFormat("{0} minutes, ", extends.Minutes);
if (extends.Seconds > 0)
builder.AppendFormat("{0} seconds, ", extends.Seconds);
if (extends.Milliseconds > 0)
{
builder.AppendFormat("{0}.{1} ms", extends.Milliseconds,
((double)extends.Ticks / TimeSpan.TicksPerMillisecond) - extends.TotalMilliseconds);
}
return builder.ToString();
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>
@@ -68,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

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
@@ -59,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.
@@ -71,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>
@@ -83,7 +96,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool CanBeNull(this Type extends)
public static bool CanBeNull([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentException("extends");
@@ -91,12 +104,26 @@ namespace ICD.Common.Utils.Extensions
return !extends.IsValueType || Nullable.GetUnderlyingType(extends) != null;
}
/// <summary>
/// Returns true if the type is anonymous.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsAnonymous([NotNull] this Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType")) &&
(type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"));
}
/// <summary>
/// Returns true if the given type is a numeric type.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool IsNumeric(this Type extends)
public static bool IsNumeric([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentException("extends");
@@ -109,7 +136,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool IsSignedNumeric(this Type extends)
public static bool IsSignedNumeric([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentException("extends");
@@ -122,7 +149,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool IsDecimalNumeric(this Type extends)
public static bool IsDecimalNumeric([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentException("extends");
@@ -135,7 +162,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool IsIntegerNumeric(this Type extends)
public static bool IsIntegerNumeric([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentException("extends");
@@ -148,7 +175,8 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static Assembly GetAssembly(this Type extends)
[NotNull]
public static Assembly GetAssembly([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -167,7 +195,7 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public static bool IsAssignableTo<T>(this Type from)
public static bool IsAssignableTo<T>([NotNull]this Type from)
{
if (from == null)
throw new ArgumentNullException("from");
@@ -181,7 +209,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public static bool IsAssignableTo(this Type from, Type to)
public static bool IsAssignableTo([NotNull]this Type from, [NotNull]Type to)
{
if (from == null)
throw new ArgumentNullException("from");
@@ -192,27 +220,62 @@ namespace ICD.Common.Utils.Extensions
return to.IsAssignableFrom(from);
}
/// <summary>
/// Extracts the inner Types from this current Type inheriting/implementing the given generic Type.
/// E.g.
/// typeof(List&lt;int&gt;).GetInnerGenericTypes(typeof(IEnumerable&lt;&gt;));
/// Returns
/// [ typeof(int) ]
/// </summary>
/// <param name="extends"></param>
/// <param name="genericType"></param>
/// <returns></returns>
[NotNull]
public static IEnumerable<Type> GetInnerGenericTypes([NotNull] this Type extends, Type genericType)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (genericType == null)
throw new ArgumentNullException("genericType");
return extends
.GetAllTypes()
.First(t => t.IsGenericType &&
t.GetGenericTypeDefinition() == genericType)
.GetGenericArguments();
}
/// <summary>
/// Returns the given type, all base types, and all implemented interfaces.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<Type> GetAllTypes(this Type extends)
[NotNull]
public static IEnumerable<Type> GetAllTypes([NotNull] this Type extends)
{
if (extends == null)
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;
}
@@ -221,22 +284,33 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<Type> GetBaseTypes(this Type extends)
[NotNull]
public static IEnumerable<Type> GetBaseTypes([NotNull]this Type extends)
{
if (extends == null)
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;
}
private static IEnumerable<Type> GetBaseTypesIterator(Type type)
[NotNull]
private static IEnumerable<Type> GetBaseTypesIterator([NotNull] Type type)
{
do
{
@@ -257,26 +331,37 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<Type> GetImmediateInterfaces(this Type extends)
[NotNull]
public static IEnumerable<Type> GetImmediateInterfaces([NotNull]this Type extends)
{
if (extends == null)
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;
}
@@ -286,20 +371,31 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<Type> GetMinimalInterfaces(this Type extends)
[NotNull]
public static IEnumerable<Type> GetMinimalInterfaces([NotNull]this Type extends)
{
if (extends == null)
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;
}
@@ -314,7 +410,8 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetNameWithoutGenericArity(this Type extends)
[NotNull]
public static string GetNameWithoutGenericArity([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -330,47 +427,45 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetMinimalName(this Type extends)
[NotNull]
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;
}
@@ -380,17 +475,29 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetNameWithoutAssemblyDetails(this Type extends)
[NotNull]
public static string GetNameWithoutAssemblyDetails([NotNull]this Type extends)
{
if (extends == null)
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;
}
@@ -401,7 +508,8 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
[NotNull]
private static string RemoveAssemblyDetails([NotNull] string fullyQualifiedTypeName)
{
StringBuilder builder = new StringBuilder();
@@ -450,7 +558,8 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends">Type. May be generic or nullable</param>
/// <returns>Full type name, fully qualified namespaces</returns>
public static string GetSyntaxName(this Type extends)
[NotNull]
public static string GetSyntaxName([NotNull]this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{
@@ -10,7 +11,8 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetUserName(this Uri extends)
[NotNull]
public static string GetUserName([NotNull] this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -23,12 +25,60 @@ namespace ICD.Common.Utils.Extensions
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetPassword(this Uri extends)
[NotNull]
public static string GetPassword([NotNull] this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.UserInfo.Split(':').Skip(1).FirstOrDefault(string.Empty);
}
/// <summary>
/// Returns true if the URI matches the default http://localhost/
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool GetIsDefault([NotNull] this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.ToString() == "http://localhost/";
}
/// <summary>
/// Returns the string representation of the given URI, replacing the password with asterixes.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string ToPrivateString([NotNull] this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
IcdUriBuilder builder = new IcdUriBuilder(extends);
builder.Password = builder.Password == null ? null : StringUtils.PasswordFormat(builder.Password);
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
"Data Source={0}";
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";
@@ -428,53 +425,53 @@ namespace ICD.Common.Utils.Globalization
}
s_SqlConnectionString = string.Format(SQL_CONNECTION_STRING_FORMAT, databasePath);
using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString))
try
{
try
using (IcdSqliteConnection sQLiteConnection = new IcdSqliteConnection(s_SqlConnectionString))
{
sQLiteConnection.Open();
}
catch (Exception)
{
s_IsDatabasePresent = false;
return;
}
s_IsDatabasePresent = true;
IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand("select count(*) from cultureinfo", sQLiteConnection);
int num = Convert.ToInt32(sQLiteCommand.ExecuteScalar());
s_AvailableCultureNames = new string[num + 1];
IcdSqliteCommand sQLiteCommand2 = new IcdSqliteCommand("select id, name, lcid, isneutralculture from cultureinfo",
sQLiteConnection);
using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand2.ExecuteReader())
{
while (sQLiteDataReader.Read())
{
int @int = sQLiteDataReader.GetInt32(0);
bool boolean = sQLiteDataReader.GetBoolean(3);
string @string = sQLiteDataReader.GetString(1);
s_DictAvailableCulturesByName[@string] = boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures;
int int2 = sQLiteDataReader.GetInt32(2);
s_DictAvailableCulturesByLcid[int2] = boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures;
s_AvailableCultureNames[@int] = @string;
}
}
s_IsDatabasePresent = true;
sQLiteCommand2 = new IcdSqliteCommand("select id, specificculture from specificcultureinfo", sQLiteConnection);
using (IcdSqliteDataReader sQLiteDataReader2 = sQLiteCommand2.ExecuteReader())
{
while (sQLiteDataReader2.Read())
IcdSqliteCommand sQLiteCommand = new IcdSqliteCommand("select count(*) from cultureinfo", sQLiteConnection);
int num = Convert.ToInt32(sQLiteCommand.ExecuteScalar());
s_AvailableCultureNames = new string[num + 1];
IcdSqliteCommand sQLiteCommand2 = new IcdSqliteCommand("select id, name, lcid, isneutralculture from cultureinfo",
sQLiteConnection);
using (IcdSqliteDataReader sQLiteDataReader = sQLiteCommand2.ExecuteReader())
{
int int3 = sQLiteDataReader2.GetInt32(0);
string string2 = sQLiteDataReader2.GetString(1);
s_DictSpecificCulture[s_AvailableCultureNames[int3]] = string2;
while (sQLiteDataReader.Read())
{
int @int = sQLiteDataReader.GetInt32(0);
bool boolean = sQLiteDataReader.GetBoolean(3);
string @string = sQLiteDataReader.GetString(1);
s_DictAvailableCulturesByName[@string] = boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures;
int int2 = sQLiteDataReader.GetInt32(2);
s_DictAvailableCulturesByLcid[int2] = boolean ? CultureTypes.NeutralCultures : CultureTypes.SpecificCultures;
s_AvailableCultureNames[@int] = @string;
}
}
sQLiteCommand2 = new IcdSqliteCommand("select id, specificculture from specificcultureinfo", sQLiteConnection);
using (IcdSqliteDataReader sQLiteDataReader2 = sQLiteCommand2.ExecuteReader())
{
while (sQLiteDataReader2.Read())
{
int int3 = sQLiteDataReader2.GetInt32(0);
string string2 = sQLiteDataReader2.GetString(1);
s_DictSpecificCulture[s_AvailableCultureNames[int3]] = string2;
}
}
}
}
string[] array = builtinCultures;
for (int i = 0; i < array.Length; i++)
catch (Exception e)
{
IcdErrorLog.Exception(e, "Error populating IcdCultureInfo cache - {0}", e.Message);
s_IsDatabasePresent = false;
return;
}
foreach (string name in builtinCultures)
{
string name = array[i];
try
{
CultureInfo cultureInfo = CultureInfo.GetCultureInfo(name);
@@ -508,7 +505,7 @@ namespace ICD.Common.Utils.Globalization
CultureTypes cultureTypes;
if (!s_DictAvailableCulturesByLcid.TryGetValue(culture, out cultureTypes))
throw new ArgumentException("not supported");
throw new ArgumentException(string.Format("Culture {0} not supported", culture));
if (!s_IsDatabasePresent || (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0)
{
@@ -553,7 +550,7 @@ namespace ICD.Common.Utils.Globalization
CultureTypes cultureTypes;
if (!s_DictAvailableCulturesByName.TryGetValue(name, out cultureTypes))
throw new ArgumentException("not supported");
throw new ArgumentException(string.Format("Culture {0} not supported", name));
if (!s_IsDatabasePresent || (cultureTypes & CultureTypes.InstalledWin32Cultures) != 0)
{
@@ -941,7 +938,11 @@ namespace ICD.Common.Utils.Globalization
return cultureInfo;
}
public new 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,9 +1,14 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
public static class GuidUtils
{
/// <summary>
/// Generates a pseudo-random guid from the given seed.
/// </summary>
public static Guid GenerateSeeded(int seed)
{
Random seeded = new Random(seed);
@@ -13,5 +18,27 @@ namespace ICD.Common.Utils
return new Guid(bytes);
}
/// <summary>
/// Combines the two guids to make a new, deterministic guid.
/// </summary>
public static Guid Combine(Guid a, Guid b)
{
byte[] aBytes = a.ToByteArray();
byte[] bBytes = b.ToByteArray();
for (int index = 0; index < aBytes.Length; index++)
aBytes[index] = (byte)(aBytes[index] ^ bBytes[index]);
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,37 +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>
<Compile Remove="ObfuscationSettings.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="ICD.Common.projectinfo" />
<None Remove="ICD.Common_SimplSharp.suo" />
@@ -39,16 +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="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>
@@ -74,19 +74,30 @@
<Reference Include="System.Data" />
</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="ConsoleColor.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" />
@@ -97,6 +108,7 @@
<Compile Include="EventArguments\BoolEventArgs.cs" />
<Compile Include="EventArguments\CharEventArgs.cs" />
<Compile Include="EventArguments\DateTimeEventArgs.cs" />
<Compile Include="EventArguments\DateTimeNullableEventArgs.cs" />
<Compile Include="EventArguments\FloatEventArgs.cs" />
<Compile Include="EventArguments\GenericEventArgs.cs" />
<Compile Include="EventArguments\IGenericEventArgs.cs" />
@@ -107,7 +119,15 @@
<None Include="CultureInfo.sqlite">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None 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" />
@@ -124,10 +144,12 @@
<Compile Include="Globalization\IcdCultureInfo.cs" />
<Compile Include="GuidUtils.cs" />
<Compile Include="IcdUriBuilder.cs" />
<Compile Include="IO\Compression\IcdZipEntry.cs" />
<Compile Include="IO\eIcdFileMode.cs" />
<Compile Include="IO\IcdBinaryReader.cs" />
<Compile Include="IO\eSeekOrigin.cs" />
<Compile Include="IO\IcdStreamWriter.cs" />
<Compile Include="Json\DateTimeIsoConverter.cs" />
<Compile Include="Json\MinimalTypeConverter.cs" />
<Compile Include="Json\ToStringJsonConverter.cs" />
<Compile Include="NullObject.cs" />
<Compile Include="ProcessorUtils.SimplSharp.cs" />
@@ -136,6 +158,7 @@
<Compile Include="ProgramUtils.Standard.cs" />
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\ObfuscationAttributes.cs" />
<Compile Include="RecursionUtils.cs" />
<Compile Include="RegexUtils.cs" />
<Compile Include="ReprBuilder.cs" />
@@ -159,6 +182,7 @@
<Compile Include="IcdEnvironment.cs" />
<Compile Include="IcdEnvironment.SimplSharp.cs" />
<Compile Include="IcdEnvironment.Standard.cs" />
<Compile Include="IO\Compression\Unzip.cs" />
<Compile Include="IO\Compression\IcdZip.cs" />
<Compile Include="IO\IcdDirectory.cs" />
<Compile Include="EnumUtils.cs" />
@@ -194,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" />
@@ -210,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" />
@@ -231,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

@@ -1,12 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.IO.Compression;
#endif
namespace ICD.Common.Utils.IO.Compression
{
@@ -15,9 +8,6 @@ namespace ICD.Common.Utils.IO.Compression
/// </summary>
public static class IcdZip
{
private const int DIRECTORY_SIGNATURE = 0x06054B50;
private const int ENTRY_SIGNATURE = 0x02014B50;
/// <summary>
/// Unzips the archive at the given path.
/// </summary>
@@ -25,14 +15,8 @@ namespace ICD.Common.Utils.IO.Compression
/// <param name="outputPath"></param>
public static void Unzip(string path, string outputPath)
{
#if SIMPLSHARP
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
if (result != CrestronZIP.ResultCode.ZR_OK)
throw new InvalidOperationException(result.ToString());
#else
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
archive.ExtractToDirectory(outputPath);
#endif
using (Unzip unzip = new Unzip(path))
unzip.ExtractToDirectory(outputPath);
}
/// <summary>
@@ -40,134 +24,8 @@ namespace ICD.Common.Utils.IO.Compression
/// </summary>
public static IEnumerable<string> GetFileNames(string path)
{
return GetEntries(path).Select(e => e.Name)
.Where(f => !f.EndsWith("/"))
.OrderBy(f => f);
}
/// <summary>
/// Gets sequence of zip file entries in the archive at the given path.
/// </summary>
public static IEnumerable<IcdZipEntry> GetEntries(string path)
{
/*
Copyright (c) 2012-2013 Alexey Yakovlev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using (IcdStream stream = IcdFile.OpenRead(path))
{
using (IcdBinaryReader reader = new IcdBinaryReader(stream))
{
if (stream.Length < 22)
yield break;
stream.Seek(-22, eSeekOrigin.End);
// find directory signature
while (reader.ReadInt32() != DIRECTORY_SIGNATURE)
{
if (stream.Position <= 5)
yield break;
// move 1 byte back
stream.Seek(-5, eSeekOrigin.Current);
}
// read directory properties
stream.Seek(6, eSeekOrigin.Current);
ushort entries = reader.ReadUInt16();
int difSize = reader.ReadInt32();
uint dirOffset = reader.ReadUInt32();
stream.Seek(dirOffset, eSeekOrigin.Begin);
// read directory entries
for (int i = 0; i < entries; i++)
{
if (reader.ReadInt32() != ENTRY_SIGNATURE)
continue;
// read file properties
reader.ReadInt32();
bool utf8 = (reader.ReadInt16() & 0x0800) != 0;
short method = reader.ReadInt16();
int timestamp = reader.ReadInt32();
uint crc32 = reader.ReadUInt32();
int compressedSize = reader.ReadInt32();
int fileSize = reader.ReadInt32();
short fileNameSize = reader.ReadInt16();
short extraSize = reader.ReadInt16();
short commentSize = reader.ReadInt16();
int headerOffset = reader.ReadInt32();
reader.ReadInt32();
int fileHeaderOffset = reader.ReadInt32();
byte[] fileNameBytes = reader.ReadBytes(fileNameSize);
stream.Seek(extraSize, eSeekOrigin.Current);
byte[] fileCommentBytes = reader.ReadBytes(commentSize);
int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset);
// decode zip file entry
Encoding encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
yield return new IcdZipEntry
{
Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length),
Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length),
Crc32 = crc32,
CompressedSize = compressedSize,
OriginalSize = fileSize,
HeaderOffset = fileHeaderOffset,
DataOffset = fileDataOffset,
Deflated = method == 8,
Timestamp = ConvertToDateTime(timestamp)
};
}
}
}
}
private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset)
{
long position = stream.Position;
stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin);
short fileNameSize = reader.ReadInt16();
short extraSize = reader.ReadInt16();
int fileOffset = (int)stream.Position + fileNameSize + extraSize;
stream.Seek(position, eSeekOrigin.Begin);
return fileOffset;
}
/// <summary>
/// Converts DOS timestamp to a <see cref="DateTime"/> instance.
/// </summary>
/// <param name="dosTimestamp">The DOS timestamp.</param>
/// <returns>The <see cref="DateTime"/> instance.</returns>
private static DateTime ConvertToDateTime(int dosTimestamp)
{
return new DateTime((dosTimestamp >> 25) + 1980,
(dosTimestamp >> 21) & 15,
(dosTimestamp >> 16) & 31,
(dosTimestamp >> 11) & 31,
(dosTimestamp >> 5) & 63,
(dosTimestamp & 31) * 2);
using (Unzip unzip = new Unzip(path))
return unzip.FileNames.ToArray();
}
}
}

View File

@@ -1,59 +0,0 @@
using System;
namespace ICD.Common.Utils.IO.Compression
{
/// <summary>
/// Zip archive entry.
/// </summary>
public sealed class IcdZipEntry
{
/// <summary>
/// Gets or sets the name of a file or a directory.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the comment.
/// </summary>
public string Comment { get; set; }
/// <summary>
/// Gets or sets the CRC32.
/// </summary>
public uint Crc32 { get; set; }
/// <summary>
/// Gets or sets the compressed size of the file.
/// </summary>
public int CompressedSize { get; set; }
/// <summary>
/// Gets or sets the original size of the file.
/// </summary>
public int OriginalSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="IcdZipEntry" /> is deflated.
/// </summary>
public bool Deflated { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a directory.
/// </summary>
public bool IsDirectory { get { return Name.EndsWith("/"); } }
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a file.
/// </summary>
public bool IsFile { get { return !IsDirectory; } }
public int HeaderOffset { get; set; }
public int DataOffset { get; set; }
}
}

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