Compare commits

...

333 Commits

Author SHA1 Message Date
Chris Cameron
59f4411620 chore: Updating changelog, incrementing patch version 2019-05-24 09:55:48 -04:00
Chris Cameron
243e285948 feat: Added empty, placeholder interface for ICD Attributes 2019-05-24 09:55:33 -04:00
Chris Cameron
ab58ba721e chore: Updating changelog, incrementing patch version 2019-05-02 10:24:34 -04:00
Chris Cameron
d7bfb07c2c fix: Fixed PriorityQueue IndexOutOfRange exception when an inner queue becomes depleted 2019-05-02 10:23:53 -04:00
Chris Cameron
736c2aee33 chore: Updating changelog, incrementing patch version 2019-04-05 14:29:44 -04:00
Chris Cameron
5756d9654b fix: Fixing FormatException when parsing some JSON DateTimes 2019-04-03 16:19:53 -04:00
Chris Cameron
f60de4321b chore: Updating changelog, incrementing minor version 2019-01-25 17:07:05 -05:00
Chris Cameron
524cdc3e31 feat: EventArgs constructor overloads 2019-01-24 11:57:39 -05:00
Chris Cameron
1d6e8d55ca refactor: Tidying 2019-01-23 11:10:35 -05:00
Chris Cameron
3ba5f11587 Merge branch 'feat/VC-4' of Common/Utils into MetLife_v5.4 2019-01-22 17:51:47 +00:00
Drew Tingen
fb7a6e27b8 chore: update changelog 2019-01-22 09:50:42 -08:00
Drew Tingen
fffc8e4a60 fix: Fixed GetApplicationRootDirectory for NetStandard 2019-01-22 09:50:32 -08:00
Drew Tingen
1d7ada87c5 refactor: Removing preprocessors and adding better multi-platform support 2019-01-22 09:47:20 -08:00
Drew Tingen
3a4438ec1e chore: update changelog 2019-01-22 09:24:37 -08:00
Drew Tingen
21ce86de0f fix: Use IcdEnvironment instead of CrestronEnvironment 2019-01-22 09:24:25 -08:00
Drew Tingen
3cb29452a2 chore: tidying 2019-01-22 09:17:35 -08:00
Drew Tingen
114a5e4ec7 Merge remote-tracking branch 'origin/dev' into feat/VC-4 2019-01-22 09:13:06 -08:00
Drew Tingen
a9d411dcfd feat: Path support for VC-4 2019-01-22 09:11:48 -08:00
Chris Cameron
e708ef9603 fix: Fix for potential memory leak with timers 2019-01-21 11:49:21 -05:00
Drew Tingen
aae4fb6185 Merge branch 'feat/mono' of Common/Utils into dev 2019-01-17 23:12:09 +00:00
Chris Cameron
8401ab1852 feat: Added SimplSharpProMono to eRuntimeEnvironment enum 2019-01-17 17:15:34 -05:00
Chris Cameron
27760f2282 fix: Resolving warning 2019-01-14 16:42:32 -05:00
Chris Cameron
80a0ca26c5 chore: Updating changelog, incrementing minor version 2019-01-10 16:11:00 -05:00
Chris Cameron
b9ec2f3222 refactor: Tidying 2019-01-10 10:45:53 -05:00
Chris Cameron
a6ad52d492 refactor: Removing log from ReflectionUtils.CreateInstance, doesn't make sense here 2019-01-10 10:25:46 -05:00
Jack Kanarish
64e11db0bc Merge branch 'MetLife_v5.4' of https://cs-gogs.icdpf.net/Common/Utils into MetLife_v5.4 2019-01-10 09:53:03 -05:00
Jack Kanarish
d01e9345d5 feat: add range attribute, which describes the valid ranges of a property if that range doesnt match the range of the datatype 2019-01-10 09:52:54 -05:00
Chris Cameron
466dc6deb5 fix: Fixing bug where xml fragments were being written with prepended document info 2019-01-09 16:33:33 -05:00
Chris Cameron
ad47c890a8 refactor: Tidying 2019-01-08 14:47:03 -05:00
Chris Cameron
108317212c feat: Adding TryGetPortForScheme method to UriExtensions 2019-01-07 15:37:33 -05:00
Chris Cameron
4a411e8990 refactor: Removing unused code 2019-01-07 12:20:13 -05:00
Chris Cameron
4cd28a8a12 fix: IcdHashSet preserves comparer when an operation creates a new IcdHashSet 2019-01-03 13:03:51 -05:00
Chris Cameron
470c35cab7 chore: Updating changelog, incrementing minor version 2019-01-02 14:18:42 -05:00
Chris Cameron
a372d97868 feat: Adding RegexUtils shims for RegexOptions 2018-12-19 07:50:23 -05:00
Chris Cameron
f208ec521b feat: Added RegexUtils method for replacing a single group in a match 2018-12-19 06:40:39 -05:00
Jack Kanarish
347cf70b6c Merge branch 'MetLife_v5.4' of https://cs-gogs.icdpf.net/Common/Utils into MetLife_v5.4 2018-12-17 09:34:47 -05:00
Jack Kanarish
1076795fae refactor: make isenumtype public for use in other classes 2018-12-17 09:34:37 -05:00
Chris Cameron
7d059bc605 Merge remote-tracking branch 'origin/dev' into MetLife_v5.4 2018-12-10 11:37:15 -05:00
Chris Cameron
dd270adf9e feat: Added methods to IcdUriBuilder for appending path 2018-12-10 11:36:31 -05:00
Jack Kanarish
efb5e014ec feat: give better information when constructor lookup fails 2018-12-04 14:36:04 -05:00
Chris Cameron
083280cc64 chore: Updating changelog 2018-12-03 14:32:21 -05:00
Rashod Davis
e222ce424b feat: Adding short parsing methods to XML utils 2018-12-03 10:32:39 -05:00
Rashod Davis
ad7176506d feat: Added GetAttributeAsEnum xml utils method 2018-12-03 10:32:16 -05:00
Chris Cameron
e4e3e9b91a feat: IcdUriBuilder better supports null Uris 2018-11-28 13:28:40 -05:00
Chris Cameron
1721116908 chore: Removing bad null reference warning 2018-11-27 12:17:29 -05:00
Chris Cameron
d8489d31b6 chore: Updating changelog, incrementing major version 2018-11-20 14:59:21 -05:00
Chris Cameron
a8552ea0e3 chore: Adding xml encoding headers 2018-11-20 14:05:08 -05:00
Chris Cameron
e700650735 Merge remote-tracking branch 'origin/MetLife_v5.2' into MetLife_v5.4 2018-11-20 13:25:40 -05:00
Chris Cameron
ed9c017856 Merge branch 'fix/console-print' of Common/Utils into dev 2018-11-16 22:22:02 +00:00
Jeffery Thompson
0ee1b440ee fix: exception in NetStandard if console message contained curly braces 2018-11-16 17:19:49 -05:00
Chris Cameron
4184f85826 chore: Updating test framework sdk 2018-11-14 15:48:17 -05:00
Chris Cameron
42cc8c2cfc feat: Adding string extension method for removing all instances of a given string, fixes unexpected use of Remove characters method 2018-11-12 10:37:16 -05:00
Drew Tingen
6b37db3530 Merge branch 'fix/csv-nullref' of Common/Utils into dev 2018-11-09 21:09:50 +00:00
Chris Cameron
ad8fa216a5 fix: Fixed NullReferenceException when writing null strings to CSV 2018-11-09 16:08:25 -05:00
Chris Cameron
ef415bb20f fix: Removing default fragment xml conformance level, fixes StartDocument on net standard 2018-11-09 14:59:22 -05:00
Chris Cameron
fe614ae8ad chore: Updating .csproj 2018-11-09 13:40:54 -05:00
Chris Cameron
2d4bc57ed8 feat: Reworked xml attribute utils for performance 2018-11-09 11:42:36 -05:00
Chris Cameron
c048c4fc65 test: Adding test case for RemoveCharacters string extension method 2018-11-09 10:09:40 -05:00
Chris Cameron
2e2eebd95f chore: Updating changelog, incrementing minor version 2018-11-08 16:25:05 -05:00
Chris Cameron
e21830a7d5 perf: Moving enumerable extensions optimizations into correct place 2018-11-08 16:15:22 -05:00
Chris Cameron
f53607018c refactor: Enumerate over XML attributes 2018-11-08 15:50:12 -05:00
Chris Cameron
e0176741d2 fix: Fixing dumb mistake in TryFirst and TryLast extensions 2018-11-08 15:49:44 -05:00
Chris Cameron
742b7e99aa feat: Adding method for getting properties with a given attribute type 2018-11-08 13:03:23 -05:00
Chris Cameron
cf0c71d39b refactor: Removing redundant generic constraints 2018-11-08 13:02:49 -05:00
Chris Cameron
376f9c0837 refactor: Removing unused code 2018-11-08 11:46:14 -05:00
Chris Cameron
8b1c53ebe1 feat: IcdXmlTextWriter exposes WriteStartDocument and WriteEndDocument 2018-11-07 14:25:24 -05:00
Chris Cameron
2e0b0da87f chore: Updating test SDK 2018-11-07 12:57:35 -05:00
Chris Cameron
8311fe5c9d perf: Micro-optimization 2018-11-06 15:45:43 -05:00
Chris Cameron
fb41d76a9c docs: Clarifying method usages 2018-11-06 15:45:34 -05:00
Chris Cameron
8f107aa209 Merge remote-tracking branch 'origin/MetLife_v5.4' into ConnectPro_v1.1 2018-11-06 13:25:17 -05:00
Jack Kanarish
2024dc0171 fix: remove constraint 2018-11-06 13:06:39 -05:00
Chris Cameron
923866dbdf feat: Validation 2018-11-02 16:28:05 -04:00
Chris Cameron
a14b4a5803 refactor: Tidying 2018-11-02 14:03:13 -04:00
Chris Cameron
fac2610b83 Merge remote-tracking branch 'origin/MetLife_v5.4' into ConnectPro_v1.1 2018-11-02 11:29:28 -04:00
Jack Kanarish
0ef0286a9f Merge remote-tracking branch 'origin/dev' into MetLife_v5.4 2018-11-02 10:46:19 -04:00
Jack Kanarish
d084553600 fix: fix an issue where use of except causes phone numbers with repeat digits to be improperly truncated 2018-11-01 17:28:27 -04:00
Chris Cameron
2b14a6b65c refactor: Tidying 2018-10-30 17:20:52 -04:00
Chris Cameron
043a50669a perf: Small optimization in StringExtensions 2018-10-30 17:06:42 -04:00
Chris Cameron
b58220d3c7 perf: Small enumerable optimizations 2018-10-30 16:13:48 -04:00
Chris Cameron
03de74a494 perf: Potential performance improvement when comparing enumerables 2018-10-30 13:47:08 -04:00
Chris Cameron
6eacc21e45 chore: Updating changelog, incrementing major version 2018-10-30 11:52:55 -04:00
Chris Cameron
db14eb1dde feat: Adding TryFromIpIdString method 2018-10-29 14:13:46 -04:00
Chris Cameron
24e665de84 refactor: Removing unused code 2018-10-29 13:42:33 -04:00
Drew Tingen
7e8618d3e5 Merge branch 'perf/hashset' of Common/Utils into dev 2018-10-29 15:30:07 +00:00
Chris Cameron
e44a18b111 refactor: Tidying 2018-10-29 10:36:44 -04:00
Chris Cameron
5cec0e10f1 perf: Significant IcdHashSet optimizations 2018-10-27 20:36:22 -04:00
Chris Cameron
e01bc9ede6 feat: Adding write string method to IcdFileStream 2018-10-27 14:57:48 -04:00
Chris Cameron
9e6c7ca6b3 Merge branch 'feat/deprecate-nvram' of Common/Utils into dev 2018-10-26 18:34:32 +00:00
Chris Cameron
2a1d58f2ae chore: Updating changelog 2018-10-25 17:22:23 -04:00
Jeffery Thompson
51dcb41cdf feat: make ProgramNumber settable 2018-10-25 15:22:04 -04:00
Chris Cameron
11cff4f5bb chore: Updating changelog 2018-10-24 11:53:05 -04:00
Chris Cameron
7c29fb72d9 refactor: Tidying 2018-10-22 16:39:53 -04:00
Chris Cameron
fc234af43a fix: Fixing potential enumeration bug in StringExtensions 2018-10-22 16:28:59 -04:00
Chris Cameron
5afc676fbe refactor: Removing redundant code 2018-10-22 14:03:03 -04:00
Chris Cameron
ba8c1d98a1 refactor: Removing unused methods 2018-10-22 11:31:06 -04:00
Chris Cameron
63af420710 perf: Replacing recursion with loop 2018-10-22 09:56:03 -04:00
Chris Cameron
25109163fb perf: StartsWith and EndsWith char extensions optimization 2018-10-19 17:30:07 -04:00
Chris Cameron
91f64a4fb1 perf: Avoid throwing exceptions in XmlReaderExtensions 2018-10-19 16:25:57 -04:00
Chris Cameron
78a3373592 chore: Updating changelog, incrementing major version 2018-10-18 11:55:51 -04:00
Chris Cameron
14cce04c12 refactor: Tidying 2018-10-18 10:07:19 -04:00
Chris Cameron
d3d1dae2e1 feat: Implementing ReadXml for DefaultXmlConverter 2018-10-17 17:13:44 -04:00
Chris Cameron
e9063682ef refactor: Tidying 2018-10-17 17:13:18 -04:00
Chris Cameron
699c734389 feat: DefaultXmlConverter knows type it is serializing to/from 2018-10-17 16:56:36 -04:00
Chris Cameron
548220ba0e feat: Shims for deserializing xml lists and dictionaries 2018-10-17 14:56:55 -04:00
Chris Cameron
692da3253f feat: Adding IcdOrderedDictionary constructor for populating with an existing dictionary 2018-10-16 14:32:27 -04:00
Chris Cameron
85ab631ef5 feat: Better implementation of DictionaryExtensions.ToInverse 2018-10-16 14:31:53 -04:00
Chris Cameron
23a068d0c9 refactor: Tidying 2018-10-15 16:53:27 -04:00
Jeffery Thompson
b015c3a3d3 feat: deprecate NVRAM, renamed NvramPath to RootConfigPath 2018-10-11 17:59:41 -04:00
Chris Cameron
566884167f fix: Potential fix for weird profiling offset 2018-10-08 16:33:52 -04:00
Chris Cameron
015ffb2c35 perf: Fixing dumb mistake in RecursionUtils that was preventing the method from bailing early 2018-10-08 15:07:07 -04:00
Chris Cameron
6540ab5177 refactor: Tidying 2018-10-08 15:06:03 -04:00
Chris Cameron
e01e41c449 feat: Indenting stopwatch results into a tree format 2018-10-08 14:54:16 -04:00
Chris Cameron
8a260e0475 refactor: Adding validation to StringUtils.Repeat 2018-10-08 14:53:43 -04:00
Chris Cameron
381355beb9 perf: BreadthFirstSearchPathManyDestinations returns an IEnumerable to support early termination 2018-10-08 14:21:54 -04:00
Chris Cameron
2a25c3d733 fix: IcdUriBuilder better handles strings without scheme info 2018-10-06 20:50:52 -04:00
Chris Cameron
6b8fc19eb3 Merge remote-tracking branch 'origin/ConnectPro_v1.0' into dev 2018-10-01 14:32:41 -04:00
Chris Cameron
c3d73e1091 fix: Improved Type GetSyntaxName method 2018-09-27 15:07:21 -04:00
Chris Cameron
1f5625218f Merge branch 'ConnectPro_v1.0' into dev 2018-09-25 09:44:28 -04:00
Chris Cameron
588badbf58 chore: Adding xml header to .csproj 2018-09-24 15:16:34 -04:00
Chris Cameron
e0fae0b112 feat: Adding EmailClient 2018-09-24 12:33:29 -04:00
Chris Cameron
315bd703c7 Merge branch 'MetLife_v5.3' of Common/Utils into dev 2018-09-19 17:54:14 +00:00
Jack Kanarish
32aae27a7f chore: update changelog 2018-09-19 11:39:26 -04:00
Jack Kanarish
db15ea24d9 Merge branch 'dev' into MetLife_v5.3 2018-09-19 10:01:43 -04:00
Jack Kanarish
667c1cdd21 fix: fix test 2018-09-17 17:12:00 -04:00
Chris Cameron
df30585917 perf: Don't use reflection in AbstractGenericJsonConverter 2018-09-17 15:52:44 -04:00
Chris Cameron
c83bd04c8f chore: Incrementing major version, updating changelog 2018-09-14 14:06:09 -04:00
Jack Kanarish
09603b0537 refactor: make instantiaton abstract 2018-09-13 14:21:59 -04:00
Chris Cameron
d41aa6d111 feat: Xml recursion methods allow cancelling recursion into child nodes 2018-09-12 20:18:56 -04:00
Chris Cameron
d0740c915b refactor: Tidying 2018-09-12 09:49:30 -04:00
Chris Cameron
9867eae704 Merge remote-tracking branch 'origin/XmlConverters' into XmlConverters 2018-09-10 21:09:34 -04:00
Chris Cameron
e1693bc738 fix: Xml converter over-reading fixes 2018-09-10 21:09:18 -04:00
Jack Kanarish
2c87d8e988 fix: add preprocessor for 2008/crestron support 2018-09-10 11:17:51 -04:00
Jack Kanarish
deff0d5408 chore: commit 2008 csproj changes 2018-09-10 11:17:37 -04:00
Jack Kanarish
0ccf80c613 Merge branch 'MetLife_v5.3' into XmlConverters 2018-09-10 11:09:01 -04:00
Chris Cameron
a3e548290f fix: Potential fixes for over-reading XML 2018-09-09 21:24:39 -04:00
Chris Cameron
12ee533cbb feat: Shims for deserializing XML to object instance 2018-09-09 20:33:56 -04:00
Chris Cameron
2ae8fa9c2d feat: Begin implementing XmlConverters 2018-09-09 14:21:25 -04:00
Chris Cameron
7b30730ea0 feat: IcdXmlReader exposes IsEmptyElement property 2018-09-09 14:14:45 -04:00
Chris Cameron
d3f1fe3425 refactor: Tidying, removing duplicate/unused code, micro-optimization 2018-09-09 14:14:20 -04:00
Chris Cameron
48abd75d30 refactor: Additional validation, tidying 2018-09-09 14:13:48 -04:00
Jack Kanarish
0bfd6c4f48 Merge branch 'MetLife_v5.2' into MetLife_v5.3
# Conflicts:
#	ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
2018-09-06 10:23:08 -04:00
Jack Kanarish
c2b980a691 Merge branch 'MetLife_v5.3' of https://cs-gogs.icdpf.net/Common/Utils into MetLife_v5.3 2018-09-06 10:15:43 -04:00
Chris Cameron
ed299b8d4f feat: Initial commit of RegexUtils 2018-08-31 09:52:53 -04:00
Chris Cameron
0c35e3225c Merge branch 'IcdHashsetExt' of Common/Utils into dev 2018-08-29 17:38:02 +00:00
Rashod Davis
254ed85d83 chore: Added comparer to ToIcdHashset extention 2018-08-29 13:07:31 -04:00
Chris Cameron
8871fad40a feat: IcdHashSet constructor overloads for IEqualityComparer param 2018-08-29 10:52:07 -04:00
Jack Kanarish
2ff7073385 feat: add CSV Writer 2018-08-28 10:52:12 -04:00
Chris Cameron
957424e5ca fix: Fixing hidden properties 2018-08-27 17:32:18 -04:00
Chris Cameron
9cdb590d9f fix: Slightly more accurate profile times 2018-08-21 14:57:19 -04:00
Chris Cameron
ca10f049e5 perf: IcdOrderedDictionary maintains a collection of values ordered by key 2018-08-21 10:27:25 -04:00
Chris Cameron
d79c272df0 perf: Removing heavy-handed validation from enum utils 2018-08-21 10:26:24 -04:00
Chris Cameron
1c9d311422 feat: List AddSorted extension returns the insertion index 2018-08-21 10:25:11 -04:00
Chris Cameron
60a19fe7f8 refactor: Removing redundant code 2018-08-21 10:24:32 -04:00
Chris Cameron
54fbd7eaa8 feat: Initial commit of AsyncEventQueue 2018-08-14 15:02:18 -04:00
Chris Cameron
fd931b0b53 fix: Fixing potential null ref 2018-08-13 16:43:10 -04:00
Chris Cameron
2a222289ca fix: Fixing net standard build 2018-08-13 16:43:01 -04:00
Chris Cameron
e437a9442c feat: Invoke threads are tracked internally and can be printed to a table 2018-08-13 15:00:15 -04:00
Chris Cameron
478261e888 refactor: Tidying 2018-08-13 14:58:31 -04:00
Chris Cameron
09445da1e9 perf: IcdHashSet AddRange micro-optimization 2018-08-09 11:01:27 -04:00
Chris Cameron
eb7f3033fb perf: Disposing text readers 2018-08-07 17:09:23 -04:00
Chris Cameron
b1b12c76dd feat: Adding AppendPropertyRaw method to ReprStringBuilder 2018-08-02 13:32:21 -04:00
Chris Cameron
5e80ddcc16 pef: Micro-optimizations to LogItem and IcdXmlAttribute equality 2018-08-01 10:41:56 -04:00
Chris Cameron
84fa69d3e0 feat: IcdOrderedDictionary exposes constructor with equality comparer 2018-08-01 10:41:34 -04:00
Chris Cameron
86335e0d44 perf: String utils micro-optimizations 2018-07-31 14:43:55 -04:00
Chris Cameron
03893c2669 refactor: Removing unused code 2018-07-31 14:41:29 -04:00
Chris Cameron
1353468da6 chore: Updating test SDK version 2018-07-30 14:24:01 -04:00
Chris Cameron
2f78a25420 feat: Profiling text shows decimals 2018-07-30 10:54:30 -04:00
Chris Cameron
68caebb28b feat: Profiling in ticks, Net Standard too fast... 2018-07-30 09:54:36 -04:00
Chris Cameron
425d651eba feat: Additional EnumUtils methods for excluding/including flags 2018-07-27 11:14:59 -04:00
Chris Cameron
918e6f5b34 fix: Fixing enum casting exception in SimplSharp 2018-07-26 13:43:34 -04:00
Chris Cameron
6a979f5c12 perf: Reducing boxing 2018-07-26 12:56:01 -04:00
Chris Cameron
8f17d59694 perf: Massive performance improvements to enum HasFlag and HasFlags extensions 2018-07-26 12:10:50 -04:00
Chris Cameron
80e6fe33c7 perf: Massive optimization to enum utils 2018-07-26 11:48:22 -04:00
Chris Cameron
668994be18 perf: Adding contrains for enum methods, faster HasFlag/s checks 2018-07-26 10:56:08 -04:00
Chris Cameron
1a87ce9f00 feat: Adding missing enum utils methods for metlife 2018-07-26 09:56:36 -04:00
Chris Cameron
3129d3e60c perf: Further reducing enum boxing 2018-07-25 15:53:39 -04:00
Chris Cameron
fd3143ea6c refactor: Tidying 2018-07-25 14:45:28 -04:00
Chris Cameron
591240d973 perf: Reducing boxing operations in enum utils 2018-07-25 14:23:25 -04:00
Chris Cameron
b8225b7842 perf: Simpler HasAnyFlags check, some tidying 2018-07-25 14:02:14 -04:00
Chris Cameron
1193c8e3bb fix: Fixing specific enum assignment bug 2018-07-25 14:00:25 -04:00
Chris Cameron
aa3559cb4e perf: Massive optimization for enum utils 2018-07-25 13:26:38 -04:00
Chris Cameron
a13daa18db feat: Profile method for performing an action a given number of times and printing results 2018-07-25 11:59:25 -04:00
Chris Cameron
fa145644d1 feat: Extension method for getting a random item from a sequence 2018-07-25 11:59:00 -04:00
Chris Cameron
5f7d1214e9 Merge remote-tracking branch 'origin/MetLife_v5.3' into dev 2018-07-23 15:11:23 -04:00
Chris Cameron
d8d9f342c9 Merge remote-tracking branch 'origin/MetLife_v5.2' into MetLife_v5.3
# Conflicts:
#	ICD.Common.Utils/Json/JsonItemWrapper.cs
2018-07-23 13:34:49 -04:00
Chris Cameron
aeb2a5d91e perf: Micro-optimization 2018-07-20 16:50:13 -04:00
Chris Cameron
74c59bd7f3 perf: Micro-optimizations 2018-07-20 16:41:44 -04:00
Chris Cameron
6fb1e53776 perf: EnumUtils micro-optimization, potential thread safety improvements 2018-07-20 11:28:16 -04:00
Chris Cameron
0c462ad614 test: Adding missing test 2018-07-20 11:21:12 -04:00
Chris Cameron
f2d32fd355 test: Updating TypeExtensionsTest 2018-07-19 15:54:16 -04:00
Chris Cameron
4df2ede630 fix: Fixed issue with GetSyntaxName giving incorrect results for nullable types 2018-07-19 15:54:06 -04:00
Chris Cameron
8d0b4ca767 chore: Updating changelog, incrementing major version 2018-07-19 13:54:08 -04:00
Chris Cameron
035289f056 refactor: Whitespace 2018-07-19 13:47:08 -04:00
Chris Cameron
929f816398 perf: Micro-optimization for Unanimous extension method 2018-07-19 13:46:53 -04:00
Chris Cameron
6f69ea7fde refactor: Catch specific exception 2018-07-19 12:38:22 -04:00
Chris Cameron
b597448bdc feat: Event profiling messages contain consuming method information 2018-07-19 11:39:31 -04:00
Chris Cameron
cc9eaca87a feat: Extension method for getting a signature representation of a MethodInfo 2018-07-19 11:38:30 -04:00
Chris Cameron
a78ff6ad80 feat: Extension method for getting syntax representation of a Type 2018-07-19 11:37:34 -04:00
Chris Cameron
939f361b54 feat: Cross-platform extension for determining if a ParameterInfo represents an "out" parameter 2018-07-19 11:36:56 -04:00
Chris Cameron
6fb290a0ab feat: Cross-platform extension method for getting MethodInfo from a delegate 2018-07-19 11:35:23 -04:00
Chris Cameron
2166596726 feat: Extension method for checking if a string contains a character 2018-07-19 11:34:48 -04:00
Chris Cameron
073c231ef1 feat: Util methods for profiling event subscribers 2018-07-18 21:35:35 -04:00
Chris Cameron
220e778a76 fix: Dammit Jeff 2018-07-18 13:41:27 -04:00
Jeffery Thompson
560d3c861d fix: try adding _SimplSharp suffix to assembly if GetType fails 2018-07-18 13:38:36 -04:00
Chris Cameron
25ebb4b43d Merge branch 'feat/date-time-extensions-inclusive' of Common/Utils into dev 2018-07-13 20:28:17 +00:00
Jeffery Thompson
f7dba764d5 feat: add parameter to extensions for inclusivity 2018-07-13 16:25:13 -04:00
Chris Cameron
f2e39566e2 Merge branch 'fix/scheduler-service-time' of Common/Utils into dev 2018-07-13 20:03:12 +00:00
Jeffery Thompson
b4ef07fd45 fix: better time management skills 2018-07-13 16:01:23 -04:00
Chris Cameron
69b97779d9 perf: Micro-optimizations 2018-07-13 15:06:06 -04:00
Chris Cameron
e8ce1e94cc Merge branch 'feat/action-scheduler' of Common/Utils into dev 2018-07-13 16:25:14 +00:00
Jeffery Thompson
73af42e0f5 fix: guarantee positive duration for Timer.Reset 2018-07-13 12:23:06 -04:00
Chris Cameron
aa7a924d2b Merge branch 'feat/action-scheduler' of Common/Utils into dev 2018-07-13 15:52:53 +00:00
Jeffery Thompson
3185593977 fix: change DateTime.Now to IcdEnvironment.GetLocalTime() 2018-07-13 11:45:22 -04:00
Chris Cameron
bfd0f30437 Merge remote-tracking branch 'origin/feat/action-scheduler' into dev 2018-07-13 10:28:37 -04:00
Chris Cameron
7b372b5a72 chore: Updating changelog 2018-07-13 09:37:00 -04:00
Jeffery Thompson
dfcdbba05f fix: fucking csproj 2018-07-12 22:32:52 -04:00
Chris Cameron
4494192bdf refactor: Tidying 2018-07-12 22:09:14 -04:00
Chris Cameron
bfbbcff7e7 fix: Fixing bad include in .csproj 2018-07-12 16:52:53 -04:00
Chris Cameron
d1b8b5a01f Merge branch 'feat/action-scheduler' of Common/Utils into dev 2018-07-12 20:18:48 +00:00
Jeffery Thompson
1070cb00f8 fix: use TryFirst instead of comparison to default(DateTime) 2018-07-12 15:48:45 -04:00
Jeffery Thompson
a46987aabf refactor: UpdateRunTime -> GetNextRunTime, add UpdateRunTime for caching GetNextRunTime value
- also swap the operands on the delta time calculation
2018-07-12 15:09:52 -04:00
Jeffery Thompson
c25a82399d fix: remove unnecessary default timer interval 2018-07-12 14:36:58 -04:00
Jeffery Thompson
1f7819e536 feat: add DateTimeExtensions.PreviousLatestTime 2018-07-12 14:03:15 -04:00
Jeffery Thompson
465ac7c42c feat: timer rescheduling, abstract scheduled action, datetime and dayofweek extensions 2018-07-12 13:33:02 -04:00
Jeffery Thompson
928f8e5e04 feat: first go at ActionSchedulerService 2018-07-11 16:35:08 -04:00
Chris Cameron
ae885974da feat: Added extension method for getting type name without trailing generic info 2018-07-06 14:58:16 -04:00
Chris Cameron
21a583a57c feat: Re-raise base exception from ReflectionUtils.CreateInstance, TargetInvocationException and TypeLoadException don't say much 2018-07-03 14:55:04 -04:00
Chris Cameron
268e201f49 chore: Removing redundant REM that breaks on linux build 2018-07-02 13:37:17 -04:00
Chris Cameron
9e2ec3de63 chore: Updating changelog and incrementing minor version 2018-07-02 09:38:50 -04:00
Rashod Davis
8ed856eb98 Merge branch 'RateLimit' of Common/Utils into dev 2018-06-29 21:28:01 +00:00
Chris Cameron
514c0eaec5 fix: Fixing bug where Timer.Reset() would continue repeating on an interval in Net Standard 2018-06-29 17:11:32 -04:00
Chris Cameron
621d83d8dc feat: Added RateLimitedEventQueue collection for throttling events 2018-06-29 16:37:45 -04:00
Chris Cameron
588e3df86e chore: Adding obfuscation to Net Standard 2018-06-29 10:07:51 -04:00
Drew Tingen
336f39c9c2 Merge branch 'MetLife_v5.3' of Common/Utils into dev 2018-06-25 20:12:38 +00:00
Jack Kanarish
5d3b80938e chore: update changelog 2018-06-25 15:48:35 -04:00
Jack Kanarish
eb2a77b772 fix: for backwards json compatibility, if get type fails, try again after stripping assembly suffixes 2018-06-25 15:42:37 -04:00
Chris Cameron
1c420d111b chore: Updating test framework and sqlite versions 2018-06-21 13:46:36 -04:00
Chris Cameron
e1279fb860 fix: Don't write console to error out 2018-06-21 13:11:35 -04:00
Chris Cameron
bf91d9e87f fix: Potential fix for timer disposal on Net Standard 2018-06-21 11:48:59 -04:00
Chris Cameron
1c89ebc5c2 refactor: Reducing duplication 2018-06-21 11:32:29 -04:00
Chris Cameron
2cd744eb1c fix: Adding missing file to csproj 2018-06-20 16:21:03 -04:00
Rashod Davis
20350c0ab7 Merge branch 'SequenceComparer' of Common/Utils into dev 2018-06-20 19:47:54 +00:00
Chris Cameron
177e59edb6 feat: Initial commit of SequenceComparer 2018-06-20 15:38:22 -04:00
Chris Cameron
d8041bd94c chore: Updating changelog, incrementing minor version 2018-06-19 14:43:39 -04:00
Chris Cameron
f2122596b4 test: Adding unit tests for BiDictionary 2018-06-18 10:06:11 -04:00
Chris Cameron
653bf361ef feat: Splitting iterators from validation 2018-06-14 11:21:46 -04:00
Chris Cameron
3689517124 feat: Adding UnEnquote string util method 2018-06-12 15:11:12 -04:00
Chris Cameron
6fdd5a138e feat: Adding SequenceEqual shim 2018-06-12 14:52:13 -04:00
Chris Cameron
fb267465ce fix: Return false when unable to add console command 2018-06-12 14:50:02 -04:00
Jack Kanarish
f818653298 Merge branch 'Enquote' of Common/Utils into dev 2018-06-12 18:07:48 +00:00
Chris Cameron
f7b5d07c38 feat: Adding enquote util method 2018-06-12 13:31:44 -04:00
Chris Cameron
1a931e6ffb feat: Additional validation 2018-06-12 13:30:45 -04:00
Chris Cameron
2fab5d6fd4 fix: Better set key/value behaviour for BiDictionary 2018-06-11 17:04:26 -04:00
Rashod Davis
8d683f875b Merge branch 'BiDictionary' of Common/Utils into dev 2018-06-11 20:07:49 +00:00
Chris Cameron
74c96aac8a fix: Prevent breaking BiDictionary mappings 2018-06-11 16:00:28 -04:00
Chris Cameron
edc9fa300e feat: Initial commit of BiDictionary 2018-06-11 15:38:28 -04:00
Chris Cameron
37a21131d9 feat: Extension method for populating a dict with a sequence of kvps 2018-06-11 11:48:36 -04:00
Chris Cameron
f674d4c60d refactor: Separating iterators from validation 2018-06-07 18:16:24 -04:00
Chris Cameron
ce163629f3 refactor: Removing redundant code 2018-06-07 16:32:09 -04:00
Chris Cameron
18b07abb44 refactor: Resolving code smells 2018-06-07 14:59:31 -04:00
Chris Cameron
4bc6258b62 refactor: Tidying 2018-06-07 13:12:41 -04:00
Chris Cameron
086aee8167 refactor: More appropriate exceptions 2018-06-07 12:29:07 -04:00
Chris Cameron
cc115bafad refactor: Removing redundant code 2018-06-07 11:26:47 -04:00
Chris Cameron
00db478ef6 fix: Fixing bad validation in EnumerableExtensions 2018-06-07 11:26:37 -04:00
Chris Cameron
aa3fc3bccc refactor: Removing redundant code 2018-06-07 11:23:44 -04:00
Chris Cameron
2602380100 refactor: IcdZip Unzip raises an exception instead of generating a message 2018-06-06 13:45:05 -04:00
Chris Cameron
1f023c14d0 chore: Updating changelog 2018-06-06 11:02:11 -04:00
Chris Cameron
6ce52b74ac feat: Moved FileNameEqualityComparer from Settings 2018-06-06 11:01:16 -04:00
Jeffery Thompson
c500f1db5d Merge branch 'zip' of Common/Utils into dev 2018-06-06 14:57:45 +00:00
Chris Cameron
da58379dfa refactor: Tidying 2018-06-06 10:54:39 -04:00
Chris Cameron
1ae6a55c7d docs: Adding MIT license to ZIP entries method 2018-06-06 10:40:15 -04:00
Chris Cameron
381b19f2e6 fix: Fixing net standard build 2018-06-06 10:39:54 -04:00
Chris Cameron
2f3b1ef57d feat: Adding methods for inspecting contents of a ZIP file 2018-06-06 10:02:39 -04:00
Chris Cameron
da31fae213 feat: Exposing OpenRead method in IcdFile 2018-06-06 10:02:00 -04:00
Chris Cameron
e4b0c9f91a feat: Initial commit of eSeekOrigin enum 2018-06-06 10:01:31 -04:00
Chris Cameron
38dd85d79d feat: Exposing additional stream features in IcdStream 2018-06-06 10:00:46 -04:00
Chris Cameron
c306dd6eb9 feat: Initial commit of IcdBinaryReader 2018-06-06 10:00:24 -04:00
Chris Cameron
6fa3cc03ad feat: Adding ElementAtOrDefault extension method that takes a default value 2018-06-05 13:40:53 -04:00
Chris Cameron
eb58e65574 perf: Don't reinvent the wheel for TryElementAt extension method 2018-06-05 13:40:26 -04:00
Chris Cameron
f24f9458ca chore: Updating changelog, incrementing patch version 2018-06-04 16:33:23 -04:00
Chris Cameron
79bbbe277f Merge remote-tracking branch 'origin/collections' into dev
# Conflicts:
#	ICD.Common.Utils/Collections/PriorityQueue.cs
2018-06-04 16:30:38 -04:00
Jack Kanarish
c03833bf9d fix: dont insert at an index which was removed 2018-05-31 17:28:02 -04:00
Jack Kanarish
f816ae771b chore: merge util changes from dev needed for sharp bug backport to metlife 5.2 2018-05-30 16:50:25 -04:00
Chris Cameron
a178ebe8cb docs: Adding missing comments to IcdHashSet 2018-05-25 16:40:37 -04:00
Chris Cameron
3cddc14880 perf: BreadthFirstSearchPathManyDestinations optimizations 2018-05-25 14:13:22 -04:00
Chris Cameron
088222db47 fix: Potential fix - use breadth first node comparer in parents map 2018-05-25 12:18:02 -04:00
Chris Cameron
9dcda7271d chore: Updating changelog and minor version 2018-05-24 14:28:07 -04:00
Chris Cameron
9dca46add5 feat: Pathfinding methods for determining if a path exists from a root to a destination 2018-05-24 10:59:51 -04:00
Chris Cameron
35593bfa47 feat: Adding GetFlagsExceptNone parameterless enum util method 2018-05-24 10:03:38 -04:00
Chris Cameron
62abd624f8 chore: Updating changelog, incrementing minor version 2018-05-23 10:29:53 -04:00
Chris Cameron
c6a32da597 Merge branch 'setequals' of Common/Utils into dev 2018-05-22 21:43:12 +00:00
Jack Kanarish
aeda403b42 feat: add setequals method to ICDHashSet 2018-05-22 17:41:27 -04:00
Chris Cameron
548431c100 chore: Updating changelog and minor version number 2018-05-18 11:35:31 -04:00
Chris Cameron
d98d222880 refactor: Removing unused using directive 2018-05-16 15:41:45 -04:00
Chris Cameron
02758c6b8e refactor: Whitespace 2018-05-15 15:03:24 -04:00
Chris Cameron
6c3be1884a feat: Adding constructors to IcdUriBuilder 2018-05-15 13:29:38 -04:00
Chris Cameron
8da73593c4 feat: Initial commit of UriUtils 2018-05-15 13:29:24 -04:00
Chris Cameron
f76f4773e5 Merge branch 'feature/interface-attributes' of Common/Utils into dev 2018-05-15 15:43:39 +00:00
Jeffery Thompson
567ee4a1ae fix: only bind to members of the same member type 2018-05-15 11:41:11 -04:00
Jeffery Thompson
72a4b19f97 feat: add ext methods to get interface attributes 2018-05-15 11:04:40 -04:00
Chris Cameron
b2c3ecb113 fix: Fixing bug where IcdUriBuilder was not correctly appending paths 2018-05-14 14:52:55 -04:00
Chris Cameron
e55c2f9474 fix: Fixing dumb mistake with UriExtensions not getting password from URI 2018-05-14 14:52:25 -04:00
Jack Kanarish
d99f0e889c Merge branch 'URI' of Common/Utils into dev 2018-05-11 20:03:30 +00:00
Chris Cameron
cddc726d4d fix: Fixing test cases 2018-05-11 15:57:04 -04:00
Chris Cameron
3f8e2b8df7 feat: Adding IcdUriBuilder and UriExtensions classes 2018-05-11 15:23:36 -04:00
Drew Tingen
1bddca9a06 Merge branch 'Collections' of Common/Utils into dev 2018-05-10 21:08:38 +00:00
Chris Cameron
db8e725fa9 feat: Adding additional enqueue methods to PriorityQueue 2018-05-10 17:02:14 -04:00
Chris Cameron
d9cf0bd3cf refactor: Changing ordered dictionary tests to avoid false positives 2018-05-10 17:01:16 -04:00
Chris Cameron
da8947aaa0 feat: Adding TryDequeue method to PriorityQueue 2018-05-10 13:55:57 -04:00
Chris Cameron
87427096d2 feat: Initial commit of PriorityQueue collection 2018-05-10 13:51:43 -04:00
Chris Cameron
b1c7e527c4 feat: Initial commit of IcdOrderedDictionary 2018-05-10 11:48:33 -04:00
Chris Cameron
718fc349f5 chore: Updating changelog and incrementing minor version 2018-05-09 11:48:47 -04:00
Chris Cameron
1a4159fc31 Merge remote-tracking branch 'origin/enumtoushort' into dev
# Conflicts:
#	CHANGELOG.md
2018-05-09 11:45:38 -04:00
Chris Cameron
110f771cc5 feat: Added util method for removing BOM characters from UTF8 data 2018-05-09 11:35:53 -04:00
Chris Cameron
e446f6e6ce Merge branch 'feature/get-relative-path' of Common/Utils into dev 2018-05-09 14:08:30 +00:00
Jeffery Thompson
7e48547483 feat: add IcdPath.GetRelativePath(string, string) 2018-05-08 17:37:50 -04:00
Jack Kanarish
4adebaf89b feat: added extension to convert from enum to ushort 2018-05-08 16:57:11 -04:00
Chris Cameron
9abf384bb4 docs: Fixing invalid xml 2018-05-07 14:26:11 -04:00
Chris Cameron
d24e17bc26 Merge branch 'EnumerableYield' of Common/Utils into dev 2018-05-04 21:12:57 +00:00
Drew Tingen
07b89edc66 chore: updating changelog and assembly version 2018-05-04 17:10:38 -04:00
Drew Tingen
e974ab655f Merge remote-tracking branch 'origin/dev' into EnumerableYield 2018-05-04 17:08:50 -04:00
Drew Tingen
e43a7200c6 feat: Adding Yield() enumerable extension, that turns an object into a single-item enumerable. 2018-05-04 17:07:29 -04:00
Chris Cameron
b41a236716 refactor: Resolving warning 2018-05-04 15:08:13 -04:00
Chris Cameron
38fa698d46 Merge branch 'boolAndUshortExtensions' of Common/Utils into dev 2018-05-03 16:11:49 +00:00
Jack Kanarish
553a79bd17 csproj changes 2018-05-03 12:11:03 -04:00
Jack Kanarish
d4f5413ce6 proper casing 2018-05-03 12:10:16 -04:00
Jack Kanarish
9b1ddf41d2 add bool and ushort extensions 2018-05-03 11:56:09 -04:00
Jack Kanarish
d22c3c2cd2 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-05-02 15:38:00 -04:00
Chris Cameron
a5cc9e694a refactor: Tidying 2018-04-26 11:05:49 -04:00
Chris Cameron
08f525eb3e feat: Clearer log message for failing to cache assembly 2018-04-25 14:09:09 -04:00
Jack Kanarish
a52b2d2c08 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-16 15:25:26 -04:00
Jack Kanarish
d33292ae8e Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-11 11:20:58 -04:00
Jack Kanarish
dd794729e8 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-10 11:07:26 -04:00
122 changed files with 7462 additions and 1331 deletions

