Compare commits

...

170 Commits

Author SHA1 Message Date
Chris Cameron
644388edad chore: Updating changelog, incrementing minor version 2019-02-07 16:55:22 -05:00
Chris Cameron
cbb05c8b79 refactor: Removing unused code 2019-02-07 16:53:33 -05:00
Chris Cameron
066c9f93a7 chore: Updating AssemblyInfo 2019-02-07 15:32:49 -05:00
Chris Cameron
9aa7459b0f chore: Updating changelog 2019-02-07 14:42:22 -05:00
Chris Cameron
f8a813c97b perf: Removing redundant code, logging micro-optimization 2019-02-07 10:37:35 -05:00
Chris Cameron
be6a6de65a perf: ReprBuilder micro-optimizations 2019-02-06 17:09:37 -05:00
Chris Cameron
ffe3e67241 perf: Better JSON serialization of nullable types 2019-02-06 13:10:52 -05:00
Chris Cameron
141d911eb0 perf: Reduced size of JSON serialized nullable types 2019-02-06 11:31:14 -05:00
Chris Cameron
d6abf3fdf6 perf: Don't serialize namespace for builtin types 2019-02-04 13:22:59 -05:00
Chris Cameron
46a1ea09b6 refactor: Moving JSON type name util into TypeExtensions 2019-02-04 12:03:15 -05:00
Chris Cameron
2dc705d335 perf: Significantly reducing the size of JSON serialized types 2019-02-04 11:38:39 -05:00
Chris Cameron
0700a357dd feat: Adding ToStringJsonConverter 2019-02-01 14:52:33 -05:00
Chris Cameron
5fb7636ed4 feat: Extension method for reading JSON token as a Guid 2019-01-30 10:52:44 -05:00
Chris Cameron
196c2a535a feat: Added shims for ReflectionUtils.SubscribeEvent for known callbacks 2019-01-29 21:59:10 -05:00
Chris Cameron
db50ada952 refactor: Tidying 2019-01-29 17:49:23 -05:00
Chris Cameron
087b04fd62 chore: Updating changelog, incrementing major version 2019-01-29 14:52:36 -05:00
Chris Cameron
ea73351d39 refactor: Removing redundant critical section, support for chaining TableBuilder methods 2019-01-28 16:58:21 -05:00
Chris Cameron
ca1fae29b0 chore: Updating nuget packages 2019-01-28 14:19:38 -05:00
Chris Cameron
1916c2b750 refactor: Reducing duplicate code 2019-01-28 14:02:16 -05:00
Chris Cameron
3af2544e70 feat: ReprBuilder better supports method chaining 2019-01-28 13:32:35 -05:00
Chris Cameron
20e8aa93f2 Merge branch 'feat/ConsoleOnPrint' of Common/Utils into dev 2019-01-28 13:04:40 +00:00
Drew Tingen
3937902f38 Merge remote-tracking branch 'origin/dev' into feat/ConsoleOnPrint
# Conflicts:
#	CHANGELOG.md
2019-01-27 22:59:25 -08:00
Drew Tingen
021d5781a3 Merge branch 'feat/json' of Common/Utils into dev 2019-01-28 06:56:15 +00:00
Drew Tingen
b429e4bc53 feat: OnConsolePrint event and better VC-4 console support 2019-01-27 22:55:24 -08:00
Chris Cameron
4a203e2449 refactor: Reducing duplicate code 2019-01-27 17:55:47 -05:00
Chris Cameron
d41203b856 docs: Updating changelog 2019-01-25 17:11:08 -05:00
Chris Cameron
2f21379e52 Merge remote-tracking branch 'origin/dev' into feat/json 2019-01-25 17:10:03 -05:00
Chris Cameron
f60de4321b chore: Updating changelog, incrementing minor version 2019-01-25 17:07:05 -05:00
Chris Cameron
bd2ea606ae docs: Updating changelog 2019-01-25 16:52:09 -05:00
Chris Cameron
1f20c07e94 refactor: Splitting JSON extensions into Reader and Writer extensions, removing unused code 2019-01-25 14:06:51 -05:00
Chris Cameron
1be588b86a refactor: Simplifying JSON converters 2019-01-25 13:57:41 -05:00
Chris Cameron
488df297fc refactor: Removing unused code 2019-01-25 13:57:19 -05:00
Chris Cameron
80a4d94358 refactor: Removing unused code 2019-01-24 17:28:59 -05:00
Chris Cameron
6a40f3ce18 refactor: Tidying 2019-01-24 16:46:07 -05:00
Chris Cameron
d564a0a423 feat: Adding shorthand method for serializing an object into a JSON message 2019-01-24 16:21:29 -05:00
Chris Cameron
a9a544c433 feat: Simplifying generic JSON converters 2019-01-24 15:58:46 -05:00
Chris Cameron
2e0837f5c8 feat: Extension methods for reading current JSON token to the given type 2019-01-24 15:58:21 -05:00
Chris Cameron
77be0ec477 refactor: Removing unused code 2019-01-24 15:57:53 -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
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
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
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
Jack Kanarish
2ff7073385 feat: add CSV Writer 2018-08-28 10:52:12 -04:00
Chris Cameron
80e6fe33c7 perf: Massive optimization to enum utils 2018-07-26 11:48:22 -04:00
79 changed files with 3283 additions and 1370 deletions

View File

@@ -6,6 +6,87 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [9.1.0] - 2019-02-07
### Added
- Added SubscribeEvent shim for delegate callbacks
- Extension method for reading JSON token as a GUID
- Added ToStringJsonConverter
### Changed
- Significantly reduced size of JSON serialized Types
- Small logging optimizations
## [9.0.0] - 2019-01-29
### Added
- IcdConsole.OnConsolePrint event
### Changed
- Better VC-4 support for IcdConsole
- JSON refactoring for simpler deserialization
## [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

View File

@@ -1,90 +0,0 @@
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

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

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

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Text;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
using Newtonsoft.Json;
using NUnit.Framework;
@@ -17,6 +18,36 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.Inconclusive();
}
[Test]
public void ReadObjectTest()
{
const string json =
"{\"name\":\"Test\",\"help\":\"Test test.\",\"type\":\"System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\",\"value\":\"Test\"}";
Dictionary<string, string> expected = new Dictionary<string, string>
{
{"name", "Test"},
{"help", "Test test."},
{"type", "System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"},
{"value", "Test"}
};
Dictionary<string, string> deserialized = new Dictionary<string, string>();
using (IcdStringReader textReader = new IcdStringReader(json))
{
using (JsonReader reader = new JsonTextReader(textReader.WrappedTextReader))
{
JsonSerializer serializer = new JsonSerializer();
reader.Read();
reader.ReadObject(serializer, (p, r, s) => deserialized.Add(p, (string)r.Value));
}
}
Assert.IsTrue(deserialized.DictionaryEqual(expected));
}
[Test]
public void GetValueAsIntTest()
{
@@ -78,53 +109,5 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.IsTrue(deserialized.SequenceEqual(new[] {1, 2, 3, 4}));
}
[Test]
public void SerializeDictionaryTest()
{
Dictionary<int, string> dict = new Dictionary<int, string>
{
{1, "Item 1"},
{10, "Item 2"},
{15, "Item 3"}
};
JsonSerializer serializer = new JsonSerializer();
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
{
using (JsonWriter writer = new JsonTextWriter(stringWriter))
{
serializer.SerializeDictionary(writer, dict);
}
}
string json = stringBuilder.ToString();
Assert.AreEqual("{\"1\":\"Item 1\",\"10\":\"Item 2\",\"15\":\"Item 3\"}", json);
}
[Test]
public void DeserializeDictionaryTest()
{
const string json = "{\"1\":\"Item 1\",\"10\":\"Item 2\",\"15\":\"Item 3\"}";
JsonSerializer serializer = new JsonSerializer();
Dictionary<int, string> deserialized;
using (StringReader stringReader = new StringReader(json))
{
using (JsonReader reader = new JsonTextReader(stringReader))
{
reader.Read();
deserialized = serializer.DeserializeDictionary<int, string>(reader).ToDictionary();
}
}
Assert.AreEqual(3, deserialized.Count);
Assert.AreEqual("Item 1", deserialized[1]);
Assert.AreEqual("Item 2", deserialized[10]);
Assert.AreEqual("Item 3", deserialized[15]);
}
}
}

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

