Compare commits

...

359 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
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
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
171 changed files with 11601 additions and 2549 deletions

View File

@@ -6,6 +6,231 @@ 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
@@ -64,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
@@ -149,6 +378,30 @@ 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
@@ -259,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
@@ -332,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

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

@@ -0,0 +1,153 @@
using System;
using ICD.Common.Properties;
namespace ICD.Common.Utils
{
public static class BigEndianBitConverter
{
private const byte FULL_BYTE = 0xFF;
public static ushort ToUshort([NotNull] byte[] value, int startIndex)
{
return unchecked((ushort)ToShort(value, startIndex));
}
public static short ToShort([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt16(value, startIndex);
const int bytes = sizeof(short);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
short result = 0;
for (int i = 0; i < bytes; i++)
result |= (short)(value[i + startIndex] << GetBitShift(i, bytes));
return result;
}
public static uint ToUint([NotNull] byte[] value, int startIndex)
{
return unchecked((uint)ToInt(value, startIndex));
}
public static int ToInt([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt32(value, startIndex);
const int bytes = sizeof(int);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
int result = 0;
for (int i = 0; i < bytes; i++)
result |= value[i + startIndex] << GetBitShift(i, bytes);
return result;
}
public static ulong ToUlong([NotNull] byte[] value, int startIndex)
{
return unchecked((ulong)ToLong(value, startIndex));
}
public static long ToLong([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt64(value, startIndex);
const int bytes = sizeof(long);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
long result = 0;
for (int i = 0; i < bytes; i++)
result |= (long)(value[i + startIndex]) << GetBitShift(i, bytes);
return result;
}
public static byte[] GetBytes(int value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(int);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(uint value)
{
return unchecked(GetBytes((int)value));
}
public static byte[] GetBytes(short value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(short);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(ushort value)
{
return unchecked(GetBytes((short)value));
}
public static byte[] GetBytes(long value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(long);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(ulong value)
{
return unchecked(GetBytes((long)value));
}
private static int GetBitShift(int byteNumber, int totalBytes)
{
return ((totalBytes - 1 - byteNumber) * 8);
}
}
}

View File

@@ -2,6 +2,9 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
@@ -10,6 +13,9 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_KeyToValue;
@@ -32,10 +38,36 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Constructor.
/// </summary>
public BiDictionary()
public BiDictionary() :
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
m_KeyToValue = new Dictionary<TKey, TValue>();
m_ValueToKey = new Dictionary<TValue, TKey>();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public BiDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
{
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
m_ValueToKey = new Dictionary<TValue, TKey>(valueComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
: this(keyComparer, valueComparer)
{
if (dict == null)
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
/// <summary>
@@ -43,13 +75,8 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="dict"></param>
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict)
: this()
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
if (dict == null)
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
#region Methods

View File

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

View File

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

View File

@@ -1,207 +1,651 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
{
private readonly List<TKey> m_OrderedKeys;
private readonly List<TValue> m_ValuesOrderedByKey;
private readonly Dictionary<TKey, TValue> m_Dictionary;
private readonly IComparer<TKey> m_Comparer;
private readonly IcdOrderedDictionary m_PrivateDictionary;
#region Properties
public int Count { get { return m_PrivateDictionary.Count; } }
public int Count { get { return m_Dictionary.Count; } }
int ICollection.Count { get { return m_PrivateDictionary.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
[CanBeNull]
public TValue this[[NotNull] TKey key]
public TValue this[TKey key]
{
get { return m_Dictionary[key]; }
set
get
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
Remove(key);
Add(key, value);
if (m_PrivateDictionary.Contains(key))
return (TValue)m_PrivateDictionary[key];
throw new KeyNotFoundException();
}
set
{
if (key == null)
throw new ArgumentNullException("key");
m_PrivateDictionary[key] = value;
}
}
#endregion
object IDictionary.this[object key]
{
get { return m_PrivateDictionary[key]; }
set { m_PrivateDictionary[key] = value; }
}
public ICollection<TKey> Keys
{
get
{
List<TKey> keys = new List<TKey>(m_PrivateDictionary.Count);
foreach (TKey key in m_PrivateDictionary.Keys)
{
keys.Add(key);
}
// Keys should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return keys;
}
}
ICollection IDictionary.Keys { get { return m_PrivateDictionary.Keys; } }
public ICollection<TValue> Values
{
get
{
List<TValue> values = new List<TValue>(m_PrivateDictionary.Count);
foreach (TValue value in m_PrivateDictionary.Values)
values.Add(value);
// Values should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return values;
}
}
ICollection IDictionary.Values { get { return m_PrivateDictionary.Values; } }
bool IDictionary.IsFixedSize { get { return ((IDictionary)m_PrivateDictionary).IsFixedSize; } }
bool IDictionary.IsReadOnly { get { return m_PrivateDictionary.IsReadOnly; } }
bool ICollection.IsSynchronized { get { return ((ICollection)m_PrivateDictionary).IsSynchronized; } }
object ICollection.SyncRoot { get { return m_PrivateDictionary; } }
/// <summary>
/// Constructor.
/// </summary>
public IcdOrderedDictionary()
: this(Comparer<TKey>.Default)
{
m_PrivateDictionary = new IcdOrderedDictionary();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdOrderedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return;
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
Add(kvp.Key, kvp.Value);
m_PrivateDictionary = new IcdOrderedDictionary();
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
m_PrivateDictionary.Add(pair.Key, pair.Value);
}
}
#region Methods
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
public void Add(KeyValuePair<TKey, TValue> item)
{
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
.GetEnumerator();
Add(item.Key, item.Value);
}
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
public void Add(TKey key, TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
m_PrivateDictionary.Add(key, value);
}
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
m_Dictionary[key] = value;
public KeyValuePair<TKey, TValue> Get(int index)
{
DictionaryEntry entry = (DictionaryEntry)m_PrivateDictionary.ObjectsArray[index];
return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
public void Clear()
{
m_OrderedKeys.Clear();
m_ValuesOrderedByKey.Clear();
m_Dictionary.Clear();
m_PrivateDictionary.Clear();
}
public bool ContainsKey([NotNull] TKey key)
public bool Contains(KeyValuePair<TKey, TValue> item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.ContainsKey(key);
}
public bool Remove([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!m_Dictionary.Remove(key))
if (item.Key == null || !m_PrivateDictionary.Contains(item.Key))
return false;
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
m_OrderedKeys.RemoveAt(index);
m_ValuesOrderedByKey.RemoveAt(index);
return true;
return m_PrivateDictionary[item.Key].Equals(item.Value);
}
public bool TryGetValue([NotNull] TKey key, out TValue value)
public bool ContainsKey(TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.TryGetValue(key, out value);
return m_PrivateDictionary.Contains(key);
}
#endregion
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
#region Private Methods
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
if (array.Rank > 1 || arrayIndex >= array.Length || array.Length - arrayIndex < m_PrivateDictionary.Count)
{
throw new ArgumentException("array");
}
int index = arrayIndex;
foreach (DictionaryEntry entry in m_PrivateDictionary)
{
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
index++;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in m_PrivateDictionary)
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
public bool Remove(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
return TryGetValue(item.Key, out value) &&
EqualityComparer<TValue>.Default.Equals(value, item.Value);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
foreach (KeyValuePair<TKey, TValue> kvp in this)
if (Contains(item))
{
array.SetValue(kvp, index);
index++;
m_PrivateDictionary.Remove(item.Key);
return true;
}
return false;
}
public bool Remove(TKey key)
{
if (key == null)
throw new ArgumentNullException("key");
if (m_PrivateDictionary.Contains(key))
{
m_PrivateDictionary.Remove(key);
return true;
}
return false;
}
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
bool keyExists = m_PrivateDictionary.Contains(key);
value = keyExists ? (TValue)m_PrivateDictionary[key] : default(TValue);
return keyExists;
}
void IDictionary.Add(object key, object value)
{
m_PrivateDictionary.Add(key, value);
}
void IDictionary.Clear()
{
m_PrivateDictionary.Clear();
}
bool IDictionary.Contains(object key)
{
return m_PrivateDictionary.Contains(key);
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return m_PrivateDictionary.GetEnumerator();
}
void IDictionary.Remove(object key)
{
m_PrivateDictionary.Remove(key);
}
void ICollection.CopyTo(Array array, int index)
{
m_PrivateDictionary.CopyTo(array, index);
}
}
internal sealed class IcdOrderedDictionary : IDictionary
{
private ArrayList m_ObjectsArray;
private Hashtable m_ObjectsTable;
private readonly int m_InitialCapacity;
private readonly IEqualityComparer m_Comparer;
private readonly bool m_ReadOnly;
/// <summary>
/// Gets the size of the table.
/// </summary>
public int Count { get { return ObjectsArray.Count; } }
/// <summary>
/// Indicates that the collection can grow.
/// </summary>
bool IDictionary.IsFixedSize { get { return m_ReadOnly; } }
/// <summary>
/// Indicates that the collection is not read-only
/// </summary>
public bool IsReadOnly { get { return m_ReadOnly; } }
/// <summary>
/// Indicates that this class is not synchronized
/// </summary>
bool ICollection.IsSynchronized { get { return false; } }
/// <summary>
/// Gets the collection of keys in the table in order.
/// </summary>
public ICollection Keys { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, true); } }
/// <summary>
/// Returns an arrayList of the values in the table
/// </summary>
public ICollection Values { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, false); } }
public ArrayList ObjectsArray
{
get { return m_ObjectsArray ?? (m_ObjectsArray = new ArrayList(m_InitialCapacity)); }
}
private Hashtable ObjectsTable
{
get { return m_ObjectsTable ?? (m_ObjectsTable = new Hashtable(m_InitialCapacity, m_Comparer)); }
}
/// <summary>
/// The SyncRoot object. Not used because IsSynchronized is false
/// </summary>
object ICollection.SyncRoot { get { return this; } }
/// <summary>
/// Gets or sets the object at the specified index
/// </summary>
public object this[int index]
{
get { return ((DictionaryEntry)ObjectsArray[index]).Value; }
set
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index < 0 || index >= ObjectsArray.Count)
{
throw new ArgumentOutOfRangeException("index");
}
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
ObjectsArray[index] = new DictionaryEntry(key, value);
ObjectsTable[key] = value;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
/// <summary>
/// Gets or sets the object with the specified key
/// </summary>
public object this[object key]
{
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
get { return ObjectsTable[key]; }
set
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (ObjectsTable.Contains(key))
{
ObjectsTable[key] = value;
ObjectsArray[IndexOfKey(key)] = new DictionaryEntry(key, value);
}
else
{
Add(key, value);
}
}
}
public IcdOrderedDictionary() : this(0)
{
}
public IcdOrderedDictionary(int capacity) : this(capacity, null)
{
}
public IcdOrderedDictionary(int capacity, IEqualityComparer comparer)
{
m_InitialCapacity = capacity;
m_Comparer = comparer;
}
private IcdOrderedDictionary(IcdOrderedDictionary dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException("dictionary");
}
m_ReadOnly = true;
m_ObjectsArray = dictionary.m_ObjectsArray;
m_ObjectsTable = dictionary.m_ObjectsTable;
m_Comparer = dictionary.m_Comparer;
m_InitialCapacity = dictionary.m_InitialCapacity;
}
/// <summary>
/// Adds a new entry to the table with the lowest-available index.
/// </summary>
public void Add(object key, object value)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
ObjectsTable.Add(key, value);
ObjectsArray.Add(new DictionaryEntry(key, value));
}
/// <summary>
/// Clears all elements in the table.
/// </summary>
public void Clear()
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
ObjectsTable.Clear();
ObjectsArray.Clear();
}
/// <summary>
/// Returns a readonly IcdOrderedDictionary for the given IcdOrderedDictionary.
/// </summary>
public IcdOrderedDictionary AsReadOnly()
{
return new IcdOrderedDictionary(this);
}
/// <summary>
/// Returns true if the key exists in the table, false otherwise.
/// </summary>
public bool Contains(object key)
{
return ObjectsTable.Contains(key);
}
/// <summary>
/// Copies the table to an array. This will not preserve order.
/// </summary>
public void CopyTo(Array array, int index)
{
ObjectsTable.CopyTo(array, index);
}
private int IndexOfKey(object key)
{
for (int i = 0; i < ObjectsArray.Count; i++)
{
object o = ((DictionaryEntry)ObjectsArray[i]).Key;
if (m_Comparer != null)
{
if (m_Comparer.Equals(o, key))
{
return i;
}
}
else
{
if (o.Equals(key))
{
return i;
}
}
}
return -1;
}
/// <summary>
/// Inserts a new object at the given index with the given key.
/// </summary>
public void Insert(int index, object key, object value)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index > Count || index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
ObjectsTable.Add(key, value);
ObjectsArray.Insert(index, new DictionaryEntry(key, value));
}
/// <summary>
/// Removes the entry at the given index.
/// </summary>
public void RemoveAt(int index)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index >= Count || index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
ObjectsArray.RemoveAt(index);
ObjectsTable.Remove(key);
}
/// <summary>
/// Removes the entry with the given key.
/// </summary>
public void Remove(object key)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (key == null)
{
throw new ArgumentNullException("key");
}
int index = IndexOfKey(key);
if (index < 0)
{
return;
}
ObjectsTable.Remove(key);
ObjectsArray.RemoveAt(index);
}
#region IDictionary implementation
/// <internalonly/>
public IDictionaryEnumerator GetEnumerator()
{
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
}
#endregion
#region IEnumerable implementation
/// <internalonly/>
IEnumerator IEnumerable.GetEnumerator()
{
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
}
#endregion
/// <summary>
/// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries
/// in the order by index.
/// </summary>
private sealed class OrderedDictionaryEnumerator : IDictionaryEnumerator
{
internal const int KEYS = 1;
internal const int VALUES = 2;
internal const int DICTIONARY_ENTRY = 3;
private readonly int m_ObjectReturnType;
private readonly IEnumerator m_ArrayEnumerator;
internal OrderedDictionaryEnumerator(ArrayList array, int objectReturnType)
{
m_ArrayEnumerator = array.GetEnumerator();
m_ObjectReturnType = objectReturnType;
}
/// <summary>
/// Retrieves the current DictionaryEntry. This is the same as Entry, but not strongly-typed.
/// </summary>
public object Current
{
get
{
if (m_ObjectReturnType == KEYS)
{
return ((DictionaryEntry)m_ArrayEnumerator.Current).Key;
}
if (m_ObjectReturnType == VALUES)
{
return ((DictionaryEntry)m_ArrayEnumerator.Current).Value;
}
return Entry;
}
}
/// <summary>
/// Retrieves the current DictionaryEntry
/// </summary>
public DictionaryEntry Entry
{
get
{
return new DictionaryEntry(((DictionaryEntry)m_ArrayEnumerator.Current).Key,
((DictionaryEntry)m_ArrayEnumerator.Current).Value);
}
}
/// <summary>
/// Retrieves the key of the current DictionaryEntry
/// </summary>
public object Key { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Key; } }
/// <summary>
/// Retrieves the value of the current DictionaryEntry
/// </summary>
public object Value { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Value; } }
/// <summary>
/// Moves the enumerator pointer to the next member
/// </summary>
public bool MoveNext()
{
return m_ArrayEnumerator.MoveNext();
}
/// <summary>
/// Resets the enumerator pointer to the beginning.
/// </summary>
public void Reset()
{
m_ArrayEnumerator.Reset();
}
}
/// <summary>
/// OrderedDictionaryKeyValueCollection implements a collection for the Values and Keys properties
/// that is "live"- it will reflect changes to the IcdOrderedDictionary on the collection made after the getter
/// was called.
/// </summary>
private sealed class OrderedDictionaryKeyValueCollection : ICollection
{
private readonly ArrayList m_Objects;
private readonly bool m_IsKeys;
public OrderedDictionaryKeyValueCollection(ArrayList array, bool isKeys)
{
m_Objects = array;
m_IsKeys = isKeys;
}
void ICollection.CopyTo(Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
if (index < 0)
throw new ArgumentOutOfRangeException("index");
foreach (object o in m_Objects)
{
array.SetValue(m_IsKeys ? ((DictionaryEntry)o).Key : ((DictionaryEntry)o).Value, index);
index++;
}
}
int ICollection.Count { get { return m_Objects.Count; } }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return m_Objects.SyncRoot; } }
IEnumerator IEnumerable.GetEnumerator()
{
return new OrderedDictionaryEnumerator(m_Objects,
m_IsKeys
? OrderedDictionaryEnumerator.KEYS
: OrderedDictionaryEnumerator.VALUES);
}
}
}
}