View File

@@ -6,6 +6,146 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [8.3.3] - 2019-05-24
### Added
- Added empty, placeholder interface for ICD Attributes
## [8.3.2] - 2019-05-02
### Changed
- Fixed PriorityQueue IndexOutOfRange exception when an inner queue becomes depleted
## [8.3.1] - 2019-04-05
### Changed
- Fixed FormatException when parsing some JSON DateTimes
## [8.3.0] - 2019-01-25
### Added
- Added SimplSharpProMono to eRuntimeEnvironment enum
- Added path support for SimplSharpProMono environment
- Added GetApplicationRootDirectory for all platforms
### Changed
- Small fixes for better VC4 support
## [8.2.0] - 2019-01-10
### Added
- Added TryGetPortForScheme method to UriExtensions
- Added range attribute for clarifying numeric fields, properties and parameters
### Changed
- IcdHashSet preserves comparer when an operation creates a new IcdHashSet
- Fixed bug where XML fragments on Net Standard were being prepended with a document header
## [8.1.0] - 2019-01-02
### Added
- Added GetAttributeAsEnum xml utils method
- Adding short parsing methods to XML utils
- Added methods to IcdUriBuilder for appending path
- Added RegexUtils method for replacing a single group in a match
## [8.0.0] - 2018-11-20
### Added
- XML TryGetAttribute methods
### Changed
- Performance improvements when working with xml attributes
- Fixed NullReferenceException when writing null strings to CSV
- Fixed bug with string formatting console input on Net Standard
### Removed
- Removed IcdXmlAttribute
## [7.1.0] - 2018-11-08
### Added
- IcdXmlTextWriter exposes WriteStartDocument and WriteEndDocument
- AttributeUtils method for getting properties with the given attribute type
### Changed
- EnumerableExtensions performance improvements
- Fixed bug in StringExtensions when removing a sequence of characters from a string
## [7.0.0] - 2018-10-30
### Changed
- Micro-optimizations in string and XML manipulations
- Significant hashset optimizations
- Deprecated NVRAM in favor of USER directory
## [6.0.0] - 2018-10-18
### Added
- CsvWriter for creating CSV files + Settings
- AppendText method for IcdFile
- IcdStreamWriter, a wrapper for a StreamWriter
- New XML conversion framework for performance improvements
### Changed
- XmlUtils is now using the improved XML conversion framework
- Better implementation of DictionaryExtensions.ToInverse
## [5.0.0] - 2018-09-14
### Added
- Stopwatch profiling methods
- Attempt to interpret old assembly naming convention when parsing types
- Added RegexUtils
### Changed
- Significant performance improvements across the board
## [4.0.0] - 2018-07-19
### Added
- Added extension method for getting type name without trailing generic info
- Added DateTime extensions for getting next/last time in a sequence
- Added action scheduler service
- Added IcdTimer methods for profiling event performance
### Changed
- Re-raise base exception from ReflectionUtils.CreateInstance, TargetInvocationException and TypeLoadException don't say much
## [3.7.0] - 2018-07-02
### Added
- Adding SequenceComparer for ordering collections of lists, arrays, etc
- Added RateLimitedEventQueue collection for throttling events
### Changed
- Potential fix for timer disposal on Net Standard
- Added workaround for older RPC servers where the typestring being broadcast would stil include _SimplSharp, now will be stripped
- Fixing bug where Timer.Reset() would continue repeating on an interval in Net Standard
## [3.6.0] - 2018-06-19
### Added
- Added ZIP features for examining the contents of an archive
- Added FileNameEqualityComparer
- Added BiDictionary for one-to-one maps
## [3.5.1] - 2018-06-04
### Changed
- PriorityQueue indexing fix
- Pathfinding optimizations
## [3.5.0] - 2018-05-24
### Added
- Added GetFlagsExceptNone parameterless enum method
- Added pathfinding methods for determining if a path exists
## [3.4.0] - 2018-05-23
### Added
- Added SetEquals method to IcdHashSet
## [3.3.0] - 2018-05-18
### Added
- Added IcdOrderedDictionary collection
- Added PriorityQueue collection
- Added IcdUriBuilder and UriExtensions for reading/writing URI data
## [3.2.0] - 2018-05-09
### Added
- Added util method for removing BOM characters from UTF8 data
- Added extension method to convert from bool to ushort and back
- Added extension method to cast enums to ushort value
## [3.1.0] - 2018-05-04
### Added
- Added Yield extension to return a single-item enumerable for an object.
## [3.0.0] - 2018-04-23
### Added
- Adding extension method for getting Informational Version from an Assembly

View File

@@ -0,0 +1,90 @@
using System.Collections.Generic;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.EventArguments;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class AsyncEventQueueTest
{
[Test]
public void ItemDequeuedFeedbackTest()
{
Assert.Inconclusive();
}
[Test]
public void CountTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
queue.OnItemDequeued +=
(sender, args) =>
{
ThreadingUtils.Sleep(200);
};
Assert.AreEqual(0, queue.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
Assert.AreEqual(5, queue.Count);
}
}
[Test]
public void EnqueueTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
List<GenericEventArgs<int>> eventArgs = new List<GenericEventArgs<int>>();
queue.OnItemDequeued +=
(sender, args) =>
{
eventArgs.Add(args);
};
Assert.AreEqual(0, eventArgs.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
ThreadingUtils.Sleep(500);
Assert.AreEqual(5, eventArgs.Count);
Assert.AreEqual(0, eventArgs[0].Data);
Assert.AreEqual(1, eventArgs[1].Data);
Assert.AreEqual(2, eventArgs[2].Data);
Assert.AreEqual(3, eventArgs[3].Data);
Assert.AreEqual(4, eventArgs[4].Data);
}
}
[Test]
public void ClearTest()
{
using (AsyncEventQueue<int> queue = new AsyncEventQueue<int>())
{
queue.OnItemDequeued +=
(sender, args) =>
{
ThreadingUtils.Sleep(200);
};
Assert.AreEqual(0, queue.Count);
for (int index = 0; index < 5; index++)
queue.Enqueue(index);
Assert.AreEqual(5, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
}
}
}

View File

@@ -0,0 +1,219 @@
using System;
using ICD.Common.Utils.Collections;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class BiDictionaryTest
{
#region Properties
[Test]
public void CountTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(2, "10");
dict.Set(3, "30");
Assert.AreEqual(2, dict.Count);
}
[Test]
public void KeysTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(2, "10");
dict.Set(3, "30");
Assert.AreEqual(2, dict.Keys.Count);
Assert.IsTrue(dict.Keys.Contains(2));
Assert.IsTrue(dict.Keys.Contains(3));
}
[Test]
public void ValuesTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
Assert.AreEqual(2, dict.Values.Count);
Assert.IsTrue(dict.Values.Contains("20"));
Assert.IsTrue(dict.Values.Contains("30"));
}
#endregion
#region Methods
[Test]
public void ClearTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
Assert.AreEqual(2, dict.Count);
dict.Clear();
Assert.AreEqual(0, dict.Count);
}
[Test]
public void ContainsKeyTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
Assert.IsFalse(dict.ContainsKey(1));
dict.Set(1, "10");
Assert.IsTrue(dict.ContainsKey(1));
}
[Test]
public void ContainsValueTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
Assert.IsFalse(dict.ContainsValue("10"));
dict.Set(1, "10");
Assert.IsTrue(dict.ContainsValue("10"));
}
[Test]
public void AddTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Add(1, "10");
Assert.AreEqual(1, dict.Count);
Assert.Throws<ArgumentException>(() => dict.Add(1, "10"));
Assert.Throws<ArgumentException>(() => dict.Add(1, "20"));
Assert.Throws<ArgumentException>(() => dict.Add(2, "10"));
Assert.DoesNotThrow(() => dict.Add(2, "20"));
Assert.AreEqual(2, dict.Count);
}
[Test]
public void SetTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
Assert.AreEqual("20", dict.GetValue(1));
Assert.AreEqual("30", dict.GetValue(3));
Assert.AreEqual(1, dict.GetKey("20"));
Assert.AreEqual(3, dict.GetKey("30"));
}
[Test]
public void GetKeyTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
Assert.AreEqual(1, dict.GetKey("20"));
Assert.AreEqual(3, dict.GetKey("30"));
}
[Test]
public void GetValueTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
Assert.AreEqual("20", dict.GetValue(1));
Assert.AreEqual("30", dict.GetValue(3));
}
[Test]
public void RemoveKeyTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Add(1, "10");
Assert.AreEqual(1, dict.Count);
dict.RemoveKey(1);
Assert.AreEqual(0, dict.Count);
}
[Test]
public void RemoveValueTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
dict.Add(1, "10");
Assert.AreEqual(1, dict.Count);
dict.RemoveValue("10");
Assert.AreEqual(0, dict.Count);
}
[Test]
public void TryGetValueTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
string value;
Assert.IsFalse(dict.TryGetValue(1, out value));
// ReSharper disable once ExpressionIsAlwaysNull
Assert.AreEqual(null, value);
dict.Add(1, "10");
Assert.IsTrue(dict.TryGetValue(1, out value));
Assert.AreEqual("10", value);
}
[Test]
public void TryGetKeyTest()
{
BiDictionary<int, string> dict = new BiDictionary<int, string>();
int value;
Assert.IsFalse(dict.TryGetKey("10", out value));
Assert.AreEqual(0, value);
dict.Add(1, "10");
Assert.IsTrue(dict.TryGetKey("10", out value));
Assert.AreEqual(1, value);
}
#endregion
}
}

View File

@@ -8,12 +8,6 @@ namespace ICD.Common.Utils.Tests.Collections
[TestFixture]
public sealed class IcdHashSetTest
{
[Test, UsedImplicitly]
public void NullSetTest()
{
Assert.AreEqual(0, IcdHashSet<int>.NullSet.Count);
}
[Test, UsedImplicitly]
public void CountTest()
{

View File

@@ -0,0 +1,126 @@
using System.Linq;
using ICD.Common.Utils.Collections;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class IcdOrderedDictionaryTest
{
#region Properties
[Test]
public void CountTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
{
{0, 0},
{1, 10},
{2, 20}
};
Assert.AreEqual(3, dict.Count);
}
[Test]
public void IsReadOnlyTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
Assert.IsFalse(dict.IsReadOnly);
}
[Test]
public void KeysTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<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()
{
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()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
{
{0, 0},
{1, 10},
{-1, -10}
};
Assert.AreEqual(0, dict[0]);
Assert.AreEqual(10, dict[1]);
Assert.AreEqual(-10, dict[-1]);
}
#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,208 @@
using System.Collections.Generic;
using ICD.Common.Utils.Collections;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class PriorityQueueTest
{
#region Properties
[Test]
public void CountTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
Assert.AreEqual(0, queue.Count);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
Assert.AreEqual(3, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
[Test]
public void IsSynchronizedTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
Assert.IsFalse(queue.IsSynchronized);
}
[Test]
public void SyncRootTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
Assert.AreEqual(queue, queue.SyncRoot);
}
#endregion
#region Methods
[Test]
public void ClearTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
Assert.AreEqual(3, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
[Test]
public void EnqueueTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
Assert.AreEqual(3, queue.Count);
}
[Test]
public void EnqueuePriorityTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1, 3);
queue.Enqueue(2, 2);
queue.Enqueue(3, 1);
Assert.AreEqual(3, queue.Count);
List<int> dequeue = new List<int>
{
queue.Dequeue(),
queue.Dequeue(),
queue.Dequeue()
};
Assert.AreEqual(3, dequeue[0]);
Assert.AreEqual(2, dequeue[1]);
Assert.AreEqual(1, dequeue[2]);
}
[Test]
public void EnqueueFirstTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1, int.MaxValue);
queue.Enqueue(2, int.MinValue);
queue.EnqueueFirst(3);
Assert.AreEqual(3, queue.Count);
Assert.AreEqual(3, queue.Dequeue());
}
[Test]
public void EnqueueRemoveTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.EnqueueRemove(4, i => i == 2);
Assert.AreEqual(3, queue.Count);
List<int> dequeue = new List<int>
{
queue.Dequeue(),
queue.Dequeue(),
queue.Dequeue()
};
Assert.AreEqual(1, dequeue[0]);
Assert.AreEqual(4, dequeue[1]);
Assert.AreEqual(3, dequeue[2]);
}
[Test]
public void EnqueueRemovePriorityTest()
{
Assert.Inconclusive();
}
[Test]
public void DequeueTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(1, 3);
queue.Enqueue(2, 2);
queue.Enqueue(3, 1);
Assert.AreEqual(3, queue.Count);
List<int> dequeue = new List<int>
{
queue.Dequeue(),
queue.Dequeue(),
queue.Dequeue()
};
Assert.AreEqual(3, dequeue[0]);
Assert.AreEqual(2, dequeue[1]);
Assert.AreEqual(1, dequeue[2]);
Assert.AreEqual(0, queue.Count);
}
[Test]
public void TryDequeueTest()
{
PriorityQueue<int> queue = new PriorityQueue<int>();
queue.Enqueue(10, 1);
queue.Enqueue(20, 2);
queue.Enqueue(30, 3);
int output;
Assert.IsTrue(queue.TryDequeue(out output));
Assert.AreEqual(10, output);
Assert.IsTrue(queue.TryDequeue(out output));
Assert.AreEqual(20, output);
Assert.IsTrue(queue.TryDequeue(out output));
Assert.AreEqual(30, output);
Assert.IsFalse(queue.TryDequeue(out output));
Assert.AreEqual(0, output);
}
[Test]
public void GetEnumeratorTest()
{
Assert.Inconclusive();
}
[Test]
public void CopyToTest()
{
Assert.Inconclusive();
}
#endregion
}
}