@@ -155,6 +155,22 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(expected, type.GetNameWithoutGenericArity());
}
[TestCase(typeof(int), "System.Int32")]
[TestCase(typeof(int?), "System.Nullable`1[[System.Int32]]")]
[TestCase(typeof(KeyValuePair<int, string>), "System.Collections.Generic.KeyValuePair`2[[System.Int32],[System.String]]")]
[TestCase(typeof(List<>), "System.Collections.Generic.List`1")]
public void GetMinimalNameTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetMinimalName());
}
[TestCase(typeof(int), "System.Int32, System.Private.CoreLib")]
[TestCase(typeof(int?), "System.Nullable`1[[System.Int32, System.Private.CoreLib]], System.Private.CoreLib")]
public void GetNameWithoutAssemblyDetailsTest(Type type, string expected)
{
Assert.AreEqual(expected, type.GetNameWithoutAssemblyDetails());
}
[TestCase(typeof(string), "string")]
[TestCase(typeof(int?), "int?")]
[TestCase(typeof(List<int?>), "List<int?>")]

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.8.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.12.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,6 @@
using ICD.Common.Utils.IO;
using ICD.Common.Utils.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Json
@@ -141,32 +140,6 @@ namespace ICD.Common.Utils.Tests.Json
Assert.AreEqual("test message", messageName);
}
[Test]
public void DeserializeTypeTest()
{
const string json = "{\"test\":10}";
JObject root = JObject.Parse(json);
JToken token = root["test"];
int value = (int)JsonUtils.Deserialize(typeof(int), token);
Assert.AreEqual(10, value);
}
[Test]
public void DeserializeTestSerializerTest()
{
const string json = "{\"test\":10}";
JObject root = JObject.Parse(json);
JToken token = root["test"];
int value = (int)JsonUtils.Deserialize(typeof(int), token, new JsonSerializer());
Assert.AreEqual(10, value);
}
public sealed class TestSerializable
{
private const string PROPERTY_NAME = "Test";

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

@@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
@@ -149,5 +151,75 @@ namespace ICD.Common.Utils.Tests
// Everything else
Assert.AreEqual(10, ReflectionUtils.ChangeType("10", typeof(int)));
}
#region Subscription Tests
[Test]
public void SubscribeEventTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
Delegate del = ReflectionUtils.SubscribeEvent<IntEventArgs>(this, eventInfo, IncrementCount);
Assert.NotNull(del);
m_Count = 0;
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(10, m_Count);
OnIncrementCount = null;
}
[Test]
public void SubscribeEventMethodInfoTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
MethodInfo methodInfo =
GetType().GetMethod("IncrementCount", BindingFlags.Instance | BindingFlags.NonPublic);
Delegate del = ReflectionUtils.SubscribeEvent(this, eventInfo, this, methodInfo);
Assert.NotNull(del);
m_Count = 0;
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(10, m_Count);
OnIncrementCount = null;
}
[Test]
public void UnsubscribeEventTest()
{
EventInfo eventInfo = GetType().GetEvent("OnIncrementCount", BindingFlags.Instance | BindingFlags.Public);
MethodInfo methodInfo =
GetType().GetMethod("IncrementCount", BindingFlags.Instance | BindingFlags.NonPublic);
Delegate del = ReflectionUtils.SubscribeEvent(this, eventInfo, this, methodInfo);
Assert.NotNull(del);
m_Count = 0;
ReflectionUtils.UnsubscribeEvent(this, eventInfo, del);
OnIncrementCount.Raise(this, new IntEventArgs(10));
Assert.AreEqual(0, m_Count);
}
// Event has to be public
public event EventHandler<IntEventArgs> OnIncrementCount;
private int m_Count;
private void IncrementCount(object sender, IntEventArgs args)
{
m_Count += args.Data;
}
#endregion
}
}

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

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

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

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
@@ -114,7 +101,6 @@ namespace ICD.Common.Utils
foreach (var type in types)
CacheType(type);
s_CachedAssemblies.Add(assembly);
return true;
}
@@ -133,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
@@ -183,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>();
@@ -197,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");
@@ -212,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");
@@ -253,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

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

@@ -1,135 +0,0 @@
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

@@ -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>
@@ -98,11 +92,10 @@ namespace ICD.Common.Utils.Collections
[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;
@@ -116,10 +109,10 @@ namespace ICD.Common.Utils.Collections
[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);
@@ -133,15 +126,12 @@ namespace ICD.Common.Utils.Collections
/// <param name="set"></param>
/// <returns></returns>
[PublicAPI]
public IcdHashSet<T> Intersection(IcdHashSet<T> set)
public IcdHashSet<T> Intersection(IEnumerable<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);
@@ -155,11 +145,22 @@ 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)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return Subtract(set).Union(setToCompare.Subtract(this));
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>
@@ -170,9 +171,10 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public bool IsSubsetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return this.All(setToCompare.Contains);
return Count <= set.Count && this.All(set.Contains);
}
/// <summary>
@@ -183,9 +185,10 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public bool IsProperSubsetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this);
return Count < set.Count && IsSubsetOf(set);
}
/// <summary>
@@ -196,9 +199,10 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public bool IsSupersetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return setToCompare.IsSubsetOf(this);
return set.IsSubsetOf(this);
}
/// <summary>
@@ -209,9 +213,10 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public bool IsProperSupersetOf(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this);
return set.IsProperSubsetOf(this);
}
/// <summary>
@@ -222,14 +227,15 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public bool SetEquals(IcdHashSet<T> set)
{
IcdHashSet<T> setToCompare = set ?? NullSet;
if (set == null)
throw new ArgumentNullException("set");
return IsSupersetOf(setToCompare) && setToCompare.IsSupersetOf(this);
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.
@@ -238,9 +244,9 @@ namespace ICD.Common.Utils.Collections
/// <returns></returns>
public bool Add(T item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
// ReSharper disable CompareNonConstrainedGenericWithNull
if (item == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("item");
if (m_Dict.ContainsKey(item))
@@ -288,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);
}
@@ -309,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

@@ -80,6 +80,20 @@ namespace ICD.Common.Utils.Collections
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()

View File

@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Collections
{
@@ -16,8 +15,6 @@ namespace ICD.Common.Utils.Collections
private readonly LinkedList<TContents> m_Collection;
private int m_MaxSize;
private readonly SafeCriticalSection m_CollectionLock;
#region Properties
/// <summary>
@@ -41,13 +38,13 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count { get { return m_CollectionLock.Execute(() => m_Collection.Count); } }
public int Count { get { return m_Collection.Count; } }
/// <summary>
/// The IsSynchronized Boolean property returns True if the
/// collection is designed to be thread safe; otherwise, it returns False.
/// </summary>
public bool IsSynchronized { get { return true; } }
public bool IsSynchronized { get { return false; } }
/// <summary>
/// The SyncRoot property returns an object, which is used for synchronizing
@@ -64,7 +61,6 @@ namespace ICD.Common.Utils.Collections
/// <param name="maxSize"></param>
public ScrollQueue(int maxSize)
{
m_CollectionLock = new SafeCriticalSection();
m_Collection = new LinkedList<TContents>();
MaxSize = maxSize;
}
@@ -76,7 +72,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
public void Clear()
{
m_CollectionLock.Execute(() => m_Collection.Clear());
m_Collection.Clear();
}
/// <summary>
@@ -86,17 +82,8 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public void Enqueue(TContents item)
{
m_CollectionLock.Enter();
try
{
m_Collection.AddLast(item);
Trim();
}
finally
{
m_CollectionLock.Leave();
}
m_Collection.AddLast(item);
Trim();
}
/// <summary>
@@ -106,18 +93,9 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public TContents Dequeue()
{
m_CollectionLock.Enter();
try
{
TContents output = m_Collection.First.Value;
m_Collection.RemoveFirst();
return output;
}
finally
{
m_CollectionLock.Leave();
}
TContents output = Peek();
m_Collection.RemoveFirst();
return output;
}
/// <summary>
@@ -127,7 +105,7 @@ namespace ICD.Common.Utils.Collections
[PublicAPI]
public TContents Peek()
{
return m_CollectionLock.Execute(() => m_Collection.First.Value);
return m_Collection.First.Value;
}
#endregion
@@ -141,24 +119,15 @@ namespace ICD.Common.Utils.Collections
public IEnumerator<TContents> GetEnumerator()
{
return m_CollectionLock.Execute(() => m_Collection.ToList(Count).GetEnumerator());
return m_Collection.GetEnumerator();
}
void ICollection.CopyTo(Array myArr, int index)
{
m_CollectionLock.Enter();
try
foreach (TContents item in m_Collection)
{
foreach (TContents item in m_Collection)
{
myArr.SetValue(item, index);
index++;
}
}
finally
{
m_CollectionLock.Leave();
myArr.SetValue(item, index);
index++;
}
}
@@ -171,17 +140,8 @@ namespace ICD.Common.Utils.Collections
/// </summary>
private void Trim()
{
m_CollectionLock.Enter();
try
{
while (Count > MaxSize)
m_Collection.RemoveFirst();
}
finally
{
m_CollectionLock.Leave();
}
while (Count > MaxSize)
m_Collection.RemoveFirst();
}
#endregion

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

@@ -40,7 +40,7 @@ namespace ICD.Common.Utils
/// Returns true if the given type is an enum.
/// </summary>
/// <returns></returns>
private static bool IsEnumType(Type type)
public static bool IsEnumType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");

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

@@ -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)
{
@@ -111,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)
@@ -122,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>
@@ -452,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

@@ -58,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>
@@ -106,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>
@@ -156,17 +178,32 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
if (index < 0)
throw new ArgumentOutOfRangeException("index");
item = default(T);
try
IList<T> list = extends as IList<T>;
if (list != null)
{
item = extends.ElementAt(index);
if (index >= list.Count)
return false;
item = list[index];
return true;
}
catch (ArgumentOutOfRangeException)
int current = 0;
foreach (T value in extends)
{
return false;
if (current == index)
{
item = value;
return true;
}
current++;
}
return false;
}
/// <summary>
@@ -227,6 +264,12 @@ namespace ICD.Common.Utils.Extensions
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())
@@ -285,6 +328,12 @@ namespace ICD.Common.Utils.Extensions
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)
@@ -383,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>
@@ -616,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");
@@ -634,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");
@@ -692,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>
@@ -707,7 +774,7 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
return new IcdHashSet<T>(extends);
return extends.ToIcdHashSet(EqualityComparer<T>.Default);
}
/// <summary>
@@ -778,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");
@@ -795,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");

View File