View File

@@ -0,0 +1,213 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class IcdSortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly List<TKey> m_OrderedKeys;
private readonly List<TValue> m_ValuesOrderedByKey;
private readonly Dictionary<TKey, TValue> m_Dictionary;
private readonly IComparer<TKey> m_Comparer;
#region Properties
public int Count { get { return m_Dictionary.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
[CanBeNull]
public TValue this[[NotNull] TKey key]
{
get { return m_Dictionary[key]; }
set
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
Remove(key);
Add(key, value);
}
}
#endregion
/// <summary>
/// Constructor.
/// </summary>
public IcdSortedDictionary()
: this(Comparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdSortedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
Add(kvp.Key, kvp.Value);
}
#region Methods
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
.GetEnumerator();
}
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
m_Dictionary[key] = value;
}
public void Clear()
{
m_OrderedKeys.Clear();
m_ValuesOrderedByKey.Clear();
m_Dictionary.Clear();
}
public bool ContainsKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.ContainsKey(key);
}
public bool Remove([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!m_Dictionary.Remove(key))
return false;
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
m_OrderedKeys.RemoveAt(index);
m_ValuesOrderedByKey.RemoveAt(index);
return true;
}
public bool TryGetValue([NotNull] TKey key, out TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.TryGetValue(key, out value);
}
#endregion
#region Private Methods
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
return TryGetValue(item.Key, out value) &&
EqualityComparer<TValue>.Default.Equals(value, item.Value);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
foreach (KeyValuePair<TKey, TValue> kvp in this)
{
array.SetValue(kvp, index);
index++;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
}
#endregion
}
}