View File

@@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.EventArguments;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class RateLimitedEventQueueTest
{
[Test]
public void ItemDequeuedFeedbackTest()
{
List<GenericEventArgs<int>> callbacks = new List<GenericEventArgs<int>>();
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 1000 })
{
queue.OnItemDequeued += (sender, args) => callbacks.Add(args);
queue.Enqueue(10);
queue.Enqueue(20);
queue.Enqueue(30);
ThreadingUtils.Sleep(100);
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
queue.OnItemDequeued += (sender, args) => { ThreadingUtils.Sleep(1000); };
ThreadingUtils.Sleep(1000);
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
ThreadingUtils.Sleep(1000);
Assert.AreEqual(2, callbacks.Count, "Third enqueue did not wait for process to complete");
ThreadingUtils.Sleep(1000);
Assert.AreEqual(3, callbacks.Count);
}
}
#region Properties
[TestCase(1000)]
public void BetweenMillisecondsTest(long milliseconds)
{
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = milliseconds })
Assert.AreEqual(milliseconds, queue.BetweenMilliseconds);
}
[Test]
public void CountTest()
{
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
{
queue.Enqueue(1);
queue.Enqueue(1);
queue.Enqueue(1);
Assert.AreEqual(3, queue.Count);
}
}
#endregion
#region Methods
[Test]
public void EnqueueTest()
{
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
{
queue.Enqueue(10);
queue.Enqueue(20);
queue.Enqueue(30);
Assert.True(queue.SequenceEqual(new[] { 10, 20, 30 }));
}
}
[Test]
public void ClearTest()
{
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
{
queue.Enqueue(1);
queue.Enqueue(1);
queue.Enqueue(1);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
}
#endregion
}
}

View File

@@ -0,0 +1,33 @@
using ICD.Common.Utils.Comparers;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Comparers
{
[TestFixture]
public sealed class SequenceComparerTest
{
[Test]
public void CompareTest()
{
SequenceComparer<int> comparer = new SequenceComparer<int>();
// Equal
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
Assert.AreEqual(0, comparer.Compare(a, b));
// A comes before B
a = new[] {1, 2};
b = new[] {1, 2, 3};
Assert.AreEqual(-1, comparer.Compare(a, b));
// B comes before A
a = new[] { 2, 2, 3 };
b = new[] { 1, 2, 3 };
Assert.AreEqual(1, comparer.Compare(a, b));
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Text;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class EncodingUtilsTest
{
[Test]
public void StripUtf8BomTest()
{
Assert.Throws<ArgumentNullException>(() => EncodingUtils.StripUtf8Bom(null));
byte[] preamble = Encoding.UTF8.GetPreamble();
string preambleString = Encoding.UTF8.GetString(preamble, 0, preamble.Length);
Assert.AreEqual(string.Empty, EncodingUtils.StripUtf8Bom(string.Empty));
Assert.AreEqual("test", EncodingUtils.StripUtf8Bom("test"));
Assert.AreEqual("test", EncodingUtils.StripUtf8Bom(preambleString + "test"));
}
}
}

View File

@@ -37,16 +37,6 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(eTestEnum.C, values[3]);
}
[Test]
public void IsEnumTypeTest()
{
Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestEnum)));
Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestFlagsEnum)));
Assert.IsTrue(EnumUtils.IsEnumType(typeof(Enum)));
Assert.IsFalse(EnumUtils.IsEnumType(typeof(EnumUtilsTest)));
Assert.Throws<ArgumentNullException>(() => EnumUtils.IsEnumType(null));
}
[Test]
public void IsEnumTypeGenericTest()
{
@@ -63,7 +53,7 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(EnumUtils.IsEnum(eTestFlagsEnum.A));
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as object));
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as Enum));
Assert.IsFalse(EnumUtils.IsEnum(null));
Assert.IsFalse(EnumUtils.IsEnum<Enum>(null));
Assert.IsFalse(EnumUtils.IsEnum(""));
}
@@ -79,15 +69,6 @@ namespace ICD.Common.Utils.Tests
#region Values
[Test]
public void GetUnderlyingValueTest()
{
Assert.AreEqual(0, EnumUtils.GetUnderlyingValue(eTestEnum.None));
Assert.AreEqual(1, EnumUtils.GetUnderlyingValue(eTestEnum.A));
Assert.AreEqual(2, EnumUtils.GetUnderlyingValue(eTestEnum.B));
Assert.AreEqual(3, EnumUtils.GetUnderlyingValue(eTestEnum.C));
}
[Test]
public void GetValuesGenericTest()
{
@@ -100,13 +81,6 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(values.Contains(eTestEnum.C));
}
[Test]
public void GetNoneValueGenericTest()
{
Assert.AreEqual(eTestEnum.None, EnumUtils.GetNoneValue<eTestEnum>());
Assert.AreEqual(eTestFlagsEnum.None, EnumUtils.GetNoneValue<eTestFlagsEnum>());
}
[Test]
public void GetValuesExceptNoneGenericTest()
{
@@ -119,18 +93,6 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(values.Contains(eTestEnum.C));
}
[Test]
public void GetValuesExceptNoneTest()
{
object[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).ToArray();
Assert.AreEqual(3, values.Length);
Assert.IsFalse(values.Contains(eTestEnum.None));
Assert.IsTrue(values.Contains(eTestEnum.A));
Assert.IsTrue(values.Contains(eTestEnum.B));
Assert.IsTrue(values.Contains(eTestEnum.C));
}
#endregion
#region Flags
@@ -142,13 +104,6 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(EnumUtils.IsFlagsEnum<eTestFlagsEnum>());
}
[Test]
public void IsFlagsEnumTest()
{
Assert.IsFalse(EnumUtils.IsFlagsEnum(typeof(eTestEnum)));
Assert.IsTrue(EnumUtils.IsFlagsEnum(typeof(eTestFlagsEnum)));
}
[Test]
public void GetFlagsIntersectionGenericTest()
{
@@ -318,20 +273,6 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(eTestFlagsEnum.None, outputB);
}
[Test]
public void ToEnumGenericTest()
{
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum(eTestEnum.A));
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum(eTestEnum.A));
}
[Test]
public void ToEnumTest()
{
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum((object)eTestEnum.A));
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum((object)eTestEnum.A));
}
#endregion
}
}

View File

@@ -301,5 +301,29 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(20, ordered[1]);
Assert.AreEqual(30, ordered[2]);
}
[Test]
public void ToInverseTest()
{
Dictionary<int, string> forward = new Dictionary<int, string>
{
{ 1, "testA" },
{ 2, "testA" },
{ 3, "testB" },
{ 4, "testB" }
};
Dictionary<string, List<int>> backwards = new Dictionary<string, List<int>>
{
{"testA", new List<int> {1, 2}},
{"testB", new List<int> {3, 4}}
};
Dictionary<string, List<int>> inverse = forward.ToInverse();
bool equal = inverse.DictionaryEqual(backwards, (v1, v2) => v1.ScrambledEquals(v2));
Assert.IsTrue(equal);
}
}
}

View File

@@ -335,9 +335,9 @@ namespace ICD.Common.Utils.Tests.Extensions
}
[Test]
public void ToDictionaryIntTest()
public void ToIndexedDictionaryTest()
{
Dictionary<int, int> values = new[] {1, 2, 3}.ToDictionary();
Dictionary<int, int> values = new[] {1, 2, 3}.ToIndexedDictionary();
Assert.AreEqual(3, values.Count);
Assert.AreEqual(1, values[0]);
@@ -346,9 +346,9 @@ namespace ICD.Common.Utils.Tests.Extensions
}
[Test]
public void ToDictionaryUIntTest()
public void ToIndexedDictionaryUIntTest()
{
Dictionary<uint, int> values = new[] {1, 2, 3}.ToDictionaryUInt();
Dictionary<uint, int> values = new[] {1, 2, 3}.ToIndexedDictionaryUInt();
Assert.AreEqual(3, values.Count);
Assert.AreEqual(1, values[0]);
@@ -400,10 +400,19 @@ namespace ICD.Common.Utils.Tests.Extensions
[Test]
public void UnanimousTest()
{
Assert.IsTrue(new[] {true, true, true}.Unanimous());
Assert.IsTrue(new[] {false, false, false}.Unanimous());
Assert.IsFalse(new[] {false, true, false}.Unanimous());
Assert.IsFalse(new bool[] { }.Unanimous());
bool result;
Assert.IsTrue(new[] {true, true, true}.Unanimous(out result));
Assert.IsTrue(result);
Assert.IsTrue(new[] {false, false, false}.Unanimous(out result));
Assert.IsFalse(result);
Assert.IsFalse(new[] {false, true, false}.Unanimous(out result));
Assert.AreEqual(default(bool), result);
Assert.IsFalse(new bool[] { }.Unanimous(out result));
Assert.AreEqual(default(bool), result);
}
[Test]

View File

@@ -13,10 +13,10 @@ namespace ICD.Common.Utils.Tests.Extensions
{
List<int> testList = new List<int>();
testList.AddSorted(2);
testList.AddSorted(3);
testList.AddSorted(1);
testList.AddSorted(2);
Assert.AreEqual(0, testList.AddSorted(2));
Assert.AreEqual(1, testList.AddSorted(3));
Assert.AreEqual(0, testList.AddSorted(1));
Assert.AreEqual(1, testList.AddSorted(2));
Assert.AreEqual(4, testList.Count);
Assert.AreEqual(1, testList[0]);
@@ -31,10 +31,10 @@ namespace ICD.Common.Utils.Tests.Extensions
List<int> testList = new List<int>();
IComparer<int> comparer = new InverseComparer();
testList.AddSorted(2, comparer);
testList.AddSorted(3, comparer);
testList.AddSorted(1, comparer);
testList.AddSorted(2, comparer);
Assert.AreEqual(0, testList.AddSorted(2, comparer));
Assert.AreEqual(0, testList.AddSorted(3, comparer));
Assert.AreEqual(2, testList.AddSorted(1, comparer));
Assert.AreEqual(1, testList.AddSorted(2, comparer));
Assert.AreEqual(4, testList.Count);
Assert.AreEqual(3, testList[0]);

View File

@@ -1,91 +1,82 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Extensions
{
[TestFixture]
public sealed class StringExtensionsTest
{
[Test]
public void IndexOfTest()
{
string first;
Assert.AreEqual(5, "test1test3test2".IndexOf(new[] { "test2", "test3" }, out first));
Assert.AreEqual("test3", first);
}
[TestFixture]
public sealed class StringExtensionsTest
{
[Test]
public void IndexOfTest()
{
string first;
Assert.AreEqual(5, "test1test3test2".IndexOf(new[] {"test2", "test3"}, out first));
Assert.AreEqual("test3", first);
}
[TestCase(true, "12345", '1')]
[TestCase(false, "12345", '2')]
public void StartsWithTest(bool expected, string value, char character)
{
Assert.AreEqual(expected, value.StartsWith(character));
}
[TestCase(true, "12345", '1')]
[TestCase(false, "12345", '2')]
public void StartsWithTest(bool expected, string value, char character)
{
Assert.AreEqual(expected, value.StartsWith(character));
}
[TestCase(true, "12345", '5')]
[TestCase(false, "12345", '2')]
public void EndsWithTest(bool expected, string value, char character)
{
Assert.AreEqual(expected, value.EndsWith(character));
}
[TestCase(true, "12345", '5')]
[TestCase(false, "12345", '2')]
public void EndsWithTest(bool expected, string value, char character)
{
Assert.AreEqual(expected, value.EndsWith(character));
}
[Test]
public void SplitCharCountTest()
{
string[] split = "123-456-789-0".Split('-', 2).ToArray();
[Test]
public void SplitCharCountTest()
{
string[] split = "123-456-789-0".Split('-', 2).ToArray();
Assert.AreEqual(2, split.Length);
Assert.AreEqual("123", split[0]);
Assert.AreEqual("456-789-0", split[1]);
}
Assert.AreEqual(2, split.Length);
Assert.AreEqual("123", split[0]);
Assert.AreEqual("456-789-0", split[1]);
}
[Test]
public void SplitChunksizeTest()
{
string[] split = "1234567890".Split(3).ToArray();
[Test]
public void SplitChunksizeTest()
{
string[] split = "1234567890".Split(3).ToArray();
Assert.AreEqual(4, split.Length);
Assert.AreEqual("123", split[0]);
Assert.AreEqual("456", split[1]);
Assert.AreEqual("789", split[2]);
Assert.AreEqual("0", split[3]);
}
Assert.AreEqual(4, split.Length);
Assert.AreEqual("123", split[0]);
Assert.AreEqual("456", split[1]);
Assert.AreEqual("789", split[2]);
Assert.AreEqual("0", split[3]);
}
[Test]
public void SplitStringDelimiterTest()
{
string[] split = "1234567890".Split("23").ToArray();
[TestCase("12345", " 12 3 4 \t 5\n")]
public void RemoveWhitespaceTest(string expected, string value)
{
Assert.AreEqual(expected, value.RemoveWhitespace());
}
Assert.AreEqual(2, split.Length);
Assert.AreEqual("14567890", string.Join("", split));
}
[TestCase("1234567890", "12345", "67890")]
[TestCase("foobarfoobar", "bar", "foofoo")]
public void RemoveStringTest(string value, string other, string expected)
{
Assert.AreEqual(expected, value.Remove(other));
}
[Test]
public void SplitStringDelimitersTest()
{
string[] split = "1234567890".Split(new[] { "23", "67" }).ToArray();
[TestCase("1234567890", new[] {'2', '6'}, "13457890")]
[TestCase("912529434324", new[] {'-', '(', ')', '.', '+'}, "912529434324")]
public void RemoveCharactersTest(string value, IEnumerable<char> characters, string expected)
{
Assert.AreEqual(expected, value.Remove(characters));
}
Assert.AreEqual(3, split.Length);
Assert.AreEqual("145890", string.Join("", split));
}
[TestCase("12345", " 12 3 4 \t 5\n")]
public void RemoveWhitespaceTest(string expected, string value)
{
Assert.AreEqual(expected, value.RemoveWhitespace());
}
[Test]
public void RemoveCharactersTest()
{
Assert.AreEqual("13457890", "1234567890".Remove(new[] { '2', '6' }));
}
[TestCase(true, "27652")]
[TestCase(false, "a27652")]
public void IsNumericTest(bool expected, string value)
{
Assert.AreEqual(expected, value.IsNumeric());
}
}
[TestCase(true, "27652")]
[TestCase(false, "a27652")]
public void IsNumericTest(bool expected, string value)
{
Assert.AreEqual(expected, value.IsNumeric());
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
@@ -86,6 +87,13 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(expected, value.IsIntegerNumeric());
}
[Test]
public void GetAssemblyTest()
{
Assembly assembly = typeof(TypeExtensionsTest).GetAssembly();
Assert.IsTrue(assembly.FullName.Contains("ICD.Common.Utils"));
}
[TestCase(typeof(string), typeof(object), true)]
[TestCase(typeof(object), typeof(string), false)]
public void IsAssignableToTest(Type a, Type b, bool expected)
@@ -134,6 +142,27 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(typeof(IEnumerable), interfaces[0]);
}
[Test]
public void GetMinimalInterfacesTest()
{
Assert.Inconclusive();
}
[TestCase(typeof(int), "Int32")]
[TestCase(typeof(List<int>), "List")]
public void GetNameWithoutGenericArityTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetNameWithoutGenericArity());
}
[TestCase(typeof(string), "string")]
[TestCase(typeof(int?), "int?")]
[TestCase(typeof(List<int?>), "List<int?>")]
public void GetSyntaxNameTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetSyntaxName());
}
private interface C
{
}

View File

@@ -0,0 +1,22 @@
using System;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Extensions
{
[TestFixture]
public sealed class UriExtensionsTest
{
[TestCase("http://username:password@test.com/", "username")]
public void GetUserName(string uriString, string expected)
{
Assert.AreEqual(expected, new Uri(uriString).GetUserName());
}
[TestCase("http://username:password@test.com/", "password")]
public void GetPassword(string uriString, string expected)
{
Assert.AreEqual(expected, new Uri(uriString).GetPassword());
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Xml;
using NUnit.Framework;
@@ -49,10 +50,10 @@ namespace ICD.Common.Utils.Tests.Extensions
reader.ReadToNextElement();
IcdXmlAttribute[] attributes = reader.GetAttributes().ToArray();
KeyValuePair<string, string>[] attributes = reader.GetAttributes().ToArray();
Assert.AreEqual(2, attributes.Length);
Assert.AreEqual("attr1", attributes[0].Name);
Assert.AreEqual("attr2", attributes[1].Name);
Assert.AreEqual("attr1", attributes[0].Key);
Assert.AreEqual("attr2", attributes[1].Key);
}
[Test]

View File

@@ -1,4 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
@@ -19,9 +20,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,92 @@
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class IcdUriBuilderTest
{
#region Properties
[TestCase("test")]
public void FragmentTest(string fragment)
{
Assert.AreEqual(fragment, new IcdUriBuilder {Fragment = fragment}.Fragment);
}
[TestCase("test")]
public void HostTest(string host)
{
Assert.AreEqual(host, new IcdUriBuilder {Host = host}.Host);
}
[TestCase("test")]
public void PasswordTest(string fragment)
{
Assert.AreEqual(fragment, new IcdUriBuilder {Password = fragment}.Password);
}
[TestCase("test")]
public void PathTest(string fragment)
{
Assert.AreEqual(fragment, new IcdUriBuilder {Path = fragment}.Path);
}
[TestCase((ushort)80)]
public void PortTest(ushort port)
{
Assert.AreEqual(port, new IcdUriBuilder {Port = port}.Port);
}
[TestCase("test")]
public void QueryTest(string query)
{
Assert.AreEqual(query, new IcdUriBuilder {Query = query}.Query);
}
[TestCase("test")]
public void SchemeTest(string scheme)
{
Assert.AreEqual(scheme, new IcdUriBuilder {Scheme = scheme}.Scheme);
}
[TestCase("test")]
public void UserNameTest(string userName)
{
Assert.AreEqual(userName, new IcdUriBuilder {UserName = userName}.UserName);
}
[Test]
public void UriTest()
{
Assert.Inconclusive();
}
#endregion
[TestCase("http://localhost/", null, null, null, null, (ushort)0, null, null, null)]
[TestCase("http://localhost:80/", null, null, null, null, (ushort)80, null, null, null)]
[TestCase("http://username@localhost/", null, null, null, null, (ushort)0, null, null, "username")]
[TestCase("http://localhost/", null, null, "password", null, (ushort)0, null, null, null)]
[TestCase("https://localhost/", null, null, null, null, (ushort)0, null, "https", null)]
[TestCase("http://localhost/test", null, null, null, "test", (ushort)0, null, null, null)]
[TestCase("http://localhost/test", null, null, null, "/test", (ushort)0, null, null, null)]
[TestCase("http://localhost//test", null, null, null, "//test", (ushort)0, null, null, null)]
public void ToStringTest(string expected, string fragment, string address, string password, string path, ushort port,
string query, string scheme, string userName)
{
IcdUriBuilder builder = new IcdUriBuilder
{
Fragment = fragment,
Host = address,
Password = password,
Path = path,
Port = port,
Query = query,
Scheme = scheme,
UserName = userName
};
Assert.AreEqual(expected, builder.ToString());
}
}
}

View File

@@ -36,18 +36,10 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
}
[UsedImplicitly]
[TestCase(-10000, 5)]
[TestCase(10000, 5)]
public void GetNumberOfDigitsTest(int number, int expected)
{
Assert.AreEqual(expected, MathUtils.GetNumberOfDigits(number));
}
[Test, UsedImplicitly]
public void GetRangesTest()
{
IEnumerable<int> values = new int[] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
int[][] ranges = MathUtils.GetRanges(values).ToArray();
Assert.AreEqual(4, ranges.Length);
@@ -68,7 +60,7 @@ namespace ICD.Common.Utils.Tests
[Test, UsedImplicitly]
public void RoundToNearestTest()
{
IEnumerable<int> values = new int[] { 0, 15, 30, 45 };
IEnumerable<int> values = new [] { 0, 15, 30, 45 };
Assert.AreEqual(15, MathUtils.RoundToNearest(21, values));
}
}

View File

@@ -178,11 +178,13 @@ namespace ICD.Common.Utils.Tests
[Test]
public void BreadthFirstSearchManyDestinationsTest()
{
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 1 }, null));
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchManyDestinations(1, null, Graph));
Assert.IsEmpty(RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 5 }, Graph));
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] { 1 }, null));
Assert.Throws<ArgumentNullException>(() => RecursionUtils.BreadthFirstSearchPathManyDestinations(1, null, Graph));
Assert.AreEqual(0, RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] { 5 }, Graph).Count());
Dictionary<int, IEnumerable<int>> paths = RecursionUtils.BreadthFirstSearchManyDestinations(1, new[] { 21, 22, 31, 43, 62 }, WideGraph);
Dictionary<int, IEnumerable<int>> paths =
RecursionUtils.BreadthFirstSearchPathManyDestinations(1, new[] {21, 22, 31, 43, 62}, WideGraph)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
//Make sure all paths were found
Assert.IsTrue(paths.Keys.Contains(21));

View File

@@ -0,0 +1,38 @@
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class RegexUtilsTest
{
[Test]
public static void MatchesTest()
{
Assert.Inconclusive();
}
[Test]
public static void MatchesOptionsTest()
{
Assert.Inconclusive();
}
[TestCase(@"[assembly: AssemblyFileVersion(""1.2.3"")][assembly: AssemblyFileVersion(""1.2.3"")]",
@"[assembly: AssemblyFileVersion(""2.0.3.0"")][assembly: AssemblyFileVersion(""2.0.3.0"")]",
@"AssemblyFileVersion\(""(?<version>(\d+\.?){4})""\)",
"version",
"1.2.3")]
public static void ReplaceGroupTest(string expected, string input, string pattern, string groupName,
string replacement)
{
string result = RegexUtils.ReplaceGroup(input, pattern, groupName, replacement);
Assert.AreEqual(expected, result);
}
[Test]
public static void ReplaceGroupFuncTest()
{
Assert.Inconclusive();
}
}
}

View File

@@ -47,14 +47,14 @@ namespace ICD.Common.Utils.Tests
section.Enter();
// ReSharper disable once NotAccessedVariable
object handle = ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
Assert.IsTrue(ThreadingUtils.Wait(() => result == 1, 1000));
section.Leave();
// ReSharper disable once RedundantAssignment
handle = ThreadingUtils.SafeInvoke(() =>
ThreadingUtils.SafeInvoke(() =>
{
result = section.TryEnter() ? 2 : 0;
section.Leave();

View File

@@ -61,10 +61,10 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual("[-3 - 5]", StringUtils.RangeFormat(-3, 5));
}
[Test, UsedImplicitly]
public void UppercaseFirstTest()
[TestCase("foobar", "Foobar")]
public void UppercaseFirstTest(string value, string expected)
{
Assert.AreEqual("Foobar", StringUtils.UppercaseFirst("foobar"));
Assert.AreEqual(expected, StringUtils.UppercaseFirst(value));
}
[TestCase("test", "Test")]
@@ -74,16 +74,24 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(expected, StringUtils.ToTitleCase(input));
}
[Test, UsedImplicitly]
public void ToIpIdStringTest()
[TestCase((byte)0x67, "0x67")]
public void ToIpIdStringTest(byte ipid, string expected)
{
Assert.AreEqual("0x67", StringUtils.ToIpIdString(0x67));
Assert.AreEqual(expected, StringUtils.ToIpIdString(ipid));
}
[Test, UsedImplicitly]
public void FromIpIdStringTest()
[TestCase("0x67", (byte)0x67)]
public void FromIpIdStringTest(string value, byte expected)
{
Assert.AreEqual(0x67, StringUtils.FromIpIdString("0x67"));
Assert.AreEqual(expected, StringUtils.FromIpIdString(value));
}
[TestCase("0x67", true, (byte)0x67)]
public void TryFromIpIdStringTest(string value, bool expected, byte expectedOutput)
{
byte output;
Assert.AreEqual(expected, StringUtils.TryFromIpIdString(value, out output));
Assert.AreEqual(expectedOutput, output);
}
[UsedImplicitly]
@@ -170,5 +178,20 @@ namespace ICD.Common.Utils.Tests
Assert.IsTrue(StringUtils.TryParse("true", out testVal));
Assert.AreEqual(true, testVal);
}
[TestCase("test", "\"test\"")]
[TestCase("\"test\"", "\"test\"")]
[TestCase("test test", "\"test test\"")]
public void EnquoteTest(string input, string expected)
{
Assert.AreEqual(expected, StringUtils.Enquote(input));
}
[TestCase("\"test\"", "test")]
[TestCase("\"test test\"", "test test")]
public void UnEnquoteTest(string input, string expected)
{
Assert.AreEqual(expected, StringUtils.UnEnquote(input));
}
}
}

View File

@@ -1,17 +0,0 @@
using ICD.Common.Properties;
using ICD.Common.Utils.Xml;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Xml
{
[TestFixture]
public sealed class IcdXmlAttributeTest
{
[Test, UsedImplicitly]
public void ValueAsIntTest()
{
IcdXmlAttribute attribute = new IcdXmlAttribute("test", "12");
Assert.AreEqual("12", attribute.Value);
}
}
}

View File