@@ -3,25 +3,37 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ICD.Common.Utils.Extensions
{
/// <summary>
/// Extension methods for working with JSON.
/// </summary>
public static class JsonExtensions
public static class JsonReaderExtensions
{
/// <summary>
/// Writes the object value.
/// Reads the current token in the reader and deserializes to the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
JsonSerializer serializer = new JsonSerializer();
return extends.ReadAsObject<T>(serializer);
}
/// <summary>
/// Reads the current token in the reader and deserializes to the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
/// <param name="converter"></param>
[PublicAPI]
public static void WriteObject(this JsonWriter extends, object value, JsonSerializer serializer,
JsonConverter converter)
/// <returns></returns>
public static T ReadAsObject<T>(this JsonReader extends, JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -29,36 +41,48 @@ namespace ICD.Common.Utils.Extensions
if (serializer == null)
throw new ArgumentNullException("serializer");
if (converter == null)
throw new ArgumentNullException("converter");
JObject jObject = JObject.FromObject(value, serializer);
jObject.WriteTo(extends, converter);
return serializer.Deserialize<T>(extends);
}
/// <summary>
/// Writes the type value.
/// Reads through the current object token and calls the callback for each property value.
/// </summary>
/// <param name="extends"></param>
/// <param name="type"></param>
[PublicAPI]
public static void WriteType(this JsonWriter extends, Type type)
/// <param name="serializer"></param>
/// <param name="readPropertyValue"></param>
public static void ReadObject(this JsonReader extends, JsonSerializer serializer,
Action<string, JsonReader, JsonSerializer> readPropertyValue)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (type == null)
{
extends.WriteNull();
if (serializer == null)
throw new ArgumentNullException("serializer");
if (readPropertyValue == null)
throw new ArgumentNullException("readPropertyValue");
if (extends.TokenType == JsonToken.Null)
return;
if (extends.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, extends.TokenType));
while (extends.Read())
{
if (extends.TokenType == JsonToken.EndObject)
break;
// Get the property
if (extends.TokenType != JsonToken.PropertyName)
continue;
string property = (string)extends.Value;
// Read into the value
extends.Read();
readPropertyValue(property, extends, serializer);
}
// Find the smallest possible name representation for the type that will still resolve
string name = Type.GetType(type.FullName) == null
? type.AssemblyQualifiedName
: type.FullName;
extends.WriteValue(name);
}
/// <summary>
@@ -167,55 +191,18 @@ namespace ICD.Common.Utils.Extensions
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// Gets the current value as a guid.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items)
/// <returns></returns>
[PublicAPI]
public static Guid GetValueAsGuid(this JsonReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="write"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items,
Action<JsonSerializer, JsonWriter, TItem> write)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
if (write == null)
throw new ArgumentNullException("write");
writer.WriteStartArray();
{
foreach (TItem item in items)
write(extends, writer, item);
}
writer.WriteEndArray();
string stringValue = extends.GetValueAsString();
return new Guid(stringValue);
}
/// <summary>
@@ -285,90 +272,5 @@ namespace ICD.Common.Utils.Extensions
reader.Read();
}
}
/// <summary>
/// Serializes the given sequence of key-value-pairs to the writer.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeDictionary<TKey, TValue>(this JsonSerializer extends, JsonWriter writer,
IEnumerable<KeyValuePair<TKey, TValue>> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
writer.WriteStartObject();
{
foreach (KeyValuePair<TKey, TValue> kvp in items)
{
writer.WritePropertyName(kvp.Key.ToString());
extends.Serialize(writer, kvp.Value);
}
}
writer.WriteEndObject();
}
/// <summary>
/// Deserializes a dictionary of items from the reader's current value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <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)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (reader == null)
throw new ArgumentNullException("reader");
if (reader.TokenType == JsonToken.Null)
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();
while (reader.TokenType != JsonToken.EndObject)
{
TKey key = (TKey)Convert.ChangeType(reader.Value, typeof(TKey), null);
// Step into the value
reader.Read();
TValue value = serializer.Deserialize<TValue>(reader);
yield return new KeyValuePair<TKey, TValue>(key, value);
// Read out of the last value
reader.Read();
}
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{
public static class JsonWriterExtensions
{
/// <summary>
/// Writes the type value.
/// </summary>
/// <param name="extends"></param>
/// <param name="type"></param>
[PublicAPI]
public static void WriteType(this JsonWriter extends, Type type)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (type == null)
{
extends.WriteNull();
return;
}
string name = type.GetMinimalName();
extends.WriteValue(name);
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
}
/// <summary>
/// Serializes the given sequence of items to the writer.
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="extends"></param>
/// <param name="writer"></param>
/// <param name="items"></param>
/// <param name="write"></param>
public static void SerializeArray<TItem>(this JsonSerializer extends, JsonWriter writer, IEnumerable<TItem> items,
Action<JsonSerializer, JsonWriter, TItem> write)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (writer == null)
throw new ArgumentNullException("writer");
if (items == null)
throw new ArgumentNullException("items");
if (write == null)
throw new ArgumentNullException("write");
writer.WriteStartArray();
{
foreach (TItem item in items)
write(extends, writer, item);
}
writer.WriteEndArray();
}
}
}

View File

@@ -71,7 +71,10 @@ namespace ICD.Common.Utils.Extensions
sigBuilder.Append("(");
firstParam = true;
#if !SIMPLSHARP
bool secondParam = false;
#endif
foreach (ParameterInfo param in method.GetParameters())
{

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

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>
@@ -101,26 +104,18 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
private static IEnumerable<string> SplitIterator(string value, char delimeter, int count)
{
if (count < 2)
while (count > 1)
{
yield return value;
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 = value.IndexOf(delimeter);
if (index < 0)
{
yield return value;
yield break;
}
string first = value.Substring(0, index);
string second = value.Substring(index + 1);
count--;
yield return first;
foreach (string item in second.Split(delimeter, count))
yield return item;
yield return value;
}
/// <summary>
@@ -142,97 +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.
/// </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");
return SplitIterator(extends, delimeter);
}
/// <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="value"></param>
/// <param name="delimeter"></param>
/// <returns></returns>
private static IEnumerable<string> SplitIterator(string value, string delimeter)
{
int dSum = 0;
int sSum = 0;
int length = value.Length;
int delimiterLength = delimeter.Length;
if (delimiterLength == 0 || length == 0 || length < delimiterLength)
{
yield return value;
yield break;
}
char[] cd = delimeter.ToCharArray();
char[] cs = value.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 && value.Substring(i, delimiterLength) == delimeter)
{
yield return value.Substring(start, i - start);
start = i + delimiterLength;
}
sSum += cs[i + delimiterLength] - cs[i];
}
if (dSum == sSum && value.Substring(length - delimiterLength, delimiterLength) == delimeter)
{
yield return value.Substring(start, length - delimiterLength - start);
yield return string.Empty;
}
else
yield return value.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>
@@ -248,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>
@@ -261,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>

View File

@@ -62,6 +62,8 @@ namespace ICD.Common.Utils.Extensions
private static readonly Dictionary<Type, Type[]> s_TypeBaseTypes;
private static readonly Dictionary<Type, Type[]> s_TypeImmediateInterfaces;
private static readonly Dictionary<Type, Type[]> s_TypeMinimalInterfaces;
private static readonly Dictionary<Type, string> s_TypeToMinimalName;
private static readonly Dictionary<Type, string> s_TypeToNameWithoutAssemblyDetails;
/// <summary>
/// Static constructor.
@@ -72,6 +74,8 @@ namespace ICD.Common.Utils.Extensions
s_TypeBaseTypes = new Dictionary<Type, Type[]>();
s_TypeImmediateInterfaces = new Dictionary<Type, Type[]>();
s_TypeMinimalInterfaces = new Dictionary<Type, Type[]>();
s_TypeToMinimalName = new Dictionary<Type, string>();
s_TypeToNameWithoutAssemblyDetails = new Dictionary<Type, string>();
}
/// <summary>
@@ -307,6 +311,128 @@ namespace ICD.Common.Utils.Extensions
return index == -1 ? name : name.Substring(0, index);
}
/// <summary>
/// Gets the smallest possible string representation for the given type that
/// can be converted back to a Type via Type.GetType(string).
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetMinimalName(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToMinimalName.TryGetValue(extends, out name))
{
// Generics are a pain
if (extends.IsGenericType)
{
string nameWithoutAssemblyDetails = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
int genericStart = nameWithoutAssemblyDetails.IndexOf('[');
if (genericStart < 0)
{
name = nameWithoutAssemblyDetails;
}
else
{
string genericParameterNames =
string.Join("],[", extends.GetGenericArguments().Select(t => t.GetMinimalName()).ToArray());
int genericEnd = nameWithoutAssemblyDetails.LastIndexOf(']');
name = new StringBuilder().Append(nameWithoutAssemblyDetails, 0, genericStart + 2)
.Append(genericParameterNames)
.Append(nameWithoutAssemblyDetails, genericEnd - 1,
nameWithoutAssemblyDetails.Length - genericEnd + 1)
.ToString();
}
}
else
{
name = Type.GetType(extends.FullName) == null
? extends.GetNameWithoutAssemblyDetails()
: extends.FullName;
}
s_TypeToMinimalName.Add(extends, name);
}
return name;
}
/// <summary>
/// Gets the string representation for the type.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string GetNameWithoutAssemblyDetails(this Type extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string name;
if (!s_TypeToNameWithoutAssemblyDetails.TryGetValue(extends, out name))
{
name = RemoveAssemblyDetails(extends.AssemblyQualifiedName);
s_TypeToNameWithoutAssemblyDetails.Add(extends, name);
}
return name;
}
/// <summary>
/// Taken from Newtonsoft.Json.Utilities.ReflectionUtils
/// Removes the assembly details from a type assembly qualified name.
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
{
StringBuilder builder = new StringBuilder();
// loop through the type name and filter out qualified assembly details from nested type names
bool writingAssemblyName = false;
bool skippingAssemblyDetails = false;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ']':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ',':
if (!writingAssemblyName)
{
writingAssemblyName = true;
builder.Append(current);
}
else
{
skippingAssemblyDetails = true;
}
break;
default:
if (!skippingAssemblyDetails)
{
builder.Append(current);
}
break;
}
}
return builder.ToString();
}
/// <summary>
/// Gets the type name as it would appear in code.
/// </summary>
@@ -317,45 +443,48 @@ namespace ICD.Common.Utils.Extensions
if (extends == null)
throw new ArgumentNullException("extends");
// Nullable
Type nullableType = Nullable.GetUnderlyingType(extends);
if (nullableType != null)
return nullableType.GetSyntaxName() + "?";
if (!(extends.IsGenericType && extends.Name.Contains('`')))
// Generic
if (extends.IsGenericType)
{
switch (extends.Name)
StringBuilder sb = new StringBuilder(extends.Name.Substring(0, extends.Name.IndexOf('`')));
sb.Append('<');
bool first = true;
foreach (Type t in extends.GetGenericArguments())
{
case "String":
return "string";
case "Int32":
return "int";
case "Decimal":
return "decimal";
case "Object":
return "object";
case "Void":
return "void";
default:
return string.IsNullOrEmpty(extends.FullName) ? extends.Name : extends.FullName;
if (!first)
sb.Append(',');
sb.Append(t.GetSyntaxName());
first = false;
}
sb.Append('>');
return sb.ToString();
}
StringBuilder sb = new StringBuilder(extends.Name.Substring(0, extends.Name.IndexOf('`')));
sb.Append('<');
bool first = true;
foreach (Type t in extends.GetGenericArguments())
// Default
switch (extends.Name)
{
if (!first)
sb.Append(',');
sb.Append(t.GetSyntaxName());
first = false;
case "String":
return "string";
case "Int32":
return "int";
case "Decimal":
return "decimal";
case "Object":
return "object";
case "Void":
return "void";
default:
return extends.Name;
}
sb.Append('>');
return sb.ToString();
}
}
}

View File