View File

@@ -4,15 +4,21 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// Provides a first-in first-out collection with enhanced insertion features.
/// </summary>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
{
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
private readonly IcdSortedDictionary<int, List<T>> m_PriorityToQueue;
private int m_Count;
#region Properties
@@ -40,7 +46,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
public PriorityQueue()
{
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
m_PriorityToQueue = new IcdSortedDictionary<int, List<T>>();
}
#region Methods
@@ -79,6 +85,20 @@ namespace ICD.Common.Utils.Collections
m_Count++;
}
/// <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++;
}
/// <summary>
/// Enqueues the item at the beginning of the queue.
/// </summary>
@@ -121,6 +141,27 @@ namespace ICD.Common.Utils.Collections
if (remove == null)
throw new ArgumentNullException("remove");
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())
{
int[] removeIndices =
@@ -129,6 +170,12 @@ namespace ICD.Common.Utils.Collections
.Reverse()
.ToArray();
if (removeIndices.Any() && kvp.Key < lowestMatchingPriority )
{
lowestMatchingPriority = kvp.Key;
firstMatchingIndex = removeIndices.Last();
}
foreach (int removeIndex in removeIndices)
{
kvp.Value.RemoveAt(removeIndex);
@@ -139,7 +186,16 @@ namespace ICD.Common.Utils.Collections
m_PriorityToQueue.Remove(kvp.Key);
}
Enqueue(item, priority);
if(deDuplicateToEndOfQueue)
Enqueue(item, priority);
else
{
if(firstMatchingIndex == null)
Enqueue(item, lowestMatchingPriority);
else
Enqueue(item, lowestMatchingPriority, firstMatchingIndex.Value);
}
}
/// <summary>

View File

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

View File