@@ -56,12 +56,12 @@ namespace ICD.Common.Utils.Tests.Xml
{
reader.ReadToNextElement();
IcdXmlAttribute[] attributes = reader.GetAttributes().ToArray();
KeyValuePair<string, string>[] attributes = reader.GetAttributes().ToArray();
Assert.AreEqual(2, attributes.Length);
Assert.AreEqual("attr1", attributes[0].Name);
Assert.AreEqual("attr1", attributes[0].Key);
Assert.AreEqual("1", attributes[0].Value);
Assert.AreEqual("attr2", attributes[1].Name);
Assert.AreEqual("attr2", attributes[1].Key);
Assert.AreEqual("2", attributes[1].Value);
}
}
@@ -90,6 +90,7 @@ namespace ICD.Common.Utils.Tests.Xml
{
paths.Add(args.Path);
nodes.Add(args.Outer);
return true;
}
);
@@ -176,13 +177,6 @@ namespace ICD.Common.Utils.Tests.Xml
}
}
[Test, UsedImplicitly]
public void IsValidXmlTest()
{
Assert.IsFalse(XmlUtils.IsValidXml(@"<Foo></Bar>"));
Assert.IsTrue(XmlUtils.IsValidXml(EXAMPLE_XML));
}
[Test]
public void FormatTest()
{
@@ -253,6 +247,7 @@ namespace ICD.Common.Utils.Tests.Xml
[TestCase("<Test><Child>A</Child></Test>", "Child", true, eTestEnum.A)]
[TestCase("<Test><Child>A, B</Child></Test>", "Child", true, eTestEnum.A | eTestEnum.B)]
public void ReadChildElementContentAsEnumTest<T>(string xml, string childElement, bool ignoreCase, T expected)
where T : struct, IConvertible
{
Assert.AreEqual(expected, XmlUtils.ReadChildElementContentAsEnum<T>(xml, childElement, ignoreCase));
}

View File

@@ -24,7 +24,6 @@ namespace ICD.Common.Utils
private static readonly IcdHashSet<Assembly> s_CachedAssemblies;
private static readonly IcdHashSet<Type> s_CachedTypes;
private static readonly Dictionary<Attribute, MethodInfo> s_AttributeToMethodCache;
private static readonly Dictionary<Attribute, Type> s_AttributeToTypeCache;
private static readonly Dictionary<Type, IcdHashSet<Attribute>> s_TypeToAttributesCache;
@@ -38,26 +37,12 @@ namespace ICD.Common.Utils
s_CachedAssemblies = new IcdHashSet<Assembly>();
s_CachedTypes = new IcdHashSet<Type>();
s_AttributeToMethodCache = new Dictionary<Attribute, MethodInfo>();
s_AttributeToTypeCache = new Dictionary<Attribute, Type>();
s_TypeToAttributesCache = new Dictionary<Type, IcdHashSet<Attribute>>();
}
#region Caching
/// <summary>
/// Pre-emptively caches the given assemblies for lookup.
/// </summary>
/// <param name="assemblies"></param>
public static void CacheAssemblies(IEnumerable<Assembly> assemblies)
{
if (assemblies == null)
throw new ArgumentNullException("assemblies");
foreach (Assembly assembly in assemblies)
CacheAssembly(assembly);
}
/// <summary>
/// Pre-emptively caches the given assembly for lookup.
/// </summary>
@@ -70,6 +55,8 @@ namespace ICD.Common.Utils
if (s_CachedAssemblies.Contains(assembly))
return true;
s_CachedAssemblies.Add(assembly);
#if SIMPLSHARP
CType[] types;
#else
@@ -84,6 +71,14 @@ namespace ICD.Common.Utils
{
foreach (Exception inner in e.LoaderExceptions)
{
if (inner is System.IO.FileNotFoundException)
{
Logger.AddEntry(eSeverity.Error,
"{0} failed to cache assembly {1} - Could not find one or more dependencies by path",
typeof(AttributeUtils).Name, assembly.GetName().Name);
continue;
}
Logger.AddEntry(eSeverity.Error, inner, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
assembly.GetName().Name);
}
@@ -106,7 +101,6 @@ namespace ICD.Common.Utils
foreach (var type in types)
CacheType(type);
s_CachedAssemblies.Add(assembly);
return true;
}
@@ -125,44 +119,23 @@ namespace ICD.Common.Utils
if (s_CachedTypes.Contains(type))
return;
s_CachedTypes.Add(type);
MethodInfo[] methods;
s_CachedTypes.Add(type);
try
{
s_TypeToAttributesCache[type] = new IcdHashSet<Attribute>(type.GetCustomAttributes<Attribute>(false));
foreach (Attribute attribute in s_TypeToAttributesCache[type])
s_AttributeToTypeCache[attribute] = type;
methods = type.GetMethods();
}
// GetMethods for Open Generic Types is not supported.
catch (NotSupportedException)
{
return;
}
// Not sure why this happens :/
catch (InvalidProgramException)
{
return;
}
foreach (MethodInfo method in methods)
CacheMethod(method);
}
/// <summary>
/// Caches the method.
/// </summary>
/// <param name="method"></param>
private static void CacheMethod(MethodInfo method)
{
if (method == null)
throw new ArgumentNullException("method");
foreach (Attribute attribute in ReflectionExtensions.GetCustomAttributes<Attribute>(method, false))
s_AttributeToMethodCache[attribute] = method;
}
#endregion
@@ -175,7 +148,6 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetClassAttributes<T>()
where T : Attribute
{
return s_AttributeToTypeCache.Select(kvp => kvp.Key)
.OfType<T>();
@@ -189,7 +161,6 @@ namespace ICD.Common.Utils
/// <returns></returns>
[CanBeNull]
public static T GetClassAttribute<T>(Type type)
where T : Attribute
{
if (type == null)
throw new ArgumentNullException("type");
@@ -204,7 +175,6 @@ namespace ICD.Common.Utils
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<T> GetClassAttributes<T>(Type type)
where T : Attribute
{
if (type == null)
throw new ArgumentNullException("type");
@@ -224,8 +194,10 @@ namespace ICD.Common.Utils
CacheType(type);
return s_TypeToAttributesCache.ContainsKey(type)
? s_TypeToAttributesCache[type].ToArray()
IcdHashSet<Attribute> attributes;
return s_TypeToAttributesCache.TryGetValue(type, out attributes)
? attributes.ToArray(attributes.Count)
: Enumerable.Empty<Attribute>();
}
@@ -243,29 +215,27 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Gets all of the cached method attributes of the given type.
/// Returns the properties on the given instance with property attributes of the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance"></param>
/// <param name="inherit"></param>
/// <returns></returns>
public static IEnumerable<T> GetMethodAttributes<T>()
where T : Attribute
public static IEnumerable<PropertyInfo> GetProperties<T>(object instance, bool inherit)
{
return s_AttributeToMethodCache.Select(p => p.Key)
.OfType<T>()
.ToArray();
}
if (instance == null)
throw new ArgumentNullException("instance");
/// <summary>
/// Gets the cached method for the given attribute.
/// </summary>
/// <param name="attribute"></param>
/// <returns></returns>
public static MethodInfo GetMethod(Attribute attribute)
{
if (attribute == null)
throw new ArgumentNullException("attribute");
return s_AttributeToMethodCache[attribute];
return instance.GetType()
#if SIMPLSHARP
.GetCType()
#else
.GetTypeInfo()
#endif
.GetProperties()
// ReSharper disable InvokeAsExtensionMethod
.Where(p => ReflectionExtensions.GetCustomAttributes<T>(p, inherit).Any());
// ReSharper restore InvokeAsExtensionMethod
}
#endregion

View File

@@ -5,7 +5,7 @@ namespace ICD.Common.Utils.Attributes
/// <summary>
/// AbstractIcdAttribute is the base class for all ICD attributes.
/// </summary>
public abstract class AbstractIcdAttribute : Attribute
public abstract class AbstractIcdAttribute : Attribute, IIcdAttribute
{
}
}

View File

@@ -0,0 +1,6 @@
namespace ICD.Common.Utils.Attributes
{
public interface IIcdAttribute
{
}
}

View File

@@ -0,0 +1,228 @@
using System;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Attributes
{
/// <summary>
/// Indicates the valid ranges for a given value if that range is not equal to the range of the data type.
/// </summary>
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = true)]
public sealed class RangeAttribute : AbstractIcdAttribute
{
#region Properties
[NotNull]
public object Min { get; private set; }
[NotNull]
public object Max { get; private set; }
[NotNull]
private Type Type { get { return Min.GetType(); } }
#endregion
#region Constructors
public RangeAttribute(ushort min, ushort max)
{
Min = min;
Max = max;
}
public RangeAttribute(short min, short max)
{
Min = min;
Max = max;
}
public RangeAttribute(uint min, uint max)
{
Min = min;
Max = max;
}
public RangeAttribute(int min, int max)
{
Min = min;
Max = max;
}
public RangeAttribute(ulong min, ulong max)
{
Min = min;
Max = max;
}
public RangeAttribute(long min, long max)
{
Min = min;
Max = max;
}
public RangeAttribute(float min, float max)
{
Min = min;
Max = max;
}
public RangeAttribute(double min, double max)
{
Min = min;
Max = max;
}
public RangeAttribute(byte min, byte max)
{
Min = min;
Max = max;
}
public RangeAttribute(sbyte min, sbyte max)
{
Min = min;
Max = max;
}
public RangeAttribute(decimal min, decimal max)
{
Min = min;
Max = max;
}
#endregion
#region Methods
/// <summary>
/// Returns true if the given value is within the range of Min to Max.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool IsInRange(object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (value.GetType() != Type)
throw new ArgumentException("the type of value does not match the type of min / max");
if (value is ushort)
{
var castMin = (ushort)Min;
var castMax = (ushort)Max;
var castVal = (ushort)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is short)
{
var castMin = (short)Min;
var castMax = (short)Max;
var castVal = (short)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is uint)
{
var castMin = (uint)Min;
var castMax = (uint)Max;
var castVal = (uint)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is int)
{
var castMin = (int)Min;
var castMax = (int)Max;
var castVal = (int)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is ulong)
{
var castMin = (ulong)Min;
var castMax = (ulong)Max;
var castVal = (ulong)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is long)
{
var castMin = (long)Min;
var castMax = (long)Max;
var castVal = (long)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is float)
{
var castMin = (float)Min;
var castMax = (float)Max;
var castVal = (float)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is double)
{
var castMin = (double)Min;
var castMax = (double)Max;
var castVal = (double)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is decimal)
{
var castMin = (decimal)Min;
var castMax = (decimal)Max;
var castVal = (decimal)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is byte)
{
var castMin = (byte)Min;
var castMax = (byte)Max;
var castVal = (byte)value;
return (castVal >= castMin && castVal <= castMax);
}
if (value is sbyte)
{
var castMin = (sbyte)Min;
var castMax = (sbyte)Max;
var castVal = (sbyte)value;
return (castVal >= castMin && castVal <= castMax);
}
throw new ArgumentException("the type of value is not a numeric type.");
}
public ushort RemapRangeToUShort(double value)
{
return (ushort)MathUtils.MapRange((double)Min, (double)Max, ushort.MinValue, ushort.MaxValue, value);
}
public ushort RemapRangeToUShort(float value)
{
return (ushort)MathUtils.MapRange((float)Min, (float)Max, ushort.MinValue, ushort.MaxValue, value);
}
public ushort RemapRangeToUShort(int value)
{
return (ushort)MathUtils.MapRange((int)Min, (int)Max, ushort.MinValue, ushort.MaxValue, value);
}
public ushort RemapRangeToUShort(ushort value)
{
return MathUtils.MapRange((ushort)Min, (ushort)Max, ushort.MinValue, ushort.MaxValue, value);
}
#endregion
}
}

View File

@@ -0,0 +1,135 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// Provides a buffer for storing items to be raised in order in a new thread.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class AsyncEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
{
/// <summary>
/// Raised to handle to the next item in the queue.
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
private readonly Queue<T> m_Queue;
private readonly SafeCriticalSection m_QueueSection;
private readonly SafeCriticalSection m_ProcessSection;
#region Properties
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
bool ICollection.IsSynchronized { get { return true; } }
object ICollection.SyncRoot { get { return this; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public AsyncEventQueue()
{
m_Queue = new Queue<T>();
m_QueueSection = new SafeCriticalSection();
m_ProcessSection = new SafeCriticalSection();
}
#region Methods
/// <summary>
/// Release resources.
/// </summary>
public void Dispose()
{
OnItemDequeued = null;
Clear();
}
/// <summary>
/// Enqueues the given item and begins processing the queue.
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
m_QueueSection.Execute(() => m_Queue.Enqueue(item));
ThreadingUtils.SafeInvoke(ProcessQueue);
}
/// <summary>
/// Clears the queued items.
/// </summary>
public void Clear()
{
m_QueueSection.Execute(() => m_Queue.Clear());
}
#endregion
#region Private Methods
/// <summary>
/// Dequeues and raises each item in sequence.
/// </summary>
private void ProcessQueue()
{
if (!m_ProcessSection.TryEnter())
return;
try
{
T item;
while (m_Queue.Dequeue(out item))
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
}
finally
{
m_ProcessSection.Leave();
}
}
#endregion
#region IEnumerable/ICollection
public IEnumerator<T> GetEnumerator()
{
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
m_QueueSection.Enter();
try
{
foreach (T item in this)
{
array.SetValue(item, index);
index++;
}
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
}
}

View File

@@ -0,0 +1,186 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// Provides a 1-to-1 map of Keys to Values with O(1) Value->Key lookup time.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_KeyToValue;
private readonly Dictionary<TValue, TKey> m_ValueToKey;
#region Properties
public int Count { get { return m_KeyToValue.Count; } }
public bool IsReadOnly { get { return false; } }
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
public ICollection<TValue> Values { get { return m_ValueToKey.Keys; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public BiDictionary()
{
m_KeyToValue = new Dictionary<TKey, TValue>();
m_ValueToKey = new Dictionary<TValue, TKey>();
}
#region Methods
public void Clear()
{
m_KeyToValue.Clear();
m_ValueToKey.Clear();
}
public bool ContainsKey(TKey key)
{
return m_KeyToValue.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return m_ValueToKey.ContainsKey(value);
}
public void Add(TKey key, TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (value == null)
throw new ArgumentNullException("value");
if (ContainsKey(key))
throw new ArgumentException("Key is already present in the collection", "key");
if (ContainsValue(value))
throw new ArgumentException("Value is already present in the collection", "value");
m_KeyToValue.Add(key, value);
m_ValueToKey.Add(value, key);
}
public void Set(TKey key, TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (value == null)
throw new ArgumentNullException("value");
RemoveKey(key);
RemoveValue(value);
Add(key, value);
}
public TKey GetKey(TValue value)
{
return m_ValueToKey[value];
}
public TValue GetValue(TKey key)
{
return m_KeyToValue[key];
}
public bool RemoveKey(TKey key)
{
if (!ContainsKey(key))
return false;
TValue value = m_KeyToValue[key];
m_KeyToValue.Remove(key);
m_ValueToKey.Remove(value);
return true;
}
public bool RemoveValue(TValue value)
{
if (!ContainsValue(value))
return false;
TKey key = m_ValueToKey[value];
return RemoveKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return m_KeyToValue.TryGetValue(key, out value);
}
public bool TryGetKey(TValue value, out TKey key)
{
return m_ValueToKey.TryGetValue(value, out key);
}
#endregion
#region IDictionary
TValue IDictionary<TKey, TValue>.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
return RemoveKey(key);
}
#endregion
#region ICollection
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return (m_KeyToValue as IDictionary<TKey, TValue>).Contains(item);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return RemoveKey(item.Key);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
}
#endregion
#region IEnumerable
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_KeyToValue.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

@@ -16,12 +16,6 @@ namespace ICD.Common.Utils.Collections
#region Properties
/// <summary>
/// Returns a new, empty hashset.
/// </summary>
[PublicAPI]
public static IcdHashSet<T> NullSet { get { return new IcdHashSet<T>(); } }
/// <summary>
/// Gets the number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
@@ -46,8 +40,8 @@ namespace ICD.Common.Utils.Collections
/// Constructor.
/// </summary>
public IcdHashSet()
: this(Enumerable.Empty<T>())
{
m_Dict = new Dictionary<T, object>();
}
/// <summary>
@@ -55,10 +49,33 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="items"></param>
public IcdHashSet(IEnumerable<T> items)
: this()
: this(EqualityComparer<T>.Default, items)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdHashSet(IEqualityComparer<T> comparer)
: this(comparer, Enumerable.Empty<T>())
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="items"></param>
public IcdHashSet(IEqualityComparer<T> comparer, IEnumerable<T> items)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (items == null)
return;
throw new ArgumentNullException("items");
m_Dict = new Dictionary<T, object>(comparer);
AddRange(items);
}
@@ -67,26 +84,35 @@ namespace ICD.Common.Utils.Collections
#region Methods
/// <summary>
/// Returns a set containing all of this sets items plus all of the items in the given set.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Union(IEnumerable<T> set)
{
IcdHashSet<T> unionSet = new IcdHashSet<T>(this);
if (set == null)
return unionSet;
throw new ArgumentNullException("set");
IcdHashSet<T> unionSet = new IcdHashSet<T>(m_Dict.Comparer, this);
unionSet.AddRange(set);
return unionSet;
}
/// <summary>
/// Returns a new set of this sets items exluding the items in the given set.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Subtract(IEnumerable<T> set)
{
IcdHashSet<T> subtractSet = new IcdHashSet<T>(this);
if (set == null)
return subtractSet;
throw new ArgumentNullException("set");
IcdHashSet<T> subtractSet = new IcdHashSet<T>(m_Dict.Comparer, this);
foreach (T item in set)
subtractSet.Remove(item);
@@ -94,23 +120,18 @@ namespace ICD.Common.Utils.Collections
return subtractSet;
}
/// <summary>
/// Returns all of the items that are common between this set and the given set.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsSubsetOf(IcdHashSet<T> set)
public IcdHashSet<T> Intersection(IEnumerable<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
return this.All(setToCompare.Contains);
}
[PublicAPI]
public IcdHashSet<T> Intersection(IcdHashSet<T> set)
{
IcdHashSet<T> intersectionSet = NullSet;
if (set == null)
return intersectionSet;
throw new ArgumentNullException("set");
foreach (T item in this.Where(set.Contains))
intersectionSet.Add(item);
IcdHashSet<T> intersectionSet = new IcdHashSet<T>(m_Dict.Comparer);
foreach (T item in set.Where(Contains))
intersectionSet.Add(item);
@@ -124,39 +145,97 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> NonIntersection(IcdHashSet<T> set)
public IcdHashSet<T> NonIntersection(IEnumerable<T> set)
{
return Subtract(set).Union(set.Subtract(this));
if (set == null)
throw new ArgumentNullException("set");
IcdHashSet<T> output = new IcdHashSet<T>(m_Dict.Comparer, this);
foreach (T item in set)
{
if (output.Contains(item))
output.Remove(item);
else
output.Add(item);
}
return output;
}
/// <summary>
/// Returns true if the given set contains all of the items in this set.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsSubsetOf(IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
return Count <= set.Count && this.All(set.Contains);
}
/// <summary>
/// Returns true if the given set contains all of the items in this set, and the sets are not equal.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsProperSubsetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
// Is a proper subset if A is a subset of B and A != B
return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this));
return Count < set.Count && IsSubsetOf(set);
}
/// <summary>
/// Returns true if this set contains all of the items in the given set.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsSupersetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
return setToCompare.IsSubsetOf(this);
if (set == null)
throw new ArgumentNullException("set");
return set.IsSubsetOf(this);
}
/// <summary>
/// Returns true if this set contains all of the items in the given set, and the sets are not equal.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool IsProperSupersetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
// B is a proper superset of A if B is a superset of A and A != B
return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this));
return set.IsProperSubsetOf(this);
}
/// <summary>
/// Returns true if this set contains all of the items in the given set, and vice versa.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public bool SetEquals(IcdHashSet<T> set)
{
if (set == null)
throw new ArgumentNullException("set");
return Count == set.Count && set.All(Contains);
}
#endregion
#region ICollection<T> Members
#region ICollection<T>
/// <summary>
/// Adds the item to the collection. Returns false if the item already exists.
@@ -196,15 +275,7 @@ namespace ICD.Common.Utils.Collections
throw new ArgumentNullException("items");
foreach (T item in items)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new InvalidOperationException("item");
if (!m_Dict.ContainsKey(item))
m_Dict[item] = null;
}
m_Dict[item] = null;
}
/// <summary>
@@ -223,6 +294,11 @@ namespace ICD.Common.Utils.Collections
/// <returns></returns>
public bool Contains(T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("item");
return m_Dict.ContainsKey(item);
}
@@ -244,12 +320,17 @@ namespace ICD.Common.Utils.Collections
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public bool Remove(T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("item");
return m_Dict.Remove(item);
}
#endregion
#region Implementation of IEnumerable
#region IEnumerable<T>
/// <summary>
/// Returns an enumerator that iterates through the collection.

View File