@@ -39,9 +39,9 @@
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
<PackageReference Include="Microsoft.Data.SQLite" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>

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,8 +75,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\AbstractIcdAttribute.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" />
@@ -84,7 +85,12 @@
<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" />
@@ -99,16 +105,19 @@
<Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\DayOfWeekExtensions.cs" />
<Compile Include="Extensions\JsonWriterExtensions.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="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="Json\ToStringJsonConverter.cs" />
<Compile Include="ProcessorUtils.SimplSharp.cs" />
<Compile Include="ProcessorUtils.Standard.cs" />
<Compile Include="ProgramUtils.SimplSharp.cs" />
@@ -146,7 +155,7 @@
<Compile Include="Extensions\EnumerableExtensions.cs" />
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="Extensions\EventHandlerExtensions.cs" />
<Compile Include="Extensions\JsonExtensions.cs" />
<Compile Include="Extensions\JsonReaderExtensions.cs" />
<Compile Include="Extensions\QueueExtensions.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Extensions\StringBuilderExtensions.cs" />
@@ -195,12 +204,16 @@
<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

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

@@ -94,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,6 +3,7 @@ using Crestron.SimplSharp.CrestronIO;
#else
using System.IO;
#endif
using System.Text;
namespace ICD.Common.Utils.IO
{
@@ -32,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

@@ -1,5 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#else
@@ -69,16 +70,16 @@ 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.ToString()))
{
folder += Path.DirectorySeparatorChar;
}
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
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

@@ -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,6 +1,8 @@
using System;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
@@ -18,6 +20,8 @@ namespace ICD.Common.Utils
Administrator = 2
}
public static event EventHandler<StringEventArgs> OnConsolePrint;
/// <summary>
/// Wraps CrestronConsole.ConsoleCommandResponse for S+ compatibility.
/// </summary>
@@ -41,17 +45,23 @@ namespace ICD.Common.Utils
message = string.Format(message, args);
#if SIMPLSHARP
try
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpPro)
{
CrestronConsole.ConsoleCommandResponse(message);
try
{
CrestronConsole.ConsoleCommandResponse(message);
}
catch (NotSupportedException)
{
Print(message);
}
return;
}
catch (NotSupportedException)
{
Print(message);
}
#else
Print(message, args);
#endif
Print(message);
}
public static void PrintLine(string message)
@@ -90,10 +100,12 @@ namespace ICD.Common.Utils
public static void Print(string message)
{
#if SIMPLSHARP
CrestronConsole.Print(message);
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono)
CrestronConsole.Print(message);
#else
Console.Write(message);
#endif
OnConsolePrint.Raise(null, new StringEventArgs(message));
}
public static void Print(string message, params object[] args)
@@ -123,6 +135,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;

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

@@ -1,5 +1,9 @@
using System;
#if !SIMPLSHARP
using System.Linq;
#endif
using System.Text;
using System.Text.RegularExpressions;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
@@ -62,6 +66,7 @@ namespace ICD.Common.Utils
/// Constructor.
/// </summary>
public IcdUriBuilder()
: this((Uri)null)
{
}
@@ -70,7 +75,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="uri"></param>
public IcdUriBuilder(string uri)
: this(new Uri(uri))
: this(new Uri(uri, UriKind.RelativeOrAbsolute))
{
}
@@ -80,6 +85,12 @@ namespace ICD.Common.Utils
/// <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();
@@ -103,7 +114,7 @@ namespace ICD.Common.Utils
StringBuilder builder = new StringBuilder();
// Scheme
string scheme = string.IsNullOrEmpty(Scheme) ? "http" : Scheme;
string scheme = string.IsNullOrEmpty(Scheme) ? Uri.UriSchemeHttp : Scheme;
builder.Append(scheme);
builder.Append(':');
@@ -152,6 +163,125 @@ namespace ICD.Common.Utils
}
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,5 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
@@ -29,12 +30,6 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
if (value == null)
{
writer.WriteNull();
return;
}
WriteJson(writer, (T)value, serializer);
}
@@ -116,30 +111,15 @@ namespace ICD.Common.Utils.Json
if (serializer == null)
throw new ArgumentNullException("serializer");
T output = default(T);
bool instantiated = false;
if (reader.TokenType == JsonToken.Null)
return default(T);
while (reader.Read())
{
if (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.EndObject)
break;
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, reader.TokenType));
if (!instantiated)
{
instantiated = true;
output = Instantiate();
}
T output = Instantiate();
// Get the property
if (reader.TokenType != JsonToken.PropertyName)
continue;
string property = (string)reader.Value;
// Read into the value
reader.Read();
ReadProperty(property, reader, output, serializer);
}
reader.ReadObject(serializer, (p, r, s) => ReadProperty(p, r, output, s));
return output;
}

View File

@@ -22,28 +22,6 @@ namespace ICD.Common.Utils.Json
private const string MESSAGE_NAME_PROPERTY = "m";
private const string MESSAGE_DATA_PROPERTY = "d";
/// <summary>
/// Forces Newtonsoft to cache the given type for faster subsequent usage.
/// </summary>
/// <typeparam name="T"></typeparam>
public static void CacheType<T>()
where T : new()
{
CacheType(typeof(T));
}
/// <summary>
/// Forces Newtonsoft to cache the given type for faster subsequent usage.
/// </summary>
public static void CacheType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
string serialized = JsonConvert.SerializeObject(ReflectionUtils.CreateInstance(type));
JsonConvert.DeserializeObject(serialized, type);
}
/// <summary>
/// Gets the token as a DateTime value.
/// </summary>
@@ -87,31 +65,6 @@ namespace ICD.Common.Utils.Json
}
}
/// <summary>
/// Pretty-prints the JSON document.
/// </summary>
/// <param name="json"></param>
[PublicAPI]
public static void Print(string json)
{
if (json == null)
throw new ArgumentNullException("json");
string formatted = Format(json);
IcdConsole.PrintLine(formatted);
}
/// <summary>
/// Serializes the given item and pretty-prints to JSON.
/// </summary>
/// <param name="value"></param>
[PublicAPI]
public static void Print(object value)
{
string formatted = Format(value);
IcdConsole.PrintLine(formatted);
}
/// <summary>
/// Serializes the given item and formats the JSON into a human-readable form.
/// </summary>
@@ -234,6 +187,22 @@ namespace ICD.Common.Utils.Json
}
}
/// <summary>
/// Serializes to json, wrapping the object with a message property to differentiate between messages.
/// E.g.
/// { a = 1 }
/// Becomes
/// { m = "Test", d = { a = 1 } }
/// </summary>
/// <param name="value"></param>
/// <param name="messageName"></param>
/// <returns></returns>
[PublicAPI]
public static string SerializeMessage(object value, string messageName)
{
return SerializeMessage(w => new JsonSerializer().Serialize(w, value), messageName);
}
/// <summary>
/// Serializes to json, wrapping the object with a message property to differentiate between messages.
/// E.g.
@@ -317,44 +286,5 @@ namespace ICD.Common.Utils.Json
},
json);
}
/// <summary>
/// Deserializes the given token based on the known type.
/// </summary>
/// <param name="type"></param>
/// <param name="token"></param>
/// <returns></returns>
public static object Deserialize(Type type, JToken token)
{
if (type == null)
throw new ArgumentNullException("type");
if (token == null)
throw new ArgumentNullException("token");
return Deserialize(type, token, new JsonSerializer());
}
/// <summary>
/// Deserializes the given token based on the known type.
/// </summary>
/// <param name="type"></param>
/// <param name="token"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public static object Deserialize(Type type, JToken token, JsonSerializer serializer)
{
if (type == null)
throw new ArgumentNullException("type");
if (token == null)
throw new ArgumentNullException("token");
if (serializer == null)
throw new ArgumentNullException("serializer");
using (JTokenReader jsonReader = new JTokenReader(token))
return serializer.Deserialize(jsonReader, type);
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
#else
using System.Reflection;
#endif
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{
/// <summary>
/// Simply reads/writes values from/to JSON as their string representation.
/// </summary>
[PublicAPI]
public sealed class ToStringJsonConverter : JsonConverter
{
private static readonly Dictionary<Type, MethodInfo> s_ParseMethods;
public override bool CanRead { get { return true; } }
/// <summary>
/// Static constructor.
/// </summary>
static ToStringJsonConverter()
{
s_ParseMethods = new Dictionary<Type, MethodInfo>();
}
#region Methods
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
MethodInfo parse = GetParseMethod(objectType);
if (parse != null)
return parse.Invoke(null, new[] {reader.Value});
throw new ArgumentException(
string.Format("{0} does not have a 'static {0} Parse(string)' method.", objectType.Name),
"objectType");
}
#endregion
[CanBeNull]
private static MethodInfo GetParseMethod(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
MethodInfo output;
if (!s_ParseMethods.TryGetValue(type, out output))
{
output = type
#if SIMPLSHARP
.GetCType()
.GetMethod("Parse",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
CType.DefaultBinder,
new CType[] {typeof(string)},
new ParameterModifier[] {});
#else
.GetTypeInfo()
.GetMethod("Parse",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
Type.DefaultBinder,
new[] {typeof(string)},
new ParameterModifier[] {});
#endif
s_ParseMethods.Add(type, output);
}
return output;
}
}
}

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

@@ -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("5.0.0.0")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("9.1.0.0")]

View File