@@ -0,0 +1,299 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public class ReverseLookupDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_KeyToValue;
private readonly Dictionary<TValue, IcdHashSet<TKey>> m_ValueToKeys;
#region Properties
public int Count { get { return m_KeyToValue.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValueToKeys.Keys; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public ReverseLookupDictionary() :
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public ReverseLookupDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
{
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
m_ValueToKeys = new Dictionary<TValue, IcdHashSet<TKey>>(valueComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
: this(keyComparer, valueComparer)
{
if (dict == null)
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict)
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
#region Methods
public void Clear()
{
m_KeyToValue.Clear();
m_ValueToKeys.Clear();
}
public bool ContainsKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue.ContainsKey(key);
}
public bool ContainsValue([NotNull] TValue value)
{
return m_ValueToKeys.ContainsKey(value);
}
public IEnumerable<KeyValuePair<TValue, IEnumerable<TKey>>> GetReverseDictionary()
{
// Cast stuff a layer deep cause weird
return m_ValueToKeys.Select(kvp => new KeyValuePair<TValue, IEnumerable<TKey>>(kvp.Key, kvp.Value.ToArray(kvp.Value.Count)));
}
public void Add([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (ContainsKey(key))
throw new ArgumentException("Key is already present in the collection", "key");
m_KeyToValue.Add(key, value);
m_ValueToKeys.GetOrAddNew(value, () => new IcdHashSet<TKey>()).Add(key);
}
public void Set([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
RemoveKey(key);
Add(key, value);
}
[NotNull]
public IEnumerable<TKey> GetKeys([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
return m_ValueToKeys[value];
}
[NotNull]
public TValue GetValue([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue[key];
}
public bool RemoveKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!ContainsKey(key))
return false;
TValue value = m_KeyToValue[key];
m_KeyToValue.Remove(key);
IcdHashSet<TKey> keys = m_ValueToKeys[value];
keys.Remove(key);
if (keys.Count == 0)
m_ValueToKeys.Remove(value);
return true;
}
/// <summary>
/// Removes the value from the collection, and any keys that were using it
/// </summary>
/// <param name="value"></param>
/// <returns>true if items were removed, false if not</returns>
/// <exception cref="ArgumentNullException"></exception>
public bool RemoveValue([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (!ContainsValue(value))
return false;
IcdHashSet<TKey> keys = m_ValueToKeys[value];
m_ValueToKeys.Remove(value);
foreach (TKey key in keys)
{
m_KeyToValue.Remove(key);
}
return true;
}
public bool TryGetValue([NotNull] TKey key, out TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue.TryGetValue(key, out value);
}
public bool TryGetKeys([NotNull] TValue value, out IEnumerable<TKey> keys)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
IcdHashSet<TKey> keysInternal;
if (m_ValueToKeys.TryGetValue(value, out keysInternal))
{
keys = keysInternal;
return true;
}
keys = Enumerable.Empty<TKey>();
return false;
}
#endregion
#region IDictionary
[NotNull]
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
bool IDictionary<TKey, TValue>.Remove([NotNull] TKey key)
{
return RemoveKey(key);
}
#endregion
#region ICollection
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return (m_KeyToValue as IDictionary<TKey, TValue>).Contains(item);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return RemoveKey(item.Key);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
}
#endregion
#region IEnumerable
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_KeyToValue.GetEnumerator();
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

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

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

View File

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

View File

@@ -116,6 +116,16 @@ 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>
@@ -128,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>
@@ -78,7 +101,9 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return extends.GetDefault(key, default(TValue));
@@ -101,7 +126,9 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
TValue value;
@@ -119,13 +146,14 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key,
TValue defaultValue)
[NotNull] TKey key, TValue defaultValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
TValue value = extends.GetDefault(key, defaultValue);
@@ -144,15 +172,15 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key)
[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>());
@@ -169,15 +197,15 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
[NotNull] TKey key,
[NotNull] Func<TValue> valueFunc)
[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)

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];
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
@@ -233,10 +234,10 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="comparer"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static bool SequenceEqual<T>([NotNull] this IEnumerable<T> extends, [NotNull] IEnumerable<T> other,
[NotNull] IEqualityComparer<T> comparer)
[NotNull] Func<T, T, bool> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -244,31 +245,8 @@ namespace ICD.Common.Utils.Extensions
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
return extends.SequenceEqual(other, comparer.Equals);
}
/// <summary>
/// Compares the two sequences for identical values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static bool SequenceEqual<T>([NotNull] this IEnumerable<T> extends, [NotNull] IEnumerable<T> other,
[NotNull] Func<T, T, bool> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
if (predicate == null)
throw new ArgumentNullException("predicate");
// Simple count check
ICollection<T> a = extends as ICollection<T>;
@@ -285,7 +263,7 @@ namespace ICD.Common.Utils.Extensions
while (hasFirst && hasSecond)
{
if (!comparer(firstPos.Current, secondPos.Current))
if (!predicate(firstPos.Current, secondPos.Current))
return false;
hasFirst = firstPos.MoveNext();
@@ -838,6 +816,26 @@ namespace ICD.Common.Utils.Extensions
}
}
/// <summary>
/// Creates a new collection and fills it with the items of the enumerable.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TCollection"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static TCollection ToCollection<T, TCollection>([NotNull] this IEnumerable<T> extends)
where TCollection : ICollection<T>
{
if (extends == null)
throw new ArgumentNullException("extends");
TCollection collection = ReflectionUtils.CreateInstance<TCollection>();
foreach (T item in extends)
collection.Add(item);
return collection;
}
/// <summary>
/// Returns the sequence as a IcdHashSet.
/// </summary>
@@ -912,6 +910,30 @@ namespace ICD.Common.Utils.Extensions
return array;
}
/// <summary>
/// Creates a generic List of the given item type.
/// </summary>
/// <param name="extends"></param>
/// <param name="itemType"></param>
/// <returns></returns>
public static IList ToList<T>([NotNull] this IEnumerable<T> extends, [NotNull] Type itemType)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (itemType == null)
throw new ArgumentNullException("itemType");
Type genericListType = typeof(List<>);
Type specificListType = genericListType.MakeGenericType(itemType);
IList list = (IList)ReflectionUtils.CreateInstance(specificListType);
foreach (object item in extends)
list.Add(item);
return list;
}
/// <summary>
/// Optimized ToList implementation with fewer allocations.
/// </summary>
@@ -966,6 +988,52 @@ namespace ICD.Common.Utils.Extensions
return output;
}
/// <summary>
/// Returns true if all of the items in the sequence are equal, or the sequence is empty.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static bool IsDistinct<TItem>([NotNull] this IEnumerable<TItem> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
IEqualityComparer<TItem> comparer = EqualityComparer<TItem>.Default;
return extends.IsDistinct(comparer);
}
/// <summary>
/// Returns true if all of the items in the sequence are equal, or the sequence is empty.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static bool IsDistinct<TItem>([NotNull] this IEnumerable<TItem> extends, [NotNull] IEqualityComparer<TItem> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
TItem other = default(TItem);
bool first = true;
foreach (TItem item in extends)
{
if (first)
{
other = item;
first = false;
continue;
}
if (!comparer.Equals(item, other))
return false;
}
return true;
}
/// <summary>
/// Gets distinct elements from the sequence based on given property.
/// </summary>
@@ -1022,6 +1090,22 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
public static T Random<T>([NotNull] this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
Random random = new Random(Guid.NewGuid().GetHashCode());
return extends.Random(random);
}
/// <summary>
/// Returns a random item from the given sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="random"></param>
/// <returns></returns>
public static T Random<T>([NotNull] this IEnumerable<T> extends, [NotNull] Random random)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -1031,7 +1115,6 @@ namespace ICD.Common.Utils.Extensions
if (sequence.Count == 0)
throw new InvalidOperationException("Sequence is empty.");
Random random = new Random(Guid.NewGuid().GetHashCode());
int index = random.Next(0, sequence.Count);
return sequence[index];
@@ -1305,6 +1388,40 @@ namespace ICD.Common.Utils.Extensions
}
}
/// <summary>
/// Returns the minimum value from the sequence, otherwise the default value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T MinOrDefault<T>([NotNull] this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.MinOrDefault(v => v);
}
/// <summary>
/// Returns the minimum value from the sequence, otherwise the default value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static TValue MinOrDefault<T, TValue>([NotNull] this IEnumerable<T> extends, [NotNull] Func<T, TValue> selector)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (selector == null)
throw new ArgumentNullException("selector");
return extends.Select(selector)
.AggregateOrDefault((a, b) => Comparer<TValue>.Default.Compare(a, b) < 0 ? a : b);
}
/// <summary>
/// Returns the maximum value from the sequence, otherwise the default value.
/// </summary>
@@ -1316,20 +1433,80 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
Comparer<T> comparer = Comparer<T>.Default;
T value = default(T);
return extends.MaxOrDefault(v => v);
}
using (IEnumerator<T> enumerator = extends.GetEnumerator())
/// <summary>
/// Returns the maximum value from the sequence, otherwise the default value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static TValue MaxOrDefault<T, TValue>([NotNull] this IEnumerable<T> extends, [NotNull] Func<T, TValue> selector)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (selector == null)
throw new ArgumentNullException("selector");
return extends.Select(selector)
.AggregateOrDefault((a, b) => Comparer<TValue>.Default.Compare(a, b) > 0 ? a : b);
}
/// <summary>
/// Applies an accumulator function over a sequence.
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="extends"></param>
/// <param name="func"></param>
/// <returns>The final accumulator value.</returns>
[CanBeNull]
public static TSource AggregateOrDefault<TSource>([NotNull] this IEnumerable<TSource> extends,
[NotNull] Func<TSource, TSource, TSource> func)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (func == null)
throw new ArgumentNullException("func");
return extends.AggregateOrDefault(func, default(TSource));
}
/// <summary>
/// Applies an accumulator function over a sequence.
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="extends"></param>
/// <param name="func"></param>
/// <param name="defaultValue"></param>
/// <returns>The final accumulator value.</returns>
[CanBeNull]
public static TSource AggregateOrDefault<TSource>([NotNull] this IEnumerable<TSource> extends,
[NotNull] Func<TSource, TSource, TSource> func,
TSource defaultValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (func == null)
throw new ArgumentNullException("func");
using (IEnumerator<TSource> enumerator = extends.GetEnumerator())
{
while (enumerator.MoveNext())
{
T x = enumerator.Current;
if (comparer.Compare(x, value) > 0)
value = x;
}
}
if (!enumerator.MoveNext())
return defaultValue;
return value;
TSource result = enumerator.Current;
while (enumerator.MoveNext())
result = func(result, enumerator.Current);
return result;
}
}
/// <summary>
@@ -1362,23 +1539,23 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return Consolidate(extends, Comparer<T>.Default);
return Consolidate(extends, EqualityComparer<T>.Default);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<T> Consolidate<T>([NotNull] this IEnumerable<T> extends,
[NotNull] IComparer<T> comparer)
[NotNull] IEqualityComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -1386,33 +1563,72 @@ namespace ICD.Common.Utils.Extensions
if (comparer == null)
throw new ArgumentNullException("comparer");
return ConsolidateIterator(extends, comparer);
return extends.Consolidate(e => e, comparer);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="predicate"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<TKey> Consolidate<TKey, TValue>([NotNull] this IEnumerable<TKey> extends,
[NotNull] Func<TKey, TValue> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
return extends.Consolidate(predicate, EqualityComparer<TValue>.Default);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="predicate"></param>
/// <param name="comparer"></param>
/// <returns></returns>
private static IEnumerable<T> ConsolidateIterator<T>([NotNull] IEnumerable<T> sequence,
[NotNull] IComparer<T> comparer)
[PublicAPI]
public static IEnumerable<TKey> Consolidate<TKey, TValue>([NotNull] this IEnumerable<TKey> extends,
[NotNull] Func<TKey, TValue> predicate,
[NotNull] IEqualityComparer<TValue> comparer)
{
bool first = true;
T last = default(T);
if (extends == null)
throw new ArgumentNullException("extends");
foreach (T item in sequence)
if (predicate == null)
throw new ArgumentNullException("predicate");
if (comparer == null)
throw new ArgumentNullException("comparer");
bool first = true;
TValue last = default(TValue);
foreach (TKey item in extends)
{
if (!first && comparer.Compare(last, item) == 0)
TValue value = predicate(item);
if (!first && comparer.Equals(last, value))
continue;
first = false;
last = item;
last = value;
yield return item;
}

View File

@@ -1,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
{
@@ -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>([NotNull] this JsonReader extends, [NotNull] JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (serializer == null)
throw new ArgumentNullException("serializer");
return serializer.Deserialize<T>(extends);
}
@@ -95,11 +83,15 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
[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);
}
@@ -141,6 +133,82 @@ namespace ICD.Common.Utils.Extensions
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);
}
/// <summary>
/// Gets the current value as a string.
@@ -232,35 +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([NotNull] this JsonReader extends, [NotNull] 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 (format == null)
throw new ArgumentNullException("format");
// No TimeZoneInfo in CF, so now things get gross
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
serial = dateTime.ToIso() + match.Groups["zone"].Value;
}
#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
string stringValue = extends.GetValueAsString();
return DateTime.ParseExact(stringValue, format, provider);
return DateTimeUtils.FromIso8601(serial);
}
}
}