@@ -0,0 +1,188 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
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; } }
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
public TValue this[TKey key]
{
get { return m_Dictionary[key]; }
set
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
if (!ContainsKey(key))
{
int index = m_OrderedKeys.AddSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
}
m_Dictionary[key] = value;
}
}
#endregion
/// <summary>
/// Constructor.
/// </summary>
public IcdOrderedDictionary()
: this(Comparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdOrderedDictionary(IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdOrderedDictionary(IComparer<TKey> comparer, IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
Add(kvp.Key, kvp.Value);
}
#region Methods
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
.GetEnumerator();
}
public void Add(TKey key, TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentException("An item with the same key has already been added.", "key");
this[key] = value;
}
public void Clear()
{
m_OrderedKeys.Clear();
m_ValuesOrderedByKey.Clear();
m_Dictionary.Clear();
}
public bool ContainsKey(TKey key)
{
return m_Dictionary.ContainsKey(key);
}
public bool Remove(TKey key)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
if (!m_Dictionary.Remove(key))
return false;
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
m_OrderedKeys.RemoveAt(index);
m_ValuesOrderedByKey.RemoveAt(index);
return true;
}
public bool TryGetValue(TKey key, out TValue value)
{
return m_Dictionary.TryGetValue(key, out value);
}
#endregion
#region Private Methods
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(KeyValuePair<TKey, TValue>[] array, int index)
{
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

@@ -0,0 +1,263 @@
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
{
/// <summary>
/// Provides a first-in first-out collection with enhanced insertion features.
/// </summary>
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
{
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
private int m_Count;
#region Properties
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count { get { return m_Count; } }
/// <summary>
/// Is the collection thread safe?
/// </summary>
public bool IsSynchronized { get { return false; } }
/// <summary>
/// Gets a reference for locking.
/// </summary>
public object SyncRoot { get { return this; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public PriorityQueue()
{
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
}
#region Methods
/// <summary>
/// Clears the collection.
/// </summary>
[PublicAPI]
public void Clear()
{
m_PriorityToQueue.Clear();
m_Count = 0;
}
/// <summary>
/// Adds the item to the end of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void Enqueue(T item)
{
Enqueue(item, int.MaxValue);
}
/// <summary>
/// Adds the item to the queue with the given priority.
/// Lower values are dequeued first.
/// </summary>
/// <param name="item"></param>
/// <param name="priority"></param>
[PublicAPI]
public void Enqueue(T item, int priority)
{
List<T> queue;
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
{
queue = new List<T>();
m_PriorityToQueue.Add(priority, queue);
}
queue.Add(item);
m_Count++;
}
/// <summary>
/// Enqueues the item at the beginning of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void EnqueueFirst(T item)
{
const int priority = int.MinValue;
List<T> queue;
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
{
queue = new List<T>();
m_PriorityToQueue.Add(priority, queue);
}
queue.Insert(0, item);
m_Count++;
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
/// This is useful for reducing duplication, or replacing items with something more pertinant.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
[PublicAPI]
public void EnqueueRemove(T item, Func<T, bool> remove)
{
if (remove == null)
throw new ArgumentNullException("remove");
EnqueueRemove(item, remove, int.MaxValue);
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
/// This is useful for reducing duplication, or replacing items with something more pertinant.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
[PublicAPI]
public void EnqueueRemove(T item, Func<T, bool> remove, int priority)
{
if (remove == null)
throw new ArgumentNullException("remove");
bool inserted = false;
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue.ToArray())
{
int[] removeIndices =
kvp.Value
.FindIndices(v => remove(v))
.Reverse()
.ToArray();
if (removeIndices.Length == 0)
continue;
foreach (int removeIndex in removeIndices)
{
kvp.Value.RemoveAt(removeIndex);
m_Count--;
}
if (!inserted)
{
int insertIndex = removeIndices[0];
if (insertIndex >= kvp.Value.Count)
kvp.Value.Add(item);
else
kvp.Value.Insert(insertIndex, item);
m_Count++;
inserted = true;
}
if (kvp.Value.Count == 0)
m_PriorityToQueue.Remove(kvp.Key);
}
if (!inserted)
Enqueue(item, priority);
}
/// <summary>
/// Dequeues the first item with the lowest priority value.
/// </summary>
/// <returns></returns>
[PublicAPI]
public T Dequeue()
{
T output;
if (TryDequeue(out output))
return output;
throw new InvalidOperationException("The queue is empty.");
}
/// <summary>
/// Attempts to dequeue an item from the queue.
/// </summary>
/// <param name="output"></param>
/// <returns></returns>
[PublicAPI]
public bool TryDequeue(out T output)
{
while (true)
{
output = default(T);
KeyValuePair<int, List<T>> kvp;
if (!m_PriorityToQueue.TryFirst(out kvp))
return false;
int priority = kvp.Key;
List<T> queue = kvp.Value;
bool found = queue.TryFirst(out output);
if (found)
{
queue.RemoveAt(0);
m_Count--;
}
if (queue.Count == 0)
m_PriorityToQueue.Remove(priority);
if (found)
return true;
}
}
/// <summary>
/// Gets an enumerator for the items.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
{
return m_PriorityToQueue.Values
.SelectMany(v => v)
.GetEnumerator();
}
/// <summary>
/// Copies the collection to the target array.
/// </summary>
/// <param name="array"></param>
/// <param name="index"></param>
public void CopyTo(Array array, int index)
{
foreach (T item in this)
array.SetValue(item, index++);
}
#endregion
#region Private Methods
/// <summary>
/// Gets an enumerator for the items.
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

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

View File

@@ -135,6 +135,7 @@ namespace ICD.Common.Utils.Collections
}
set
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
@@ -172,6 +173,7 @@ namespace ICD.Common.Utils.Collections
public void Add(TKey key, TValue value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using ICD.Common.Utils.IO;
namespace ICD.Common.Utils.Comparers
{
public sealed class FileNameEqualityComparer : IEqualityComparer<string>
{
private static FileNameEqualityComparer s_Instance;
public static FileNameEqualityComparer Instance
{
get { return s_Instance ?? (s_Instance = new FileNameEqualityComparer()); }
}
private FileNameEqualityComparer()
{
}
public bool Equals(string x, string y)
{
return GetHashCode(x) == GetHashCode(y);
}
public int GetHashCode(string obj)
{
return obj == null ? 0 : IcdPath.GetFileName(obj).GetHashCode();
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
namespace ICD.Common.Utils.Comparers
{
public sealed class SequenceComparer<T> : IComparer<IEnumerable<T>>
{
private readonly IComparer<T> m_ItemComparer;
/// <summary>
/// Constructor.
/// </summary>
public SequenceComparer()
: this(Comparer<T>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public SequenceComparer(IComparer<T> comparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
m_ItemComparer = comparer;
}
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
{
if (x == null)
throw new ArgumentNullException("x");
if (y == null)
throw new ArgumentNullException("y");
using (IEnumerator<T> firstPos = x.GetEnumerator())
{
using (IEnumerator<T> secondPos = y.GetEnumerator())
{
bool hasFirst = firstPos.MoveNext();
bool hasSecond = secondPos.MoveNext();
while (hasFirst && hasSecond)
{
int result = m_ItemComparer.Compare(firstPos.Current, secondPos.Current);
if (result != 0)
return result;
hasFirst = firstPos.MoveNext();
hasSecond = secondPos.MoveNext();
}
if (!hasFirst && !hasSecond)
return 0;
if (!hasFirst)
return -1;
return 1;
}
}
}
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.IO;
namespace ICD.Common.Utils.Csv
{
public sealed class CsvWriter : IDisposable
{
private const string QUOTATION_MARK = "\"";
private const string DOUBLE_QUOTE_MARK = "\"\"";
private readonly IcdTextWriter m_Writer;
private readonly string m_Seperator;
private readonly string m_LineTerminator;
private readonly bool m_AlwaysEscape;
private bool m_NewLine;
/// <summary>
/// Constructor.
/// </summary>
public CsvWriter(IcdTextWriter writer, bool spaceAfterComma, bool alwaysEscape, string newline, params string[] header)
{
m_NewLine = true;
m_Writer = writer;
m_Seperator = spaceAfterComma ? ", " : ",";
m_AlwaysEscape = alwaysEscape;
m_LineTerminator = newline;
if(header.Any())
AppendRow(header);
}
~CsvWriter()
{
Dispose();
}
/// <summary>
/// Calls ToString() for each item and adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params object[] row)
{
foreach (object value in row)
AppendValue(value);
AppendNewline();
}
/// <summary>
/// Adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params string[] row)
{
foreach (string value in row)
AppendValue(value);
AppendNewline();
}
/// <summary>
/// Calls ToString() on the item and adds it to the builder.
/// </summary>
/// <param name="value"></param>
[PublicAPI]
public void AppendValue(object value)
{
AppendValue(string.Format("{0}", value));
}
/// <summary>
/// Adds a value to the builder.
/// </summary>
/// <param name="value"></param>
[PublicAPI]
public void AppendValue(string value)
{
value = value ?? string.Empty;
if (!m_NewLine)
m_Writer.WrappedTextWriter.Write(m_Seperator);
if (m_AlwaysEscape || value.Contains(","))
{
value = value.Replace(QUOTATION_MARK, DOUBLE_QUOTE_MARK);
// Append the value, surrounded by quotes
m_Writer.WrappedTextWriter.Write(QUOTATION_MARK);
m_Writer.WrappedTextWriter.Write(value);
m_Writer.WrappedTextWriter.Write(QUOTATION_MARK);
}
else
{
m_Writer.WrappedTextWriter.Write(value);
}
m_NewLine = false;
}
/// <summary>
/// Adds a New Line To the Builder
/// </summary>
[PublicAPI]
public void AppendNewline()
{
m_Writer.WrappedTextWriter.Write(m_LineTerminator);
m_NewLine = true;
}
public void Dispose()
{
m_Writer.Dispose();
}
/// <summary>
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
/// </summary>
/// <param name="writer"></param>
/// <param name="settings"></param>
/// <param name="header"></param>
/// <returns></returns>
[PublicAPI]
public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header)
{
return new CsvWriter(writer,
settings.InsertSpaceAfterComma,
settings.AlwaysEscapeEveryValue,
settings.NewLineSequence,
header);
}
}
}

View File

@@ -0,0 +1,46 @@
using ICD.Common.Properties;
namespace ICD.Common.Utils.Csv
{
public sealed class CsvWriterSettings
{
private bool m_InsertSpaceAfterComma = true;
private bool m_AlwaysEscapeEveryValue = true;
private string m_NewLineSequence = IcdEnvironment.NewLine;
/// <summary>
/// Gets/Sets whether to insert a space between elements, after the comma
/// Defaults to true.
/// </summary>
[PublicAPI]
public bool InsertSpaceAfterComma
{
get { return m_InsertSpaceAfterComma; }
set { m_InsertSpaceAfterComma = value; }
}
/// <summary>
/// Gets/Sets whether to always escape the values.
/// If true, values are recorded surrounded by quotes, regardless of if they contain a comma or not. Quotes are escaped.
/// If false, values are recorded as the value without quotes, unless escaping is required.
/// Defaults to true.
/// </summary>
[PublicAPI]
public bool AlwaysEscapeEveryValue
{
get { return m_AlwaysEscapeEveryValue; }
set { m_AlwaysEscapeEveryValue = value; }
}
/// <summary>
/// Gets/Sets the newline character or characters to deliniate records.
/// Defaults to System.NewLine.
/// </summary>
[PublicAPI]
public string NewLineSequence
{
get { return m_NewLineSequence; }
set { m_NewLineSequence = value; }
}
}
}

View File

@@ -0,0 +1,91 @@
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Net.Mail;
#endif
using ICD.Common.Properties;
namespace ICD.Common.Utils.Email
{
public sealed class EmailClient
{
private readonly EmailStringCollection m_To;
private readonly EmailStringCollection m_Cc;
private readonly EmailStringCollection m_Bcc;
#region Properties
public string Host { get; set; }
public ushort Port { get; set; }
public bool Secure { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string From { get; set; }
public EmailStringCollection To { get { return m_To; } }
public EmailStringCollection Cc { get { return m_Cc; } }
public EmailStringCollection Bcc { get { return m_Bcc; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public EmailClient()
{
m_To = new EmailStringCollection();
m_Cc = new EmailStringCollection();
m_Bcc = new EmailStringCollection();
}
#region Methods
/// <summary>
/// Sends the email.
/// </summary>
/// <param name="subject"></param>
/// <param name="message"></param>
/// <returns>Error codes.</returns>
[PublicAPI]
public eMailErrorCode Send(string subject, string message)
{
#if SIMPLSHARP
var response = CrestronMailFunctions.SendMail(Host, Port, Secure, Username, Password, From, m_To.ToString(), "",
m_Cc.ToString(), m_Bcc.ToString(), subject, message,
CrestronMailFunctions.eMailPriority.Normal, 0, "");
return MailErrorCodeUtils.FromSimplMailCode(response);
#else
try
{
MailMessage mailMessage = new MailMessage
{
From = new MailAddress(From),
To = {m_To.ToString()},
CC = {m_Cc.ToString()},
Bcc = {m_Bcc.ToString()},
Subject = subject,
Body = message
};
SmtpClient client = new SmtpClient
{
Port = Port,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Host = Host
};
client.Send(mailMessage);
}
catch (SmtpException ex)
{
return MailErrorCodeUtils.FromNetStandardMailCode(ex.StatusCode);
}
return eMailErrorCode.Ok;
#endif
}
#endregion
}
}

View File

@@ -0,0 +1,134 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Email
{
/// <summary>
/// Stores addresses/paths and provides them in a ; delimited string.
/// </summary>
public sealed class EmailStringCollection : ICollection<string>
{
private const char DELIMITER = ';';
private readonly List<string> m_Items;
/// <summary>
/// Gets the number of items.
/// </summary>
public int Count { get { return m_Items.Count; } }
public bool IsReadOnly { get { return false; } }
/// <summary>
/// Constructor.
/// </summary>
public EmailStringCollection()
{
m_Items = new List<string>();
}
#region Methods
/// <summary>
/// Adds the item to the collection.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public bool Add(string item)
{
if (m_Items.Contains(item))
return false;
m_Items.Add(item);
return true;
}
/// <summary>
/// Removes the item from the collection.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public bool Remove(string item)
{
if (!m_Items.Contains(item))
return false;
m_Items.Remove(item);
return true;
}
/// <summary>
/// Clears all items.
/// </summary>
public void Clear()
{
m_Items.Clear();
}
/// <summary>
/// Returns true if the collection contains the item.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public bool Contains(string item)
{
return m_Items.Contains(item);
}
/// <summary>
/// Gets the items as a ; delimited string.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Join(DELIMITER.ToString(), m_Items.ToArray());
}
#endregion
#region Collection
void ICollection<string>.Add(string item)
{
Add(item);
}
/// <summary>
/// Adds multiple email addresses, seperated by ;
/// </summary>
/// <param name="items"></param>
public void AddMany(string items)
{
items = items.RemoveWhitespace();
string [] splitItems = items.Split(DELIMITER);
foreach (string item in splitItems)
Add(item);
}
public void CopyTo(string[] array, int arrayIndex)
{
m_Items.CopyTo(array, arrayIndex);
}
#endregion
#region Enumerable
public IEnumerator<string> GetEnumerator()
{
return m_Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

@@ -0,0 +1,233 @@
using System;
#if STANDARD
using System.Net.Mail;
#endif
#if SIMPLSHARP
using Crestron.SimplSharp;
#endif
namespace ICD.Common.Utils.Email
{
public enum eMailErrorCode
{
//
// Summary:
// The transaction could not occur. You receive this error when the specified SMTP
// host cannot be found.
GeneralFailure = -1,
//
// Summary:
// A system status or system Help reply.
SystemStatus = 211,
//
// Summary:
// A Help message was returned by the service.
HelpMessage = 214,
//
// Summary:
// The SMTP service is ready.
ServiceReady = 220,
//
// Summary:
// The SMTP service is closing the transmission channel.
ServiceClosingTransmissionChannel = 221,
//
// Summary:
// The email was successfully sent to the SMTP service.
Ok = 250,
//
// Summary:
// The user mailbox is not located on the receiving server; the server forwards
// the e-mail.
UserNotLocalWillForward = 251,
//
// Summary:
// The specified user is not local, but the receiving SMTP service accepted the
// message and attempted to deliver it. This status code is defined in RFC 1123,
// which is available at http://www.ietf.org/.
CannotVerifyUserWillAttemptDelivery = 252,
//
// Summary:
// The SMTP service is ready to receive the e-mail content.
StartMailInput = 354,
//
// Summary:
// The SMTP service is not available; the server is closing the transmission channel.
ServiceNotAvailable = 421,
//
// Summary:
// The destination mailbox is in use.
MailboxBusy = 450,
//
// Summary:
// The SMTP service cannot complete the request. This error can occur if the client's
// IP address cannot be resolved (that is, a reverse lookup failed). You can also
// receive this error if the client domain has been identified as an open relay
// or source for unsolicited e-mail (spam). For details, see RFC 2505, which is
// available at http://www.ietf.org/.
LocalErrorInProcessing = 451,
//
// Summary:
// The SMTP service does not have sufficient storage to complete the request.
InsufficientStorage = 452,
//
// Summary:
// The client was not authenticated or is not allowed to send mail using the specified
// SMTP host.
ClientNotPermitted = 454,
//
// Summary:
// The SMTP service does not recognize the specified command.
CommandUnrecognized = 500,
//
// Summary:
// The syntax used to specify a command or parameter is incorrect.
SyntaxError = 501,
//
// Summary:
// The SMTP service does not implement the specified command.
CommandNotImplemented = 502,
//
// Summary:
// The commands were sent in the incorrect sequence.
BadCommandSequence = 503,
//
// Summary:
// The SMTP service does not implement the specified command parameter.
CommandParameterNotImplemented = 504,
//
// Summary:
// The SMTP server is configured to accept only TLS connections, and the SMTP client
// is attempting to connect by using a non-TLS connection. The solution is for the
// user to set EnableSsl=true on the SMTP Client.
MustIssueStartTlsFirst = 530,
//
// Summary:
// The destination mailbox was not found or could not be accessed.
MailboxUnavailable = 550,
//
// Summary:
// The user mailbox is not located on the receiving server. You should resend using
// the supplied address information.
UserNotLocalTryAlternatePath = 551,
//
// Summary:
// The message is too large to be stored in the destination mailbox.
ExceededStorageAllocation = 552,
//
// Summary:
// The syntax used to specify the destination mailbox is incorrect.
MailboxNameNotAllowed = 553,
//
// Summary:
// The transaction failed.
TransactionFailed = 554
}
public static class MailErrorCodeUtils
{
#if STANDARD
public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code)
{
switch (code)
{
case SmtpStatusCode.BadCommandSequence:
return eMailErrorCode.BadCommandSequence;
case SmtpStatusCode.CannotVerifyUserWillAttemptDelivery:
return eMailErrorCode.CannotVerifyUserWillAttemptDelivery;
case SmtpStatusCode.ClientNotPermitted:
return eMailErrorCode.ClientNotPermitted;
case SmtpStatusCode.CommandNotImplemented:
return eMailErrorCode.CommandNotImplemented;
case SmtpStatusCode.CommandParameterNotImplemented:
return eMailErrorCode.CommandParameterNotImplemented;
case SmtpStatusCode.CommandUnrecognized:
return eMailErrorCode.CommandUnrecognized;
case SmtpStatusCode.ExceededStorageAllocation:
return eMailErrorCode.ExceededStorageAllocation;
case SmtpStatusCode.GeneralFailure:
return eMailErrorCode.GeneralFailure;
case SmtpStatusCode.HelpMessage:
return eMailErrorCode.HelpMessage;
case SmtpStatusCode.InsufficientStorage:
return eMailErrorCode.InsufficientStorage;
case SmtpStatusCode.LocalErrorInProcessing:
return eMailErrorCode.LocalErrorInProcessing;
case SmtpStatusCode.MailboxBusy:
return eMailErrorCode.MailboxBusy;
case SmtpStatusCode.MailboxNameNotAllowed:
return eMailErrorCode.MailboxNameNotAllowed;
case SmtpStatusCode.MailboxUnavailable:
return eMailErrorCode.MailboxUnavailable;
case SmtpStatusCode.MustIssueStartTlsFirst:
return eMailErrorCode.MustIssueStartTlsFirst;
case SmtpStatusCode.Ok:
return eMailErrorCode.Ok;
case SmtpStatusCode.ServiceClosingTransmissionChannel:
return eMailErrorCode.ServiceClosingTransmissionChannel;
case SmtpStatusCode.ServiceNotAvailable:
return eMailErrorCode.ServiceNotAvailable;
case SmtpStatusCode.ServiceReady:
return eMailErrorCode.ServiceReady;
case SmtpStatusCode.StartMailInput:
return eMailErrorCode.StartMailInput;
case SmtpStatusCode.SyntaxError:
return eMailErrorCode.SyntaxError;
case SmtpStatusCode.SystemStatus:
return eMailErrorCode.SystemStatus;
case SmtpStatusCode.TransactionFailed:
return eMailErrorCode.TransactionFailed;
case SmtpStatusCode.UserNotLocalTryAlternatePath:
return eMailErrorCode.UserNotLocalTryAlternatePath;
case SmtpStatusCode.UserNotLocalWillForward:
return eMailErrorCode.UserNotLocalWillForward;
default:
throw new ArgumentOutOfRangeException(nameof(code), code, null);
}
}
#endif
#if SIMPLSHARP
public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code)
{
switch (code)
{
case CrestronMailFunctions.SendMailErrorCodes.SMTP_OK:
return eMailErrorCode.Ok;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_FATAL:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_ILLEGAL_CMD:
return eMailErrorCode.CommandUnrecognized;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_CONNECT:
return eMailErrorCode.ServiceNotAvailable;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_SEND:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_RECV:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_CONNECT:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_BUFFERS:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTHENTICATION:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTH_LOGIN_UNSUPPORTED:
return eMailErrorCode.MustIssueStartTlsFirst;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_INV_PARAM:
return eMailErrorCode.SyntaxError;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_ETHER_NOT_ENABLED:
return eMailErrorCode.ServiceNotAvailable;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_NO_SERVER_ADDRESS:
return eMailErrorCode.ServiceNotAvailable;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_SEND_FAILED:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_GENERAL_SENDMAIL_ERROR:
return eMailErrorCode.GeneralFailure;
case CrestronMailFunctions.SendMailErrorCodes.SMTP_INVALID_FIRMWARE:
return eMailErrorCode.GeneralFailure;
default:
throw new ArgumentOutOfRangeException("code");
}
}
#endif
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Text;
using ICD.Common.Properties;
namespace ICD.Common.Utils
{
public static class EncodingUtils
{
/// <summary>
/// Strips leading Byte Order Mark characters from the given UTF8 data.
/// </summary>
/// <param name="data">Input string to remove leading BOM chars from.</param>
/// <returns>Input string with leading BOM chars removed.</returns>
/// <exception cref="ArgumentNullException">Data is null.</exception>
[PublicAPI]
public static string StripUtf8Bom(string data)
{
if (data == null)
throw new ArgumentNullException("data");
byte[] preamble = Encoding.UTF8.GetPreamble();
string preambleString = Encoding.UTF8.GetString(preamble, 0, preamble.Length);
if (data.StartsWith(preambleString, StringComparison.Ordinal))
data = data.Remove(0, preambleString.Length);
return data;
}
}
}

View File

@@ -4,7 +4,6 @@ using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using System.Globalization;
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
@@ -14,33 +13,19 @@ namespace ICD.Common.Utils
{
public static class EnumUtils
{
private static readonly Dictionary<Type, object[]> s_EnumValuesCache;
private static readonly Dictionary<Type, Dictionary<object, object[]>> s_EnumFlagsCache;
private static readonly Dictionary<Type, object> s_EnumValuesCache;
private static readonly Dictionary<Type, Dictionary<int, object>> s_EnumFlagsCache;
/// <summary>
/// Static constructor.
/// </summary>
static EnumUtils()
{
s_EnumValuesCache = new Dictionary<Type, object[]>();
s_EnumFlagsCache = new Dictionary<Type, Dictionary<object, object[]>>();
s_EnumValuesCache = new Dictionary<Type, object>();
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
}
/// <summary>
/// Returns true if the given type is an enum.
/// </summary>
/// <returns></returns>
public static bool IsEnumType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return type.IsAssignableTo(typeof(Enum)) || type
#if !SIMPLSHARP
.GetTypeInfo()
#endif
.IsEnum;
}
#region Validation
/// <summary>
/// Returns true if the given type is an enum.
@@ -51,15 +36,58 @@ namespace ICD.Common.Utils
return IsEnumType(typeof(T));
}
/// <summary>
/// Returns true if the given type is an enum.
/// </summary>
/// <returns></returns>
public static bool IsEnumType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return type
#if !SIMPLSHARP
.GetTypeInfo()
#endif
.IsEnum || type.IsAssignableTo(typeof(Enum));
}
/// <summary>
/// Returns true if the given value is an enum.
/// </summary>
/// <returns></returns>
public static bool IsEnum(object value)
public static bool IsEnum<T>(T value)
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
return value != null && IsEnumType(value.GetType());
}
/// <summary>
/// Returns true if the given enum type has the Flags attribute set.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static bool IsFlagsEnum<T>()
{
return IsFlagsEnum(typeof(T));
}
/// <summary>
/// Returns true if the given enum type has the Flags attribute set.
/// </summary>
/// <returns></returns>
public static bool IsFlagsEnum(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return type
#if !SIMPLSHARP
.GetTypeInfo()
#endif
.IsDefined(typeof(FlagsAttribute), false);
}
/// <summary>
/// Returns true if the given value is defined as part of the given enum type.
/// </summary>
@@ -67,77 +95,82 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static bool IsDefined<T>(T value)
where T : struct, IConvertible
{
if (!IsEnumType(typeof(T)))
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
if (!IsFlagsEnum<T>())
return GetValues<T>().Any(v => v.Equals(value));
int valueInt = (int)GetUnderlyingValue(value);
// Check if all of the flag values are defined
foreach (T flag in GetFlags(value))
{
int flagInt = (int)GetUnderlyingValue(flag);
valueInt = valueInt - flagInt;
}
return valueInt == 0;
T all = GetFlagsAllValue<T>();
return HasFlags(all, value);
}
#region Values
/// <summary>
/// Gets the underlying value of the enum.
/// Returns true if the given value is defined as part of the given enum type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public static object GetUnderlyingValue<T>(T value)
public static bool IsDefined(Type type, int value)
{
if (!IsEnumType(typeof(T)))
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
if (type == null)
throw new ArgumentNullException("type");
#if SIMPLSHARP
return Convert.ChangeType(value, ToEnum(value).GetTypeCode(), CultureInfo.InvariantCulture);
#else
return Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
#endif
if (!IsEnumType(type))
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
int all = GetFlagsAllValue(type);
return HasFlags(all, value);
}
#endregion
#region Values
/// <summary>
/// Gets the values from an enumeration.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetValues<T>()
where T : struct, IConvertible
{
return GetValues(typeof(T)).Cast<T>();
}
/// <summary>
/// Gets the values from an enumeration.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<object> GetValues(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
Type type = typeof(T);
// Reflection is slow and this method is called a lot, so we cache the results.
if (!s_EnumValuesCache.ContainsKey(type))
s_EnumValuesCache[type] = GetValuesUncached(type).ToArray();
object cache;
if (!s_EnumValuesCache.TryGetValue(type, out cache))
{
cache = GetValuesUncached<T>().ToArray();
s_EnumValuesCache[type] = cache;
}
return s_EnumValuesCache[type];
return cache as T[];
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static IEnumerable<object> GetValuesUncached(Type type)
public static IEnumerable<int> GetValues(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValuesUncached(type);
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// </summary>
/// <returns></returns>
private static IEnumerable<T> GetValuesUncached<T>()
where T : struct, IConvertible
{
return GetValuesUncached(typeof(T)).Select(i => (T)(object)i);
}
/// <summary>
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
/// </summary>
/// <returns></returns>
private static IEnumerable<int> GetValuesUncached(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -152,20 +185,8 @@ namespace ICD.Common.Utils
.GetTypeInfo()
#endif
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Select(x => x.GetValue(null));
}
/// <summary>
/// Gets the 0 value for the given enum type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetNoneValue<T>()
{
if (!IsEnumType(typeof(T)))
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
return (T)(object)0;
.Select(x => x.GetValue(null))
.Cast<int>();
}
/// <summary>
@@ -174,27 +195,21 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetValuesExceptNone<T>()
where T : struct, IConvertible
{
if (!IsEnumType(typeof(T)))
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
return GetValuesExceptNone(typeof(T)).Cast<T>();
return GetFlagsExceptNone<T>();
}
/// <summary>
/// Gets the values from an enumeration except the 0 value.
/// Gets the values from an enumeration except the 0 value without performing any caching. This is slow because of reflection.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<object> GetValuesExceptNone(Type type)
public static IEnumerable<int> GetValuesExceptNone(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!IsEnumType(type))
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
return GetValues(type).Where(v => (int)v != 0);
return GetValues(type).Except(0);
}
#endregion
@@ -202,35 +217,55 @@ namespace ICD.Common.Utils
#region Flags
/// <summary>
/// Returns true if the given enum type has the Flags attribute set.
/// Excludes b from a.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool IsFlagsEnum<T>()
public static T ExcludeFlags<T>(T a, T b)
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
int aInt = (int)(object)a;
int bInt = (int)(object)b;
return IsFlagsEnum(typeof(T));
return (T)(object)ExcludeFlags(aInt, bInt);
}
/// <summary>
/// Returns true if the given enum type has the Flags attribute set.
/// Excludes b from a.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool IsFlagsEnum(Type type)
public static int ExcludeFlags(int a, int b)
{
if (type == null)
throw new ArgumentNullException("type");
return a & ~b;
}
if (!IsEnumType(type))
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
/// <summary>
/// Includes a and b.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static T IncludeFlags<T>(T a, T b)
where T : struct, IConvertible
{
int aInt = (int)(object)a;
int bInt = (int)(object)b;
return type
#if !SIMPLSHARP
.GetTypeInfo()
#endif
.IsDefined(typeof(FlagsAttribute), false);
return (T)(object)IncludeFlags(aInt, bInt);
}
/// <summary>
/// Includes a and b.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static int IncludeFlags(int a, int b)
{
return a | b;
}
/// <summary>
@@ -240,15 +275,34 @@ namespace ICD.Common.Utils
/// <param name="values"></param>
/// <returns></returns>
public static T GetFlagsIntersection<T>(params T[] values)
where T : struct, IConvertible
{
if (values == null)
throw new ArgumentNullException("values");
if (values.Length == 0)
return GetNoneValue<T>();
return default(T);
int output = (int)(object)values.First();
foreach (T item in values.Skip(1))
output &= (int)(object)item;
int output = 0;
bool first = true;
return (T)Enum.ToObject(typeof(T), output);
foreach (T value in values)
{
if (first)
{
output = (int)(object)value;
first = false;
}
else
{
output &= (int)(object)value;
}
if (output == 0)
return default(T);
}
return (T)(object)output;
}
/// <summary>
@@ -259,11 +313,12 @@ namespace ICD.Common.Utils
/// <param name="b"></param>
/// <returns></returns>
public static T GetFlagsIntersection<T>(T a, T b)
where T : struct, IConvertible
{
int aInt = (int)(object)a;
int bInt = (int)(object)b;
return (T)Enum.ToObject(typeof(T), aInt & bInt);
return (T)(object)(aInt & bInt);
}
/// <summary>
@@ -273,20 +328,38 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<T> GetFlags<T>(T value)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
Type type = typeof(T);
int valueInt = (int)(object)value;
if (!s_EnumFlagsCache.ContainsKey(type))
s_EnumFlagsCache.Add(type, new Dictionary<object, object[]>());
Dictionary<int, object> cache;
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
{
cache = new Dictionary<int, object>();
s_EnumFlagsCache[type] = cache;
}
if (!s_EnumFlagsCache[type].ContainsKey(value))
s_EnumFlagsCache[type][value] = GetValues<T>().Where(e => HasFlag(value, e)).Cast<object>().ToArray();
object flags;
if (!cache.TryGetValue(valueInt, out flags))
{
flags = GetValues<T>().Where(e => HasFlag(value, e)).ToArray();
cache[valueInt] = flags;
}
return s_EnumFlagsCache[type][value].Cast<T>();
return flags as T[];
}
/// <summary>
/// Gets all of the set flags on the given enum type except 0.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> GetFlagsExceptNone<T>()
where T : struct, IConvertible
{
T allValue = GetFlagsAllValue<T>();
return GetFlagsExceptNone(allValue);
}
/// <summary>
@@ -296,13 +369,9 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<T> GetFlagsExceptNone<T>(T value)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
T none = GetNoneValue<T>();
return GetFlags(value).Except(none);
return GetFlags(value).Except(default(T));
}
/// <summary>
@@ -315,12 +384,9 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<T> GetAllFlagCombinationsExceptNone<T>(T value)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1 ;
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1;
return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v));
}
@@ -330,14 +396,25 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetFlagsAllValue<T>()
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
int output = GetValues<T>().Aggregate(0, (current, value) => current | (int)(object)value);
return (T)Enum.ToObject(typeof(T), output);
}
/// <summary>
/// Gets an enum value of the given type with every flag set.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static int GetFlagsAllValue(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return GetValuesUncached(type).Aggregate(0, (current, value) => current | value);
}
/// <summary>
/// Returns true if the enum contains the given flag.
/// </summary>
@@ -346,16 +423,20 @@ namespace ICD.Common.Utils
/// <param name="flag"></param>
/// <returns></returns>
public static bool HasFlag<T>(T value, T flag)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return HasFlags(value, flag);
}
if (!IsEnum(flag))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", flag == null ? "NULL" : flag.GetType().Name), "flag");
return ToEnum(value).HasFlag(ToEnum(flag));
/// <summary>
/// Returns true if the enum contains the given flag.
/// </summary>
/// <param name="value"></param>
/// <param name="flag"></param>
/// <returns></returns>
public static bool HasFlag(int value, int flag)
{
return HasFlags(value, flag);
}
/// <summary>
@@ -366,16 +447,23 @@ namespace ICD.Common.Utils
/// <param name="flags"></param>
/// <returns></returns>
public static bool HasFlags<T>(T value, T flags)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
int a = (int)(object)value;
int b = (int)(object)flags;
if (!IsEnum(flags))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", flags == null ? "NULL" : flags.GetType().Name), "flags");
return HasFlags(a, b);
}
return ToEnum(value).HasFlags(ToEnum(flags));
/// <summary>
/// Returns true if the enum contains the given flag.
/// </summary>
/// <param name="value"></param>
/// <param name="flags"></param>
/// <returns></returns>
public static bool HasFlags(int value, int flags)
{
return (value & flags) == flags;
}
/// <summary>
@@ -385,12 +473,11 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static bool HasSingleFlag<T>(T value)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
int numeric = (int)(object)value;
return (int)(object)value != (int)(object)GetNoneValue<T>() && !HasMultipleFlags(value);
return HasAnyFlags(numeric) && !HasMultipleFlags(numeric);
}
/// <summary>
@@ -399,11 +486,8 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
public static bool HasMultipleFlags<T>(T value)
where T : struct, IConvertible
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return HasMultipleFlags((int)(object)value);
}
@@ -415,7 +499,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static bool HasMultipleFlags(int value)
{
return ((value & (value - 1)) != 0);
return (value & (value - 1)) != 0;
}
/// <summary>
@@ -425,8 +509,20 @@ namespace ICD.Common.Utils
/// <returns></returns>
[PublicAPI]
public static bool HasAnyFlags<T>(T value)
where T : struct, IConvertible
{
return GetFlagsExceptNone(value).Any();
return HasAnyFlags((int)(object)value);
}
/// <summary>
/// Returns true if the enum has any flags set.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI]
public static bool HasAnyFlags(int value)
{
return value > 0;
}
/// <summary>
@@ -437,6 +533,7 @@ namespace ICD.Common.Utils
/// <returns></returns>
[PublicAPI]
public static bool HasAnyFlags<T>(T value, T other)
where T : struct, IConvertible
{
T intersection = GetFlagsIntersection(value, other);
return HasAnyFlags(intersection);
@@ -454,10 +551,8 @@ namespace ICD.Common.Utils
/// <param name="ignoreCase"></param>
/// <returns></returns>
public static T Parse<T>(string data, bool ignoreCase)
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
T output;
if (TryParse(data, ignoreCase, out output))
return output;
@@ -466,6 +561,26 @@ namespace ICD.Common.Utils
throw new FormatException(message);
}
/// <summary>
/// Shorthand for parsing string to enum.
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
public static int Parse(Type type, string data, bool ignoreCase)
{
if (type == null)
throw new ArgumentNullException("type");
int output;
if (TryParse(type, data, ignoreCase, out output))
return output;
string message = string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name);
throw new FormatException(message);
}
/// <summary>
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
/// </summary>
@@ -475,10 +590,8 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
public static bool TryParse<T>(string data, bool ignoreCase, out T result)
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
result = default(T);
try
@@ -492,6 +605,32 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
/// <param name="ignoreCase"></param>
/// <param name="result"></param>
/// <returns></returns>
public static bool TryParse(Type type, string data, bool ignoreCase, out int result)
{
if (type == null)
throw new ArgumentNullException("type");
result = 0;
try
{
result = (int)Enum.Parse(type, data, ignoreCase);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Shorthand for parsing string to enum.
/// Will fail if the resulting value is not defined as part of the enum.
@@ -501,10 +640,8 @@ namespace ICD.Common.Utils
/// <param name="ignoreCase"></param>
/// <returns></returns>
public static T ParseStrict<T>(string data, bool ignoreCase)
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
T output;
try
@@ -523,6 +660,37 @@ namespace ICD.Common.Utils
return output;
}
/// <summary>
/// Shorthand for parsing string to enum.
/// Will fail if the resulting value is not defined as part of the enum.
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
public static int ParseStrict(Type type, string data, bool ignoreCase)
{
if (type == null)
throw new ArgumentNullException("type");
int output;
try
{
output = Parse(type, data, ignoreCase);
}
catch (Exception e)
{
throw new FormatException(
string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name), e);
}
if (!IsDefined(type, output))
throw new ArgumentOutOfRangeException(string.Format("{0} is not a valid {1}", output, type.Name));
return output;
}
/// <summary>
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
/// Will fail if the resulting value is not defined as part of the enum.
@@ -533,10 +701,8 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
public static bool TryParseStrict<T>(string data, bool ignoreCase, out T result)
where T : struct, IConvertible
{
if (!IsEnumType<T>())
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
result = default(T);
try
@@ -550,21 +716,6 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Converts the given enum value to an Enum.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static Enum ToEnum<T>(T value)
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return (Enum)(object)value;
}
#endregion
}
}