@@ -3,6 +3,7 @@ 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
{
@@ -97,23 +98,23 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// Returns true if there is a path from the given root to the given destination node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="child"></param>
/// <param name="destination"></param>
/// <param name="getChildren"></param>
/// <returns></returns>
public static bool BreadthFirstSearch<T>(T root, T child, Func<T, IEnumerable<T>> getChildren)
public static bool BreadthFirstSearch<T>(T root, T destination, Func<T, IEnumerable<T>> getChildren)
{
if (getChildren == null)
throw new ArgumentNullException("getChildren");
return BreadthFirstSearchPath(root, child, getChildren) != null;
return BreadthFirstSearch(root, destination, getChildren, EqualityComparer<T>.Default);
}
/// <summary>
/// Returns true if there is a path from the given root to the given child node.
/// Returns true if there is a path from the given root to the given destination node.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
@@ -159,9 +160,9 @@ namespace ICD.Common.Utils
Queue<T> process = new Queue<T>();
process.Enqueue(root);
while (process.Count > 0)
T current;
while (process.Dequeue(out current))
{
T current = process.Dequeue();
yield return current;
foreach (T child in getChildren(current))
@@ -214,10 +215,9 @@ namespace ICD.Common.Utils
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
while (queue.Count > 0)
T current;
while (queue.Dequeue(out current))
{
T current = queue.Dequeue();
foreach (T node in getChildren(current))
{
if (nodeParents.ContainsKey(node))
@@ -235,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");
@@ -266,18 +278,17 @@ namespace ICD.Common.Utils
throw new ArgumentNullException("comparer");
IcdHashSet<T> destinationsToBeProcessed = new IcdHashSet<T>(destinations);
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)).ToArray())
{
destinationsToBeProcessed.Remove(destination);
pathsToReturn.Add(destination, new[] {root});
destinationsToBeProcessed.Remove(destination);
yield return new KeyValuePair<T, IEnumerable<T>>(destination, new[] {root});
}
if (destinationsToBeProcessed.Count == 0)
return pathsToReturn;
yield break;
Queue<T> queue = new Queue<T>();
queue.Enqueue(root);
@@ -298,15 +309,15 @@ namespace ICD.Common.Utils
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)).ToArray())
{
destinationsToBeProcessed.Remove(destination);
pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
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
@@ -167,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
@@ -182,6 +184,7 @@ namespace ICD.Common.Utils
/// 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)
@@ -195,6 +198,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[NotNull]
public static T CreateInstance<T>(params object[] parameters)
{
if (parameters == null)
@@ -207,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)
@@ -237,6 +242,7 @@ namespace ICD.Common.Utils
/// <param name="type"></param>
/// <param name="parameters"></param>
/// <returns></returns>
[NotNull]
public static ConstructorInfo GetConstructor(Type type, params object[] parameters)
{
if (type == null)
@@ -245,13 +251,18 @@ namespace ICD.Common.Utils
if (parameters == null)
throw new ArgumentNullException("parameters");
return
type
ConstructorInfo info;
if(!type
#if SIMPLSHARP
.GetCType()
.GetCType()
#endif
.GetConstructors()
.First(c => MatchesConstructorParameters(c, parameters));
.GetConstructors()
.Where(c => MatchesConstructorParameters(c, parameters))
.TryFirst(out info))
throw new ArgumentException("Couldn't find a constructor matching the given parameters.");
return info;
}
/// <summary>
@@ -259,6 +270,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
[NotNull]
public static Assembly LoadAssemblyFromPath(string path)
{
if (path == null)
@@ -325,8 +337,9 @@ namespace ICD.Common.Utils
if (valueType.IsIntegerNumeric())
return Enum.ToObject(type, value);
if (value is string)
return Enum.Parse(type, value as string, false);
string valueAsString = value as string;
if (valueAsString != null)
return Enum.Parse(type, valueAsString, false);
}
return Convert.ChangeType(value, type, null);
@@ -340,13 +353,58 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Subscribes to the event on the given instance using the handler and callback method.
/// Subscribes to the event on the given instance using the event handler.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="handler">The instance with the callback MethodInfo. Null for static types.</param>
/// <param name="callback">The MethodInfo for the callback method.</param>
/// <param name="eventHandler">The EventHandler for the callback.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, Action<object> eventHandler)
{
if (eventInfo == null)
throw new ArgumentNullException("eventInfo");
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
object handler = eventHandler.Target;
MethodInfo callback = EventHandlerExtensions.GetMethodInfo(eventHandler);
return SubscribeEvent(instance, eventInfo, handler, callback);
}
/// <summary>
/// Subscribes to the event on the given instance using the event handler.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="eventHandler">The EventHandler for the callback.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent<T>(object instance, EventInfo eventInfo, Action<object, T> eventHandler)
{
if (eventInfo == null)
throw new ArgumentNullException("eventInfo");
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
object handler = eventHandler.Target;
MethodInfo callback = EventHandlerExtensions.GetMethodInfo(eventHandler);
return SubscribeEvent(instance, eventInfo, handler, callback);
}
/// <summary>
/// Subscribes to the event on the given instance using the handler and eventHandler method.
/// </summary>
/// <param name="instance">The instance with the event. Null for static types.</param>
/// <param name="eventInfo">The EventInfo for the event.</param>
/// <param name="handler">The instance with the eventHandler MethodInfo. Null for static types.</param>
/// <param name="callback">The MethodInfo for the eventHandler method.</param>
/// <returns></returns>
[NotNull]
public static Delegate SubscribeEvent(object instance, EventInfo eventInfo, object handler, MethodInfo callback)
{
if (eventInfo == null)

View File

@@ -1,4 +1,7 @@
using System.Text.RegularExpressions;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ICD.Common.Utils
{
@@ -30,5 +33,83 @@ namespace ICD.Common.Utils
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

@@ -12,8 +12,7 @@ namespace ICD.Common.Utils
{
private readonly object m_Instance;
private readonly List<string> m_PropertyOrder;
private readonly Dictionary<string, string> m_PropertyValues;
private readonly List<KeyValuePair<string, string>> m_PropertyOrder;
/// <summary>
/// Constructor.
@@ -23,8 +22,7 @@ namespace ICD.Common.Utils
{
m_Instance = instance;
m_PropertyOrder = new List<string>();
m_PropertyValues = new Dictionary<string, string>();
m_PropertyOrder = new List<KeyValuePair<string, string>>();
}
/// <summary>
@@ -32,14 +30,10 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AppendProperty(string name, object value)
public ReprBuilder AppendProperty(string name, object value)
{
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
string valueString = GetValueStringRepresentation(value);
m_PropertyValues[name] = valueString;
return AppendPropertyRaw(name, valueString);
}
/// <summary>
@@ -47,12 +41,10 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AppendPropertyRaw(string name, string value)
public ReprBuilder AppendPropertyRaw(string name, string value)
{
m_PropertyOrder.Remove(name);
m_PropertyOrder.Add(name);
m_PropertyValues[name] = value;
m_PropertyOrder.Add(new KeyValuePair<string, string>(name, value));
return this;
}
/// <summary>
@@ -62,7 +54,7 @@ namespace ICD.Common.Utils
public override string ToString()
{
if (m_Instance == null)
return GetValueStringRepresentation(m_Instance);
return GetValueStringRepresentation(null);
StringBuilder builder = new StringBuilder();
@@ -71,12 +63,11 @@ namespace ICD.Common.Utils
for (int index = 0; index < m_PropertyOrder.Count; index++)
{
string property = m_PropertyOrder[index];
builder.Append(property);
builder.Append('=');
KeyValuePair<string, string> pair = m_PropertyOrder[index];
string valueString = m_PropertyValues[property];
builder.Append(valueString);
builder.Append(pair.Key);
builder.Append('=');
builder.Append(pair.Value);
if (index < m_PropertyOrder.Count - 1)
builder.Append(", ");

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
@@ -228,18 +227,7 @@ namespace ICD.Common.Utils.Services
if (tService == null)
throw new ArgumentNullException("tService");
try
{
m_ServicesSection.Enter();
object service;
m_Services.TryGetValue(tService, out service);
return service;
}
finally
{
m_ServicesSection.Leave();
}
return m_ServicesSection.Execute(() => m_Services.GetDefault(tService));
}
/// <summary>
@@ -265,7 +253,7 @@ namespace ICD.Common.Utils.Services
/// <returns></returns>
private IEnumerable<object> GetServicesInstance()
{
return m_ServicesSection.Execute(() => m_Services.Values.ToList());
return m_ServicesSection.Execute(() => m_Services.Values.ToArray(m_Services.Count));
}
/// <summary>

View File

@@ -423,7 +423,12 @@ namespace ICD.Common.Utils
[PublicAPI]
public static string Repeat(string input, int count)
{
return count == 0 ? string.Empty : new StringBuilder().Insert(0, input, count).ToString();
if (count < 0)
throw new ArgumentOutOfRangeException("count");
return input == null || count == 0
? string.Empty
: new StringBuilder().Insert(0, input, count).ToString();
}
/// <summary>
@@ -559,6 +564,30 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Formats "0xFF" to an IPID.
/// </summary>
/// <param name="value"></param>
/// <param name="result"></param>
/// <returns></returns>
public static bool TryFromIpIdString(string value, out byte result)
{
if (value == null)
throw new ArgumentNullException("value");
result = 0;
try
{
result = (byte)Convert.ToInt64(value, 16);
return true;
}
catch (ArgumentOutOfRangeException)
{
return false;
}
}
/// <summary>
/// Removes all whitespace from the string.
/// </summary>
@@ -566,7 +595,7 @@ namespace ICD.Common.Utils
/// <returns></returns>
public static string RemoveWhitespace(string text)
{
return text == null ? null : new string(text.Where(c => !Char.IsWhiteSpace(c)).ToArray());
return text == null ? null : text.RemoveWhitespace();
}
/// <summary>

View File

@@ -17,7 +17,6 @@ namespace ICD.Common.Utils
private const char INTERSECT = '+';
private readonly List<string[]> m_Rows;
private readonly SafeCriticalSection m_RowsSection;
private readonly string[] m_Columns;
/// <summary>
@@ -39,7 +38,6 @@ namespace ICD.Common.Utils
public TableBuilder(params string[] columns)
{
m_Rows = new List<string[]>();
m_RowsSection = new SafeCriticalSection();
m_Columns = columns;
}
@@ -49,9 +47,10 @@ namespace ICD.Common.Utils
/// Clears all of the rows.
/// </summary>
[PublicAPI]
public void ClearRows()
public TableBuilder ClearRows()
{
m_RowsSection.Execute(() => m_Rows.Clear());
m_Rows.Clear();
return this;
}
/// <summary>
@@ -59,11 +58,11 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AddRow(params object[] row)
public TableBuilder AddRow(params object[] row)
{
string[] stringRow = row.Select(o => string.Format("{0}", o))
.ToArray();
AddRow(stringRow);
return AddRow(stringRow);
}
/// <summary>
@@ -71,31 +70,33 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AddRow(params string[] row)
public TableBuilder AddRow(params string[] row)
{
if (row != null && row.Length != m_Columns.Length)
throw new ArgumentException("Row must match columns length.");
m_RowsSection.Execute(() => m_Rows.Add(row));
m_Rows.Add(row);
return this;
}
/// <summary>
/// Adds an empty row to the builder.
/// </summary>
[PublicAPI]
public void AddEmptyRow()
public TableBuilder AddEmptyRow()
{
AddRow(new string[m_Columns.Length]);
return AddRow(new string[m_Columns.Length]);
}
[PublicAPI]
public void AddSeparator()
public TableBuilder AddSeparator()
{
AddRow(null);
return AddRow(null);
}
[PublicAPI]
public void AddHeader(params string[] row)
public TableBuilder AddHeader(params string[] row)
{
if (row.Length != m_Columns.Length)
throw new ArgumentException("Row must match columns length.");
@@ -103,6 +104,8 @@ namespace ICD.Common.Utils
AddSeparator();
AddRow(row);
AddSeparator();
return this;
}
/// <summary>
@@ -112,30 +115,21 @@ namespace ICD.Common.Utils
{
StringBuilder sb = new StringBuilder();
m_RowsSection.Enter();
int[] columnWidths = GetColumnWidths();
try
AppendRow(sb, m_Columns, columnWidths);
AppendSeparator(sb, columnWidths);
foreach (string[] row in m_Rows)
{
int[] columnWidths = GetColumnWidths();
AppendRow(sb, m_Columns, columnWidths);
AppendSeparator(sb, columnWidths);
foreach (string[] row in m_Rows)
{
if (row == null)
AppendSeparator(sb, columnWidths);
else
AppendRow(sb, row, columnWidths);
}
AppendSeparator(sb, columnWidths);
}
finally
{
m_RowsSection.Leave();
if (row == null)
AppendSeparator(sb, columnWidths);
else
AppendRow(sb, row, columnWidths);
}
AppendSeparator(sb, columnWidths);
return sb.ToString();
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
@@ -18,6 +19,8 @@ namespace ICD.Common.Utils.Timers
private readonly Stopwatch m_Stopwatch;
private static int s_ProfileIndentCount;
#region Properties
public long ElapsedTicks { get { return m_Stopwatch.ElapsedTicks; } }
@@ -104,9 +107,18 @@ namespace ICD.Common.Utils.Timers
if (action == null)
throw new ArgumentNullException("action");
IcdStopwatch stopwatch = StartNew();
action();
PrintProfile(stopwatch.ElapsedTicks, name);
s_ProfileIndentCount++;
try
{
IcdStopwatch stopwatch = StartNew();
action();
PrintProfile(stopwatch.ElapsedTicks, name);
}
finally
{
s_ProfileIndentCount--;
}
}
/// <summary>
@@ -122,11 +134,20 @@ namespace ICD.Common.Utils.Timers
if (func == null)
throw new ArgumentNullException("func");
IcdStopwatch stopwatch = StartNew();
T output = func();
PrintProfile(stopwatch.ElapsedTicks, name);
s_ProfileIndentCount++;
return output;
try
{
IcdStopwatch stopwatch = StartNew();
T output = func();
PrintProfile(stopwatch.ElapsedTicks, name);
return output;
}
finally
{
s_ProfileIndentCount--;
}
}
/// <summary>
@@ -192,12 +213,16 @@ namespace ICD.Common.Utils.Timers
// TODO - Print a fancy table with a total duration for the sequence
List<T> output = new List<T>();
using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
{
// ReSharper disable once AccessToDisposedClosure
while (Profile(() => enumerator.MoveNext(), name))
yield return enumerator.Current;
output.Add(enumerator.Current);
}
return output;
}
/// <summary>
@@ -208,20 +233,27 @@ namespace ICD.Common.Utils.Timers
/// <param name="name"></param>
public static void Profile(EventHandler eventHandler, object sender, string name)
{
if (eventHandler == null)
s_ProfileIndentCount++;
try
{
PrintProfile(0, string.Format("{0} - No invocations", name));
return;
if (eventHandler == null)
{
PrintProfile(0, string.Format("{0} - No invocations", name));
return;
}
}
finally
{
s_ProfileIndentCount--;
}
// TODO - Print a fancy table with a total duration for the event
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
foreach (EventHandler subscriber in eventHandler.GetInvocationList())
// ReSharper restore PossibleInvalidCastExceptionInForeachLoop
foreach (EventHandler subscriber in eventHandler.GetInvocationList().Cast<EventHandler>())
{
string subscriberName = string.Format("{0} - {1}.{2}", name, subscriber.Target.GetType().Name,
subscriber.GetMethodInfo().GetSignature(true));
subscriber.GetMethodInfo().GetSignature(true));
EventHandler subscriber1 = subscriber;
Profile(() => subscriber1(sender, EventArgs.Empty), subscriberName);
@@ -239,20 +271,27 @@ namespace ICD.Common.Utils.Timers
public static void Profile<T>(EventHandler<T> eventHandler, object sender, T eventArgs, string name)
where T : EventArgs
{
if (eventHandler == null)
s_ProfileIndentCount++;
try
{
PrintProfile(0, string.Format("{0} - No invocations", name));
return;
if (eventHandler == null)
{
PrintProfile(0, string.Format("{0} - No invocations", name));
return;
}
}
finally
{
s_ProfileIndentCount--;
}
// TODO - Print a fancy table with a total duration for the event
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
foreach (EventHandler<T> subscriber in eventHandler.GetInvocationList())
// ReSharper restore PossibleInvalidCastExceptionInForeachLoop
foreach (EventHandler<T> subscriber in eventHandler.GetInvocationList().Cast<EventHandler<T>>())
{
string subscriberName = string.Format("{0} - {1}.{2}", name, subscriber.Target.GetType().Name,
subscriber.GetMethodInfo().GetSignature(true));
subscriber.GetMethodInfo().GetSignature(true));
EventHandler<T> subscriber1 = subscriber;
Profile(() => subscriber1(sender, eventArgs), subscriberName);
@@ -269,8 +308,13 @@ namespace ICD.Common.Utils.Timers
if (elapsed >= RED_MILLISECONDS)
color = eConsoleColor.Red;
IcdConsole.Print(color, "{0:n}ms", elapsed);
IcdConsole.PrintLine(" to execute {0}", name);
string padding = null;
if (s_ProfileIndentCount > 0)
padding = StringUtils.Repeat(" ", s_ProfileIndentCount - 1);
IcdConsole.Print(color, "{0}{1:n}ms ", padding, elapsed);
IcdConsole.PrintLine("to execute {0}", name);
}
#endregion

View File

@@ -20,7 +20,7 @@ namespace ICD.Common.Utils.Timers
private readonly Timer m_Timer;
private int m_RepeatPeriod;
#endif
private readonly Action m_Callback;
private Action m_Callback;
/// <summary>
/// Returns true if this instance has been disposed.
@@ -88,6 +88,8 @@ namespace ICD.Common.Utils.Timers
Stop();
m_Timer.Dispose();
m_Callback = null;
IsDisposed = true;
}

View File

@@ -1,9 +1,17 @@
using System;
using System.Collections.Generic;
namespace ICD.Common.Utils
{
public static class UriUtils
{
private static readonly Dictionary<string, ushort> s_SchemeToPort =
new Dictionary<string, ushort>(StringComparer.OrdinalIgnoreCase)
{
{Uri.UriSchemeHttp, 80},
{Uri.UriSchemeHttps, 443}
};
/// <summary>
/// Attempts to parse the given URI string into a System.Uri instance.
/// </summary>
@@ -27,5 +35,16 @@ namespace ICD.Common.Utils
return false;
}
}
/// <summary>
/// Gets the port number for the given URI scheme.
/// </summary>
/// <param name="scheme"></param>
/// <param name="port"></param>
/// <returns></returns>
public static bool TryGetPortForScheme(string scheme, out ushort port)
{
return s_SchemeToPort.TryGetValue(scheme, out port);
}
}
}

View File

@@ -0,0 +1,183 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
using System.Xml;
#endif
using ICD.Common.Properties;
namespace ICD.Common.Utils.Xml
{
public abstract class AbstractGenericXmlConverter<T> : AbstractXmlConverter
{
/// <summary>
/// Creates a new instance of T.
/// </summary>
/// <returns></returns>
protected abstract T Instantiate();
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
public sealed override void WriteXml(IcdXmlTextWriter writer, string elementName, object value)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (value == null)
{
writer.WriteElementString(elementName, null);
return;
}
WriteXml(writer, elementName, (T)value);
}
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
[PublicAPI]
public void WriteXml(IcdXmlTextWriter writer, string elementName, T value)
{
if (writer == null)
throw new ArgumentNullException("writer");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
{
writer.WriteElementString(elementName, null);
return;
}
writer.WriteStartElement(elementName);
{
WriteAttributes(writer, value);
WriteElements(writer, value);
}
writer.WriteEndElement();
}
/// <summary>
/// Override to write attributes to the root element.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected virtual void WriteAttributes(IcdXmlTextWriter writer, T value)
{
}
/// <summary>
/// Override to write elements to the writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected virtual void WriteElements(IcdXmlTextWriter writer, T value)
{
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
public sealed override object ReadXml(IcdXmlReader reader)
{
return ReadXmlTyped(reader);
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
[PublicAPI]
public virtual T ReadXmlTyped(IcdXmlReader reader)
{
// Read into the first node
if (reader.NodeType != XmlNodeType.Element && !reader.ReadToNextElement())
throw new FormatException();
T output = default(T);
bool instantiated = false;
while (true)
{
if (!instantiated)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.IsEmptyElement)
return default(T);
output = Instantiate();
instantiated = true;
// Read the root attributes
while (reader.MoveToNextAttribute())
ReadAttribute(reader, output);
// Read out of the root element
if (!reader.Read())
throw new FormatException();
continue;
default:
// Keep reading until we reach the root element.
if (!reader.Read())
throw new FormatException();
continue;
}
}
switch (reader.NodeType)
{
case XmlNodeType.Element:
ReadElement(reader, output);
continue;
case XmlNodeType.EndElement:
// Read out of the end element
reader.Read();
return output;
default:
if (!reader.Read())
return output;
break;
}
}
}
/// <summary>
/// Override to handle the current attribute.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected virtual void ReadAttribute(IcdXmlReader reader, T instance)
{
}
/// <summary>
/// Override to handle the current element.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected virtual void ReadElement(IcdXmlReader reader, T instance)
{
// Skip the element
reader.Skip();
}
}
}