View File

@@ -1,8 +1,13 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{

View File

@@ -1,5 +1,10 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
namespace ICD.Common.Utils.Extensions
{

View File

@@ -1,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([NotNull]this JsonWriter extends, [CanBeNull]Type type)
public static void WriteType([NotNull] this JsonWriter extends, [CanBeNull] Type type)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -69,7 +92,7 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
extends.WritePropertyName(propertyName);
extends.WriteValue(value);
extends.WriteDateTime(value);
}
/// <summary>

View File

@@ -349,6 +349,22 @@ namespace ICD.Common.Utils.Extensions
#region Binary Search
/// <summary>
/// Returns the index of the item in the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.BinarySearch(item, Comparer<T>.Default);
}
/// <summary>
/// Returns the index of the item in the list.
/// </summary>

View File

@@ -128,5 +128,214 @@ namespace ICD.Common.Utils.Extensions
.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,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
@@ -141,6 +143,46 @@ namespace ICD.Common.Utils.Extensions
Math.Min(chunkSize, extends.Length - (i * chunkSize))));
}
/// <summary>
/// Joins the strings in the enumerable.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string Join([NotNull] this IEnumerable<string> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Join(string.Empty);
}
/// <summary>
/// Joins the strings in the enumerable.
/// </summary>
/// <param name="extends"></param>
/// <param name="delimiter"></param>
/// <returns></returns>
public static string Join([NotNull] this IEnumerable<string> extends, string delimiter)
{
if (extends == null)
throw new ArgumentNullException("extends");
StringBuilder builder = new StringBuilder();
bool isFirst = true;
foreach (string item in extends)
{
if (!isFirst)
builder.Append(delimiter);
builder.Append(item);
isFirst = false;
}
return builder.ToString();
}
/// <summary>
/// Removes whitespace from the string.
/// </summary>
@@ -156,6 +198,21 @@ namespace ICD.Common.Utils.Extensions
return new string(extends.Where(c => !char.IsWhiteSpace(c)).ToArray());
}
/// <summary>
/// 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>
@@ -213,7 +270,7 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return extends.All(char.IsDigit);
return extends.AnyAndAll(char.IsDigit);
}
/// <summary>
@@ -256,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

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

View File

@@ -46,5 +46,39 @@ namespace ICD.Common.Utils.Extensions
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; }
}
}

View File