View File

@@ -6,7 +6,17 @@
/// Constructor.
/// </summary>
/// <param name="data"></param>
public BoolEventArgs(bool data) : base(data)
public BoolEventArgs(bool data)
: base(data)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="eventArgs"></param>
public BoolEventArgs(BoolEventArgs eventArgs)
: this(eventArgs.Data)
{
}
}

View File

@@ -9,5 +9,14 @@
public StringEventArgs(string data) : base(data)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="eventArgs"></param>
public StringEventArgs(StringEventArgs eventArgs)
: base(eventArgs.Data)
{
}
}
}

View File

@@ -0,0 +1,10 @@
namespace ICD.Common.Utils.Extensions
{
public static class BoolExtensions
{
public static ushort ToUShort(this bool b)
{
return b ? (ushort)1 : (ushort)0;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
namespace ICD.Common.Utils.Extensions
{
@@ -25,11 +26,42 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
public static string ToLongTimeStringWithMilliseconds(this DateTime extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
// Todo - Better handle different cultures
return extends.ToString("HH:mm:ss:fff");
}
/// <summary>
/// Returns the closest DateTime to the target time that is greater than the target time
/// </summary>
/// <param name="target"></param>
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, params DateTime[] times)
{
if (times == null)
throw new ArgumentNullException("times");
DateTime earliestTime;
bool success = times.OrderBy(dt => dt).TryFirst(dt => inclusive ? target <= dt : target < dt, out earliestTime);
return success ? earliestTime : (DateTime?) null;
}
/// <summary>
/// Returns the closest DateTime to the target time that is less than the target time
/// </summary>
/// <param name="target"></param>
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
/// <param name="times"></param>
/// <returns></returns>
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, params DateTime[] times)
{
if (times == null)
throw new ArgumentNullException("null");
DateTime latestTime;
bool success = times.OrderByDescending(dt => dt).TryFirst(dt => inclusive ? target >= dt : target > dt, out latestTime);
return success ? latestTime : (DateTime?) null;
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace ICD.Common.Utils.Extensions
{
public static class DayOfWeekExtensions
{
public static bool IsWeekday(this DayOfWeek day)
{
return !IsWeekend(day);
}
public static bool IsWeekend(this DayOfWeek day)
{
return day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;
}
}
}

View File

@@ -66,6 +66,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="key"></param>
/// <returns></returns>
[CanBeNull]
[PublicAPI]
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
{
@@ -98,7 +99,8 @@ namespace ICD.Common.Utils.Extensions
if (key == null)
throw new ArgumentNullException("key");
return extends.ContainsKey(key) ? extends[key] : defaultValue;
TValue value;
return extends.TryGetValue(key, out value) ? value : defaultValue;
}
/// <summary>
@@ -110,6 +112,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="key"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
[CanBeNull]
[PublicAPI]
public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key,
TValue defaultValue)
@@ -121,8 +124,10 @@ namespace ICD.Common.Utils.Extensions
if (key == null)
throw new ArgumentNullException("key");
extends[key] = extends.GetDefault(key, defaultValue);
return extends[key];
TValue value = extends.GetDefault(key, defaultValue);
extends[key] = value;
return value;
}
/// <summary>
@@ -229,7 +234,8 @@ namespace ICD.Common.Utils.Extensions
foreach (KeyValuePair<TKey, TValue> pair in other)
{
if (extends.ContainsKey(pair.Key) && comparer.Equals(pair.Value, extends[pair.Key]))
TValue value;
if (extends.TryGetValue(pair.Key, out value) && comparer.Equals(pair.Value, value))
continue;
extends[pair.Key] = pair.Value;
@@ -239,6 +245,26 @@ namespace ICD.Common.Utils.Extensions
return change;
}
/// <summary>
/// Adds the sequence of items to the dictionary.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="items"></param>
[PublicAPI]
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<KeyValuePair<TKey, TValue>> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (items == null)
throw new ArgumentNullException("items");
foreach (KeyValuePair<TKey, TValue> item in items)
extends.Add(item);
}
/// <summary>
/// Adds the sequence of items to the dictionary.
/// </summary>
@@ -430,12 +456,26 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Dictionary<TValue, TKey> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
Dictionary<TValue, List<TKey>> output = new Dictionary<TValue, List<TKey>>();
foreach (KeyValuePair<TKey, TValue> kvp in extends)
{
List<TKey> keys;
if (!output.TryGetValue(kvp.Value, out keys))
{
keys = new List<TKey>();
output.Add(kvp.Value, keys);
}
keys.Add(kvp.Key);
}
return output;
}
}
}

View File

@@ -12,45 +12,36 @@ namespace ICD.Common.Utils.Extensions
/// <param name="value">Flag to check for</param>
/// <returns></returns>
[PublicAPI]
public static bool HasFlag(this Enum extends, Enum value)
public static bool HasFlag<T>(this T extends, T value)
where T : struct, IConvertible
{
if (extends == null)
throw new ArgumentNullException("extends");
if (value == null)
throw new ArgumentNullException("value");
if (EnumUtils.HasMultipleFlags(value))
throw new ArgumentException("Value has multiple flags", "value");
return extends.HasFlags(value);
return EnumUtils.HasFlag(extends, value);
}
/// <summary>
/// Check to see if a flags enumeration has all of the given flags set.
/// </summary>
/// <param name="extends"></param>
/// <param name="value"></param>
/// <param name="values"></param>
/// <returns></returns>
[PublicAPI]
public static bool HasFlags(this Enum extends, Enum value)
public static bool HasFlags<T>(this T extends, T values)
where T : struct, IConvertible
{
if (extends == null)
throw new ArgumentNullException("extends");
return EnumUtils.HasFlags(extends, values);
}
if (value == null)
throw new ArgumentNullException("value");
// Not as good as the .NET 4 version of this function, but should be good enough
if (extends.GetType() != value.GetType())
{
string message = string.Format("Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
value.GetType(), extends.GetType());
throw new ArgumentException(message);
}
int num = (int)(object)value;
return ((int)(object)extends & num) == num;
/// <summary>
/// Returns the enum value as a
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI("S+")]
public static ushort ToUShort<T>(this T extends)
where T : struct, IConvertible
{
return (ushort)(object)extends;
}
}
}

View File

@@ -42,15 +42,8 @@ namespace ICD.Common.Utils.Extensions
if (predicate == null)
throw new ArgumentNullException("predicate");
T output = defaultItem;
foreach (T item in extends.Where(predicate))
{
output = item;
break;
}
return output;
T output;
return extends.TryFirst(predicate, out output) ? output : defaultItem;
}
/// <summary>
@@ -65,7 +58,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return extends.TryFirst(i => true, out item);
IList<T> list = extends as IList<T>;
if (list == null)
return extends.TryFirst(i => true, out item);
item = default(T);
if (list.Count <= 0)
return false;
item = list[0];
return true;
}
/// <summary>
@@ -113,7 +117,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return extends.TryLast(i => true, out item);
IList<T> list = extends as IList<T>;
if (list == null)
return extends.TryLast(i => true, out item);
item = default(T);
if (list.Count <= 0)
return false;
item = list[list.Count - 1];
return true;
}
/// <summary>
@@ -164,25 +179,72 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("extends");
if (index < 0)
throw new ArgumentException("Index must be greater or equal to 0", "index");
throw new ArgumentOutOfRangeException("index");
item = default(T);
int eachIndex = 0;
foreach (T each in extends)
IList<T> list = extends as IList<T>;
if (list != null)
{
if (eachIndex == index)
if (index >= list.Count)
return false;
item = list[index];
return true;
}
int current = 0;
foreach (T value in extends)
{
if (current == index)
{
item = each;
item = value;
return true;
}
eachIndex++;
current++;
}
return false;
}
/// <summary>
/// Gets the element at the given index. Returns the specified default value if the index does not exist.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="index"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static T ElementAtOrDefault<T>(this IEnumerable<T> extends, int index, T defaultValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
T output;
return extends.TryElementAt(index, out output) ? output : defaultValue;
}
/// <summary>
/// Compares the two sequences for identical values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static bool SequenceEqual<T>(this IEnumerable<T> extends, IEnumerable<T> other, IEqualityComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
return extends.SequenceEqual(other, comparer.Equals);
}
/// <summary>
/// Compares the two sequences for identical values.
/// </summary>
@@ -196,12 +258,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (extends == null)
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
// Simple count check
ICollection<T> a = extends as ICollection<T>;
ICollection<T> b = other as ICollection<T>;
if (a != null && b != null && a.Count != b.Count)
return false;
using (IEnumerator<T> firstPos = extends.GetEnumerator())
{
using (IEnumerator<T> secondPos = other.GetEnumerator())
@@ -235,7 +303,7 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (extends == null)
if (other == null)
throw new ArgumentNullException("other");
return extends.ScrambledEquals(other, EqualityComparer<T>.Default);
@@ -254,12 +322,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (extends == null)
if (other == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
// Simple count check
ICollection<T> a = extends as ICollection<T>;
ICollection<T> b = other as ICollection<T>;
if (a != null && b != null && a.Count != b.Count)
return false;
Dictionary<T, int> count = new Dictionary<T, int>(comparer);
foreach (T item in extends)
@@ -304,9 +378,21 @@ namespace ICD.Common.Utils.Extensions
if (match == null)
throw new ArgumentNullException("match");
return FindIndicesIterator(extends, match);
}
/// <summary>
/// Returns the indices that match the predicate.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="match"></param>
/// <returns></returns>
private static IEnumerable<int> FindIndicesIterator<T>(IEnumerable<T> sequence, Predicate<T> match)
{
int index = 0;
foreach (T item in extends)
foreach (T item in sequence)
{
if (match(item))
yield return index;
@@ -346,7 +432,11 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
extends.ForEach(item => { });
// ReSharper disable UnusedVariable
foreach (T item in extends)
// ReSharper restore UnusedVariable
{
}
}
/// <summary>
@@ -400,8 +490,21 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return PrependIterator(extends, item);
}
/// <summary>
/// Prepends the item to the start of the sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="item"></param>
/// <returns></returns>
private static IEnumerable<T> PrependIterator<T>(IEnumerable<T> sequence, T item)
{
yield return item;
foreach (T next in extends)
foreach (T next in sequence)
yield return next;
}
#endif
@@ -422,9 +525,22 @@ namespace ICD.Common.Utils.Extensions
if (items == null)
throw new ArgumentNullException("items");
return PrependManyIterator(extends, items);
}
/// <summary>
/// Prepends the items to the start of the sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="items"></param>
/// <returns></returns>
private static IEnumerable<T> PrependManyIterator<T>(IEnumerable<T> sequence, params T[] items)
{
foreach (T item in items)
yield return item;
foreach (T each in extends)
foreach (T each in sequence)
yield return each;
}
@@ -441,8 +557,21 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
foreach (T first in extends)
return AppendIterator(extends, item);
}
/// <summary>
/// Appends the item to the end of the sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="item"></param>
/// <returns></returns>
private static IEnumerable<T> AppendIterator<T>(IEnumerable<T> sequence, T item)
{
foreach (T first in sequence)
yield return first;
yield return item;
}
#endif
@@ -463,8 +592,21 @@ namespace ICD.Common.Utils.Extensions
if (items == null)
throw new ArgumentNullException("items");
foreach (T each in extends)
return AppendManyIterator(extends, items);
}
/// <summary>
/// Appends the items to the end of the sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="items"></param>
/// <returns></returns>
private static IEnumerable<T> AppendManyIterator<T>(IEnumerable<T> sequence, params T[] items)
{
foreach (T each in sequence)
yield return each;
foreach (T item in items)
yield return item;
}
@@ -482,9 +624,21 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return PadRightIterator(extends, count);
}
/// <summary>
/// Pads the given sequence to the given count size with default items.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="count"></param>
/// <returns></returns>
private static IEnumerable<T> PadRightIterator<T>(IEnumerable<T> sequence, int count)
{
int index = 0;
foreach (T item in extends)
foreach (T item in sequence)
{
yield return item;
index++;
@@ -515,7 +669,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> extends, IComparer<T> comparer)
public static IOrderedEnumerable<T> Order<T>(this IEnumerable<T> extends, IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -533,7 +687,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> extends, IComparer<T> comparer)
public static IOrderedEnumerable<T> OrderDescending<T>(this IEnumerable<T> extends, IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -591,8 +745,22 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
ICollection<T> collection = extends as ICollection<T> ?? extends.ToArray();
collection.CopyTo(array, index);
if (array == null)
throw new ArgumentNullException("array");
ICollection<T> collection = extends as ICollection<T>;
if (collection != null)
{
collection.CopyTo(array, index);
return;
}
int current = 0;
foreach (T item in extends)
{
array[index + current] = item;
current++;
}
}
/// <summary>
@@ -606,7 +774,25 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return new IcdHashSet<T>(extends);
return extends.ToIcdHashSet(EqualityComparer<T>.Default);
}
/// <summary>
/// Returns the sequence as a IcdHashSet.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static IcdHashSet<T> ToIcdHashSet<T>(this IEnumerable<T> extends, IEqualityComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (comparer == null)
throw new ArgumentNullException("comparer");
return new IcdHashSet<T>(comparer, extends);
}
/// <summary>
@@ -659,7 +845,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> extends)
public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -676,7 +862,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static Dictionary<uint, T> ToDictionaryUInt<T>(this IEnumerable<T> extends)
public static Dictionary<uint, T> ToIndexedDictionaryUInt<T>(this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -750,6 +936,28 @@ namespace ICD.Common.Utils.Extensions
return extends.Distinct(new PredicateEqualityComparer<TItem, TProperty>(propertyComparer, getProperty));
}
/// <summary>
/// Returns a random item from the given sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T Random<T>(this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
IList<T> sequence = extends as IList<T> ?? extends.ToArray();
if (sequence.Count == 0)
throw new InvalidOperationException("Sequence is empty.");
Random random = new Random(Guid.NewGuid().GetHashCode());
int index = random.Next(0, sequence.Count);
return sequence[index];
}
/// <summary>
/// Returns other if the sequence is empty.
/// Returns other if the sequence is non-empty and there are two different elements.
@@ -764,8 +972,8 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
T[] array = extends.Distinct().ToArray();
return array.Length == 1 ? array[0] : other;
T item;
return extends.Unanimous(out item) ? item : other;
}
/// <summary>
@@ -774,14 +982,57 @@ namespace ICD.Common.Utils.Extensions
/// Returns true if the sequence is non-empty and all elements are the same.
/// </summary>
/// <param name="extends"></param>
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool Unanimous<T>(this IEnumerable<T> extends)
public static bool Unanimous<T>(this IEnumerable<T> extends, out T result)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Distinct().Count() == 1;
return extends.Unanimous(EqualityComparer<T>.Default, out result);
}
/// <summary>
/// Returns false if the sequence is empty.
/// Returns false if the sequence is non-empty and there are two different elements.
/// Returns true if the sequence is non-empty and all elements are the same.
/// </summary>
/// <param name="extends"></param>
/// <param name="comparer"></param>
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool Unanimous<T>(this IEnumerable<T> extends, IEqualityComparer<T> comparer, out T result)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (comparer == null)
throw new ArgumentNullException("comparer");
result = default(T);
T output = default(T);
bool empty = true;
foreach (T entry in extends)
{
if (empty)
{
empty = false;
output = entry;
continue;
}
if (!comparer.Equals(entry, output))
return false;
}
if (empty)
return false;
result = output;
return true;
}
/// <summary>
@@ -796,7 +1047,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
using (IEnumerator<T> enumerator = extends.GetEnumerator())
return PartitionIterator(extends, partitionSize);
}
/// <summary>
/// Partitions a sequence into sequences of the given length.
/// </summary>
/// <param name="sequence"></param>
/// <param name="partitionSize"></param>
/// <returns></returns>
private static IEnumerable<IEnumerable<T>> PartitionIterator<T>(IEnumerable<T> sequence, int partitionSize)
{
using (IEnumerator<T> enumerator = sequence.GetEnumerator())
{
while (enumerator.MoveNext())
yield return YieldBatchElements(enumerator, partitionSize - 1);
@@ -818,6 +1080,17 @@ namespace ICD.Common.Utils.Extensions
return output;
}
/// <summary>
/// Wraps this object instance into an IEnumerable consisting of a single item.
/// </summary>
/// <typeparam name="T">Type of the object.</typeparam>
/// <param name="item">The instance that will be wrapped.</param>
/// <returns>An IEnumerable&lt;T&gt; consisting of a single item.</returns>
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
/// <summary>
/// Given a sequence [A, B, C] returns a sequence [[A, B], [B, C]]
/// </summary>
@@ -825,6 +1098,20 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<T[]> GetAdjacentPairs<T>(this IEnumerable<T> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return GetAdjacentPairsIterator(extends);
}
/// <summary>
/// Given a sequence [A, B, C] returns a sequence [[A, B], [B, C]]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
private static IEnumerable<T[]> GetAdjacentPairsIterator<T>(IEnumerable<T> extends)
{
T previous = default(T);
bool first = true;
@@ -832,7 +1119,7 @@ namespace ICD.Common.Utils.Extensions
foreach (T item in extends)
{
if (!first)
yield return new[] {previous, item};
yield return new[] { previous, item };
first = false;
previous = item;
@@ -916,18 +1203,22 @@ namespace ICD.Common.Utils.Extensions
{
if (!sourceIterator.MoveNext())
throw new InvalidOperationException("Sequence contains no elements");
TSource min = sourceIterator.Current;
TKey minKey = selector(min);
while (sourceIterator.MoveNext())
{
TSource candidate = sourceIterator.Current;
TKey candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) >= 0)
continue;
min = candidate;
minKey = candidateProjected;
}
return min;
}
}
@@ -1001,16 +1292,33 @@ namespace ICD.Common.Utils.Extensions
if (comparer == null)
throw new ArgumentNullException("comparer");
return ConsolidateIterator(extends, comparer);
}
/// <summary>
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sequence"></param>
/// <param name="comparer"></param>
/// <returns></returns>
private static IEnumerable<T> ConsolidateIterator<T>(IEnumerable<T> sequence, IComparer<T> comparer)
{
bool first = true;
T last = default(T);
foreach (T item in extends)
foreach (T item in sequence)
{
if (!first && comparer.Compare(last, item) == 0)
continue;
first = false;
last = item;
yield return item;
}
}
@@ -1125,23 +1433,40 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="TFirst"></typeparam>
/// <typeparam name="TSecond"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="callback"></param>
/// <returns></returns>
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> extends,
IEnumerable<TSecond> other,
Func<TFirst, TSecond, TResult> callback)
{
if (first == null)
throw new ArgumentNullException("first");
if (extends == null)
throw new ArgumentNullException("extends");
if (second == null)
throw new ArgumentNullException("second");
if (other == null)
throw new ArgumentNullException("other");
if (callback == null)
throw new ArgumentNullException("callback");
return ZipIterator(extends, other, callback);
}
/// <summary>
/// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
/// </summary>
/// <typeparam name="TFirst"></typeparam>
/// <typeparam name="TSecond"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <param name="callback"></param>
/// <returns></returns>
private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> callback)
{
using (IEnumerator<TFirst> enumerator1 = first.GetEnumerator())
{
using (IEnumerator<TSecond> enumerator2 = second.GetEnumerator())

View File

@@ -1,4 +1,9 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
namespace ICD.Common.Utils.Extensions
{
@@ -34,5 +39,22 @@ namespace ICD.Common.Utils.Extensions
if (extends != null)
extends(sender, args);
}
/// <summary>
/// Cross-platform shim for getting MethodInfo for the delegate.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static MethodInfo GetMethodInfo(this Delegate extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
#if SIMPLSHARP
return extends.GetMethod();
#else
return extends.Method;
#endif
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -154,12 +155,14 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
[PublicAPI]
public static T GetValueAsEnum<T>(this JsonReader extends)
where T : struct, IConvertible
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.TokenType == JsonToken.String)
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
return (T)(object)extends.GetValueAsInt();
}
@@ -252,17 +255,30 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("read");
if (reader.TokenType == JsonToken.Null)
yield break;
return Enumerable.Empty<TItem>();
if (reader.TokenType != JsonToken.StartArray)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
return DeserializeArrayIterator(extends, reader, read);
}
/// <summary>
/// Deserializes an array of items from the reader's current value.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="serializer"></param>
/// <param name="reader"></param>
/// <param name="read"></param>
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
Func<JsonSerializer, JsonReader, TItem> read)
{
// Step into the first value
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
TItem output = read(extends, reader);
TItem output = read(serializer, reader);
yield return output;
// Read out of the last value
@@ -308,7 +324,8 @@ namespace ICD.Common.Utils.Extensions
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="reader"></param>
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends, JsonReader reader)
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends,
JsonReader reader)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -317,11 +334,24 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("reader");
if (reader.TokenType == JsonToken.Null)
yield break;
return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject, reader.TokenType));
return DeserializeDictionaryIterator<TKey, TValue>(extends, reader);
}
/// <summary>
/// Deserializes a dictionary of items from the reader's current value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="serializer"></param>
/// <param name="reader"></param>
private static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionaryIterator<TKey, TValue>(
JsonSerializer serializer, JsonReader reader)
{
// Step into the first key
reader.Read();
@@ -332,7 +362,7 @@ namespace ICD.Common.Utils.Extensions
// Step into the value
reader.Read();
TValue value = extends.Deserialize<TValue>(reader);
TValue value = serializer.Deserialize<TValue>(reader);
yield return new KeyValuePair<TKey, TValue>(key, value);

View File

@@ -82,12 +82,12 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param>
/// <param name="item"></param>
[PublicAPI]
public static void AddSorted<T>(this List<T> extends, T item)
public static int AddSorted<T>(this List<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.AddSorted(item, Comparer<T>.Default);
return extends.AddSorted(item, Comparer<T>.Default);
}
/// <summary>
@@ -98,7 +98,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="comparer"></param>
[PublicAPI]
public static void AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer)
public static int AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -106,29 +106,13 @@ namespace ICD.Common.Utils.Extensions
if (comparer == null)
throw new ArgumentNullException("comparer");
if (extends.Count == 0)
{
extends.Add(item);
return;
}
if (comparer.Compare(extends[extends.Count - 1], item) <= 0)
{
extends.Add(item);
return;
}
if (comparer.Compare(extends[0], item) >= 0)
{
extends.Insert(0, item);
return;
}
int index = extends.BinarySearch(item, comparer);
if (index < 0)
index = ~index;
extends.Insert(index, item);
return index;
}
/// <summary>
@@ -140,7 +124,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param>
/// <param name="predicate"></param>
[PublicAPI]
public static void AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate)
public static int AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -149,7 +133,7 @@ namespace ICD.Common.Utils.Extensions
throw new ArgumentNullException("predicate");
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
extends.AddSorted(item, comparer);
return extends.AddSorted(item, comparer);
}
/// <summary>

View File

@@ -0,0 +1,127 @@
using System;
using System.Text;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
namespace ICD.Common.Utils.Extensions
{
public static class MethodInfoExtensions
{
/// <summary>
/// Return the method signature as a string.
/// </summary>
/// <param name="method">The Method</param>
/// <returns>Method signature</returns>
public static string GetSignature(this MethodInfo method)
{
return method.GetSignature(false);
}
/// <summary>
/// Return the method signature as a string.
/// </summary>
/// <param name="method">The Method</param>
/// <param name="callable">Return as a callable string(public void a(string b) would return a(b))</param>
/// <returns>Method signature</returns>
public static string GetSignature(this MethodInfo method, bool callable)
{
bool firstParam = true;
StringBuilder sigBuilder = new StringBuilder();
if (callable == false)
{
if (method.IsPublic)
sigBuilder.Append("public ");
else if (method.IsPrivate)
sigBuilder.Append("private ");
else if (method.IsAssembly)
sigBuilder.Append("internal ");
if (method.IsFamily)
sigBuilder.Append("protected ");
if (method.IsStatic)
sigBuilder.Append("static ");
Type returnType = method.ReturnType;
sigBuilder.Append(returnType.GetSyntaxName());
sigBuilder.Append(' ');
}
sigBuilder.Append(method.Name);
// Add method generics
if (method.IsGenericMethod)
{
sigBuilder.Append("<");
foreach (Type g in method.GetGenericArguments())
{
if (firstParam)
firstParam = false;
else
sigBuilder.Append(", ");
sigBuilder.Append(g.GetSyntaxName());
}
sigBuilder.Append(">");
}
sigBuilder.Append("(");
firstParam = true;
#if !SIMPLSHARP
bool secondParam = false;
#endif
foreach (ParameterInfo param in method.GetParameters())
{
if (firstParam)
{
firstParam = false;
#if SIMPLSHARP
// TODO - RestrictionViolationException: System.Runtime.CompilerServices.ExtensionAttribute - Not allowed due to restrictions
#else
if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false))
{
if (callable)
{
secondParam = true;
continue;
}
sigBuilder.Append("this ");
}
}
else if (secondParam)
{
secondParam = false;
#endif
}
else
sigBuilder.Append(", ");
if (param.ParameterType.IsByRef)
sigBuilder.Append("ref ");
else if (param.GetIsOut())
sigBuilder.Append("out ");
if (!callable)
{
Type parameterType = param.ParameterType;
sigBuilder.Append(parameterType.GetSyntaxName());
sigBuilder.Append(' ');
}
sigBuilder.Append(param.Name);
}
sigBuilder.Append(")");
return sigBuilder.ToString();
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
namespace ICD.Common.Utils.Extensions
{
public static class ParameterInfoExtensions
{
/// <summary>
/// Returns true if the given parameter is an "out" parameter.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool GetIsOut(this ParameterInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
#if SIMPLSHARP
return extends.Attributes.HasFlag(ParameterAttributes.Out);
#else
return extends.IsOut;
#endif
}
}
}

View File

@@ -37,7 +37,6 @@ namespace ICD.Common.Utils.Extensions
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributes<T>(this ICustomAttributeProvider extends, bool inherits)
where T : Attribute
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -83,5 +82,48 @@ namespace ICD.Common.Utils.Extensions
return extends.GetCustomAttributes<T>(inherits).First();
}
#if NETSTANDARD
/// <summary>
/// Returns the custom attributes attached to the member.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this Type extends)
where T : Attribute
{
return extends.GetCustomAttributes<T>(true)
.Union(extends.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetCustomAttributes<T>(true)))
.Distinct();
}
/// <summary>
/// Returns the custom attributes attached to the member.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="inherits"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this MemberInfo extends)
where T : Attribute
{
return extends.GetCustomAttributes<T>(true)
.Union(extends.DeclaringType?
.GetInterfaces()
.SelectMany(interfaceType => interfaceType
.GetMember(
extends.Name,
extends.MemberType,
BindingFlags.Instance)
.FirstOrDefault()?
.GetCustomAttributes<T>(true) ?? Enumerable.Empty<T>())?
.Except(null) ?? Enumerable.Empty<T>())
.Distinct();
}
#endif
}
}

View File