View File

@@ -0,0 +1,25 @@
using ICD.Common.Properties;
namespace ICD.Common.Utils.Xml
{
public abstract class AbstractXmlConverter : IXmlConverter
{
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
public abstract void WriteXml(IcdXmlTextWriter writer, string elementName, object value);
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
[PublicAPI]
public abstract object ReadXml(IcdXmlReader reader);
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
namespace ICD.Common.Utils.Xml
{
public sealed class DefaultXmlConverter : AbstractXmlConverter
{
private static readonly Dictionary<Type, DefaultXmlConverter> s_Instances;
private readonly Type m_SerializeType;
/// <summary>
/// Static constructor.
/// </summary>
static DefaultXmlConverter()
{
s_Instances = new Dictionary<Type, DefaultXmlConverter>();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="serializeType"></param>
private DefaultXmlConverter(Type serializeType)
{
m_SerializeType = serializeType;
}
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
public override void WriteXml(IcdXmlTextWriter writer, string elementName, object value)
{
string elementString = IcdXmlConvert.ToString(value);
writer.WriteElementString(elementName, elementString);
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
public override object ReadXml(IcdXmlReader reader)
{
string value = reader.ReadElementContentAsString();
return IcdXmlConvert.FromString(m_SerializeType, value);
}
/// <summary>
/// Gets the converter instance for the given serialization type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IXmlConverter Instance(Type type)
{
DefaultXmlConverter converter;
if (!s_Instances.TryGetValue(type, out converter))
{
converter = new DefaultXmlConverter(type);
s_Instances[type] = converter;
}
return converter;
}
}
}

View File

@@ -0,0 +1,22 @@
namespace ICD.Common.Utils.Xml
{
public interface IXmlConverter
{
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
void WriteXml(IcdXmlTextWriter writer, string elementName, object value);
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
object ReadXml(IcdXmlReader reader);
}
}

View File

@@ -1,80 +0,0 @@
using System;
namespace ICD.Common.Utils.Xml
{
/// <summary>
/// IcdXmlAttribute represents an attribute="value" pair from xml.
/// </summary>
public struct IcdXmlAttribute : IEquatable<IcdXmlAttribute>
{
private readonly string m_Name;
private readonly string m_Value;
public string Name { get { return m_Name; } }
public string Value { get { return m_Value; } }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public IcdXmlAttribute(string name, string value)
{
m_Name = name;
m_Value = value;
}
/// <summary>
/// Implementing default equality.
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns></returns>
public static bool operator ==(IcdXmlAttribute a1, IcdXmlAttribute a2)
{
return a1.Equals(a2);
}
/// <summary>
/// Implementing default inequality.
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns></returns>
public static bool operator !=(IcdXmlAttribute a1, IcdXmlAttribute a2)
{
return !a1.Equals(a2);
}
public bool Equals(IcdXmlAttribute other)
{
return m_Name == other.m_Name &&
m_Value == other.m_Value;
}
/// <summary>
/// Returns true if this instance is equal to the given object.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public override bool Equals(object other)
{
return other is IcdXmlAttribute && Equals((IcdXmlAttribute)other);
}
/// <summary>
/// Gets the hashcode for this instance.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + (m_Name == null ? 0 : m_Name.GetHashCode());
hash = hash * 23 + (m_Value == null ? 0 : m_Value.GetHashCode());
return hash;
}
}
}
}

View File

@@ -1,4 +1,8 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
@@ -9,6 +13,101 @@ namespace ICD.Common.Utils.Xml
{
public static class IcdXmlConvert
{
/// <summary>
/// Serializes the given instance to an xml string.
/// </summary>
/// <param name="elementName"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string SerializeObject(string elementName, object value)
{
StringBuilder builder = new StringBuilder();
using (IcdStringWriter stringWriter = new IcdStringWriter(builder))
{
using (IcdXmlTextWriter writer = new IcdXmlTextWriter(stringWriter))
SerializeObject(writer, elementName, value);
}
return builder.ToString();
}
/// <summary>
/// Serializes the given instance to xml.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value"></param>
/// <returns></returns>
public static void SerializeObject(IcdXmlTextWriter writer, string elementName, object value)
{
if (writer == null)
throw new ArgumentNullException("writer");
IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(value);
converter.WriteXml(writer, elementName, value);
}
/// <summary>
/// Deserializes the given xml to an instance of the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xml"></param>
/// <returns></returns>
public static T DeserializeObject<T>(string xml)
{
return (T)DeserializeObject(typeof(T), xml);
}
/// <summary>
/// Deserializes the given xml to an instance of the given type.
/// </summary>
/// <param name="type"></param>
/// <param name="xml"></param>
/// <returns></returns>
public static object DeserializeObject(Type type, string xml)
{
if (type == null)
throw new ArgumentNullException("type");
using (IcdXmlReader reader = new IcdXmlReader(xml))
return DeserializeObject(type, reader);
}
/// <summary>
/// Deserializes the current node to an instance of the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <returns></returns>
public static T DeserializeObject<T>(IcdXmlReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
return (T)DeserializeObject(typeof(T), reader);
}
/// <summary>
/// Deserializes the current node to an instance of the given type.
/// </summary>
/// <param name="type"></param>
/// <param name="reader"></param>
/// <returns></returns>
public static object DeserializeObject(Type type, IcdXmlReader reader)
{
if (type == null)
throw new ArgumentNullException("type");
if (reader == null)
throw new ArgumentNullException("reader");
IXmlConverter converter = XmlConverterAttribute.GetConverterForType(type);
return converter.ReadXml(reader);
}
public static string ToString(int value)
{
return XmlConvert.ToString(value);
@@ -97,5 +196,49 @@ namespace ICD.Common.Utils.Xml
return child.ToString();
}
public static T FromString<T>(string value)
{
return (T)FromString(typeof(T), value);
}
public static object FromString(Type type, string value)
{
if (type == null)
throw new ArgumentNullException("type");
if (type == typeof(bool))
return bool.Parse(value);
if (type == typeof(byte))
return byte.Parse(value);
if (type == typeof(decimal))
return decimal.Parse(value);
if (type == typeof(char))
return value.Single();
if (type == typeof(double))
return double.Parse(value);
if (type == typeof(Guid))
return new Guid(value);
if (type == typeof(float))
return float.Parse(value);
if (type == typeof(int))
return int.Parse((value));
if (type == typeof(long))
return long.Parse(value);
if (type == typeof(sbyte))
return sbyte.Parse(value);
if (type == typeof(short))
return short.Parse(value);
if (type == typeof(TimeSpan))
return TimeSpan.Parse(value);
if (type == typeof(uint))
return uint.Parse(value);
if (type == typeof(ulong))
return ulong.Parse(value);
if (type == typeof(ushort))
return ushort.Parse(value);
return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
}
}
}

View File

@@ -138,19 +138,6 @@ namespace ICD.Common.Utils.Xml
}
}
public string ReadElementContentAsString()
{
try
{
return m_Reader.ReadElementContentAsString();
}
catch (XmlException e)
{
string message = string.Format("Unable to read element '{0}' content as string", m_Reader.Name);
throw new IcdXmlException(message, e, e.LineNumber, e.LinePosition);
}
}
public string ReadOuterXml()
{
try
@@ -175,6 +162,19 @@ namespace ICD.Common.Utils.Xml
}
}
public string ReadElementContentAsString()
{
try
{
return m_Reader.ReadElementContentAsString();
}
catch (XmlException e)
{
string message = string.Format("Unable to read element '{0}' content as string", m_Reader.Name);
throw new IcdXmlException(message, e, e.LineNumber, e.LinePosition);
}
}
public long ReadElementContentAsLong()
{
try
@@ -199,6 +199,13 @@ namespace ICD.Common.Utils.Xml
}
}
public bool ReadElementContentAsBoolean()
{
// ReadElementContentAsBoolean() is too case sensitive
string value = ReadElementContentAsString();
return bool.Parse(value);
}
#endregion
}
}