@@ -0,0 +1,457 @@
/*
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronIO.Compression;
#else
using System.IO;
using System.IO.Compression;
#endif
namespace ICD.Common.Utils.IO.Compression
{
/// <summary>
/// Unzip helper class.
/// </summary>
public sealed class Unzip : IDisposable
{
/// <summary>
/// Zip archive entry.
/// </summary>
public sealed class Entry
{
/// <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="Entry" /> is deflated.
/// </summary>
public bool Deflated { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="Entry" /> 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="Entry" /> is a file.
/// </summary>
public bool IsFile { get { return !IsDirectory; } }
public int HeaderOffset { get; set; }
public int DataOffset { get; set; }
}
/// <summary>
/// CRC32 calculation helper.
/// </summary>
private sealed class Crc32Calculator
{
private static readonly uint[] s_Crc32Table =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
private uint m_CrcValue = 0xffffffff;
public uint Crc32 { get { return m_CrcValue ^ 0xffffffff; } }
public void UpdateWithBlock(byte[] buffer, int numberOfBytes)
{
for (var i = 0; i < numberOfBytes; i++)
m_CrcValue = (m_CrcValue >> 8) ^ s_Crc32Table[buffer[i] ^ m_CrcValue & 0xff];
}
}
/// <summary>
/// Provides data for the ExtractProgress event.
/// </summary>
public sealed class FileProgressEventArgs : EventArgs
{
/// <summary>
/// Gets the current file.
/// </summary>
public int CurrentFile { get; private set; }
/// <summary>
/// Gets the total files.
/// </summary>
public int TotalFiles { get; private set; }
/// <summary>
/// Gets the name of the file.
/// </summary>
public string FileName { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="FileProgressEventArgs"/> class.
/// </summary>
/// <param name="currentFile">The current file.</param>
/// <param name="totalFiles">The total files.</param>
/// <param name="fileName">Name of the file.</param>
public FileProgressEventArgs(int currentFile, int totalFiles, string fileName)
{
CurrentFile = currentFile;
TotalFiles = totalFiles;
FileName = fileName;
}
}
private const int ENTRY_SIGNATURE = 0x02014B50;
private const int FILE_SIGNATURE = 0x04034b50;
private const int DIRECTORY_SIGNATURE = 0x06054B50;
private const int BUFFER_SIZE = 16 * 1024;
/// <summary>
/// Occurs when a file or a directory is extracted from an archive.
/// </summary>
public event EventHandler<FileProgressEventArgs> OnExtractProgress;
private Entry[] m_Entries;
private Stream Stream { get; set; }
private BinaryReader Reader { get; set; }
/// <summary>
/// Gets the file names.
/// </summary>
public IEnumerable<string> FileNames
{
get { return Entries.Select(e => e.Name).Where(f => !f.EndsWith("/")).OrderBy(f => f); }
}
/// <summary>
/// Gets zip file entries.
/// </summary>
public Entry[] Entries { get { return m_Entries ?? (m_Entries = ReadZipEntries().ToArray()); } }
/// <summary>
/// Initializes a new instance of the <see cref="Unzip" /> class.
/// </summary>
/// <param name="fileName">Name of the file.</param>
public Unzip(string fileName)
: this(File.OpenRead(fileName))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Unzip" /> class.
/// </summary>
/// <param name="stream">The stream.</param>
public Unzip(Stream stream)
{
Stream = stream;
Reader = new BinaryReader(Stream);
}
/// <summary>
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (Stream != null)
{
Stream.Dispose();
Stream = null;
}
if (Reader != null)
{
Reader.Close();
Reader = null;
}
}
/// <summary>
/// Extracts the contents of the zip file to the given directory.
/// </summary>
/// <param name="directoryName">Name of the directory.</param>
public void ExtractToDirectory(string directoryName)
{
for (int index = 0; index < Entries.Length; index++)
{
var entry = Entries[index];
// create target directory for the file
var fileName = Path.Combine(directoryName, entry.Name);
var dirName = Path.GetDirectoryName(fileName);
Directory.CreateDirectory(dirName);
// save file if it is not only a directory
if (!entry.IsDirectory)
Extract(entry.Name, fileName);
var extractProgress = OnExtractProgress;
if (extractProgress != null)
extractProgress(this, new FileProgressEventArgs(index + 1, Entries.Length, entry.Name));
}
}
/// <summary>
/// Extracts the specified file to the specified name.
/// </summary>
/// <param name="fileName">Name of the file in zip archive.</param>
/// <param name="outputFileName">Name of the output file.</param>
public void Extract(string fileName, string outputFileName)
{
var entry = GetEntry(fileName);
using (var outStream = File.Create(outputFileName))
Extract(entry, outStream);
var fileInfo = new FileInfo(outputFileName);
if (fileInfo.Length != entry.OriginalSize)
{
throw new FormatException(string.Format(
"Corrupted archive: {0} has an uncompressed size {1} which does not match its expected size {2}",
outputFileName, fileInfo.Length, entry.OriginalSize));
}
#if !SIMPLSHARP
File.SetLastWriteTime(outputFileName, entry.Timestamp);
#endif
}
private Entry GetEntry(string fileName)
{
var entry = Entries.FirstOrDefault(e => e.Name == fileName);
if (entry == null)
throw new FileNotFoundException("File not found in the archive: " + fileName);
return entry;
}
/// <summary>
/// Extracts the specified file to the output <see cref="Stream"/>.
/// </summary>
/// <param name="fileName">Name of the file in zip archive.</param>
/// <param name="outputStream">The output stream.</param>
public void Extract(string fileName, Stream outputStream)
{
Extract(GetEntry(fileName), outputStream);
}
/// <summary>
/// Extracts the specified entry.
/// </summary>
/// <param name="entry">Zip file entry to extract.</param>
/// <param name="outputStream">The stream to write the data to.</param>
/// <exception cref="System.InvalidOperationException"> is thrown when the file header signature doesn't match.</exception>
public void Extract(Entry entry, Stream outputStream)
{
// check file signature
Stream.Seek(entry.HeaderOffset, SeekOrigin.Begin);
if (Reader.ReadInt32() != FILE_SIGNATURE)
throw new FormatException("File signature doesn't match.");
// move to file data
Stream.Seek(entry.DataOffset, SeekOrigin.Begin);
var inputStream = Stream;
if (entry.Deflated)
inputStream = new DeflateStream(Stream, CompressionMode.Decompress, true);
// allocate buffer, prepare for CRC32 calculation
var count = entry.OriginalSize;
var bufferSize = Math.Min(BUFFER_SIZE, entry.OriginalSize);
var buffer = new byte[bufferSize];
var crc32Calculator = new Crc32Calculator();
while (count > 0)
{
// decompress data
var read = inputStream.Read(buffer, 0, bufferSize);
if (read == 0)
break;
crc32Calculator.UpdateWithBlock(buffer, read);
// copy to the output stream
outputStream.Write(buffer, 0, read);
count -= read;
}
if (crc32Calculator.Crc32 != entry.Crc32)
{
throw new FormatException(string.Format(
"Corrupted archive: CRC32 doesn't match on file {0}: expected {1:x8}, got {2:x8}.",
entry.Name, entry.Crc32, crc32Calculator.Crc32));
}
}
private IEnumerable<Entry> ReadZipEntries()
{
if (Stream.Length < 22)
yield break;
Stream.Seek(-22, SeekOrigin.End);
// find directory signature
while (Reader.ReadInt32() != DIRECTORY_SIGNATURE)
{
if (Stream.Position <= 5)
yield break;
// move 1 byte back
Stream.Seek(-5, SeekOrigin.Current);
}
// read directory properties
Stream.Seek(6, SeekOrigin.Current);
var entries = Reader.ReadUInt16();
var difSize = Reader.ReadInt32();
var dirOffset = Reader.ReadUInt32();
Stream.Seek(dirOffset, SeekOrigin.Begin);
// read directory entries
for (int i = 0; i < entries; i++)
{
if (Reader.ReadInt32() != ENTRY_SIGNATURE)
continue;
// read file properties
// TODO: Replace with a proper class to make this method a lot shorter.
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();
var fileNameBytes = Reader.ReadBytes(fileNameSize);
Stream.Seek(extraSize, SeekOrigin.Current);
var fileCommentBytes = Reader.ReadBytes(commentSize);
var fileDataOffset = CalculateFileDataOffset(fileHeaderOffset);
// decode zip file entry
var encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
yield return new Entry
{
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 int CalculateFileDataOffset(int fileHeaderOffset)
{
var position = Stream.Position;
Stream.Seek(fileHeaderOffset + 26, SeekOrigin.Begin);
var fileNameSize = Reader.ReadInt16();
var extraSize = Reader.ReadInt16();
var fileOffset = (int)Stream.Position + fileNameSize + extraSize;
Stream.Seek(position, SeekOrigin.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>
public 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);
}
}
}

View File

@@ -1,10 +1,10 @@
using System;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp.CrestronIO;
#else
using ICD.Common.Utils.Extensions;
using System.IO;
using Microsoft.DotNet.PlatformAbstractions;
using System.Reflection;
#endif
namespace ICD.Common.Utils.IO
@@ -13,10 +13,11 @@ namespace ICD.Common.Utils.IO
{
public static string GetApplicationDirectory()
{
#if SIMPLSHARP
#if !NETSTANDARD
return Directory.GetApplicationDirectory();
#else
return ApplicationEnvironment.ApplicationBasePath;
string pathToDll = Assembly.GetExecutingAssembly().GetPath();
return pathToDll == null ? null : IcdPath.GetDirectoryName(pathToDll);
#endif
}
@@ -26,10 +27,10 @@ namespace ICD.Common.Utils.IO
/// <returns></returns>
public static string GetApplicationRootDirectory()
{
#if SIMPLSHARP
#if !NETSTANDARD
return Directory.GetApplicationRootDirectory();
#else
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetPath());
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetPath());
#endif
}

View File

@@ -12,7 +12,7 @@ namespace ICD.Common.Utils.IO
public static class IcdFile
{
[PublicAPI]
public static string ReadToEnd(string path, Encoding encoding)
public static string ReadToEnd([NotNull] string path, [NotNull] Encoding encoding)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -28,7 +28,7 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static DateTime GetLastWriteTime(string path)
public static DateTime GetLastWriteTime([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -37,16 +37,25 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static bool Exists(string path)
public static bool Exists([CanBeNull] string path)
{
// Consistent with Net Standard
if (path == null)
throw new ArgumentNullException("path");
return false;
return File.Exists(path);
try
{
return File.Exists(path);
}
// Crestron's AdjustPathForMono method throws an exception that is inconsistent with Net Standard...
catch (Exception)
{
return false;
}
}
[PublicAPI]
public static void Copy(string pathFrom, string pathTo)
public static void Copy([NotNull] string pathFrom, [NotNull] string pathTo)
{
if (pathFrom == null)
throw new ArgumentNullException("pathFrom");
@@ -58,7 +67,7 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static void Delete(string path)
public static void Delete([NotNull] string path)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -78,9 +87,9 @@ namespace ICD.Common.Utils.IO
}
[PublicAPI]
public static IcdFileStream Open(string path, FileMode mode)
public static IcdFileStream Open(string path, eIcdFileMode mode)
{
return new IcdFileStream(File.Open(path, mode));
return new IcdFileStream(File.Open(path, mode.ToFileMode()));
}
[PublicAPI]
@@ -89,6 +98,12 @@ namespace ICD.Common.Utils.IO
return File.GetCreationTime(path);
}
[PublicAPI]
public static long GetLength(string path)
{
return new FileInfo(path).Length;
}
[PublicAPI]
public static IcdFileStream Create(string path)
{
@@ -100,5 +115,29 @@ namespace ICD.Common.Utils.IO
{
return new IcdStreamWriter(File.AppendText(path));
}
public static void WriteAllBytes(string path, byte[] bytes)
{
using (FileStream stream = File.OpenWrite(path))
stream.Write(bytes, 0, bytes.Length);
}
public static void Move([NotNull] string sourceFileName, [NotNull] string destFileName)
{
File.Move(sourceFileName, destFileName);
}
#if !SIMPLSHARP
public static void SetAttributes(string path, FileAttributes attributes, bool recursive)
{
File.SetAttributes(path, attributes);
if (!recursive || !Directory.Exists(path))
return;
foreach (string innerPath in Directory.GetFileSystemEntries(path))
SetAttributes(innerPath, attributes, true);
}
#endif
}
}

View File

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

View File

@@ -1,7 +1,7 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
#else
using System.IO;
#endif
@@ -11,6 +11,10 @@ namespace ICD.Common.Utils.IO
{
private readonly StreamReader m_StreamReader;
public StreamReader WrappedStreamReader { get { return m_StreamReader; } }
public bool EndOfStream { get { return m_StreamReader.EndOfStream; } }
/// <summary>
/// Constructor.
/// </summary>
@@ -23,6 +27,21 @@ namespace ICD.Common.Utils.IO
m_StreamReader = new StreamReader(memoryStream.WrappedMemoryStream);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="path"></param>
public IcdStreamReader(string path)
{
if (path == null)
throw new ArgumentNullException("path");
if (!IcdFile.Exists(path))
throw new FileNotFoundException("Error creating stream reader, file not found");
m_StreamReader = new StreamReader(path);
}
~IcdStreamReader()
{
Dispose();
@@ -37,5 +56,10 @@ namespace ICD.Common.Utils.IO
{
m_StreamReader.Dispose();
}
public string ReadLine()
{
return m_StreamReader.ReadLine();
}
}
}

View File

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

View File

@@ -0,0 +1,46 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
namespace ICD.Common.Utils.IO
{
/// <summary>
/// Specifies how the operating system should open a file.
/// </summary>
public enum eIcdFileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
public static class IcdFileModeExtensions
{
public static FileMode ToFileMode(this eIcdFileMode extends)
{
switch (extends)
{
case eIcdFileMode.CreateNew:
return FileMode.CreateNew;
case eIcdFileMode.Create:
return FileMode.Create;
case eIcdFileMode.Open:
return FileMode.Open;
case eIcdFileMode.OpenOrCreate:
return FileMode.OpenOrCreate;
case eIcdFileMode.Truncate:
return FileMode.Truncate;
case eIcdFileMode.Append:
return FileMode.Append;
default:
throw new ArgumentOutOfRangeException("extends");
}
}
}
}

View File

@@ -1,9 +1,10 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp;
#else
using System.Diagnostics;
@@ -13,6 +14,7 @@ namespace ICD.Common.Utils
{
public static class IcdConsole
{
private const string NEWLINE = "\r\n";
public enum eAccessLevel
{
Operator = 0,
@@ -22,6 +24,57 @@ namespace ICD.Common.Utils
public static event EventHandler<StringEventArgs> OnConsolePrint;
private static readonly SafeCriticalSection s_Section;
private static readonly Regex s_NewLineRegex;
private static bool? s_IsConsoleApp;
/// <summary>
/// Returns true if the application is being run from an interactive console,
/// false if the application is being run as a headless service.
/// </summary>
/// <value></value>
public static bool IsConsoleApp
{
get
{
if (s_IsConsoleApp == null)
{
#if !NETSTANDARD
s_IsConsoleApp = false;
#else
try
{
// Hack
int unused = Console.WindowHeight;
s_IsConsoleApp = true;
if (Console.Title.Length > 0)
s_IsConsoleApp = true;
if (!Environment.UserInteractive)
s_IsConsoleApp = false;
}
catch
{
s_IsConsoleApp = false;
}
#endif
}
return s_IsConsoleApp.Value;
}
}
/// <summary>
/// Static constructor.
/// </summary>
static IcdConsole()
{
s_Section = new SafeCriticalSection();
s_NewLineRegex = new Regex("(?!<\r)\n");
}
/// <summary>
/// Wraps CrestronConsole.ConsoleCommandResponse for S+ compatibility.
/// </summary>
@@ -30,7 +83,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void ConsoleCommandResponseLine(string message, params object[] args)
{
ConsoleCommandResponse(message + IcdEnvironment.NewLine, args);
ConsoleCommandResponse(message + NEWLINE, args);
}
/// <summary>
@@ -41,38 +94,56 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void ConsoleCommandResponse(string message, params object[] args)
{
// No console in Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return;
if (args != null && args.Any())
message = string.Format(message, args);
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
message = FixLineEndings(message);
#if !NETSTANDARD
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
{
try
{
CrestronConsole.ConsoleCommandResponse(message);
}
catch (NotSupportedException)
{
PrintLine(message);
}
return;
return;
}
#endif
PrintLine(message);
PrintLine(message);
}
public static void PrintLine(string message)
{
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
CrestronConsole.PrintLine(message);
s_Section.Enter();
string fixedMessage = FixLineEndings(message);
try
{
#if !NETSTANDARD
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
CrestronConsole.PrintLine(fixedMessage);
#else
Console.WriteLine(message);
Trace.WriteLine(AnsiUtils.StripAnsi(fixedMessage));
if (IsConsoleApp)
Console.WriteLine(fixedMessage);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message + IcdEnvironment.NewLine));
}
finally
{
s_Section.Leave();
}
OnConsolePrint.Raise(null, new StringEventArgs(message + NEWLINE));
}
public static void PrintLine(string message, params object[] args)
@@ -83,14 +154,8 @@ namespace ICD.Common.Utils
public static void PrintLine(eConsoleColor color, string message)
{
#if SIMPLSHARP
PrintLine(color.FormatAnsi(message));
#else
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
System.Console.WriteLine(message);
System.Console.ResetColor();
#endif
string ansi = color.FormatAnsi(message);
PrintLine(ansi);
}
public static void PrintLine(eConsoleColor color, string message, params object[] args)
@@ -101,13 +166,28 @@ namespace ICD.Common.Utils
public static void Print(string message)
{
#if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
CrestronConsole.Print(message);
s_Section.Enter();
string fixedMessage = FixLineEndings(message);
try
{
#if !NETSTANDARD
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
CrestronConsole.Print(fixedMessage);
#else
Console.Write(message);
Trace.Write(AnsiUtils.StripAnsi(fixedMessage));
if (IsConsoleApp)
Console.Write(fixedMessage);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message));
}
finally
{
s_Section.Leave();
}
OnConsolePrint.Raise(null, new StringEventArgs(fixedMessage));
}
public static void Print(string message, params object[] args)
@@ -118,14 +198,8 @@ namespace ICD.Common.Utils
public static void Print(eConsoleColor color, string message)
{
#if SIMPLSHARP
Print(color.FormatAnsi(message));
#else
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
System.Console.Write(message);
System.Console.ResetColor();
#endif
string ansi = color.FormatAnsi(message);
Print(ansi);
}
public static void Print(eConsoleColor color, string message, params object[] args)
@@ -136,9 +210,9 @@ namespace ICD.Common.Utils
public static bool SendControlSystemCommand(string command, ref string result)
{
#if SIMPLSHARP
// No console on VC4
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
#if !NETSTANDARD
// No console on Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
return CrestronConsole.SendControlSystemCommand(command, ref result);
@@ -149,9 +223,13 @@ namespace ICD.Common.Utils
public static bool AddNewConsoleCommand(Action<string> callback, string command, string help, eAccessLevel accessLevel)
{
#if SIMPLSHARP
#if !NETSTANDARD
// Avoid crashing Simpl applications
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
return false;
// No console in Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
if (CrestronConsole.ConsoleRegistered)
@@ -163,5 +241,21 @@ namespace ICD.Common.Utils
return false;
#endif
}
/// <summary>
/// Code running on SimplSharpProMono uses \n for newline (due to linux environment),
/// Which causes console output to be unreadable on most SSH clients. This converts those
/// endings to \r\n, since Crestron's SSH server doesn't do it automatically.
/// This is a hack until Crestron fixes their SSH server.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string FixLineEndings(string input)
{
if (IcdEnvironment.CrestronSeries != IcdEnvironment.eCrestronSeries.FourSeries)
return input;
return s_NewLineRegex.Replace(input, NEWLINE);
}
}
}

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
@@ -18,16 +18,6 @@ namespace ICD.Common.Utils
public static string NewLine { get { return CrestronEnvironment.NewLine; } }
public static eRuntimeEnvironment RuntimeEnvironment
{
get
{
return CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? eRuntimeEnvironment.SimplSharpProMono
: GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment);
}
}
/// <summary>
/// Gets the network address(es) of the processor.
/// </summary>
@@ -100,7 +90,7 @@ namespace ICD.Common.Utils
/// Gets the dhcp status of the processor.
/// </summary>
[PublicAPI]
public static string DhcpStatus
public static bool DhcpStatus
{
get
{
@@ -112,18 +102,51 @@ namespace ICD.Common.Utils
{
short id = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(type);
if (id >= InitialParametersClass.NumberOfEthernetInterfaces)
return null;
return false;
string status = CrestronEthernetHelper.GetEthernetParameter(param, id);
if (!string.IsNullOrEmpty(status) && !status.Equals(INVALID_VALUE))
return status;
return status == "ON";
return null;
return false;
}
catch (ArgumentException)
{
return null;
return false;
}
}
}
/// <summary>
/// Gets the SSL state of the processor.
/// </summary>
[PublicAPI]
public static bool SslEnabled
{
get
{
const CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET param =
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_SSL_OFF_STATUS;
const EthernetAdapterType type = EthernetAdapterType.EthernetLANAdapter;
try
{
short id = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(type);
if (id >= InitialParametersClass.NumberOfEthernetInterfaces)
return false;
string status = CrestronEthernetHelper.GetEthernetParameter(param, id);
if (!string.IsNullOrEmpty(status) && !status.Equals(INVALID_VALUE))
// GET_SSL_OFF_STATUS return OFF for SSL is Enabled and ON for SSL is disabled
return status == "OFF";
return false;
}
catch (ArgumentException)
{
return false;
}
}
}
@@ -132,7 +155,7 @@ namespace ICD.Common.Utils
/// Gets the hostname of the processor.
/// </summary>
[PublicAPI]
public static IEnumerable<string> Hostname
public static IEnumerable<string> Hostnames
{
get
{
@@ -169,12 +192,35 @@ namespace ICD.Common.Utils
/// </summary>
static IcdEnvironment()
{
// Cache the runtime environment
s_Framework = eFramework.Crestron;
s_CrestronRuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL
? eCrestronRuntimeEnvironment.SimplPlus
: eCrestronRuntimeEnvironment.SimplSharpPro;
s_CrestronDevicePlatform = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
? eCrestronDevicePlatform.Appliance
: eCrestronDevicePlatform.Server;
// todo: Make this check more robust
s_CrestronSeries = Type.GetType("Mono.Runtime") != null ? eCrestronSeries.FourSeries : eCrestronSeries.ThreeSeries;
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler;
CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler;
}
#region Methods
/// <summary>
/// Gets the name of the local time zone.
/// </summary>
/// <returns></returns>
public static string GetLocalTimeZoneName()
{
return CrestronEnvironment.GetTimeZone().Name;
}
public static DateTime GetLocalTime()
{
return CrestronEnvironment.GetLocalTime();
@@ -225,19 +271,6 @@ namespace ICD.Common.Utils
}
}
public static eRuntimeEnvironment GetRuntimeEnvironment(Crestron.SimplSharp.eRuntimeEnvironment runtimeEnvironment)
{
switch (runtimeEnvironment)
{
case Crestron.SimplSharp.eRuntimeEnvironment.SIMPL:
return eRuntimeEnvironment.SimplSharp;
case Crestron.SimplSharp.eRuntimeEnvironment.SimplSharpPro:
return eRuntimeEnvironment.SimplSharpPro;
default:
throw new ArgumentOutOfRangeException("runtimeEnvironment");
}
}
#endregion
#region Private Methods

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