@@ -37,6 +37,9 @@ namespace ICD.Common.Utils.Extensions
index = thisIndex;
first = item;
if (index == 0)
break;
}
return index;
@@ -54,7 +57,7 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return extends.StartsWith(character.ToString());
return extends.Length > 0 && character == extends[0];
}
/// <summary>
@@ -69,7 +72,7 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return extends.EndsWith(character.ToString());
return extends.Length > 0 && character == extends[extends.Length - 1];
}
/// <summary>
@@ -88,26 +91,31 @@ namespace ICD.Common.Utils.Extensions
if (count < 1)
throw new ArgumentException("Value must be greater or equal to 1", "count");
if (count < 2)
return SplitIterator(extends, delimeter, count);
}
/// <summary>
/// Splits the string by the given delimiter, returning up to the given number of substrings.
/// E.g. "a:b:c".Split(':', 2) returns ["a", "b:c"]
/// </summary>
/// <param name="value"></param>
/// <param name="delimeter"></param>
/// <param name="count"></param>
/// <returns></returns>
private static IEnumerable<string> SplitIterator(string value, char delimeter, int count)
{
while (count > 1)
{
yield return extends;
yield break;
int index = value.IndexOf(delimeter);
if (index < 0)
break;
yield return value.Substring(0, index);
value = value.Substring(index + 1);
count--;
}
int index = extends.IndexOf(delimeter);
if (index < 0)
{
yield return extends;
yield break;
}
string first = extends.Substring(0, index);
string second = extends.Substring(index + 1);
count--;
yield return first;
foreach (string item in second.Split(delimeter, count))
yield return item;
yield return value;
}
/// <summary>
@@ -129,86 +137,6 @@ namespace ICD.Common.Utils.Extensions
.Select(i => extends.Substring(i * chunkSize, Math.Min(chunkSize, extends.Length - (i * chunkSize))));
}
/// <summary>
/// Splits a string by a given substring.
/// Taken from
/// https://social.msdn.microsoft.com/Forums/en-US/914a350f-e0e9-45e0-91a4-6b4b2168e780/string-split-function
/// </summary>
/// <param name="extends"></param>
/// <param name="delimeter"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<string> Split(this string extends, string delimeter)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (delimeter == null)
throw new ArgumentNullException("delimeter");
int dSum = 0;
int sSum = 0;
int length = extends.Length;
int delimiterLength = delimeter.Length;
if (delimiterLength == 0 || length == 0 || length < delimiterLength)
{
yield return extends;
yield break;
}
char[] cd = delimeter.ToCharArray();
char[] cs = extends.ToCharArray();
for (int i = 0; i < delimiterLength; i++)
{
dSum += cd[i];
sSum += cs[i];
}
int start = 0;
for (int i = start; i < length - delimiterLength; i++)
{
if (i >= start && dSum == sSum && extends.Substring(i, delimiterLength) == delimeter)
{
yield return extends.Substring(start, i - start);
start = i + delimiterLength;
}
sSum += cs[i + delimiterLength] - cs[i];
}
if (dSum == sSum && extends.Substring(length - delimiterLength, delimiterLength) == delimeter)
{
yield return extends.Substring(start, length - delimiterLength - start);
yield return string.Empty;
}
else
yield return extends.Substring(start, length - start);
}
/// <summary>
/// Splits a string by the given substrings.
/// </summary>
/// <param name="extends"></param>
/// <param name="delimeters"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<string> Split(this string extends, IEnumerable<string> delimeters)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (delimeters == null)
throw new ArgumentNullException("delimeters");
string[] delimitersArray = delimeters as string[] ?? delimeters.ToArray();
return delimitersArray.Length == 0
? new[] {extends}
: extends.Split(delimitersArray.First())
.SelectMany(s => s.Split(delimitersArray.Skip(1)));
}
/// <summary>
/// Removes whitespace from the string.
/// </summary>
@@ -224,7 +152,31 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Removes the given characters from the string.
/// Removes all occurances of the given string.
/// </summary>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <returns></returns>
public static string Remove(this string extends, string other)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (other == null)
throw new ArgumentNullException("other");
if (string.IsNullOrEmpty(other))
return extends;
int index;
while ((index = extends.IndexOf(other, StringComparison.Ordinal)) >= 0)
extends = extends.Remove(index, other.Length);
return extends;
}
/// <summary>
/// Removes all occurances the given characters from the string.
/// </summary>
/// <param name="extends"></param>
/// <param name="characters"></param>
@@ -237,8 +189,9 @@ namespace ICD.Common.Utils.Extensions
if (characters == null)
throw new ArgumentNullException("characters");
var cSet = characters.ToIcdHashSet();
return new string(extends.Where(c => !characters.Contains(c)).ToArray());
return new string(extends.Where(c => !cSet.Contains(c)).ToArray());
}
/// <summary>
@@ -254,5 +207,19 @@ namespace ICD.Common.Utils.Extensions
return extends.All(char.IsDigit);
}
/// <summary>
/// Returns true if the string contains the given character.
/// </summary>
/// <param name="extends"></param>
/// <param name="character"></param>
/// <returns></returns>
public static bool Contains(this string extends, char character)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Contains(character.ToString());
}
}
}

View File

@@ -7,9 +7,6 @@ namespace ICD.Common.Utils.Extensions
{
public static string ToReadableString(this TimeSpan extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
StringBuilder builder = new StringBuilder();
if (extends.Days > 0)

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICD.Common.Utils.Collections;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
@@ -184,18 +185,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (!s_TypeAllTypes.ContainsKey(extends))
Type[] types;
if (!s_TypeAllTypes.TryGetValue(extends, out types))
{
Type[] types =
extends.GetBaseTypes()
.Concat(extends.GetInterfaces())
.Prepend(extends)
.ToArray();
types = extends.GetBaseTypes()
.Concat(extends.GetInterfaces())
.Prepend(extends)
.ToArray();
s_TypeAllTypes.Add(extends, types);
s_TypeAllTypes[extends] = types;
}
return s_TypeAllTypes[extends];
return types;
}
/// <summary>
@@ -208,20 +209,18 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (!s_TypeBaseTypes.ContainsKey(extends))
Type[] types;
if (!s_TypeBaseTypes.TryGetValue(extends, out types))
{
Type[] types = GetBaseTypesInternal(extends).ToArray();
s_TypeBaseTypes.Add(extends, types);
types = GetBaseTypesIterator(extends).ToArray();
s_TypeBaseTypes[extends] = types;
}
return s_TypeBaseTypes[extends];
return types;
}
private static IEnumerable<Type> GetBaseTypesInternal(Type type)
private static IEnumerable<Type> GetBaseTypesIterator(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
do
{
type = type
@@ -246,7 +245,8 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (!s_TypeImmediateInterfaces.ContainsKey(extends))
Type[] immediateInterfaces;
if (!s_TypeImmediateInterfaces.TryGetValue(extends, out immediateInterfaces))
{
IEnumerable<Type> allInterfaces = extends.GetInterfaces();
@@ -256,12 +256,12 @@ namespace ICD.Common.Utils.Extensions
.SelectMany(t => t.GetImmediateInterfaces())
.Distinct();
Type[] immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
immediateInterfaces = allInterfaces.Except(childInterfaces).ToArray();
s_TypeImmediateInterfaces.Add(extends, immediateInterfaces);
s_TypeImmediateInterfaces[extends] = immediateInterfaces;
}
return s_TypeImmediateInterfaces[extends];
return immediateInterfaces;
}
/// <summary>
@@ -274,17 +274,91 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (!s_TypeMinimalInterfaces.ContainsKey(extends))
Type[] minimalInterfaces;
if (!s_TypeMinimalInterfaces.TryGetValue(extends, out minimalInterfaces))
{
Type[] allInterfaces = extends.GetInterfaces();
Type[] minimalInterfaces =
allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
minimalInterfaces = allInterfaces.Except(allInterfaces.SelectMany(t => t.GetInterfaces()))
.ToArray();
s_TypeMinimalInterfaces.Add(extends, minimalInterfaces);
s_TypeMinimalInterfaces[extends] = minimalInterfaces;
}
return s_TypeMinimalInterfaces[extends];
return minimalInterfaces;
}
/// <summary>
/// Gets the Type name without any trailing generic information.
///
/// E.g.
/// List`1
/// Becomes
/// List
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetNameWithoutGenericArity(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name = extends.Name;
int index = name.IndexOf('`');
return index == -1 ? name : name.Substring(0, index);
}
/// <summary>
/// Gets the type name as it would appear in code.
/// </summary>
/// <param name="extends">Type. May be generic or nullable</param>
/// <returns>Full type name, fully qualified namespaces</returns>
public static string GetSyntaxName(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
// Nullable
Type nullableType = Nullable.GetUnderlyingType(extends);
if (nullableType != null)
return nullableType.GetSyntaxName() + "?";
// Generic
if (extends.IsGenericType)
{
StringBuilder sb = new StringBuilder(extends.Name.Substring(0, extends.Name.IndexOf('`')));
sb.Append('<');
bool first = true;
foreach (Type t in extends.GetGenericArguments())
{
if (!first)
sb.Append(',');
sb.Append(t.GetSyntaxName());
first = false;
}
sb.Append('>');
return sb.ToString();
}
// Default
switch (extends.Name)
{
case "String":
return "string";
case "Int32":
return "int";
case "Decimal":
return "decimal";
case "Object":
return "object";
case "Void":
return "void";
default:
return extends.Name;
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Linq;
namespace ICD.Common.Utils.Extensions
{
public static class UriExtensions
{
/// <summary>
/// Gets the username from the given URI.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetUserName(this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.UserInfo.Split(':').FirstOrDefault(string.Empty);
}
/// <summary>
/// Gets the password from the given URI.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetPassword(this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.UserInfo.Split(':').Skip(1).FirstOrDefault(string.Empty);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace ICD.Common.Utils.Extensions
{
public static class UShortExtensions
{
public static bool ToBool(this ushort u)
{
return u != 0;
}
}
}

View File

@@ -1,49 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Condition=" '$(EAZFUSCATOR_NET_HOME)' != '' and Exists('$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets') " Project="$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>ICD.Common.Utils</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>true</Deterministic>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>true</Deterministic>
<Authors>Chris Cameron, Jeff Thompson</Authors>
<PackageId>ICD.Common.Utils</PackageId>
<PackageProjectUrl></PackageProjectUrl>
<RepositoryUrl>https://cs-gogs.icdpf.net/Common/Utils</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<EazfuscatorIntegration>MSBuild</EazfuscatorIntegration>
<EazfuscatorActiveConfiguration>Release</EazfuscatorActiveConfiguration>
<EazfuscatorCompatibilityVersion>2018.2</EazfuscatorCompatibilityVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
<DefineConstants>TRACE;STANDARD</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="SIMPLSharpLogs\**" />
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
<None Remove="SIMPLSharpLogs\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="ObfuscationSettings.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="ICD.Common.projectinfo" />
<None Remove="ICD.Common_SimplSharp.suo" />
<None Remove="ICD.SimplSharp.projectinfo" />
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.4" />
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />

View File

@@ -1,4 +1,5 @@
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -74,9 +75,24 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
<Compile Include="Attributes\IIcdAttribute.cs" />
<Compile Include="Attributes\RangeAttribute.cs" />
<Compile Include="Collections\BiDictionary.cs" />
<Compile Include="Collections\AsyncEventQueue.cs" />
<Compile Include="Collections\IcdOrderedDictionary.cs" />
<Compile Include="Collections\PriorityQueue.cs" />
<Compile Include="Collections\RateLimitedEventQueue.cs" />
<Compile Include="Collections\WeakKeyDictionary.cs" />
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
<Compile Include="Comparers\PredicateComparer.cs" />
<Compile Include="Comparers\SequenceComparer.cs" />
<Compile Include="ConsoleColor.cs" />
<Compile Include="Email\EmailClient.cs" />
<Compile Include="Email\eMailErrorCode.cs" />
<Compile Include="Email\EmailStringCollection.cs" />
<Compile Include="EncodingUtils.cs" />
<Compile Include="Csv\CsvWriter.cs" />
<Compile Include="Csv\CsvWriterSettings.cs" />
<Compile Include="EventArguments\BoolEventArgs.cs" />
<Compile Include="EventArguments\CharEventArgs.cs" />
<Compile Include="EventArguments\DateTimeEventArgs.cs" />
@@ -88,9 +104,20 @@
<Compile Include="EventArguments\UShortEventArgs.cs" />
<Compile Include="EventArguments\XmlRecursionEventArgs.cs" />
<None Include="ObfuscationSettings.cs" />
<Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\DayOfWeekExtensions.cs" />
<Compile Include="Extensions\ListExtensions.cs" />
<Compile Include="Comparers\PredicateEqualityComparer.cs" />
<Compile Include="Extensions\MethodInfoExtensions.cs" />
<Compile Include="Extensions\ParameterInfoExtensions.cs" />
<Compile Include="Extensions\UriExtensions.cs" />
<Compile Include="Extensions\UShortExtensions.cs" />
<Compile Include="IcdUriBuilder.cs" />
<Compile Include="IO\Compression\IcdZipEntry.cs" />
<Compile Include="IO\IcdBinaryReader.cs" />
<Compile Include="IO\eSeekOrigin.cs" />
<Compile Include="IO\IcdStreamWriter.cs" />
<Compile Include="ProcessorUtils.SimplSharp.cs" />
<Compile Include="ProcessorUtils.Standard.cs" />
<Compile Include="ProgramUtils.SimplSharp.cs" />
@@ -98,7 +125,12 @@
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RecursionUtils.cs" />
<Compile Include="RegexUtils.cs" />
<Compile Include="ReprBuilder.cs" />
<Compile Include="Services\Scheduler\AbstractScheduledAction.cs" />
<Compile Include="Services\Scheduler\ActionSchedulerService.cs" />
<Compile Include="Services\Scheduler\IActionSchedulerService.cs" />
<Compile Include="Services\Scheduler\IScheduledAction.cs" />
<Compile Include="Services\Logging\ILoggerService.cs" />
<Compile Include="Services\Logging\LogItem.cs" />
<Compile Include="Services\Logging\LogItemEventArgs.cs" />
@@ -115,7 +147,7 @@
<Compile Include="IcdEnvironment.cs" />
<Compile Include="IcdEnvironment.SimplSharp.cs" />
<Compile Include="IcdEnvironment.Standard.cs" />
<Compile Include="IcdZip.cs" />
<Compile Include="IO\Compression\IcdZip.cs" />
<Compile Include="IO\IcdDirectory.cs" />
<Compile Include="EnumUtils.cs" />
<Compile Include="Extensions\AssemblyExtensions.cs" />
@@ -171,12 +203,17 @@
<Compile Include="Timers\Repeater.cs" />
<Compile Include="Timers\SafeTimer.cs" />
<Compile Include="TryUtils.cs" />
<Compile Include="UriUtils.cs" />
<Compile Include="Xml\AbstractGenericXmlConverter.cs" />
<Compile Include="Xml\AbstractXmlConverter.cs" />
<Compile Include="Xml\DefaultXmlConverter.cs" />
<Compile Include="Xml\IcdXmlConvert.cs" />
<Compile Include="Xml\IcdXmlDocument.cs" />
<Compile Include="Xml\IcdXmlException.cs" />
<Compile Include="Xml\IcdXmlReader.cs" />
<Compile Include="Xml\IcdXmlTextWriter.cs" />
<Compile Include="Xml\IcdXmlAttribute.cs" />
<Compile Include="Xml\IXmlConverter.cs" />
<Compile Include="Xml\XmlConverterAttribute.cs" />
<Compile Include="Xml\XmlReaderExtensions.cs" />
<Compile Include="Xml\XmlUtils.cs" />
<None Include="Properties\ControlSystem.cfg" />

View File

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

View File

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

View File

@@ -0,0 +1,69 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
namespace ICD.Common.Utils.IO
{
public sealed class IcdBinaryReader : IDisposable
{
private readonly BinaryReader m_Reader;
public BinaryReader WrappedReader { get { return m_Reader; } }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="stream"></param>
public IcdBinaryReader(IcdStream stream)
: this(new BinaryReader(stream.WrappedStream))
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="reader"></param>
public IcdBinaryReader(BinaryReader reader)
{
m_Reader = reader;
}
public void Dispose()
{
m_Reader.Dispose();
}
public void Close()
{
m_Reader.Close();
}
public ushort ReadUInt16()
{
return m_Reader.ReadUInt16();
}
public int ReadInt32()
{
return m_Reader.ReadInt32();
}
public short ReadInt16()
{
return m_Reader.ReadInt16();
}
public uint ReadUInt32()
{
return m_Reader.ReadUInt32();
}
public byte[] ReadBytes(short numberOfBytesToRead)
{
return m_Reader.ReadBytes(numberOfBytesToRead);
}
}
}

View File

@@ -2,6 +2,7 @@
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
using ICD.Common.Utils.Extensions;
using System.IO;
using Microsoft.DotNet.PlatformAbstractions;
#endif
@@ -19,6 +20,19 @@ namespace ICD.Common.Utils.IO
#endif
}
/// <summary>
/// This gets the application root directory for Crestron systems
/// </summary>
/// <returns></returns>
public static string GetApplicationRootDirectory()
{
#if SIMPLSHARP
return Directory.GetApplicationRootDirectory();
#else
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetPath());
#endif
}
public static bool Exists(string path)
{
if (path == null)

View File

@@ -66,6 +66,11 @@ namespace ICD.Common.Utils.IO
File.Delete(path);
}
public static IcdStream OpenRead(string path)
{
return new IcdFileStream(File.OpenRead(path));
}
[PublicAPI]
public static IcdFileStream OpenWrite(string path)
{
@@ -89,5 +94,11 @@ namespace ICD.Common.Utils.IO
{
return new IcdFileStream(File.Create(path));
}
[PublicAPI]
public static IcdStreamWriter AppendText(string path)
{
return new IcdStreamWriter(File.AppendText(path));
}
}
}

View File

@@ -3,13 +3,12 @@ using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
using System.Text;
namespace ICD.Common.Utils.IO
{
public sealed class IcdFileStream : IcdStream
{
public int Position { get; set; }
public FileStream WrappedFileStream { get { return WrappedStream as FileStream; } }
/// <summary>
@@ -34,5 +33,11 @@ namespace ICD.Common.Utils.IO
WrappedFileStream.Dispose();
#endif
}
public void Write(string data, Encoding encoding)
{
byte[] info = encoding.GetBytes(data);
WrappedFileStream.Write(info, 0, info.Length);
}
}
}

View File

@@ -8,8 +8,6 @@ namespace ICD.Common.Utils.IO
{
public sealed class IcdMemoryStream : IcdStream
{
public int Position { get; set; }
public MemoryStream WrappedMemoryStream { get { return WrappedStream as MemoryStream; } }
/// <summary>

View File

@@ -1,5 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
@@ -68,5 +69,17 @@ namespace ICD.Common.Utils.IO
return Path.ChangeExtension(path, ext);
}
public static string GetRelativePath(string folder, string filespec)
{
Uri pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar))
folder += Path.DirectorySeparatorChar;
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
}
}

View File

@@ -13,6 +13,10 @@ namespace ICD.Common.Utils.IO
public Stream WrappedStream { get { return m_Stream; } }
public long Length { get { return m_Stream.Length; } }
public long Position { get { return m_Stream.Position; } }
/// <summary>
/// Constructor.
/// </summary>
@@ -29,5 +33,10 @@ namespace ICD.Common.Utils.IO
{
m_Stream.Dispose();
}
public void Seek(long offset, eSeekOrigin seekOrigin)
{
m_Stream.Seek(offset, seekOrigin.ToSeekOrigin());
}
}
}

View File

@@ -0,0 +1,21 @@
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
using System.IO;
#endif
namespace ICD.Common.Utils.IO
{
public sealed class IcdStreamWriter : IcdTextWriter
{
public StreamWriter WrappedStreamWriter { get { return WrappedTextWriter as StreamWriter; } }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="baseStreamWriter"></param>
public IcdStreamWriter(StreamWriter baseStreamWriter) : base(baseStreamWriter)
{
}
}
}

View File

@@ -1,13 +1,13 @@
#if SIMPLSHARP
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
namespace ICD.Common.Utils.IO
{
public class IcdTextReader
public class IcdTextReader : IDisposable
{
private readonly TextReader m_TextReader;
@@ -17,5 +17,10 @@ namespace ICD.Common.Utils.IO
{
m_TextReader = textReader;
}
public void Dispose()
{
m_TextReader.Dispose();
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
namespace ICD.Common.Utils.IO
{
public enum eSeekOrigin
{
Begin,
Current,
End,
}
public static class SeekOriginExtensions
{
/// <summary>
/// Converts the seek origin enum to a system seek origin.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static SeekOrigin ToSeekOrigin(this eSeekOrigin extends)
{
switch (extends)
{
case eSeekOrigin.Begin:
return SeekOrigin.Begin;
case eSeekOrigin.Current:
return SeekOrigin.Current;
case eSeekOrigin.End:
return SeekOrigin.End;
default:
throw new ArgumentOutOfRangeException("extends");
}
}
}
}

View File

@@ -3,6 +3,8 @@ using System.Linq;
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Diagnostics;
#endif
namespace ICD.Common.Utils
@@ -48,7 +50,7 @@ namespace ICD.Common.Utils
Print(message);
}
#else
Print(message, args);
Print(message);
#endif
}
@@ -74,7 +76,7 @@ namespace ICD.Common.Utils
#else
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
System.Console.Error.WriteLine(message);
System.Console.WriteLine(message);
System.Console.ResetColor();
#endif
}
@@ -107,7 +109,7 @@ namespace ICD.Common.Utils
#else
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
System.Console.Error.Write(message);
System.Console.Write(message);
System.Console.ResetColor();
#endif
}
@@ -121,6 +123,10 @@ namespace ICD.Common.Utils
public static bool SendControlSystemCommand(string command, ref string result)
{
#if SIMPLSHARP
// No console on VC4
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
return false;
return CrestronConsole.SendControlSystemCommand(command, ref result);
#else
return false;
@@ -138,8 +144,10 @@ namespace ICD.Common.Utils
return false;
CrestronConsole.AddNewConsoleCommand(str => callback(str), command, help, (ConsoleAccessLevelEnum)(int)accessLevel);
#endif
return true;
#else
return false;
#endif
}
}
}

View File

@@ -12,7 +12,12 @@ namespace ICD.Common.Utils
public static eRuntimeEnvironment RuntimeEnvironment
{
get { return GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment); }
get
{
return CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? eRuntimeEnvironment.SimplSharpProMono
: GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment);
}
}
/// <summary>

View File

@@ -11,6 +11,7 @@ namespace ICD.Common.Utils
{
SimplSharp,
SimplSharpPro,
SimplSharpProMono,
Standard
}

View File

@@ -8,16 +8,6 @@ namespace ICD.Common.Utils
{
public static class IcdErrorLog
{
private const string CONSOLE_RED = "\x1B[31;1m";
private const string CONSOLE_GREEN = "\x1B[32;1m";
private const string CONSOLE_YELLOW = "\x1B[33;1m";
private const string CONSOLE_BLUE = "\x1B[34;1m";
//private const string CONSOLE_MAGENTA = "\x1B[35;1m";
private const string CONSOLE_CYAN = "\x1B[36;1m";
//private const string CONSOLE_WHITE = "\x1B[37;1m";
private const string CONSOLE_YELLOW_ON_RED_BACKGROUND = "\x1B[93;41m";
private const string CONSOLE_RESET = "\x1B[0m";
private static readonly SafeCriticalSection s_LoggingSection;
/// <summary>
@@ -36,7 +26,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_RED);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_RED);
ErrorLog.Error(message);
#else
System.Console.ForegroundColor = ConsoleColor.Red;
@@ -64,7 +54,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_YELLOW);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW);
ErrorLog.Warn(message);
#else
System.Console.ForegroundColor = ConsoleColor.Yellow;
@@ -92,7 +82,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_BLUE);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_BLUE);
ErrorLog.Notice(message);
#else
System.Console.ForegroundColor = ConsoleColor.Blue;
@@ -120,7 +110,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_GREEN);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_GREEN);
ErrorLog.Ok(message);
#else
System.Console.ForegroundColor = ConsoleColor.Green;
@@ -148,7 +138,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_YELLOW_ON_RED_BACKGROUND);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW_ON_RED_BACKGROUND);
ErrorLog.Exception(message, ex);
#else
System.Console.ForegroundColor = ConsoleColor.Yellow;
@@ -179,7 +169,7 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
message = FormatConsoleColor(message, CONSOLE_CYAN);
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_CYAN);
ErrorLog.Info(message);
#else
System.Console.ForegroundColor = ConsoleColor.Cyan;
@@ -207,7 +197,7 @@ namespace ICD.Common.Utils
/// <returns></returns>
private static string FormatConsoleColor(string text, string color)
{
return string.Format("{0}{1}{2}", color, text, CONSOLE_RESET);
return string.Format("{0}{1}{2}", color, text, ConsoleColorExtensions.CONSOLE_RESET);
}
}
}

View File