View File

@@ -70,6 +70,16 @@ namespace ICD.Common.Utils.Xml
#region Methods
public void WriteStartDocument()
{
m_Writer.WriteStartDocument();
}
public void WriteEndDocument()
{
m_Writer.WriteEndDocument();
}
public void WriteStartElement(string elementName)
{
m_Writer.WriteStartElement(elementName);
@@ -149,8 +159,8 @@ namespace ICD.Common.Utils.Xml
{
return new XmlWriterSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
Indent = true
Indent = true,
ConformanceLevel = ConformanceLevel.Auto
};
}
#endif

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Attributes;
namespace ICD.Common.Utils.Xml
{
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class XmlConverterAttribute : AbstractIcdAttribute
{
private static readonly Dictionary<Type, IXmlConverter> s_InstanceTypeToConverter;
private static readonly Dictionary<Type, IXmlConverter> s_ConverterTypeToConverter;
private readonly Type m_ConverterType;
/// <summary>
/// Gets the converter type.
/// </summary>
public Type ConverterType { get { return m_ConverterType; } }
/// <summary>
/// Static constructor.
/// </summary>
static XmlConverterAttribute()
{
s_InstanceTypeToConverter = new Dictionary<Type, IXmlConverter>();
s_ConverterTypeToConverter = new Dictionary<Type, IXmlConverter>();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="converterType"></param>
public XmlConverterAttribute(Type converterType)
{
m_ConverterType = converterType;
}
/// <summary>
/// Gets the XML converter for the given instance.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static IXmlConverter GetConverterForInstance(object value)
{
return value == null ? DefaultXmlConverter.Instance(typeof(object)) : GetConverterForType(value.GetType());
}
/// <summary>
/// Gets the XML converter for the given type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IXmlConverter GetConverterForType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
IXmlConverter converter;
if (!s_InstanceTypeToConverter.TryGetValue(type, out converter))
{
XmlConverterAttribute attribute = AttributeUtils.GetClassAttribute<XmlConverterAttribute>(type);
Type converterType = attribute == null ? null : attribute.ConverterType;
converter = converterType == null ? DefaultXmlConverter.Instance(type) : LazyLoadConverter(converterType);
s_InstanceTypeToConverter[type] = converter;
}
return converter;
}
/// <summary>
/// Lazy-loads the converter of the given type.
/// </summary>
/// <param name="converterType"></param>
/// <returns></returns>
private static IXmlConverter LazyLoadConverter(Type converterType)
{
if (converterType == null)
throw new ArgumentNullException("converterType");
IXmlConverter converter;
if (!s_ConverterTypeToConverter.TryGetValue(converterType, out converter))
{
converter = ReflectionUtils.CreateInstance(converterType) as IXmlConverter;
s_ConverterTypeToConverter[converterType] = converter;
}
return converter;
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
@@ -27,7 +28,21 @@ namespace ICD.Common.Utils.Xml
if (extends == null)
throw new ArgumentNullException("extends");
return extends.GetAttribute(name) != null;
string unused;
return extends.TryGetAttribute(name, out unused);
}
/// <summary>
/// Returns true if the attribute exists.
/// </summary>
/// <param name="extends"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryGetAttribute(this IcdXmlReader extends, string name, out string value)
{
return (value = extends.GetAttribute(name)) != null;
}
/// <summary>
@@ -36,22 +51,16 @@ namespace ICD.Common.Utils.Xml
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<IcdXmlAttribute> GetAttributes(this IcdXmlReader extends)
public static IEnumerable<KeyValuePair<string, string>> GetAttributes(this IcdXmlReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (!extends.HasAttributes)
return Enumerable.Empty<IcdXmlAttribute>();
List<IcdXmlAttribute> attributes = new List<IcdXmlAttribute>();
while (extends.MoveToNextAttribute())
attributes.Add(new IcdXmlAttribute(extends.Name, extends.Value));
yield return new KeyValuePair<string, string>(extends.Name, extends.Value);
// Move back to element.
extends.MoveToElement();
return attributes.ToArray();
}
/// <summary>
@@ -115,7 +124,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="extends"></param>
/// <param name="callback"></param>
[PublicAPI]
public static void Recurse(this IcdXmlReader extends, Action<XmlRecursionEventArgs> callback)
public static void Recurse(this IcdXmlReader extends, Func<XmlRecursionEventArgs, bool> callback)
{
if (extends == null)
throw new ArgumentNullException("extends");
@@ -193,14 +202,11 @@ namespace ICD.Common.Utils.Xml
if (extends == null)
throw new ArgumentNullException("extends");
try
{
return extends.GetChildElements(element).First();
}
catch (InvalidOperationException e)
{
throw new FormatException("No child element with name " + element, e);
}
IcdXmlReader output;
if (extends.GetChildElements(element).TryFirst(out output))
return output;
throw new FormatException("No child element with name " + element);
}
/// <summary>
@@ -304,20 +310,12 @@ namespace ICD.Common.Utils.Xml
}
}
public static bool TryGetChildElementAsString(this IcdXmlReader extends, string element, out string output)
{
output = null;
if (extends == null)
throw new ArgumentNullException("extends");
try
{
output = extends.GetChildElementsAsString(element).First();
return true;
}
catch (InvalidOperationException)
{
return false;
}
return extends.GetChildElementsAsString(element).TryFirst(out output);
}
#endregion
@@ -384,6 +382,21 @@ namespace ICD.Common.Utils.Xml
return ushort.Parse(content);
}
/// <summary>
/// Parses the element content as a short.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
public static short ReadElementContentAsShort(this IcdXmlReader extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string content = extends.ReadElementContentAsString();
return short.Parse(content);
}
/// <summary>
/// Parses the element content as an enum.
/// </summary>