@@ -0,0 +1,287 @@
using System;
#if !SIMPLSHARP
using System.Linq;
#endif
using System.Text;
using System.Text.RegularExpressions;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
/// <summary>
/// Simple Compact Framework UriBuilder implementation.
/// </summary>
public sealed class IcdUriBuilder
{
#region Properties
/// <summary>
/// Gets or sets the fragment portion of the URI.
/// </summary>
public string Fragment { get; set; }
/// <summary>
/// Gets or sets the Domain Name System (DNS) host name or IP address of a server.
/// </summary>
public string Host { get; set; }
/// <summary>
/// Gets or sets the password associated with the user that accesses the URI.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Gets or sets the path to the resource referenced by the URI.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets the port number of the URI.
/// </summary>
public ushort Port { get; set; }
/// <summary>
/// Gets or sets any query information included in the URI.
/// </summary>
public string Query { get; set; }
/// <summary>
/// Gets or sets the scheme name of the URI.
/// </summary>
public string Scheme { get; set; }
/// <summary>
/// The user name associated with the user that accesses the URI.
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Gets the Uri instance constructed by the specified UriBuilder instance.
/// </summary>
public Uri Uri { get { return new Uri(ToString()); } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public IcdUriBuilder()
: this((Uri)null)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="uri"></param>
public IcdUriBuilder(string uri)
: this(new Uri(uri, UriKind.RelativeOrAbsolute))
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="uri"></param>
public IcdUriBuilder(Uri uri)
{
if (uri == null)
return;
if (!uri.IsAbsoluteUri)
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
Fragment = uri.Fragment;
Host = uri.Host;
Password = uri.GetPassword();
Path = uri.AbsolutePath;
Port = (ushort)uri.Port;
Query = uri.Query;
Scheme = uri.Scheme;
UserName = uri.GetUserName();
}
/// <summary>
/// Builds the string representation for the URI.
/// </summary>
/// <returns></returns>
public override string ToString()
{
// URI = scheme:[//authority]path[?query][#fragment]
// authority = [userinfo@]host[:port]
// userinfo = username[:password]
StringBuilder builder = new StringBuilder();
// Scheme
string scheme = string.IsNullOrEmpty(Scheme) ? Uri.UriSchemeHttp : Scheme;
builder.Append(scheme);
builder.Append(':');
// Authority
builder.Append("//");
if (!string.IsNullOrEmpty(UserName))
{
builder.Append(UserName);
if (!string.IsNullOrEmpty(Password))
{
builder.Append(':');
builder.Append(Password);
}
builder.Append('@');
}
string host = string.IsNullOrEmpty(Host) ? "localhost" : Host;
builder.Append(host);
if (Port != 0)
{
builder.Append(':');
builder.Append(Port);
}
// Path
if (string.IsNullOrEmpty(Path) || !Path.StartsWith("/"))
builder.Append('/');
builder.Append(Path);
// Query
if (!string.IsNullOrEmpty(Query))
{
builder.Append('?');
builder.Append(Query);
}
// Fragment
if (!string.IsNullOrEmpty(Fragment))
{
builder.Append('#');
builder.Append(Fragment);
}
return builder.ToString();
}
/// <summary>
/// Appends the given path to the current path, ensuring only one separator between parts.
/// </summary>
/// <param name="parts"></param>
/// <returns></returns>
public void AppendPath(params string[] parts)
{
parts = parts.Prepend(Path).ToArray(parts.Length + 1);
Path = Combine(parts);
}
#region Flurl
// The following region is taken from Flurl https://github.com/tmenier/Flurl
/*
MIT License
Copyright (c) 2018 Todd Menier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/// <summary>
/// Basically a Path.Combine for URLs. Ensures exactly one '/' seperates each segment,
/// and exactly on '&amp;' seperates each query parameter.
/// URL-encodes illegal characters but not reserved characters.
/// </summary>
/// <param name="parts">URL parts to combine.</param>
public static string Combine(params string[] parts)
{
if (parts == null)
throw new ArgumentNullException("parts");
string result = "";
bool inQuery = false, inFragment = false;
foreach (var part in parts)
{
if (string.IsNullOrEmpty(part))
continue;
if (result.EndsWith("?") || part.StartsWith("?"))
result = CombineEnsureSingleSeperator(result, part, '?');
else if (result.EndsWith("#") || part.StartsWith("#"))
result = CombineEnsureSingleSeperator(result, part, '#');
else if (inFragment)
result += part;
else if (inQuery)
result = CombineEnsureSingleSeperator(result, part, '&');
else
result = CombineEnsureSingleSeperator(result, part, '/');
if (part.Contains("#"))
{
inQuery = false;
inFragment = true;
}
else if (!inFragment && part.Contains("?"))
{
inQuery = true;
}
}
return EncodeIllegalCharacters(result, false);
}
private static string CombineEnsureSingleSeperator(string a, string b, char seperator)
{
if (string.IsNullOrEmpty(a)) return b;
if (string.IsNullOrEmpty(b)) return a;
return a.TrimEnd(seperator) + seperator + b.TrimStart(seperator);
}
/// <summary>
/// URL-encodes characters in a string that are neither reserved nor unreserved. Avoids encoding reserved characters such as '/' and '?'. Avoids encoding '%' if it begins a %-hex-hex sequence (i.e. avoids double-encoding).
/// </summary>
/// <param name="s">The string to encode.</param>
/// <param name="encodeSpaceAsPlus">If true, spaces will be encoded as + signs. Otherwise, they'll be encoded as %20.</param>
/// <returns>The encoded URL.</returns>
private static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus) {
if (string.IsNullOrEmpty(s))
return s;
if (encodeSpaceAsPlus)
s = s.Replace(" ", "+");
// Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
// in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600
// no % characters, so avoid the regex overhead
if (!s.Contains("%"))
return Uri.EscapeUriString(s);
// pick out all %-hex-hex matches and avoid double-encoding
return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
return Uri.EscapeUriString(a) + b;
});
}
#endregion
}
}

View File

@@ -1,43 +0,0 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.IO.Compression;
#endif
namespace ICD.Common.Utils
{
/// <summary>
/// Utils for managing archives.
/// </summary>
public static class IcdZip
{
/// <summary>
/// Unzips the archive at the given path.
/// </summary>
/// <param name="path"></param>
/// <param name="outputPath"></param>
/// <param name="message"></param>
public static bool Unzip(string path, string outputPath, out string message)
{
try
{
#if SIMPLSHARP
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
message = result.ToString();
return result == CrestronZIP.ResultCode.ZR_OK;
#else
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
archive.ExtractToDirectory(outputPath);
message = "Success";
return true;
#endif
}
catch (Exception e)
{
message = e.Message;
return false;
}
}
}
}

View File

@@ -10,10 +10,7 @@ namespace ICD.Common.Utils.Json
/// Creates a new instance of T.
/// </summary>
/// <returns></returns>
protected virtual T Instantiate()
{
return ReflectionUtils.CreateInstance<T>();
}
protected abstract T Instantiate();
/// <summary>
/// Writes the JSON representation of the object.
@@ -47,6 +44,18 @@ namespace ICD.Common.Utils.Json
[PublicAPI]
public virtual void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (serializer == null)
throw new ArgumentNullException("serializer");
if (value == null)
{
writer.WriteNull();
return;
}
writer.WriteStartObject();
{
WriteProperties(writer, value, serializer);
@@ -98,6 +107,12 @@ namespace ICD.Common.Utils.Json
[PublicAPI]
public virtual T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer)
{
if (reader == null)
throw new ArgumentNullException("reader");
if (serializer == null)
throw new ArgumentNullException("serializer");
T output = default(T);
bool instantiated = false;
@@ -120,12 +135,7 @@ namespace ICD.Common.Utils.Json
// Read into the value
reader.Read();
switch (property)
{
default:
ReadProperty(property, reader, output, serializer);
break;
}
ReadProperty(property, reader, output, serializer);
}
return output;

View File

@@ -1,4 +1,5 @@
using System;
using System.Text.RegularExpressions;
using ICD.Common.Utils.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -71,7 +72,26 @@ namespace ICD.Common.Utils.Json
string itemString = (string)token.SelectToken(ITEM_TOKEN);
Type type = Type.GetType(typeString);
if (type == null)
{
typeString = typeString.Replace("_SimplSharp", "").Replace("_NetStandard", "");
type = Type.GetType(typeString);
}
if (type == null)
{
typeString = AddSimplSharpSuffix(typeString);
type = Type.GetType(typeString);
}
return JsonConvert.DeserializeObject(itemString, type);
}
private static string AddSimplSharpSuffix(string typeString)
{
return Regex.Replace(typeString,
"(?'prefix'[^,]+, )(?'assembly'[^,]*)(?'suffix', .*)",
"${prefix}${assembly}_SimplSharp${suffix}");
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using ICD.Common.Properties;
@@ -16,9 +15,6 @@ namespace ICD.Common.Utils.Json
[PublicAPI]
public static class JsonUtils
{
// 2016-02-26T19:24:59
private const string DATE_FORMAT = @"yyyy-MM-dd\THH:mm:ss";
private const string MESSAGE_NAME_PROPERTY = "m";
private const string MESSAGE_DATA_PROPERTY = "d";
@@ -44,6 +40,16 @@ namespace ICD.Common.Utils.Json
JsonConvert.DeserializeObject(serialized, type);
}
/// <summary>
/// Gets the data as a DateTime value.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static DateTime ParseDateTime(string data)
{
return DateTime.Parse(data);
}
/// <summary>
/// Gets the token as a DateTime value.
/// </summary>
@@ -55,11 +61,7 @@ namespace ICD.Common.Utils.Json
if (token == null)
throw new ArgumentNullException("token");
#if SIMPLSHARP
return DateTime.ParseExact((string)token, DATE_FORMAT, CultureInfo.CurrentCulture);
#else
return (DateTime)token;
#endif
return ParseDateTime((string)token);
}
/// <summary>
@@ -81,6 +83,10 @@ namespace ICD.Common.Utils.Json
output = ParseDateTime(token);
return true;
}
catch (FormatException)
{
return false;
}
catch (InvalidCastException)
{
return false;
@@ -205,8 +211,11 @@ namespace ICD.Common.Utils.Json
StringBuilder builder = new StringBuilder();
using (JsonTextWriter writer = new JsonTextWriter(new IcdStringWriter(builder).WrappedStringWriter))
serializeMethod(writer);
using (IcdStringWriter stringWriter = new IcdStringWriter(builder))
{
using (JsonTextWriter writer = new JsonTextWriter(stringWriter.WrappedStringWriter))
serializeMethod(writer);
}
return builder.ToString();
}
@@ -224,8 +233,11 @@ namespace ICD.Common.Utils.Json
if (deserializeMethod == null)
throw new ArgumentNullException("deserializeMethod");
using (JsonTextReader reader = new JsonTextReader(new IcdStringReader(json).WrappedStringReader))
return deserializeMethod(reader);
using (IcdStringReader stringReader = new IcdStringReader(json))
{
using (JsonTextReader reader = new JsonTextReader(stringReader.WrappedStringReader))
return deserializeMethod(reader);
}
}
/// <summary>

View File

@@ -137,30 +137,6 @@ namespace ICD.Common.Utils
0.0f, 1.0f, new TimeSpan(value.Ticks).TotalSeconds);
}
/// <summary>
/// Gets the digit count for the given number.
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
public static int GetNumberOfDigits(int number)
{
int output = number.ToString().Length;
if (number < 0)
output--;
return output;
}
/// <summary>
/// Gets the digit count for the given number.
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
public static int GetNumberOfDigits(uint number)
{
return number.ToString().Length;
}
/// <summary>
/// Takes a sequence of numbers:
/// 1, 3, 5, 6, 7, 8, 9, 10, 12
@@ -172,18 +148,29 @@ namespace ICD.Common.Utils
if (numbers == null)
throw new ArgumentNullException("numbers");
return GetRangesIterator(numbers);
}
/// <summary>
/// Takes a sequence of numbers:
/// 1, 3, 5, 6, 7, 8, 9, 10, 12
/// And calculates the continuous ranges:
/// (1, 1), (3, 3), (5, 10), (12, 12)
/// </summary>
private static IEnumerable<int[]> GetRangesIterator(IEnumerable<int> numbers)
{
int[] currentRange = null;
foreach (int number in numbers.Order())
{
if (currentRange == null)
currentRange = new[] {number, number};
currentRange = new[] { number, number };
else if (currentRange[1] == number - 1)
currentRange = new[] {currentRange[0], number};
currentRange = new[] { currentRange[0], number };
else
{
yield return currentRange;
currentRange = new[] {number, number};
currentRange = new[] { number, number };
}
}

View File

@@ -17,7 +17,15 @@ namespace ICD.Common.Utils
/// Gets the path to the root directory of the processor.
/// </summary>
[PublicAPI]
public static string RootPath { get { return IcdDirectory.GetDirectoryRoot("\\"); } }
public static string RootPath {
get
{
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
return IcdDirectory.GetApplicationRootDirectory();
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
}
}
/// <summary>
/// Gets the path to the program directory
@@ -26,10 +34,23 @@ namespace ICD.Common.Utils
public static string ProgramPath { get { return IcdDirectory.GetApplicationDirectory(); } }
/// <summary>
/// Gets the path to the NVRAM directory.
/// Gets the path to the root config directory,
/// which contains common and program-specific config directories.
/// </summary>
[PublicAPI]
public static string NvramPath { get { return Join(RootPath, "NVRAM"); } }
public static string RootConfigPath
{
get
{
#if SIMPLSHARP
return Join(RootPath, "User");
#elif LINUX
return Join(RootPath, "opt", "ICD.Connect");
#else
return Join(RootPath, "ProgramData", "ICD.Connect");
#endif
}
}
/// <summary>
/// Returns the absolute path to the configuration directory.
@@ -40,8 +61,11 @@ namespace ICD.Common.Utils
{
get
{
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
return Join(RootConfigPath, "ProgramConfig");
string directoryName = string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
return Join(NvramPath, directoryName);
return Join(RootConfigPath, directoryName);
}
}
@@ -49,7 +73,7 @@ namespace ICD.Common.Utils
/// Returns the absolute path to the common configuration directory.
/// </summary>
[PublicAPI]
public static string CommonConfigPath { get { return Join(NvramPath, "CommonConfig"); } }
public static string CommonConfigPath { get { return Join(RootConfigPath, "CommonConfig"); } }
/// <summary>
/// Returns the absolute path to the common config library directory.

View File

@@ -97,8 +97,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void RestartProgram()
{
// TODO
throw new NotImplementedException();
throw new NotSupportedException();
}
/// <summary>
@@ -107,8 +106,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void Reboot()
{
// TODO
throw new NotImplementedException();
throw new NotSupportedException();
}
#endregion

View File

@@ -102,7 +102,7 @@ namespace ICD.Common.Utils
return output;
}
foreach (string line in progInfo.Split(new[] {"\n\r", "\r\n", "\n", "\r"}))
foreach (string line in progInfo.Split(new[] {'\r', '\n'}))
{
if (string.IsNullOrEmpty(line))
continue;

View File

@@ -8,17 +8,11 @@ namespace ICD.Common.Utils
{
public static partial class ProgramUtils
{
/// <summary>
/// Gets the program number.
/// </summary>
[PublicAPI]
public static uint ProgramNumber
{
get
{
return 1;
}
}
/// <summary>
/// Gets the program number.
/// </summary>
[PublicAPI]
public static uint ProgramNumber { get; set; } = 1;
/// <summary>
/// Gets the compile date of the program.

View File

@@ -33,6 +33,10 @@ namespace ICD.Common.Utils
name = name.Substring(0, proLength).PadRight(26);
break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
// No console
return;
case IcdEnvironment.eRuntimeEnvironment.Standard:
name += ' ';
break;

View File

@@ -3,5 +3,5 @@ using System.Reflection;
[assembly: AssemblyTitle("ICD.Common.Utils")]
[assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2018")]
[assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("8.3.3.0")]

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
@@ -71,22 +70,68 @@ namespace ICD.Common.Utils
if (node == null)
throw new ArgumentNullException("node");
if (visited.Contains(node))
return GetCliqueIterator(map, visited, node);
}
/// <summary>
/// Gets the clique containing the node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="map"></param>
/// <param name="visited"></param>
/// <param name="node"></param>
/// <returns></returns>
private static IEnumerable<T> GetCliqueIterator<T>(IDictionary<T, IEnumerable<T>> map, IcdHashSet<T> visited, T node)
{
if (!visited.Add(node))
yield break;
if (!map.ContainsKey(node))
IEnumerable<T> adjacent;
if (!map.TryGetValue(node, out adjacent))
yield break;
visited.Add(node);
yield return node;
IEnumerable<T> adjacent = map.GetDefault(node, Enumerable.Empty<T>());
foreach (T item in adjacent.SelectMany(a => GetClique(map, visited, a)))
yield return item;
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="child"></param>
/// <param name="getChildren"></param>
/// <returns></returns>
public static bool BreadthFirstSearch<T>(T root, T child, Func<T, IEnumerable<T>> getChildren)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchPath(root, child, getChildren) != null;
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="destination"></param>
/// <param name="getChildren"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static bool BreadthFirstSearch<T>(T root, T destination, Func<T, IEnumerable<T>> getChildren, IEqualityComparer<T> comparer)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
if (comparer == null)
throw new ArgumentNullException("comparer");
return BreadthFirstSearchPath(root, destination, getChildren, comparer) != null;
}
/// <summary>
/// Returns all of the nodes in the tree via breadth-first search.
/// </summary>
@@ -99,6 +144,18 @@ namespace ICD.Common.Utils
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchIterator(root, getChildren);
}
/// <summary>
/// Returns all of the nodes in the tree via breadth-first search.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="getChildren"></param>
/// <returns></returns>
private static IEnumerable<T> BreadthFirstSearchIterator<T>(T root, Func<T, IEnumerable<T>> getChildren)
{
Queue<T> process = new Queue<T>();
process.Enqueue(root);
@@ -155,7 +212,7 @@ namespace ICD.Common.Utils
Queue<T> queue = new Queue<T>();
queue.Enqueue(root);
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
while (queue.Count > 0)
{
@@ -178,26 +235,38 @@ namespace ICD.Common.Utils
return null;
}
[NotNull]
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchManyDestinations<T>(T root,
IEnumerable<T> destinations,
Func<T, IEnumerable<T>>
getChildren)
/// <summary>
/// Returns the shortest path from root to each destination via breadth-first search.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="destinations"></param>
/// <param name="getChildren"></param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<T, IEnumerable<T>>>
BreadthFirstSearchPathManyDestinations<T>(T root, IEnumerable<T> destinations, Func<T, IEnumerable<T>> getChildren)
{
if (destinations == null)
throw new ArgumentNullException("destinations");
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchPathManyDestinations(root, destinations, getChildren, EqualityComparer<T>.Default);
}
[NotNull]
public static Dictionary<T, IEnumerable<T>> BreadthFirstSearchPathManyDestinations<T>(T root,
IEnumerable<T>
destinations,
Func<T, IEnumerable<T>>
getChildren,
IEqualityComparer<T>
comparer)
/// <summary>
/// Returns the shortest path from root to each destination via breadth-first search.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="destinations"></param>
/// <param name="getChildren"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<T, IEnumerable<T>>>
BreadthFirstSearchPathManyDestinations<T>(T root, IEnumerable<T> destinations, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T> comparer)
{
if (destinations == null)
throw new ArgumentNullException("destinations");
@@ -209,27 +278,22 @@ namespace ICD.Common.Utils
throw new ArgumentNullException("comparer");
IcdHashSet<T> destinationsToBeProcessed = new IcdHashSet<T>(destinations);
IcdHashSet<T> destinationsProcessed = new IcdHashSet<T>();
Dictionary<T, IEnumerable<T>> pathsToReturn = new Dictionary<T, IEnumerable<T>>();
// Edge case, root is the destination
foreach (T destination in
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)))
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)).ToArray())
{
destinationsProcessed.Add(destination);
pathsToReturn.Add(destination, new[] {root});
destinationsToBeProcessed.Remove(destination);
yield return new KeyValuePair<T, IEnumerable<T>>(destination, new[] {root});
}
foreach (T destination in destinationsProcessed)
destinationsToBeProcessed.Remove(destination);
destinationsProcessed.Clear();
if (destinationsToBeProcessed.Count == 0)
return pathsToReturn;
yield break;
Queue<T> queue = new Queue<T>();
queue.Enqueue(root);
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
while (queue.Count > 0)
{
@@ -242,23 +306,18 @@ namespace ICD.Common.Utils
T closureNode = node;
foreach (T destination in
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)))
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)).ToArray())
{
destinationsProcessed.Add(destination);
pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
}
foreach (T destination in destinationsProcessed)
destinationsToBeProcessed.Remove(destination);
destinationsProcessed.Clear();
yield return
new KeyValuePair<T, IEnumerable<T>>(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
}
if (destinationsToBeProcessed.Count == 0)
return pathsToReturn;
yield break;
}
}
return pathsToReturn;
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
@@ -8,7 +9,6 @@ using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Reflection;
using Activator = Crestron.SimplSharp.Reflection.Activator;
#else
using System.IO;
using System.Reflection;
using Microsoft.Extensions.DependencyModel;
using System.Runtime.Loader;
@@ -33,18 +33,11 @@ namespace ICD.Common.Utils
if (parameters == null)
throw new ArgumentNullException("parameters");
#if SIMPLSHARP
IEnumerable<CType>
#else
IEnumerable<Type>
#endif
parameterTypes = constructor.GetParameters().Select(p => p.ParameterType);
return ParametersMatchTypes(parameterTypes, parameters);
return MatchesMethodParameters(constructor, parameters);
}
/// <summary>
/// Returns true if the parameters match the method parameters.
/// Returns true if the parameter values match the method signature parameters.
/// </summary>
/// <param name="method"></param>
/// <param name="parameters"></param>
@@ -78,14 +71,7 @@ namespace ICD.Common.Utils
if (property == null)
throw new ArgumentNullException("property");
#if SIMPLSHARP
CType
#else
Type
#endif
propertyType = property.PropertyType;
return ParametersMatchTypes(new[] {propertyType}, new[] {parameter});
return ParameterMatchesType(property.PropertyType, parameter);
}
/// <summary>
@@ -97,7 +83,7 @@ namespace ICD.Common.Utils
private static bool ParametersMatchTypes(
#if SIMPLSHARP
IEnumerable<CType>
#else
#else
IEnumerable<Type>
#endif
types, IEnumerable<object> parameters)
@@ -109,21 +95,21 @@ namespace ICD.Common.Utils
throw new ArgumentNullException("parameters");
#if SIMPLSHARP
CType[]
IList<CType>
#else
Type[]
IList<Type>
#endif
typesArray = types as
#if SIMPLSHARP
CType[]
IList<CType>
#else
Type[]
IList<Type>
#endif
?? types.ToArray();
?? types.ToArray();
object[] parametersArray = parameters as object[] ?? parameters.ToArray();
IList<object> parametersArray = parameters as IList<object> ?? parameters.ToArray();
if (parametersArray.Length != typesArray.Length)
if (parametersArray.Count != typesArray.Count)
return false;
// Compares each pair of items in the two arrays.
@@ -175,26 +161,6 @@ namespace ICD.Common.Utils
: null;
}
/// <summary>
/// Returns true if the given type has a public parameterless constructor.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool HasPublicParameterlessConstructor(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
const BindingFlags binding = BindingFlags.Instance | BindingFlags.Public;
#if SIMPLSHARP
return ((CType)type).GetConstructor(binding, null, new CType[0], null)
#else
return type.GetConstructor(binding, null, new Type[0], null)
#endif
!= null;
}
/// <summary>
/// Platform independant delegate instantiation.
/// </summary>
@@ -202,6 +168,7 @@ namespace ICD.Common.Utils
/// <param name="firstArgument"></param>
/// <param name="method"></param>
/// <returns></returns>
[NotNull]
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method)
{
return
@@ -213,11 +180,25 @@ namespace ICD.Common.Utils
.CreateDelegate(type, firstArgument, method);
}
/// <summary>
/// Creates an instance of the given type, calling the default constructor.
/// </summary>
/// <returns></returns>
[NotNull]
public static T CreateInstance<T>(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return (T)CreateInstance(type);
}
/// <summary>
/// Creates an instance of the given type, calling the default constructor.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[NotNull]
public static T CreateInstance<T>(params object[] parameters)
{
if (parameters == null)
@@ -230,6 +211,7 @@ namespace ICD.Common.Utils
/// Creates an instance of the given type, calling the default constructor.
/// </summary>
/// <returns></returns>
[NotNull]
public static object CreateInstance(Type type, params object[] parameters)
{
if (type == null)
@@ -238,63 +220,49 @@ namespace ICD.Common.Utils
if (parameters == null)
throw new ArgumentNullException("parameters");
ConstructorInfo constructor =
type
#if SIMPLSHARP
.GetCType()
#endif
.GetConstructors()
.FirstOrDefault(c => MatchesConstructorParameters(c, parameters));
ConstructorInfo constructor = GetConstructor(type, parameters);
try
{
if (constructor != null)
return constructor.Invoke(parameters);
return constructor.Invoke(parameters);
}
catch (TypeLoadException e)
{
throw new TypeLoadException(e.GetBaseException().Message);
throw e.GetBaseException();
}
catch (TargetInvocationException e)
{
throw e.GetBaseException();
}
string message = string.Format("Unable to find constructor for {0}", type.Name);
throw new InvalidOperationException(message);
}
/// <summary>
/// Creates an instance of the given type, calling the default constructor.
/// Gets the constructor matching the given parameters.
/// </summary>
/// <param name="type"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public static T CreateInstance<T>(Type type)
[NotNull]
public static ConstructorInfo GetConstructor(Type type, params object[] parameters)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsAssignableTo(typeof(T)))
throw new InvalidOperationException("Type is not assignable to T");
if (parameters == null)
throw new ArgumentNullException("parameters");
return (T)CreateInstance(type);
}
ConstructorInfo info;
if(!type
#if SIMPLSHARP
.GetCType()
#endif
.GetConstructors()
.Where(c => MatchesConstructorParameters(c, parameters))
.TryFirst(out info))
throw new ArgumentException("Couldn't find a constructor matching the given parameters.");
/// <summary>
/// Gets the custom attributes added to the given assembly.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assembly"></param>
/// <returns></returns>
public static IEnumerable<T> GetCustomAttributes<T>(Assembly assembly)
where T : Attribute
{
if (assembly == null)
throw new ArgumentNullException("assembly");
try
{
return assembly.GetCustomAttributes<T>();
}
catch (FileNotFoundException)
{
return Enumerable.Empty<T>();
}
return info;
}
/// <summary>
@@ -302,6 +270,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
[NotNull]
public static Assembly LoadAssemblyFromPath(string path)
{
if (path == null)
@@ -336,39 +305,6 @@ namespace ICD.Common.Utils
#endif
}
/// <summary>
/// Finds the corresponding property info on the given type.
/// </summary>
/// <param name="type"></param>
/// <param name="property"></param>
/// <returns></returns>
public static PropertyInfo GetImplementation(Type type, PropertyInfo property)
{
if (type == null)
throw new ArgumentNullException("type");
if (property == null)
throw new ArgumentNullException("property");
if (type.IsInterface)
throw new InvalidOperationException("Type must not be an interface");
property = type
#if SIMPLSHARP
.GetCType()
#else
.GetTypeInfo()
#endif
.GetProperty(property.Name, property.PropertyType);
if (property == null)
return null;
return property.DeclaringType == type
? property
: GetImplementation(property.DeclaringType, property);
}
/// <summary>
/// Changes the given value to the given type.
/// </summary>
@@ -423,6 +359,7 @@ namespace ICD.Common.Utils
/// <param name="handler">The instance with the callback MethodInfo. Null for static types.</param>
/// <param name="callback">The MethodInfo for the callback method.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback)
{
if (eventInfo == null)

View File

@@ -0,0 +1,115 @@
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ICD.Common.Utils
{
public static class RegexUtils
{
/// <summary>
/// Shim to perform Match and Matches in one call.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="match"></param>
/// <returns></returns>
public static bool Matches(string input, string pattern, out Match match)
{
match = Regex.Match(input, pattern);
return match.Success;
}
/// <summary>
/// Shim to perform Match and Matches in one call.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="options"></param>
/// <param name="match"></param>
/// <returns></returns>
public static bool Matches(string input, string pattern, RegexOptions options, out Match match)
{
match = Regex.Match(input, pattern, options);
return match.Success;
}
/// <summary>
/// Uses the pattern to replace the specified group with the provided replacement string.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="groupName"></param>
/// <param name="replacement"></param>
/// <returns></returns>
public static string ReplaceGroup(string input, string pattern, string groupName, string replacement)
{
return ReplaceGroup(input, pattern, groupName, replacement, RegexOptions.None);
}
/// <summary>
/// Uses the pattern to replace the specified group with the provided replacement string.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="groupName"></param>
/// <param name="replacement"></param>
/// <returns></returns>
public static string ReplaceGroup(string input, string pattern, string groupName, Func<Match, string> replacement)
{
return ReplaceGroup(input, pattern, groupName, replacement, RegexOptions.None);
}
/// <summary>
/// Uses the pattern to replace the specified group with the provided replacement string.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="groupName"></param>
/// <param name="replacement"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string ReplaceGroup(string input, string pattern, string groupName, string replacement, RegexOptions options)
{
return ReplaceGroup(input, pattern, groupName, match => replacement, options);
}
/// <summary>
/// Uses the pattern to replace the specified group with the provided replacement string.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="groupName"></param>
/// <param name="replacement"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string ReplaceGroup(string input, string pattern, string groupName, Func<Match, string> replacement, RegexOptions options)
{
MatchEvaluator evaluator =
m =>
{
string replacementString = replacement(m);
Group group = m.Groups[groupName];
StringBuilder sb = new StringBuilder();
int previousCaptureEnd = 0;
foreach (Capture capture in group.Captures.Cast<Capture>())
{
int currentCaptureEnd = capture.Index + capture.Length - m.Index;
int currentCaptureLength = capture.Index - m.Index - previousCaptureEnd;
sb.Append(m.Value.Substring(previousCaptureEnd, currentCaptureLength));
sb.Append(replacementString);
previousCaptureEnd = currentCaptureEnd;
}
sb.Append(m.Value.Substring(previousCaptureEnd));
return sb.ToString();
};
return Regex.Replace(input, pattern, evaluator, options);
}
}
}

View File

@@ -13,7 +13,7 @@ namespace ICD.Common.Utils
private readonly object m_Instance;
private readonly List<string> m_PropertyOrder;
private readonly Dictionary<string, object> m_PropertyValues;
private readonly Dictionary<string, string> m_PropertyValues;
/// <summary>
/// Constructor.
@@ -24,11 +24,11 @@ namespace ICD.Common.Utils
m_Instance = instance;
m_PropertyOrder = new List<string>();
m_PropertyValues = new Dictionary<string, object>();
m_PropertyValues = new Dictionary<string, string>();
}
/// <summary>
/// Adds the property with the given name and value to the
/// Adds the property with the given name and value to the builder.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
@@ -37,6 +37,21 @@ namespace ICD.Common.Utils
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
string valueString = GetValueStringRepresentation(value);
m_PropertyValues[name] = valueString;
}
/// <summary>
/// Adds the property with the given name and value to the builder without any additonal formatting.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AppendPropertyRaw(string name, string value)
{
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
m_PropertyValues[name] = value;
}
@@ -60,8 +75,7 @@ namespace ICD.Common.Utils
builder.Append(property);
builder.Append('=');
object value = m_PropertyValues[property];
string valueString = GetValueStringRepresentation(value);
string valueString = m_PropertyValues[property];
builder.Append(valueString);
if (index < m_PropertyOrder.Count - 1)

View File

@@ -66,6 +66,7 @@ namespace ICD.Common.Utils
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}

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