View File

@@ -55,12 +55,12 @@ namespace ICD.Common.Utils.Xml
/// <param name="xml"></param>
/// <returns></returns>
[PublicAPI]
public static IEnumerable<IcdXmlAttribute> GetAttributes(string xml)
public static IEnumerable<KeyValuePair<string, string>> GetAttributes(string xml)
{
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
reader.ReadToNextElement();
return reader.GetAttributes();
return reader.GetAttributes().ToArray();
}
}
@@ -71,13 +71,30 @@ namespace ICD.Common.Utils.Xml
/// <param name="name"></param>
/// <returns></returns>
[PublicAPI]
public static IcdXmlAttribute GetAttribute(string xml, string name)
public static string GetAttribute(string xml, string name)
{
IcdXmlAttribute output;
if (GetAttributes(xml).TryFirst(a => a.Name == name, out output))
return output;
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
reader.ReadToNextElement();
return reader.GetAttribute(name);
}
}
throw new FormatException(string.Format("No attribute with name {0}", name));
/// <summary>
/// Convenience method for getting attribute by name.
/// </summary>
/// <param name="xml"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryGetAttribute(string xml, string name, out string value)
{
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
reader.ReadToNextElement();
return reader.TryGetAttribute(name, out value);
}
}
/// <summary>
@@ -128,6 +145,21 @@ namespace ICD.Common.Utils.Xml
}
}
/// <summary>
/// Gets the value of the attribute with the given name and returns as a bool.
/// </summary>
/// <param name="xml"></param>
/// <param name="name"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
[PublicAPI]
public static T GetAttributeAsEnum<T>(string xml, string name, bool ignoreCase)
where T : struct, IConvertible
{
string attribute = GetAttributeAsString(xml, name);
return EnumUtils.Parse<T>(attribute, ignoreCase);
}
#endregion
#region Recurse
@@ -138,7 +170,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="xml"></param>
/// <param name="callback"></param>
[PublicAPI]
public static void Recurse(string xml, Action<XmlRecursionEventArgs> callback)
public static void Recurse(string xml, Func<XmlRecursionEventArgs, bool> callback)
{
if (callback == null)
throw new ArgumentNullException("callback");
@@ -152,7 +184,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="xml"></param>
/// <param name="path"></param>
/// <param name="callback"></param>
private static void Recurse(string xml, Stack<string> path, Action<XmlRecursionEventArgs> callback)
private static void Recurse(string xml, Stack<string> path, Func<XmlRecursionEventArgs, bool> callback)
{
if (path == null)
throw new ArgumentNullException("path");
@@ -168,10 +200,11 @@ namespace ICD.Common.Utils.Xml
path.Push(childReader.Name);
string[] pathOutput = path.Reverse().ToArray(path.Count);
callback(new XmlRecursionEventArgs(xml, pathOutput));
foreach (string child in childReader.GetChildElementsAsString())
Recurse(child, path, callback);
if (callback(new XmlRecursionEventArgs(xml, pathOutput)))
{
foreach (string child in childReader.GetChildElementsAsString())
Recurse(child, path, callback);
}
path.Pop();
}
@@ -373,6 +406,19 @@ namespace ICD.Common.Utils.Xml
return reader.ReadElementContentAsUShort();
}
/// <summary>
/// Gets the content of an immediate child.
/// </summary>
/// <param name="xml"></param>
/// <param name="childElement"></param>
/// <returns></returns>
[PublicAPI]
public static short ReadChildElementContentAsShort(string xml, string childElement)
{
using (IcdXmlReader reader = GetChildElement(xml, childElement))
return reader.ReadElementContentAsShort();
}
/// <summary>
/// Gets the content of an immediate child.
/// </summary>
@@ -395,9 +441,8 @@ namespace ICD.Common.Utils.Xml
[PublicAPI]
public static bool ReadChildElementContentAsBoolean(string xml, string childElement)
{
// IcdXmlReader.ReadElementContentAsBoolean() is too case sensitive
string output = ReadChildElementContentAsString(xml, childElement);
return bool.Parse(output);
using (IcdXmlReader reader = GetChildElement(xml, childElement))
return reader.ReadElementContentAsBoolean();
}
/// <summary>
@@ -534,6 +579,25 @@ namespace ICD.Common.Utils.Xml
}
}
/// <summary>
/// Gets the content of the immediate child. Returns null if the child element was not found.
/// </summary>
/// <param name="xml"></param>
/// <param name="childElement"></param>
/// <returns></returns>
[PublicAPI]
public static short? TryReadChildElementContentAsShort(string xml, string childElement)
{
try
{
return ReadChildElementContentAsShort(xml, childElement);
}
catch (FormatException)
{
return null;
}
}
/// <summary>
/// Gets the content of the immediate child. Returns null if the child element was not found.
/// </summary>
@@ -856,6 +920,26 @@ namespace ICD.Common.Utils.Xml
return ReadListFromXml(xml, rootElement, childElement, readChild);
}
/// <summary>
/// Deserializes the xml to a dictionary.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="xml"></param>
/// <param name="rootElement"></param>
/// <param name="childElement"></param>
/// <param name="keyElement"></param>
/// <param name="valueElement"></param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<TKey, TValue>> ReadDictFromXml<TKey, TValue>(
string xml, string rootElement, string childElement, string keyElement, string valueElement)
{
Func<string, TKey> readKey = IcdXmlConvert.DeserializeObject<TKey>;
Func<string, TValue> readValue = IcdXmlConvert.DeserializeObject<TValue>;
return ReadDictFromXml(xml, rootElement, childElement, keyElement, valueElement, readKey, readValue);
}
/// <summary>
/// Calls childElementCallback for each item in the list.
/// </summary>
@@ -876,6 +960,20 @@ namespace ICD.Common.Utils.Xml
yield return readChild(child);
}
/// <summary>
/// Deserializes the xml to a list.
/// </summary>
/// <param name="xml"></param>
/// <param name="rootElement"></param>
/// <param name="childElement"></param>
public static IEnumerable<T> ReadListFromXml<T>(string xml, string rootElement, string childElement)
{
Func<string, T> readChild = IcdXmlConvert.DeserializeObject<T>;
return ReadListFromXml(xml, rootElement, childElement, readChild);
}
#endregion
#region Write Content
@@ -1000,36 +1098,7 @@ namespace ICD.Common.Utils.Xml
#endregion
/// <summary>
/// Returns true if the given xml is valid.
/// </summary>
/// <param name="xml"></param>
/// <returns></returns>
[PublicAPI]
public static bool IsValidXml(string xml)
{
try
{
IcdXmlDocument document = new IcdXmlDocument();
document.LoadXml(xml);
return true;
}
catch (IcdXmlException)
{
return false;
}
}
/// <summary>
/// Prints the xml document.
/// </summary>
/// <param name="xml"></param>
[PublicAPI]
public static void Print(string xml)
{
string result = Format(xml);
IcdConsole.PrintLine(result);
}
#region Print
/// <summary>
/// Formats the given xml string into a human readable structure with indentations.
@@ -1048,12 +1117,12 @@ namespace ICD.Common.Utils.Xml
IcdXmlDocument document = new IcdXmlDocument();
document.LoadXml(xml);
document.WriteContentTo(writer);
writer.Flush();
return builder.ToString();
}
}
return builder.ToString();
}
#endregion
}
}