mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-14 04:54:55 +00:00
Compare commits
627 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0de417b665 | ||
|
|
da57fcb988 | ||
|
|
c6b6711eb2 | ||
|
|
138a6f3470 | ||
|
|
e84bc721ee | ||
|
|
2988912d44 | ||
|
|
be94503e6a | ||
|
|
6877b5c800 | ||
|
|
569066edc4 | ||
|
|
73a716f9b9 | ||
|
|
4e430731cc | ||
|
|
2b8f2a125a | ||
|
|
cc25c41805 | ||
|
|
2487a70891 | ||
|
|
5c5a25775b | ||
|
|
27f6267f54 | ||
|
|
430eac8cb6 | ||
|
|
b47e305d9f | ||
|
|
0c8f8242e8 | ||
|
|
a52e77c1e7 | ||
|
|
2aa05f3ee8 | ||
|
|
2700f5018a | ||
|
|
b09b22266b | ||
|
|
1c206c5539 | ||
|
|
56a48175c6 | ||
|
|
0acd0ae5d4 | ||
|
|
d2856c1983 | ||
|
|
fa65a9de65 | ||
|
|
e4b292f145 | ||
|
|
11a5533fcd | ||
|
|
e0d0763306 | ||
|
|
7d42158bc7 | ||
|
|
92a28813e0 | ||
|
|
3b313a442c | ||
|
|
16fb3683dd | ||
|
|
bb9bcf6cdf | ||
|
|
0490ffd572 | ||
|
|
3bf0aff0a0 | ||
|
|
e5b10a96f7 | ||
|
|
0c3c87308c | ||
|
|
68d1f1033a | ||
|
|
af66147648 | ||
|
|
16067bca20 | ||
|
|
57e64788c4 | ||
|
|
bc605bf8ae | ||
|
|
be50aa3314 | ||
|
|
1b79bd21df | ||
|
|
7d5ec0e636 | ||
|
|
79c1c60d79 | ||
|
|
033008616f | ||
|
|
84b5b636f6 | ||
|
|
451cf08c0f | ||
|
|
63d76d8cef | ||
|
|
c08a6283b8 | ||
|
|
df42a6674f | ||
|
|
ae53812a98 | ||
|
|
74ff651798 | ||
|
|
29013a2bf5 | ||
|
|
baa00f7b00 | ||
|
|
67a2b11ee6 | ||
|
|
8f5fee2401 | ||
|
|
d00d2febf3 | ||
|
|
af3d335079 | ||
|
|
375e3154c6 | ||
|
|
37d799295c | ||
|
|
06f40e5d22 | ||
|
|
11c3e33fc1 | ||
|
|
fc60e2b675 | ||
|
|
531e9dfcf8 | ||
|
|
f601f9cda7 | ||
|
|
841b692727 | ||
|
|
5f2c5e6dcb | ||
|
|
7ae9e86e1d | ||
|
|
e6dec641f3 | ||
|
|
8b848e5127 | ||
|
|
f328598f63 | ||
|
|
79db70211f | ||
|
|
1390af967f | ||
|
|
28335ad99c | ||
|
|
6967e9b013 | ||
|
|
7784b99f75 | ||
|
|
b3c1daaab5 | ||
|
|
ae10abd71e | ||
|
|
5aca963da0 | ||
|
|
40330d0007 | ||
|
|
a26783bd67 | ||
|
|
d58b8a9db9 | ||
|
|
5083f3d7ab | ||
|
|
43fd348fae | ||
|
|
8cdddc94bc | ||
|
|
25d799fe7d | ||
|
|
f807db480e | ||
|
|
985a81f961 | ||
|
|
0a9a382355 | ||
|
|
86fabce6da | ||
|
|
a7ab2ab3fe | ||
|
|
370cadbaeb | ||
|
|
e7bdcdfca5 | ||
|
|
cab1e237d5 | ||
|
|
da5e1d83a0 | ||
|
|
63237b6fba | ||
|
|
a55480f8e5 | ||
|
|
d1f8096933 | ||
|
|
c41e61391e | ||
|
|
766e2da4e3 | ||
|
|
b83fba337f | ||
|
|
766c265dac | ||
|
|
204d2a475d | ||
|
|
b5542fb0d2 | ||
|
|
4ff4e9f3c6 | ||
|
|
456a8f74f9 | ||
|
|
ffb00f960d | ||
|
|
b44cbe0093 | ||
|
|
cc79e88702 | ||
|
|
1db7d4b45d | ||
|
|
5bf8fcc71f | ||
|
|
b05373157a | ||
|
|
8427f2c0c6 | ||
|
|
572f069797 | ||
|
|
e44bd3e2cc | ||
|
|
419b070375 | ||
|
|
8411a27173 | ||
|
|
e0287cfc7b | ||
|
|
d7bee510c9 | ||
|
|
45a3c5c5a1 | ||
|
|
d090ab0085 | ||
|
|
e044c97af7 | ||
|
|
c7e8f09eeb | ||
|
|
9b53d77d6b | ||
|
|
c1b9426e32 | ||
|
|
237b5d3e86 | ||
|
|
68365553ed | ||
|
|
3abb792c5c | ||
|
|
342ac4ebca | ||
|
|
4174ed2e1f | ||
|
|
41d86ecf48 | ||
|
|
6b28ae44d6 | ||
|
|
ca857a7aed | ||
|
|
ccb961fc2e | ||
|
|
8fa1de991b | ||
|
|
5acfed40ba | ||
|
|
6141a763ae | ||
|
|
e61dcd8596 | ||
|
|
9819c44dcc | ||
|
|
f9a8be85d6 | ||
|
|
3381977050 | ||
|
|
81d1a97304 | ||
|
|
37d5468e20 | ||
|
|
03be66983d | ||
|
|
a13f1fd687 | ||
|
|
f0bcb3bbc9 | ||
|
|
1e934c9877 | ||
|
|
64cc662ee7 | ||
|
|
4aa76bd647 | ||
|
|
59f4585ae6 | ||
|
|
2235eeeb9d | ||
|
|
2a7a051f1f | ||
|
|
f174c32721 | ||
|
|
c349757c00 | ||
|
|
8c70c8d534 | ||
|
|
0d85fe8106 | ||
|
|
c868034769 | ||
|
|
55c0a7dc7e | ||
|
|
4321ad6157 | ||
|
|
9a68f0446d | ||
|
|
0574314580 | ||
|
|
e5fc154858 | ||
|
|
7bc262b4e2 | ||
|
|
867338eca2 | ||
|
|
8625f0a3a4 | ||
|
|
09516d2f11 | ||
|
|
76591155d3 | ||
|
|
caa4878a37 | ||
|
|
a5d3a7c19d | ||
|
|
55457253b2 | ||
|
|
b8722af752 | ||
|
|
bf8c320d66 | ||
|
|
28f4818ca3 | ||
|
|
00c876be7a | ||
|
|
1a0ea4119e | ||
|
|
0ece381939 | ||
|
|
ffeb313456 | ||
|
|
aeba163a0f | ||
|
|
49a154ebed | ||
|
|
4e1ed312a5 | ||
|
|
3316ef9343 | ||
|
|
5ca76f7e7e | ||
|
|
66b8107233 | ||
|
|
dd6dfa191e | ||
|
|
5aa8b15472 | ||
|
|
c6cfdd4d72 | ||
|
|
ab699406a6 | ||
|
|
15330d7dd0 | ||
|
|
34cfe8c7a3 | ||
|
|
6d93911766 | ||
|
|
8dd8d48a8b | ||
|
|
c8edac0f14 | ||
|
|
00a75c6d72 | ||
|
|
75404e8b20 | ||
|
|
a9f1c57d1e | ||
|
|
27bb1e3482 | ||
|
|
6173be0a1d | ||
|
|
66502d3617 | ||
|
|
0db03f7fdf | ||
|
|
a74cdc5614 | ||
|
|
7f306801b3 | ||
|
|
51ec3dd8ff | ||
|
|
7cc8359284 | ||
|
|
7ece973c2f | ||
|
|
d7e890d6c3 | ||
|
|
816f8984be | ||
|
|
449bd41cb3 | ||
|
|
8b53700c58 | ||
|
|
6d5eb07b1f | ||
|
|
67445de8b9 | ||
|
|
e093a57172 | ||
|
|
878784586f | ||
|
|
c6b2cd78cc | ||
|
|
5d9369b582 | ||
|
|
a822bc406b | ||
|
|
c6a755f080 | ||
|
|
21c68905d0 | ||
|
|
4bbf0aec37 | ||
|
|
0a87c4efe7 | ||
|
|
9882febede | ||
|
|
80b1412632 | ||
|
|
a1d480d24b | ||
|
|
9c0d6a87df | ||
|
|
0da0c41fe2 | ||
|
|
f8030a45f4 | ||
|
|
48ff54de82 | ||
|
|
99945b41a4 | ||
|
|
d31b698cea | ||
|
|
5212cb9a7a | ||
|
|
ee670487dd | ||
|
|
4f34b3da7c | ||
|
|
9bf0a9b8a5 | ||
|
|
f7b35e64ec | ||
|
|
bfab3c77b0 | ||
|
|
4a68c0caad | ||
|
|
07ee6ce586 | ||
|
|
6f5deaf1ea | ||
|
|
3da9b23e12 | ||
|
|
9deafaec9b | ||
|
|
28ea05e2f6 | ||
|
|
f53d05936c | ||
|
|
4339f02698 | ||
|
|
25fb5a0ad8 | ||
|
|
6240d3fccf | ||
|
|
88ff67b356 | ||
|
|
1d0ba453e3 | ||
|
|
121d267aad | ||
|
|
f23b051c3a | ||
|
|
b5ec51d9a4 | ||
|
|
62f54f1213 | ||
|
|
dd3c0f530b | ||
|
|
fc74865c5b | ||
|
|
4de7944286 | ||
|
|
dbad18925f | ||
|
|
1a027bcea0 | ||
|
|
7053faede1 | ||
|
|
582cbe9157 | ||
|
|
00803fcd66 | ||
|
|
9912097a33 | ||
|
|
cad3cf60cf | ||
|
|
0431b5afe4 | ||
|
|
d1c21ae7b4 | ||
|
|
f34e307a93 | ||
|
|
2ad9adf86c | ||
|
|
bbb09666dd | ||
|
|
dd3c6b13fd | ||
|
|
f9dccd30e5 | ||
|
|
db0029bbc1 | ||
|
|
a5289e01f7 | ||
|
|
9de5acfbe7 | ||
|
|
a22c3626ab | ||
|
|
d90c60126e | ||
|
|
49d12d454f | ||
|
|
760ab259fa | ||
|
|
40e00e0ab7 | ||
|
|
64405a1cd6 | ||
|
|
ffb217839c | ||
|
|
0d8dbf4a30 | ||
|
|
79344a3667 | ||
|
|
5e61098e71 | ||
|
|
e0663bdbfd | ||
|
|
96946d6323 | ||
|
|
5067f5fdb3 | ||
|
|
d002dda568 | ||
|
|
08aeba756a | ||
|
|
b29ae4c8c8 | ||
|
|
c1418ae4e2 | ||
|
|
5978d492cf | ||
|
|
a1775b281f | ||
|
|
75ddc37cf1 | ||
|
|
d07373b69e | ||
|
|
3ebfa76970 | ||
|
|
ac5da50e98 | ||
|
|
6c6d272511 | ||
|
|
022e625143 | ||
|
|
b496594cd6 | ||
|
|
e99a7f313f | ||
|
|
ed2cf84a7e | ||
|
|
91684f25f2 | ||
|
|
be5024271f | ||
|
|
dd90227ca1 | ||
|
|
2fa6ff3de6 | ||
|
|
311d63204f | ||
|
|
82d0598352 | ||
|
|
4229a6a73f | ||
|
|
f5c6b64ab7 | ||
|
|
3f62e7e929 | ||
|
|
a82645e902 | ||
|
|
985578985a | ||
|
|
c453717998 | ||
|
|
fde3106389 | ||
|
|
94265b1915 | ||
|
|
9b3294382f | ||
|
|
d59eb00d6b | ||
|
|
d051ddb098 | ||
|
|
a53ad74811 | ||
|
|
69eb4b3d34 | ||
|
|
8e5486e1ef | ||
|
|
ecf7e626f3 | ||
|
|
3b5c9ed51b | ||
|
|
e2646194b8 | ||
|
|
0adad154c6 | ||
|
|
0b8eb82e3b | ||
|
|
13d1072d10 | ||
|
|
8d2321c350 | ||
|
|
55f494e2f5 | ||
|
|
859a2b0830 | ||
|
|
9d5dfb3b64 | ||
|
|
c60acf52ba | ||
|
|
311bf683e0 | ||
|
|
d025946948 | ||
|
|
b74d2c9d60 | ||
|
|
ac4c0eccc9 | ||
|
|
0cae292899 | ||
|
|
9cdc8b26a2 | ||
|
|
80786dd84c | ||
|
|
bf8061046b | ||
|
|
201639b30b | ||
|
|
89f0214cce | ||
|
|
49f8f116fd | ||
|
|
7598a451cc | ||
|
|
f03f49b376 | ||
|
|
da4415a11c | ||
|
|
67c07b8833 | ||
|
|
11c4d10a51 | ||
|
|
d1ec8a45d1 | ||
|
|
352121e25c | ||
|
|
0d5ee31d61 | ||
|
|
ec7004e053 | ||
|
|
ea0bcec2c3 | ||
|
|
55bf458a2b | ||
|
|
7562dddeff | ||
|
|
f66ddc7e03 | ||
|
|
b5fc25de7c | ||
|
|
d4ef4c3e1a | ||
|
|
2a8a936b6f | ||
|
|
bbe596c116 | ||
|
|
7689ffc3c8 | ||
|
|
1ab247f204 | ||
|
|
5b5ee258ca | ||
|
|
4695a80575 | ||
|
|
79a98d8c10 | ||
|
|
b68c5f1c97 | ||
|
|
3deec98f44 | ||
|
|
eb52b164bf | ||
|
|
fb8abe2aa4 | ||
|
|
f61305856f | ||
|
|
de00acbf79 | ||
|
|
5ffca5e7a3 | ||
|
|
2037c990b2 | ||
|
|
13a2493f4c | ||
|
|
09e0a20443 | ||
|
|
7870d2c375 | ||
|
|
7cf3d18127 | ||
|
|
f39b3897fc | ||
|
|
ab2fab0b3a | ||
|
|
9b69b2a712 | ||
|
|
0a1f637b45 | ||
|
|
e0ace12ef9 | ||
|
|
9f1541f843 | ||
|
|
b9f5349055 | ||
|
|
0c7f82835c | ||
|
|
41755cb472 | ||
|
|
238d6518ab | ||
|
|
89cde5c9a0 | ||
|
|
38204314e7 | ||
|
|
9e8cedfbfa | ||
|
|
52229c1472 | ||
|
|
38c24d42a7 | ||
|
|
cff1ec8d7e | ||
|
|
5a64a6c269 | ||
|
|
68d98021a5 | ||
|
|
0e16606d75 | ||
|
|
602f170091 | ||
|
|
aae8bc5323 | ||
|
|
11004085c5 | ||
|
|
6ca015a561 | ||
|
|
73b1c4819c | ||
|
|
f913af6efe | ||
|
|
099517df7d | ||
|
|
6a7b58f50f | ||
|
|
2273d5b3a1 | ||
|
|
aa17f29305 | ||
|
|
e65578584a | ||
|
|
96d2df8871 | ||
|
|
b4d82a855e | ||
|
|
83bc344ab3 | ||
|
|
0821dcb3ca | ||
|
|
b6f64bb9b2 | ||
|
|
6ae110a997 | ||
|
|
efdb48f2c3 | ||
|
|
9d02ee0022 | ||
|
|
8c0416f70e | ||
|
|
0f27d68edf | ||
|
|
f8fc68e08f | ||
|
|
fc4ae67e40 | ||
|
|
0f618198b5 | ||
|
|
9a6f197aa0 | ||
|
|
45ee3e70b0 | ||
|
|
b09f614ef5 | ||
|
|
027c9ffe82 | ||
|
|
dc1b60e629 | ||
|
|
091ad10068 | ||
|
|
71bb320057 | ||
|
|
e6b0fd5f7b | ||
|
|
2a2bff2e92 | ||
|
|
9c07b3da04 | ||
|
|
438b3d48e4 | ||
|
|
629ceec84e | ||
|
|
bb41d59a4b | ||
|
|
c23d81e023 | ||
|
|
18abe78550 | ||
|
|
f33218f7a6 | ||
|
|
4938dad882 | ||
|
|
5900f5974b | ||
|
|
81f7eb9c66 | ||
|
|
440c1c62f1 | ||
|
|
63b590a9ea | ||
|
|
f8c88630fc | ||
|
|
3f1f9611ec | ||
|
|
47c611a0b4 | ||
|
|
e388f35efb | ||
|
|
89061b5cbf | ||
|
|
ff6f15d3ef | ||
|
|
02e6fe9f8c | ||
|
|
edb5534b9d | ||
|
|
6af3e5b2ff | ||
|
|
65f76402cc | ||
|
|
5700cf4ce2 | ||
|
|
7c238b9fef | ||
|
|
6eda848410 | ||
|
|
7b44867f34 | ||
|
|
149f480add | ||
|
|
0f5632ced8 | ||
|
|
9df5f7ae37 | ||
|
|
6e056c9dcc | ||
|
|
7add461a1a | ||
|
|
cb3677bd72 | ||
|
|
69ad4bb240 | ||
|
|
1a868d5a7d | ||
|
|
bb6110931a | ||
|
|
909ebfbc97 | ||
|
|
3d068aeb68 | ||
|
|
1f6915a825 | ||
|
|
00e7fd6a6b | ||
|
|
e3f356a5db | ||
|
|
2d401959b6 | ||
|
|
7cc5a47d6a | ||
|
|
8c160c77c4 | ||
|
|
65c50e8e90 | ||
|
|
3a2fc0eef7 | ||
|
|
1be852685d | ||
|
|
6201184fab | ||
|
|
e8acb42bb9 | ||
|
|
d3d812265e | ||
|
|
b915401109 | ||
|
|
3e3a4e33cb | ||
|
|
b680bab67a | ||
|
|
7666021925 | ||
|
|
7b6092c291 | ||
|
|
f7740aaea2 | ||
|
|
c31b2b556b | ||
|
|
8866a3e1e6 | ||
|
|
e3273934a7 | ||
|
|
0152259a25 | ||
|
|
98c3f17602 | ||
|
|
bb3fba100d | ||
|
|
9adad373f6 | ||
|
|
844f8b7b46 | ||
|
|
43d7ea944e | ||
|
|
6526b2b811 | ||
|
|
ead365e251 | ||
|
|
f24e6d2597 | ||
|
|
311f2dfaf4 | ||
|
|
1c38d9f04a | ||
|
|
02c18d152f | ||
|
|
fc482ec685 | ||
|
|
d649fb9dc4 | ||
|
|
4d76abe046 | ||
|
|
d6d3bfa9e0 | ||
|
|
536742a1c6 | ||
|
|
dc824b2034 | ||
|
|
0cc588ce15 | ||
|
|
b996a295f6 | ||
|
|
4312ad1a61 | ||
|
|
7792b01629 | ||
|
|
4953e40f9e | ||
|
|
69f872ced1 | ||
|
|
7e3aef4cb3 | ||
|
|
a6fbd5a6d2 | ||
|
|
6f67064d34 | ||
|
|
723759f805 | ||
|
|
59f4411620 | ||
|
|
243e285948 | ||
|
|
198bf8dac0 | ||
|
|
78f5c4a7b6 | ||
|
|
c0ed7f9b26 | ||
|
|
6d08ee3bef | ||
|
|
2b376bff47 | ||
|
|
9f0e66e4a1 | ||
|
|
4c93515f83 | ||
|
|
9ad49540d2 | ||
|
|
cbf7a3d79c | ||
|
|
5a4d3cabac | ||
|
|
dc0e65125a | ||
|
|
9d732ef4b2 | ||
|
|
ecdb4f4fb5 | ||
|
|
6f5188909b | ||
|
|
3fa089b85e | ||
|
|
fef667191b | ||
|
|
28abb69c85 | ||
|
|
0bf88240b2 | ||
|
|
294afd1834 | ||
|
|
475f381991 | ||
|
|
1460bf2924 | ||
|
|
ab58ba721e | ||
|
|
d7bfb07c2c | ||
|
|
33b1d29781 | ||
|
|
78c20201ab | ||
|
|
66f4e797ed | ||
|
|
94ac37b32a | ||
|
|
22978e90c4 | ||
|
|
995a57f8e4 | ||
|
|
d6d767d8f9 | ||
|
|
0931d6f1ec | ||
|
|
24859aaa31 | ||
|
|
798e9cc0a7 | ||
|
|
1dac1bfb67 | ||
|
|
829c24b667 | ||
|
|
715c198763 | ||
|
|
d8741773c5 | ||
|
|
26b924dd48 | ||
|
|
8892e6e223 | ||
|
|
23afa8e025 | ||
|
|
7d1cc3139f | ||
|
|
30ea6ced9e | ||
|
|
3041d0f402 | ||
|
|
f4693df048 | ||
|
|
40342e2d9e | ||
|
|
b2ca43baf1 | ||
|
|
f36b9f7856 | ||
|
|
bcf13ef972 | ||
|
|
100b8e4753 | ||
|
|
134ee85067 | ||
|
|
c054a9d752 | ||
|
|
ef19e20d67 | ||
|
|
3fd344a308 | ||
|
|
17e7f4d3c9 | ||
|
|
87d1f4da16 | ||
|
|
88a4801f8e | ||
|
|
c55e35ac6e | ||
|
|
24601b0ef2 | ||
|
|
4fa46ff5a0 | ||
|
|
827ab2cd42 | ||
|
|
158c261d19 | ||
|
|
c607d1254e | ||
|
|
e90914664f | ||
|
|
fc855d407b | ||
|
|
e3a4713b3b | ||
|
|
f85896e947 | ||
|
|
131e2f87f4 | ||
|
|
4a330637a7 | ||
|
|
fa124a0afc | ||
|
|
72fd823643 | ||
|
|
644388edad | ||
|
|
cbb05c8b79 | ||
|
|
066c9f93a7 | ||
|
|
9aa7459b0f | ||
|
|
7557e01c9f | ||
|
|
f8a813c97b | ||
|
|
be6a6de65a | ||
|
|
ffe3e67241 | ||
|
|
141d911eb0 | ||
|
|
d6abf3fdf6 | ||
|
|
46a1ea09b6 | ||
|
|
2dc705d335 | ||
|
|
0700a357dd | ||
|
|
5fb7636ed4 | ||
|
|
196c2a535a | ||
|
|
db50ada952 | ||
|
|
087b04fd62 | ||
|
|
ea73351d39 | ||
|
|
ca1fae29b0 | ||
|
|
1916c2b750 | ||
|
|
3af2544e70 | ||
|
|
20e8aa93f2 | ||
|
|
3937902f38 | ||
|
|
021d5781a3 | ||
|
|
b429e4bc53 | ||
|
|
4a203e2449 | ||
|
|
d41203b856 | ||
|
|
2f21379e52 | ||
|
|
bd2ea606ae | ||
|
|
1f20c07e94 | ||
|
|
1be588b86a | ||
|
|
488df297fc | ||
|
|
80a4d94358 | ||
|
|
6a40f3ce18 | ||
|
|
d564a0a423 | ||
|
|
a9a544c433 | ||
|
|
2e0837f5c8 | ||
|
|
77be0ec477 |
447
CHANGELOG.md
447
CHANGELOG.md
@@ -6,6 +6,448 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [17.2.0] - 2023-06-18
|
||||
### Added
|
||||
- Added BigEndianBitConverter
|
||||
- Added EnumUtils.GetInverseFlags
|
||||
- Added ReverseLookupDictionary collection
|
||||
- Added NotifyFlagsChanged type
|
||||
|
||||
### Changed
|
||||
- SafeMutex now implements IDisposable
|
||||
|
||||
## [17.1.0] - 2023-03-22
|
||||
### Changed
|
||||
- Improved methods for human formatted timespan, including without milliseconds.
|
||||
- Removed Obfuscation
|
||||
|
||||
## [17.0.0] 2022-12-02
|
||||
### Changed
|
||||
- IcdEnvironment - Added CrestronDevicePlatform, removed from CrestronRuntimeEnvironment, to support SimplWindows on VC-4
|
||||
- Fixed IcdEnvironment CrestronRuntimeEnvironment uses in IcdConsole, PathUtils, and ProgramUtils
|
||||
- Fixed preprocessors in IcdDirectory
|
||||
- Added exception handling to ThreadedWorkerQueue
|
||||
|
||||
## [16.0.5] 2022-07-11
|
||||
### Changed
|
||||
- Fixed console command responses in Simpl+ Runtime Environment
|
||||
- Changed Crestron apps to not report as an interactive console
|
||||
- Fixed NETSTANDARD vs SIMPLSHARP preprocessors in various files
|
||||
- Fixed package reference conditions for NetStandard pakcages
|
||||
|
||||
## [16.0.4] 2022-07-01
|
||||
### Changed
|
||||
- Fixed PreProcessors for NETSTANDARD vs SIMPLSHARP for 4-series builds
|
||||
- Updated Crestron SDK to 2.18.96
|
||||
|
||||
## [16.0.3] 2022-06-23
|
||||
### Changed
|
||||
- Throwing better exception when trying to get unknown timezones
|
||||
|
||||
## [16.0.2] 2022-05-23
|
||||
### Changed
|
||||
- Fixed an issue in IcdUriBuilder where relative pathes were not being built into a valid URI.
|
||||
|
||||
## [16.0.1] 2021-10-28
|
||||
### Changed
|
||||
- Changed sqlite connection strings in IcdCultureInfo & IcdTimeZoneInfo to work with SimplSharp.
|
||||
|
||||
## [16.0.0] 2021-10-04
|
||||
### Added
|
||||
- Added IcdAutoResetEvent and IcdManaualResetEvent
|
||||
|
||||
### Changed
|
||||
- EnumUtils - Fixed bug where not all values were returned for GetValueExceptNone
|
||||
- ThreadedWorkerQueue - Added BetweenTime property, to wait between process callbacks
|
||||
- ThreadedWorkerQueue - Added RunProcess option to stop queue from processing items
|
||||
- ThreadedWorkerQueue - Added WaitForFlush events to wait until the queue is empty
|
||||
- ThreadedWorkerQueue - Added count property
|
||||
- ThreadedWorkerQueue - Now implements IDisposable
|
||||
|
||||
### Removed
|
||||
- Removed RateLimitedEventQueue and tests - features added to ThreadedWorkerQueue
|
||||
- Removed TryEnter from SafeCriticalSection - incorrect behavior on Crestron systems
|
||||
|
||||
## [15.2.0] - 2021-08-18
|
||||
### Added
|
||||
- TryParse overload in StringUtils, attempts to parse a GUID from a string
|
||||
|
||||
## [15.1.0] - 2021-08-03
|
||||
### Added
|
||||
- Enum Extension "SetFlags", takes a bool to set or unset the given flags
|
||||
- BiDictionay - Added constructors with TKey and TValue comparers
|
||||
- ILoggerService - Added Flush() method
|
||||
- Added log entries for intentional reboot and program restart
|
||||
- Added ThreadingUtils TimeSpan overloads
|
||||
|
||||
### Changed
|
||||
- IcdTimeZoneInfo - fixed issue when unable to read SQL
|
||||
- WeakKeyDictionary - Fixe GetHashCode to handle null values
|
||||
- ProcessorUtils - NetStandard - removed "Microsoft" from model name
|
||||
- Fixed null handing in SequenceComparer and UndefinedVersionComparer
|
||||
- EnumUtils - GetFlags no longer returns composite flags
|
||||
|
||||
## [15.0.0] - 2021-05-14
|
||||
### Added
|
||||
- Ported CsvReader for CF 3.5 compatibility from: https://github.com/tspence/csharp-csv-reader
|
||||
- Added enum extension method for cycling to the next enum value
|
||||
- Added GetLocalTimeZoneName method to IcdEnvironment
|
||||
- Added MatchAny method to RegexUtils
|
||||
- Added OnSystemDeviceAddedRemoved and associated raise methods to IcdEnvironment for NETSTANDARD
|
||||
- Added GetParentUri method to UriExtensions
|
||||
- Added RegistryExtensions for working with Windows registry
|
||||
- Added session change event to IcdEnvironment for login/logout feedback
|
||||
- Added OrderedDictionary collection
|
||||
|
||||
### Changed
|
||||
- Updated TimeZones.sqlite to include daylight time zone info, added a new display name column.
|
||||
- Implemented ProcessorUtils for Windows
|
||||
- Renamed OrderedDictionary to SortedDictionary for consistency with .Net
|
||||
- Fixed a bug where SafeTimer.Trigger() would run the callback twice on .Net Standard
|
||||
- Fixed a bug where XML deserialization would fail to read out of empty elements
|
||||
|
||||
### Removed
|
||||
- ANSI color is no longer enabled on .Net Standard by default - it must be enabled by the calling application
|
||||
|
||||
## [14.2.0] - 2021-02-04
|
||||
### Changed
|
||||
- ProcessorUtils Uptime methods changed to StartTime
|
||||
|
||||
## [14.1.0] - 2021-01-21
|
||||
### Added
|
||||
- Added overload to GuidUtils that takes an enumerable of guids and combines them into a new deterministic guid
|
||||
|
||||
### Changed
|
||||
- A SafeTimer constructor that executes the callback immediately now does this instead of waiting infinitely
|
||||
|
||||
## [14.0.0] - 2021-01-14
|
||||
### Added
|
||||
- Added Get and Set extensions to PropertyInfo in SIMPLSHARP to mimic overloads avaliable in NETSTANDARD
|
||||
- Added Collection extensions for setting and adding ranges of items
|
||||
- Added a method for getting the total number of seconds in a date
|
||||
- Added extensions to raise events with common event args using the data directly
|
||||
- Added property to IcdEnvironment to determine whether SSL communication is enabled
|
||||
- Added IcdTimeZoneInfo, a very light implementation of System.TimeZoneInfo for the .NET Compact Framework
|
||||
- Added ThreadedWorkerQueue - a threadsafe way to enqueue items and have a worker thread process them one at a time
|
||||
- Added eDaysOfWeek flags enum
|
||||
- Added support for reading the primitive type double to IcdXmlReader and XmlUtils
|
||||
- Added ProcessorUtils.GetSystemStartTime() to get DateTime the system started instead of a TimeSpan
|
||||
|
||||
### Changed
|
||||
- Repeater changed to use configured callbacks instead of a dumb event
|
||||
- Scheduled action callbacks allow a TimeSpan to be returned to delay actions
|
||||
- Handling a Crestron bug where File.Exists throws an exception on 4-Series instead of returning false
|
||||
- Changed ProcessorUtils.ModelVersion to be a string, Crestron pulls model version from CrestronEnvironment
|
||||
- For 4-series console outputs, replacing \n with \r\n to help console readability
|
||||
- Changed RuntimeEnvironment to be 3 variables - Framework for Crestron vs Standard, CrestronSeries for 3 vs 4, and CrestronRuntimeEnvironment for Simpl vs SimplSharpPro vs Server
|
||||
|
||||
## [13.0.0] - 2020-09-03
|
||||
### Added
|
||||
- Added util methods for removing duplicate whitespace in strings
|
||||
- Added dequeue overload to ScrollQueue
|
||||
|
||||
### Changed
|
||||
- Replaced Crestron Unzip with Yallie Unzip
|
||||
- Fixed "version" regex for 4-series
|
||||
- Fixed date parsing error for 4-series
|
||||
- Split SimplSharpProMono runtime environment into SimplSharpProSever
|
||||
- Fixed log formatting on 4-series
|
||||
|
||||
## [12.1.0] - 2020-07-14
|
||||
### Added
|
||||
- ReflectionExtensions : GetProperty, SetProperty, CallMethod extensions for NetStandard
|
||||
- Added attributes for controlling obfuscation
|
||||
- Added AggregateOrDefault extension method for applying an accumulator function over a sequence that returns a default value if the sequence is empty
|
||||
|
||||
### Changed
|
||||
- DHCP status is a boolean
|
||||
- Changed Hostname property to Hostnames
|
||||
|
||||
## [12.0.0] - 2020-06-18
|
||||
### Added
|
||||
- Added ToCollection extension method for copying an enumerable to a new collection
|
||||
- TableBuilder supports multi-line content
|
||||
- Added eIcdFileMode for IO platform agnosticism
|
||||
- Extension method for determining if a Type is anonymous
|
||||
- Extension method for getting inner generic Types
|
||||
- Added extension method for dynamically converting a sequence to a generic list of the given item type
|
||||
- Added methods for getting UserData paths
|
||||
- Added methods for reading GUIDs from XML
|
||||
- Added methods for reading DateTimes from XML
|
||||
- Added method for combining GUIDs
|
||||
- Added method for getting the EventArgs type for an EventHandler
|
||||
- Added methods for getting a JSON value as a float or double
|
||||
- Added dictionary Remove method for outputting the removed value
|
||||
- Added IGenericEventArgs interface
|
||||
- Added MinimalTypeConverter for serializing Types to JSON
|
||||
- Added common JSON serializer settings for common, platform agnostic DateTime and Type conversion
|
||||
|
||||
### Changed
|
||||
- Rewrote JsonItemWrapper serialization for JsonConvert friendliness
|
||||
- Reflection optimizations
|
||||
- Fixed NullParameterException in TableBuilder
|
||||
- Improvements to EnumUtils, less reliance on casting to/from int
|
||||
- Cleaned up TimeSpan.ToReadableString() output
|
||||
- Fixed a bug where System.Reflection exceptions can't be caught in S#
|
||||
- TableBuilder no longer draws redundant separators
|
||||
- Fixed a bug where CompiledOn date was not being parsed correctly due to culture
|
||||
- S# DateTimes are serialized to JSON in ISO-8601 format
|
||||
- Deadlock detection works better for false positives
|
||||
- Improved LogItem JSON serialization
|
||||
- Improved NiceName method to better handle syntax, whitespace and punctuation
|
||||
- Fixed a bug where IcdCultureInfo would fail to load on Crestron 4-series processors
|
||||
- Clarifying which culture failed to load when IcdCultureInfo throws an exception
|
||||
|
||||
## [11.1.1] - 2020-08-21
|
||||
### Removed
|
||||
- Removed the OnItemTrimmed event from the ScrollQueue due to deadlocks
|
||||
|
||||
## [11.1.0] - 2020-05-19
|
||||
### Added
|
||||
- ScrollQueue - Added OnItemTrimmed event
|
||||
- Added DateTimeNullableEventArgs
|
||||
|
||||
## [11.0.0] - 2020-03-20
|
||||
### Added
|
||||
- Added Not null tag for ICDUriBuilder Constructor that takes a URI as an argument.
|
||||
- Added MathUtils methods for converting to and from percentages
|
||||
- Added enum extensions for finding the inclusion and exclusion of enum flags
|
||||
- Added DateTime extensions for adding years, months, days, hours, minutes and wrapping without modifying other values
|
||||
- Added shims for deserializing an XML array using a callback for each item
|
||||
- Added methods for serializing an XML array
|
||||
- Added WriteAllByte method on IcdFile.
|
||||
- Added PathUtils for building paths in the HTML directory
|
||||
- Added public access to GetValues enumeration extension
|
||||
- Added extensions for getting JsonReader values as long or ulong
|
||||
- Added DateTimeUtils methods for creating DateTimes from epoch seconds or milliseconds
|
||||
- Added utils for splitting ANSI into spans for conversion to XAML, HTML, etc
|
||||
|
||||
### Changed
|
||||
- Fixed exception trying to get DHCP status of network interfaces on Linux
|
||||
- Fixed a bug where color formatted console output on Net Standard was not raising the OnConsolePrint event
|
||||
- Simplifying ANSI color methods, better cross-platform color support
|
||||
- Console uses unicode for table drawing on Net Standard
|
||||
- Using UTC for tracking scheduled events, fixes issues with DST
|
||||
- Using UTC for tracking durations
|
||||
- Fixed a bug where table width calculations were not considering unprintable characters
|
||||
|
||||
## [10.3.0] - 2020-01-20
|
||||
### Changed
|
||||
- Network/MAC/DNS address utils are now enumerating all adapter types
|
||||
- Ignoring Crestron ethernet parameters that say "Invalid Value"
|
||||
- Skipping network interfaces with an invalid adapter id
|
||||
|
||||
## [10.2.0] - 2019-12-04
|
||||
### Added
|
||||
- Added shim methods for finding closest DateTimes from a sequence
|
||||
|
||||
## [10.1.0] - 2019-11-18
|
||||
### Added
|
||||
- Added PathUtils methods for getting ProgramData paths
|
||||
- Added a method for determining if a URI is defaults
|
||||
- Added MaxOrDefault extension method for enumerables
|
||||
- Added a method for finding an item in a sorted list by a given predicate
|
||||
|
||||
### Changed
|
||||
- NullObject implements IComparable, fixes issues with null keys in ordered dictionaries
|
||||
- IcdSqliteConnection CreateFile method will create directories recursively
|
||||
|
||||
## [10.0.0] - 2019-10-07
|
||||
### Added
|
||||
- IcdEnvironment.GetUtcTime() to get UTC representaiton of current time.
|
||||
- Extension methods for determining if a sequence is in order
|
||||
- Overload for calculating the modulus of longs
|
||||
- Default implementation for AbstractGenericXmlConverter Instantiate method
|
||||
- Additional binary search extensions, now working for all ILists
|
||||
- Added NullObject as a means of having null keys in hash tables
|
||||
|
||||
### Changed
|
||||
- Potential fix for unhelpful exception messages coming from SafeCriticalSection.Execute
|
||||
- Small performance improvement when copying arrays
|
||||
|
||||
## [9.9.0] - 2019-09-16
|
||||
### Added
|
||||
- Added a method for converting 24 hour to 12 hour format
|
||||
- Added a method for determining if a culture uses 24 hour format
|
||||
- Added math util method for modulus
|
||||
- Added TimeSpan extension methods for cycling hours and minutes without modifying the day
|
||||
- Added a dictionary extension method for getting or adding a new value via func
|
||||
- Added CultureInfo extensions for converting between 12 hour and 24 hour time formatting
|
||||
- Added environment methods for setting the current date and time
|
||||
- Added BinarySearch extension method for all IList types
|
||||
- Added PathUtils methods to get ProgramData directory
|
||||
|
||||
### Changed
|
||||
- The Root Config path in Net Standard will now be the ICD.Connect folder in the current environments ProgramData directory
|
||||
- Fixed a bug where CultureInfo was not being cloned correctly
|
||||
- List AddSorted extensions now work for all IList types
|
||||
|
||||
## [9.8.0] - 2019-09-03
|
||||
### Added
|
||||
- Added Public API Properties to get the program install date based on the creation date of core dll file for NetStandard and SimplSharp
|
||||
- Implemented processor utils for NetStandard to get the system uptime and the program uptime
|
||||
- Added methods for deserializing an XML array
|
||||
|
||||
### Changed
|
||||
- Improvements to JSON DateTime parsing, particularly in Net Standard
|
||||
|
||||
## [9.7.1] - 2019-08-17
|
||||
### Changed
|
||||
- Fixed CultureInfo SQLite conection string for 4-series compatibility
|
||||
|
||||
## [9.7.0] - 2019-08-15
|
||||
### Added
|
||||
- Added logger timestamps to non simplsharp programs
|
||||
- Added Net Standard Support for JSON DateTime formats
|
||||
- Added EmailValidation class
|
||||
|
||||
### Changed
|
||||
- JSON dict serialization serializes keys instead of converting to property name
|
||||
- Fixed a bug where ANSI color encoded strings with percentages were being scrambled
|
||||
|
||||
## [9.6.0] - 2019-07-03
|
||||
### Added
|
||||
- Added RecursionUtils method to get a single clique given a starting node
|
||||
- Breadth First Search can now search graphs in addition to trees
|
||||
- Added StartOfDay and EndOfDay DateTime extension methods
|
||||
|
||||
### Changed
|
||||
- Fixed bug in IcdUriBuilder where Query property behaved differently to UriBuilder
|
||||
- Throwing an exception when attempting to read a non-primitive JSON token as a string
|
||||
|
||||
## [9.5.0] - 2019-06-10
|
||||
### Added
|
||||
- Added Shim to read a list from xml with no root element
|
||||
- Added a URI query builder
|
||||
|
||||
### Changed
|
||||
- Fixed JSON DateTime parsing in .Net Standard
|
||||
- Fixed threading exception in TypeExtensions
|
||||
- Fixes for platform-agnostic culture handling
|
||||
|
||||
## [9.4.0] - 2019-05-10
|
||||
### Added
|
||||
- Added extension method for peeking queues
|
||||
- Added extension method for getting or adding a new item to a dictionary
|
||||
- Added methods for serializing additional types, arrays and dictionaries to JSON
|
||||
- AbstractGenericJsonConverter exposes virtual methods for overriding object serialization/deserialization
|
||||
- Added RemoveRange method to IcdHashSet
|
||||
- Added IcdCultureInfo and CultureInfo database for localization
|
||||
|
||||
### Changed
|
||||
- IcdUriBuilder constructors behave closer to UriBuilder, Host defaults to "localhost"
|
||||
|
||||
## [9.3.0] - 2019-04-16
|
||||
### Added
|
||||
- Added SPlusUtils with ConvertToInt method taking LowWord/HighWord ushorts
|
||||
- Added JsonReader extension methods for reading DateTimes
|
||||
- Added JsonReader extension methods for writing properties
|
||||
- IcdStreamWriter exposes WriteLine(string)
|
||||
- Added ProgramLogsPath to PathUtils
|
||||
|
||||
### Changed
|
||||
- Fixes for VC4 compatibility
|
||||
- Fixed JSON DateTime parsing for timezone information
|
||||
- Small reflection optimizations
|
||||
|
||||
## [9.2.0] - 2019-03-01
|
||||
### Added
|
||||
- Added Type IsAssignableTo extension shim
|
||||
- Added constructor to BiDictionary to instantiate from an existing dict
|
||||
|
||||
### Changed
|
||||
- Fixed bug preventing deserialization of XML lists
|
||||
- Crestron ConsoleResponse uses PrintLine instead of Print
|
||||
- Use PrintLine instead of ConsoleResponse on Crestron server
|
||||
|
||||
## [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.9.3] - 2020-08-17
|
||||
### Changed
|
||||
- Workaround for logged XML format exceptions when failing to parse floats
|
||||
|
||||
## [8.9.2] - 2020-07-28
|
||||
### Changed
|
||||
- StringExtensions - fixed an issue with IsNumeric where empty strings would return true
|
||||
|
||||
## [8.9.1] - 2020-05-27
|
||||
### Changed
|
||||
- Changed ProcessorUtils to use CrestronEnvironment to retrive serial number - this fixes issues with new serial numbers that aren't deciaml TSIDs
|
||||
|
||||
## [8.9.0] - 2020-04-30
|
||||
### Changed
|
||||
- ProgramUtils and ProcessorUtils return dates instead of strings for date properties
|
||||
|
||||
## [8.8.1] - 2020-02-18
|
||||
### Changed
|
||||
- IcdTimer - fixed issue that prevented OnElapsed event from firing when Length is less than (or close to) Heartbeat Interval
|
||||
|
||||
## [8.8.0] - 2020-01-23
|
||||
### Added
|
||||
- Added an overload to PriorityQueue for determing the de-duplication behaviour
|
||||
|
||||
## [8.7.2] - 2019-10-29
|
||||
### Changed
|
||||
- Fixed a bug with PriorityQueue de-duplication where a new command would be inserted in the wrong position
|
||||
|
||||
## [8.7.1] - 2019-08-22
|
||||
### Changed
|
||||
- Fixed a bug with the IcdOrderedDict index setter that was creating additional values
|
||||
|
||||
## [8.7.0] - 2019-06-24
|
||||
### Added
|
||||
- IcdXmlException exposes line number and position properties
|
||||
|
||||
## [8.6.1] - 2019-06-14
|
||||
### Changed
|
||||
- Fixed a bug where stopped timers on NetStandard would still have a periodic callback duration
|
||||
|
||||
## [8.6.0] - 2019-06-14
|
||||
### Changed
|
||||
- Overhaul of RangeAttribute remap methods to better avoid overflows
|
||||
|
||||
## [8.5.0] - 2019-06-06
|
||||
### Added
|
||||
- Adding features to IcdEnvironment for tracking program initialization state
|
||||
|
||||
## [8.4.1] - 2019-06-05
|
||||
### Changed
|
||||
- Caching the program/processor start time and calculating the uptime from those values instead of polling the crestron processor
|
||||
|
||||
## [8.4.0] - 2019-05-15
|
||||
### Added
|
||||
- Added GUID utils for generating seeded GUIDs
|
||||
- Added extension method for getting stable hashcodes from strings
|
||||
- Added environment and processor utilities for determining DNS status and hostname
|
||||
|
||||
### Changed
|
||||
- RangeAttribute improvements for better type safety
|
||||
- PathUtils breaking out ProgramConfigDirectory and CommonConfigDirectory from the full paths
|
||||
|
||||
## [8.3.3] - 2019-05-24
|
||||
### Added
|
||||
- Added empty, placeholder interface for ICD Attributes
|
||||
|
||||
## [8.3.2] - 2019-05-02
|
||||
### Changed
|
||||
- Fixed PriorityQueue IndexOutOfRange exception when an inner queue becomes depleted
|
||||
|
||||
## [8.3.1] - 2019-04-05
|
||||
### Changed
|
||||
- Fixed FormatException when parsing some JSON DateTimes
|
||||
@@ -70,7 +512,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- New XML conversion framework for performance improvements
|
||||
|
||||
### Changed
|
||||
- XmlUtils is now using the improved XML conversion framework
|
||||
- XmlUtils is now using the improved XML conversion framework
|
||||
- Better implementation of DictionaryExtensions.ToInverse
|
||||
|
||||
## [5.0.0] - 2018-09-14
|
||||
@@ -143,8 +585,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- Adding extension method for getting Informational Version from an Assembly
|
||||
- Adding WeakKeyDictionary for caching
|
||||
- Reflection util methods
|
||||
|
||||
|
||||
### Changed
|
||||
- JSON serialization/deserialization features moved into base converter
|
||||
- Removed suffix from assembly name
|
||||
|
||||
27
ICD.Common.Utils.Tests/AnsiUtilsTest.cs
Normal file
27
ICD.Common.Utils.Tests/AnsiUtilsTest.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class AnsiUtilsTest
|
||||
{
|
||||
[Test]
|
||||
public void ToSpansTest()
|
||||
{
|
||||
string ansi = "\x1b[30mblack\x1b[37mwhite\x1b[0mdefault";
|
||||
AnsiSpan[] spans = AnsiUtils.ToSpans(ansi).ToArray();
|
||||
|
||||
Assert.AreEqual(3, spans.Length);
|
||||
|
||||
Assert.AreEqual("black", spans[0].Text);
|
||||
Assert.AreEqual("30", spans[0].Code);
|
||||
|
||||
Assert.AreEqual("white", spans[1].Text);
|
||||
Assert.AreEqual("37", spans[1].Code);
|
||||
|
||||
Assert.AreEqual("default", spans[2].Text);
|
||||
Assert.AreEqual("0", spans[2].Code);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
ICD.Common.Utils.Tests/Attributes/RangeAttributeTest.cs
Normal file
86
ICD.Common.Utils.Tests/Attributes/RangeAttributeTest.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using RangeAttribute = ICD.Common.Utils.Attributes.RangeAttribute;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Attributes
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class RangeAttributeTest : AbstractIcdAttributeTest<RangeAttribute>
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(1.0f)]
|
||||
[TestCase(1.0)]
|
||||
public void MinTest(object min)
|
||||
{
|
||||
Assert.AreEqual(min, new RangeAttribute(min, min).Min);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(1.0f)]
|
||||
[TestCase(1.0)]
|
||||
public void MaxTest(object max)
|
||||
{
|
||||
Assert.AreEqual(max, new RangeAttribute(max, max).Max);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[TestCase((double)0, (double)0)]
|
||||
[TestCase((double)1, (double)1)]
|
||||
[TestCase(ushort.MaxValue, double.MaxValue)]
|
||||
[TestCase(short.MinValue, double.MinValue)]
|
||||
public void RemapToDoubleTest(object value, double expected)
|
||||
{
|
||||
Assert.AreEqual(expected, RangeAttribute.RemapToDouble(value));
|
||||
}
|
||||
|
||||
[TestCase((double)0, typeof(ushort), (ushort)32767)]
|
||||
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
|
||||
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
|
||||
public void RemapFromDoubleTest(double value, Type type, object expected)
|
||||
{
|
||||
Assert.AreEqual(expected, RangeAttribute.RemapFromDouble(value, type));
|
||||
}
|
||||
|
||||
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
|
||||
[TestCase(short.MaxValue, typeof(ushort), short.MaxValue)]
|
||||
public static void Clamp(object value, Type type, object expected)
|
||||
{
|
||||
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
|
||||
}
|
||||
|
||||
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
|
||||
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
|
||||
public void Clamp(double value, Type type, double expected)
|
||||
{
|
||||
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
|
||||
}
|
||||
|
||||
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
|
||||
[TestCase(short.MaxValue, typeof(ushort), ushort.MaxValue)]
|
||||
public void RemapTest(object value, Type type, object expected)
|
||||
{
|
||||
Assert.AreEqual(expected, RangeAttribute.Remap(value, type));
|
||||
}
|
||||
|
||||
[TestCase(0, 100, ushort.MaxValue, typeof(ushort), ushort.MaxValue)]
|
||||
[TestCase(0, 100, ushort.MaxValue, typeof(short), short.MaxValue)]
|
||||
public void ClampMinMaxThenRemapTest(object min, object max, object value, Type type, object expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new RangeAttribute(min, max).ClampMinMaxThenRemap(value, type));
|
||||
}
|
||||
|
||||
[TestCase(0, 100, ushort.MaxValue, 100)]
|
||||
[TestCase(0, 100, ushort.MinValue, 0)]
|
||||
public void RemapMinMaxTest(object min, object max, object value, object expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new RangeAttribute(min, max).RemapMinMax(value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
332
ICD.Common.Utils.Tests/BigEndianBitConverterTest.cs
Normal file
332
ICD.Common.Utils.Tests/BigEndianBitConverterTest.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class BigEndianBitConverterTest
|
||||
{
|
||||
[TestCase(ushort.MaxValue, new byte[] { 0xFF, 0xFF }, 0)]
|
||||
[TestCase(ushort.MinValue, new byte[] { 0x00, 0x00 }, 0)]
|
||||
[TestCase((ushort)255, new byte[] { 0x00, 0xFF }, 0)]
|
||||
[TestCase((ushort)65280, new byte[] { 0xFF, 0x00 }, 0)]
|
||||
[TestCase(ushort.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(ushort.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((ushort)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase((ushort)65280, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((ushort)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
|
||||
[TestCase((ushort)15, new byte[] { 0x00, 0x0F }, 0)]
|
||||
public void ToUshortTest(ushort expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUshort(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUshortExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUshort(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUshort(values, 8));
|
||||
}
|
||||
|
||||
[TestCase(short.MaxValue, new byte[] { 0x7F, 0xFF }, 0)]
|
||||
[TestCase(short.MinValue, new byte[] { 0x80, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00 }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF }, 0)]
|
||||
[TestCase((short)255, new byte[] { 0x00, 0xFF }, 0)]
|
||||
[TestCase((short)-256, new byte[] { 0xFF, 0x00 }, 0)]
|
||||
[TestCase(short.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(short.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((short)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase((short)-256, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((short)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
|
||||
[TestCase((short)15, new byte[] { 0x00, 0x0F }, 0)]
|
||||
public void ToShortTest(short expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToShort(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToShortExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToShort(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToShort(values, 8));
|
||||
}
|
||||
|
||||
[TestCase(uint.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(uint.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase((uint)255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(4278190080, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(uint.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(uint.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((uint)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(4278190080, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((uint)15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase((uint)1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
|
||||
public void ToUintTest(uint expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUint(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUintExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUint(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUint(values, 6));
|
||||
}
|
||||
|
||||
[TestCase(int.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(int.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(-16777216, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(int.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
|
||||
[TestCase(int.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase(255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(-16777216, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase(15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase(1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
|
||||
public void ToIntTest(int expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToInt(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToIntExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToInt(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToInt(values, 6));
|
||||
}
|
||||
|
||||
[TestCase(ulong.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(ulong.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase((ulong)255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(18374686479671623680, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(ulong.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
|
||||
2)]
|
||||
[TestCase(ulong.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase((ulong)255,
|
||||
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(18374686479671623680,
|
||||
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase((ulong)67555089642946815,
|
||||
new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
|
||||
[TestCase((ulong)4487102673719295, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, 0)]
|
||||
public void ToUlongTest(ulong expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUlong(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToUlongExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUlong(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUlong(values, 2));
|
||||
}
|
||||
|
||||
[TestCase(long.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(long.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
|
||||
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
|
||||
[TestCase(-72057594037927936, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(long.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
|
||||
2)]
|
||||
[TestCase(long.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
|
||||
[TestCase(255,
|
||||
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
|
||||
[TestCase(-72057594037927936,
|
||||
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
|
||||
[TestCase(67555093906456320, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00, 0xF0 },
|
||||
2)]
|
||||
[TestCase(4487102659035120, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 0)]
|
||||
public void ToLongTest(long expectedResult, byte[] value, int startIndex)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
|
||||
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToLong(value, startIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToLongExceptionTest()
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToLong(null, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, 9));
|
||||
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToLong(values, 2));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, int.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00 }, int.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, 255)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, -1)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, -16777216)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, 15728895)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 1044735)]
|
||||
public void GetBytesIntTest(byte[] expectedResult, int value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, uint.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, uint.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 4278190080)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, (uint)15728895)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, (uint)1044735)]
|
||||
public void GetBytesUintTest(byte[] expectedResult, uint value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF }, short.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00 }, short.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00 }, (short)0)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF }, (short)-1)]
|
||||
[TestCase(new byte[] { 0x00, 0xFF }, (short)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00 }, (short)-256)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0 }, (short)240)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F }, (short)15)]
|
||||
public void GetBytesShortTest(byte[] expectedResult, short value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF }, ushort.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00 }, ushort.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0xFF }, (ushort)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00 }, (ushort)65280)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0 }, (ushort)240)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F }, (ushort)15)]
|
||||
public void GetBytesUshortTest(byte[] expectedResult, ushort value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, long.MaxValue)]
|
||||
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },long.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, (long)0)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (long)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, (long)-1)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -72057594037927936)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00 }, 67555093906456320)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 4487102659035120)]
|
||||
public void GetBytesLongTest(byte[] expectedResult, long value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ulong.MaxValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ulong.MinValue)]
|
||||
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (ulong)255)]
|
||||
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 18374686479671623680)]
|
||||
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF }, (ulong)67555089642946815)]
|
||||
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, (ulong)4487102673719295)]
|
||||
public void GetBytesUlongTest(byte[] expectedResult, ulong value)
|
||||
{
|
||||
// We use system BitConverter for systems that are already big-endian
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Assert.Inconclusive();
|
||||
|
||||
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -7,120 +8,85 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[TestFixture]
|
||||
public sealed class IcdOrderedDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
public void OrderingTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{2, 20}
|
||||
{"1", "a"},
|
||||
{"2", "b"},
|
||||
{"3", "c"}
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] {"1", "2", "3"}));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "b", "c" }));
|
||||
|
||||
// Remove the key and add to the end of the dictionary
|
||||
dict.Remove("2");
|
||||
dict["2"] = "d";
|
||||
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "c", "d" }));
|
||||
|
||||
// No key change
|
||||
dict["1"] = "e";
|
||||
|
||||
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
|
||||
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "e", "c", "d" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadOnlyTest()
|
||||
public void GetTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
|
||||
|
||||
Assert.IsFalse(dict.IsReadOnly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
{"1", "a"},
|
||||
{"2", "b"},
|
||||
{"3", "c"}
|
||||
};
|
||||
|
||||
int[] keys = dict.Keys.ToArray();
|
||||
KeyValuePair<string, string> kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("a", kvp.Value);
|
||||
|
||||
Assert.AreEqual(3, keys.Length);
|
||||
Assert.AreEqual(-1, keys[0]);
|
||||
Assert.AreEqual(0, keys[1]);
|
||||
Assert.AreEqual(1, keys[2]);
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("b", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
// Remove the key and add to the end of the dictionary
|
||||
dict.Remove("2");
|
||||
dict["2"] = "d";
|
||||
|
||||
kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("a", kvp.Value);
|
||||
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("d", kvp.Value);
|
||||
|
||||
// No key change
|
||||
dict["1"] = "e";
|
||||
|
||||
kvp = dict.Get(0);
|
||||
Assert.AreEqual("1", kvp.Key);
|
||||
Assert.AreEqual("e", kvp.Value);
|
||||
|
||||
kvp = dict.Get(1);
|
||||
Assert.AreEqual("3", kvp.Key);
|
||||
Assert.AreEqual("c", kvp.Value);
|
||||
|
||||
kvp = dict.Get(2);
|
||||
Assert.AreEqual("2", kvp.Key);
|
||||
Assert.AreEqual("d", kvp.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] values = dict.Values.ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.AreEqual(-10, values[0]);
|
||||
Assert.AreEqual(0, values[1]);
|
||||
Assert.AreEqual(10, values[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
Assert.AreEqual(0, dict[0]);
|
||||
Assert.AreEqual(10, dict[1]);
|
||||
Assert.AreEqual(-10, dict[-1]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
136
ICD.Common.Utils.Tests/Collections/IcdSortedDictionaryTest.cs
Normal file
136
ICD.Common.Utils.Tests/Collections/IcdSortedDictionaryTest.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdSortedDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{2, 20}
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadOnlyTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
|
||||
|
||||
Assert.IsFalse(dict.IsReadOnly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] keys = dict.Keys.ToArray();
|
||||
|
||||
Assert.AreEqual(3, keys.Length);
|
||||
Assert.AreEqual(-1, keys[0]);
|
||||
Assert.AreEqual(0, keys[1]);
|
||||
Assert.AreEqual(1, keys[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] values = dict.Values.ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.AreEqual(-10, values[0]);
|
||||
Assert.AreEqual(0, values[1]);
|
||||
Assert.AreEqual(10, values[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerTest()
|
||||
{
|
||||
// ReSharper disable UseObjectOrCollectionInitializer
|
||||
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
|
||||
// ReSharper restore UseObjectOrCollectionInitializer
|
||||
|
||||
dict[0] = 0;
|
||||
dict[1] = 10;
|
||||
dict[-1] = -10;
|
||||
dict[-1] = -11;
|
||||
|
||||
Assert.AreEqual(0, dict[0]);
|
||||
Assert.AreEqual(10, dict[1]);
|
||||
Assert.AreEqual(-11, dict[-1]);
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
|
||||
int[] expectedKeys = {-1, 0, 1 };
|
||||
int[] expectedValues = {-11, 0, 10};
|
||||
|
||||
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
|
||||
Assert.AreEqual(expectedValues, dict.Values.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -133,8 +133,8 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
};
|
||||
|
||||
Assert.AreEqual(1, dequeue[0]);
|
||||
Assert.AreEqual(4, dequeue[1]);
|
||||
Assert.AreEqual(3, dequeue[2]);
|
||||
Assert.AreEqual(3, dequeue[1]);
|
||||
Assert.AreEqual(4, dequeue[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -168,6 +168,29 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryDequeueTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
queue.Enqueue(10, 1);
|
||||
queue.Enqueue(20, 2);
|
||||
queue.Enqueue(30, 3);
|
||||
|
||||
int output;
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(10, output);
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(20, output);
|
||||
|
||||
Assert.IsTrue(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(30, output);
|
||||
|
||||
Assert.IsFalse(queue.TryDequeue(out output));
|
||||
Assert.AreEqual(0, output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class RateLimitedEventQueueTest
|
||||
{
|
||||
[Test]
|
||||
public void ItemDequeuedFeedbackTest()
|
||||
{
|
||||
List<GenericEventArgs<int>> callbacks = new List<GenericEventArgs<int>>();
|
||||
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 1000 })
|
||||
{
|
||||
queue.OnItemDequeued += (sender, args) => callbacks.Add(args);
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
|
||||
|
||||
queue.OnItemDequeued += (sender, args) => { ThreadingUtils.Sleep(1000); };
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Third enqueue did not wait for process to complete");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(3, callbacks.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
[TestCase(1000)]
|
||||
public void BetweenMillisecondsTest(long milliseconds)
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = milliseconds })
|
||||
Assert.AreEqual(milliseconds, queue.BetweenMilliseconds);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
Assert.True(queue.SequenceEqual(new[] { 10, 20, 30 }));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public class ReverseLookupDictionaryTest
|
||||
{
|
||||
|
||||
//todo: Finish!
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Keys.Count);
|
||||
|
||||
Assert.IsTrue(dict.Keys.Contains(2));
|
||||
Assert.IsTrue(dict.Keys.Contains(3));
|
||||
Assert.IsFalse(dict.Keys.Contains(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Values.Count);
|
||||
|
||||
Assert.IsTrue(dict.Values.Contains("20"));
|
||||
Assert.IsTrue(dict.Values.Contains("30"));
|
||||
Assert.IsFalse(dict.Values.Contains("40"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
|
||||
dict.Clear();
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsKey(1));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsValue("10"));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsValue("10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "10"));
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "20"));
|
||||
|
||||
Assert.DoesNotThrow(() => dict.Add(2, "10"));
|
||||
Assert.DoesNotThrow(() => dict.Add(3, "20"));
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
dict.Set(4, "20");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
Assert.AreEqual("40", dict.GetValue(4));
|
||||
|
||||
CollectionAssert.AreEquivalent(new[]{1,4}, dict.GetKeys("20"));
|
||||
CollectionAssert.AreEquivalent(new[]{3}, dict.GetKeys("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetKeysTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "Odd");
|
||||
dict.Add(2, "Even");
|
||||
dict.Set(3, "Odd");
|
||||
dict.Add(4, "Value");
|
||||
dict.Set(4, "Even");
|
||||
dict.Add(5, "Odd");
|
||||
dict.Add(6, "Even");
|
||||
|
||||
CollectionAssert.AreEquivalent(new[]{1,3,5}, dict.GetKeys("Odd"));
|
||||
CollectionAssert.AreEquivalent(new[]{2,4,6}, dict.GetKeys("Even"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
dict.Set(2, "20");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
Assert.AreEqual("20", dict.GetValue(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveKeyTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveKey(1);
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueSingleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueMultipleTest1()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueWithOthersTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
dict.Add(4, "20");
|
||||
dict.Add(5, "20");
|
||||
dict.Add(6, "some other string");
|
||||
|
||||
Assert.AreEqual(6, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
string value;
|
||||
Assert.IsFalse(dict.TryGetValue(1, out value));
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
Assert.AreEqual(null, value);
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetValue(1, out value));
|
||||
Assert.AreEqual("10", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetKeysMultipleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
IEnumerable<int> value;
|
||||
Assert.IsFalse(dict.TryGetKeys("10", out value));
|
||||
|
||||
dict.Add(1, "10");
|
||||
dict.Add(11, "100");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1}, value);
|
||||
Assert.IsTrue(dict.TryGetKeys("100", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{11}, value);
|
||||
|
||||
|
||||
dict.Add(2, "10");
|
||||
dict.Add(3, "10");
|
||||
dict.Add(12, "100");
|
||||
dict.Add(13, "100");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1,2,3}, value);
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("100", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{11,12,13}, value);
|
||||
|
||||
Assert.IsFalse(dict.TryGetKeys("string", out value));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetKeysSingleTest()
|
||||
{
|
||||
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
|
||||
|
||||
IEnumerable<int> value;
|
||||
Assert.IsFalse(dict.TryGetKeys("10", out value));
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKeys("10", out value));
|
||||
CollectionAssert.AreEquivalent(new[]{1}, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,14 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[Test, UsedImplicitly]
|
||||
public void MaxSizeTest()
|
||||
{
|
||||
int unused;
|
||||
|
||||
ScrollQueue<int> test = new ScrollQueue<int>(5);
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(1);
|
||||
test.Enqueue(2);
|
||||
test.Enqueue(3);
|
||||
test.Enqueue(4);
|
||||
test.Enqueue(0, out unused);
|
||||
test.Enqueue(1, out unused);
|
||||
test.Enqueue(2, out unused);
|
||||
test.Enqueue(3, out unused);
|
||||
test.Enqueue(4, out unused);
|
||||
|
||||
Assert.AreEqual(5, test.Count);
|
||||
|
||||
@@ -25,7 +27,7 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
Assert.AreEqual(3, test.Count);
|
||||
Assert.AreEqual(2, test.Peek());
|
||||
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(0, out unused);
|
||||
|
||||
Assert.AreEqual(3, test.Count);
|
||||
Assert.AreEqual(3, test.Peek());
|
||||
@@ -34,8 +36,10 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[Test, UsedImplicitly]
|
||||
public void ClearTest()
|
||||
{
|
||||
int unused;
|
||||
|
||||
ScrollQueue<int> test = new ScrollQueue<int>(5);
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(0, out unused);
|
||||
test.Clear();
|
||||
|
||||
Assert.AreEqual(0, test.Count);
|
||||
@@ -44,9 +48,11 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[Test, UsedImplicitly]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
int unused;
|
||||
|
||||
ScrollQueue<int> test = new ScrollQueue<int>(5);
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(1);
|
||||
test.Enqueue(0, out unused);
|
||||
test.Enqueue(1, out unused);
|
||||
|
||||
Assert.AreEqual(2, test.Count);
|
||||
|
||||
@@ -59,9 +65,11 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[Test, UsedImplicitly]
|
||||
public void DequeueTest()
|
||||
{
|
||||
int unused;
|
||||
|
||||
ScrollQueue<int> test = new ScrollQueue<int>(5);
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(1);
|
||||
test.Enqueue(0, out unused);
|
||||
test.Enqueue(1, out unused);
|
||||
|
||||
Assert.AreEqual(0, test.Dequeue());
|
||||
Assert.AreEqual(1, test.Count);
|
||||
@@ -70,9 +78,11 @@ namespace ICD.Common.Utils.Tests.Collections
|
||||
[Test, UsedImplicitly]
|
||||
public void PeekTest()
|
||||
{
|
||||
int unused;
|
||||
|
||||
ScrollQueue<int> test = new ScrollQueue<int>(5);
|
||||
test.Enqueue(0);
|
||||
test.Enqueue(1);
|
||||
test.Enqueue(0, out unused);
|
||||
test.Enqueue(1, out unused);
|
||||
|
||||
Assert.AreEqual(0, test.Peek());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Comparers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Comparers
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class UndefinedVersionComparerTest
|
||||
{
|
||||
[Test]
|
||||
public void CompareToTest()
|
||||
{
|
||||
Assert.AreEqual(0, UndefinedVersionComparer.Instance.Compare(new Version(0, 0), new Version(0, 0, 0, 0)));
|
||||
Assert.AreEqual(-1, UndefinedVersionComparer.Instance.Compare(new Version(0, 0), new Version(1, 0, 0, 0)));
|
||||
Assert.AreEqual(1, UndefinedVersionComparer.Instance.Compare(new Version(1, 0), new Version(0, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Comparers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Comparers
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class UndefinedVersionEqualityComparerTest
|
||||
{
|
||||
[Test]
|
||||
public void Equals()
|
||||
{
|
||||
Assert.IsTrue(UndefinedVersionEqualityComparer.Instance.Equals(new Version(0, 0), new Version(0, 0, 0, 0)));
|
||||
Assert.IsFalse(UndefinedVersionEqualityComparer.Instance.Equals(new Version(0, 0), new Version(1, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
17
ICD.Common.Utils.Tests/DateTimeUtilsTest.cs
Normal file
17
ICD.Common.Utils.Tests/DateTimeUtilsTest.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class DateTimeUtilsTest
|
||||
{
|
||||
[TestCase(1, 1)]
|
||||
[TestCase(0, 12)]
|
||||
[TestCase(12, 12)]
|
||||
[TestCase(23, 11)]
|
||||
public void To12HourTest(int hour, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected, DateTimeUtils.To12Hour(hour));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@ using System.Linq;
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EnumUtilsTest
|
||||
{
|
||||
public sealed class EnumUtilsTest
|
||||
{
|
||||
public enum eTestEnum
|
||||
{
|
||||
None = 0,
|
||||
A = 1,
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 3,
|
||||
}
|
||||
@@ -22,9 +22,10 @@ namespace ICD.Common.Utils.Tests
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 4,
|
||||
D = 32
|
||||
D = 32,
|
||||
BandC = B | C
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void GetValuesTest()
|
||||
{
|
||||
@@ -36,7 +37,7 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(eTestEnum.B, values[2]);
|
||||
Assert.AreEqual(eTestEnum.C, values[3]);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void IsEnumTypeGenericTest()
|
||||
{
|
||||
@@ -57,17 +58,29 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsFalse(EnumUtils.IsEnum(""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDefinedTest()
|
||||
{
|
||||
Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20));
|
||||
Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2));
|
||||
[Test]
|
||||
public void IsDefinedTest()
|
||||
{
|
||||
Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20));
|
||||
Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2));
|
||||
|
||||
Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8));
|
||||
Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3));
|
||||
}
|
||||
Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8));
|
||||
Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3));
|
||||
}
|
||||
|
||||
#region Values
|
||||
#region Values
|
||||
|
||||
[Test]
|
||||
public void GetNamesTest()
|
||||
{
|
||||
string[] names = EnumUtils.GetNames<eTestEnum>().ToArray();
|
||||
|
||||
Assert.AreEqual(4, names.Length);
|
||||
Assert.IsTrue(names.Contains("None"));
|
||||
Assert.IsTrue(names.Contains("A"));
|
||||
Assert.IsTrue(names.Contains("B"));
|
||||
Assert.IsTrue(names.Contains("C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValuesGenericTest()
|
||||
@@ -107,7 +120,9 @@ namespace ICD.Common.Utils.Tests
|
||||
[Test]
|
||||
public void GetFlagsIntersectionGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestFlagsEnum.B, EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.B | eTestFlagsEnum.C));
|
||||
Assert.AreEqual(eTestFlagsEnum.B,
|
||||
EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B,
|
||||
eTestFlagsEnum.B | eTestFlagsEnum.C));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -138,11 +153,11 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(aValues.Contains(eTestFlagsEnum.D));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllFlagCombinationsExceptNoneGenericTest()
|
||||
{
|
||||
eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D;
|
||||
eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray();
|
||||
[Test]
|
||||
public void GetAllFlagCombinationsExceptNoneGenericTest()
|
||||
{
|
||||
eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D;
|
||||
eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray();
|
||||
|
||||
Assert.AreEqual(15, aValues.Length);
|
||||
Assert.IsFalse(aValues.Contains(eTestFlagsEnum.None));
|
||||
@@ -167,7 +182,18 @@ namespace ICD.Common.Utils.Tests
|
||||
public void GetFlagsAllValueGenericTest()
|
||||
{
|
||||
eTestFlagsEnum value = EnumUtils.GetFlagsAllValue<eTestFlagsEnum>();
|
||||
Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, value);
|
||||
Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D,
|
||||
value);
|
||||
}
|
||||
|
||||
[TestCase(eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.BandC, eTestFlagsEnum.A | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, eTestFlagsEnum.None)]
|
||||
[TestCase(eTestFlagsEnum.None, eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
|
||||
[TestCase(eTestFlagsEnum.D, eTestFlagsEnum.A | eTestFlagsEnum.BandC)]
|
||||
public void GetInverseFlagsTest(eTestFlagsEnum expected, eTestFlagsEnum value)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.GetInverseFlags(value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -181,7 +207,8 @@ namespace ICD.Common.Utils.Tests
|
||||
public void HasFlagsGenericTest()
|
||||
{
|
||||
Assert.IsTrue(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.B));
|
||||
Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.C));
|
||||
Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B,
|
||||
eTestFlagsEnum.A | eTestFlagsEnum.C));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -200,28 +227,28 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(EnumUtils.HasMultipleFlags(eTestFlagsEnum.A | eTestFlagsEnum.B));
|
||||
}
|
||||
|
||||
[TestCase(false, eTestFlagsEnum.None)]
|
||||
[TestCase(true, eTestFlagsEnum.B)]
|
||||
[TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)]
|
||||
public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value));
|
||||
}
|
||||
[TestCase(false, eTestFlagsEnum.None)]
|
||||
[TestCase(true, eTestFlagsEnum.B)]
|
||||
[TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)]
|
||||
public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value));
|
||||
}
|
||||
|
||||
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)]
|
||||
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)]
|
||||
[TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)]
|
||||
[TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)]
|
||||
public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other));
|
||||
}
|
||||
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)]
|
||||
[TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)]
|
||||
[TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)]
|
||||
[TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)]
|
||||
public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other)
|
||||
{
|
||||
Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other));
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
#region Conversion
|
||||
|
||||
[Test]
|
||||
[Test]
|
||||
public void ParseGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.A, EnumUtils.Parse<eTestEnum>("A", false));
|
||||
@@ -243,35 +270,47 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(default(eTestEnum), output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseStrictGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict<eTestEnum>("1", false));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestEnum>("4", false));
|
||||
[Test]
|
||||
public void ParseStrictGenericTest()
|
||||
{
|
||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict<eTestEnum>("1", false));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestEnum>("4", false));
|
||||
|
||||
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict<eTestFlagsEnum>("3", false));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestFlagsEnum>("8", false));
|
||||
}
|
||||
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict<eTestFlagsEnum>("3", false));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => EnumUtils.ParseStrict<eTestFlagsEnum>("8", false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryParseStrictGenericTest()
|
||||
{
|
||||
eTestEnum outputA;
|
||||
[Test]
|
||||
public void TryParseStrictGenericTest()
|
||||
{
|
||||
eTestEnum outputA;
|
||||
|
||||
Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA));
|
||||
Assert.AreEqual(eTestEnum.A, outputA);
|
||||
Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA));
|
||||
Assert.AreEqual(eTestEnum.A, outputA);
|
||||
|
||||
Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA));
|
||||
Assert.AreEqual(eTestEnum.None, outputA);
|
||||
Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA));
|
||||
Assert.AreEqual(eTestEnum.None, outputA);
|
||||
|
||||
eTestFlagsEnum outputB;
|
||||
eTestFlagsEnum outputB;
|
||||
|
||||
Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB));
|
||||
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB);
|
||||
Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB));
|
||||
Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB);
|
||||
|
||||
Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB));
|
||||
Assert.AreEqual(eTestFlagsEnum.None, outputB);
|
||||
}
|
||||
Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB));
|
||||
Assert.AreEqual(eTestFlagsEnum.None, outputB);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Formatting
|
||||
|
||||
[TestCase(eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | (eTestFlagsEnum)8,
|
||||
"A, B, C, 8")]
|
||||
public void ToStringUndefinedTest(eTestFlagsEnum value, string expected)
|
||||
{
|
||||
string toString = EnumUtils.ToStringUndefined(value);
|
||||
Assert.AreEqual(expected, toString);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class DateTimeExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public static void ToShortTimeStringTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
public sealed class DateTimeExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void ToShortTimeStringTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void ToLongTimeStringWithMillisecondsTest()
|
||||
public void ToLongTimeStringWithMillisecondsTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
@@ -32,5 +32,17 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
Assert.AreEqual(expected, value.HasFlags(flags));
|
||||
}
|
||||
|
||||
[TestCase(eTestEnum.A, eTestEnum.B)]
|
||||
[TestCase(eTestEnum.B, eTestEnum.C)]
|
||||
[TestCase(eTestEnum.C, eTestEnum.A)]
|
||||
[TestCase(eTestEnum.A | eTestEnum.B, null)]
|
||||
public void CycleNextTest(eTestEnum value, eTestEnum? expected)
|
||||
{
|
||||
if (EnumUtils.HasMultipleFlags(value))
|
||||
Assert.Catch(typeof(InvalidOperationException), () => value.CycleNext());
|
||||
else
|
||||
Assert.AreEqual(expected, value.CycleNext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +284,15 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
Assert.AreEqual(5, items.Length);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(true, 1, 2, 3, 4)]
|
||||
[TestCase(true, 1, 2, 2, 3, 4)]
|
||||
[TestCase(false, 4, 3, 2, 1)]
|
||||
public void AreOrderedTest(bool expected, params int[] items)
|
||||
{
|
||||
Assert.AreEqual(expected, items.AreOrdered());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OrderTest()
|
||||
{
|
||||
@@ -528,7 +537,7 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
[Test]
|
||||
public void ConsolidateComparerTest()
|
||||
{
|
||||
string[] sequence = new[] {"A", "B", "B", "C"}.Consolidate(Comparer<string>.Default).ToArray();
|
||||
string[] sequence = new[] {"A", "B", "B", "C"}.Consolidate(EqualityComparer<string>.Default).ToArray();
|
||||
|
||||
Assert.AreEqual(3, sequence.Length, StringUtils.ArrayFormat(sequence));
|
||||
Assert.AreEqual("A", sequence[0]);
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using ICD.Common.Utils.IO;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
@@ -17,6 +23,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 +114,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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -9,14 +8,14 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
public sealed class ListExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void AddSortedTest()
|
||||
public void InsertSortedTest()
|
||||
{
|
||||
List<int> testList = new List<int>();
|
||||
|
||||
Assert.AreEqual(0, testList.AddSorted(2));
|
||||
Assert.AreEqual(1, testList.AddSorted(3));
|
||||
Assert.AreEqual(0, testList.AddSorted(1));
|
||||
Assert.AreEqual(1, testList.AddSorted(2));
|
||||
Assert.AreEqual(0, testList.InsertSorted(2));
|
||||
Assert.AreEqual(1, testList.InsertSorted(3));
|
||||
Assert.AreEqual(0, testList.InsertSorted(1));
|
||||
Assert.AreEqual(1, testList.InsertSorted(2));
|
||||
|
||||
Assert.AreEqual(4, testList.Count);
|
||||
Assert.AreEqual(1, testList[0]);
|
||||
@@ -26,15 +25,15 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSortedComparerTest()
|
||||
public void InsertSortedComparerTest()
|
||||
{
|
||||
List<int> testList = new List<int>();
|
||||
IComparer<int> comparer = new InverseComparer();
|
||||
|
||||
Assert.AreEqual(0, testList.AddSorted(2, comparer));
|
||||
Assert.AreEqual(0, testList.AddSorted(3, comparer));
|
||||
Assert.AreEqual(2, testList.AddSorted(1, comparer));
|
||||
Assert.AreEqual(1, testList.AddSorted(2, comparer));
|
||||
Assert.AreEqual(0, testList.InsertSorted(2, comparer));
|
||||
Assert.AreEqual(0, testList.InsertSorted(3, comparer));
|
||||
Assert.AreEqual(2, testList.InsertSorted(1, comparer));
|
||||
Assert.AreEqual(1, testList.InsertSorted(2, comparer));
|
||||
|
||||
Assert.AreEqual(4, testList.Count);
|
||||
Assert.AreEqual(3, testList[0]);
|
||||
@@ -43,82 +42,6 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
Assert.AreEqual(1, testList[3]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PadRightTest()
|
||||
{
|
||||
List<int> testList = new List<int>();
|
||||
testList.PadRight(10);
|
||||
|
||||
Assert.AreEqual(10, testList.Count);
|
||||
Assert.AreEqual(0, testList.Sum());
|
||||
|
||||
testList = new List<int>
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
};
|
||||
|
||||
testList.PadRight(10);
|
||||
|
||||
Assert.AreEqual(10, testList.Count);
|
||||
Assert.AreEqual(15, testList.Sum());
|
||||
|
||||
testList = new List<int>
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
};
|
||||
|
||||
testList.PadRight(1);
|
||||
|
||||
Assert.AreEqual(5, testList.Count);
|
||||
Assert.AreEqual(15, testList.Sum());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PadRightDefaultTest()
|
||||
{
|
||||
List<int> testList = new List<int>();
|
||||
testList.PadRight(10, 1);
|
||||
|
||||
Assert.AreEqual(10, testList.Count);
|
||||
Assert.AreEqual(10, testList.Sum());
|
||||
|
||||
testList = new List<int>
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
};
|
||||
|
||||
testList.PadRight(10, 1);
|
||||
|
||||
Assert.AreEqual(10, testList.Count);
|
||||
Assert.AreEqual(20, testList.Sum());
|
||||
|
||||
testList = new List<int>
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
};
|
||||
|
||||
testList.PadRight(1, 1);
|
||||
|
||||
Assert.AreEqual(5, testList.Count);
|
||||
Assert.AreEqual(15, testList.Sum());
|
||||
}
|
||||
|
||||
private sealed class InverseComparer : IComparer<int>
|
||||
{
|
||||
public int Compare(int x, int y)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
@@ -10,5 +12,221 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#region Get/Set Property Tests
|
||||
|
||||
|
||||
private sealed class PropertyTestClass
|
||||
{
|
||||
private readonly InternalPropertyTestClass m_InternalClass;
|
||||
public string PropertyString { get; set; }
|
||||
public int PropertyInt { get; set; }
|
||||
public DateTime PropertyDateTime { get; set; }
|
||||
public InternalPropertyTestClass InternalClass { get { return m_InternalClass; } }
|
||||
|
||||
public PropertyTestClass()
|
||||
{
|
||||
m_InternalClass = new InternalPropertyTestClass();
|
||||
}
|
||||
|
||||
internal sealed class InternalPropertyTestClass
|
||||
{
|
||||
private readonly DeepPropertyTestClass m_DeepClass;
|
||||
|
||||
public string InternalPropertyString { get; set; }
|
||||
public int InternalPropertyInt { get; set; }
|
||||
public DateTime InternalPropertyDateTime { get; set; }
|
||||
|
||||
public DeepPropertyTestClass DeepClass { get { return m_DeepClass; } }
|
||||
|
||||
public InternalPropertyTestClass()
|
||||
{
|
||||
m_DeepClass = new DeepPropertyTestClass();
|
||||
}
|
||||
|
||||
internal sealed class DeepPropertyTestClass
|
||||
{
|
||||
public string DeepPropertyString { get; set; }
|
||||
public int DeepPropertyInt { get; set; }
|
||||
public DateTime DeepPropertyDateTime { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void GetPropertyTest()
|
||||
{
|
||||
var testClass = new PropertyTestClass();
|
||||
|
||||
int testInt = 15;
|
||||
string testString = "TestString";
|
||||
var testDateTime = DateTime.Parse("1987-08-27");
|
||||
|
||||
testClass.PropertyInt = testInt;
|
||||
testClass.PropertyString = testString;
|
||||
testClass.PropertyDateTime = testDateTime;
|
||||
|
||||
int internalTestInt = 34;
|
||||
string internalTestString = "InternalTestString";
|
||||
var internalTestDateTime = DateTime.Parse("2011-12-05");
|
||||
|
||||
testClass.InternalClass.InternalPropertyInt = internalTestInt;
|
||||
testClass.InternalClass.InternalPropertyString = internalTestString;
|
||||
testClass.InternalClass.InternalPropertyDateTime = internalTestDateTime;
|
||||
|
||||
int deepTestInt = int.MaxValue;
|
||||
string deepTestString = "HelloThere";
|
||||
var deepTestDateTime = DateTime.Parse("1776-07-04");
|
||||
|
||||
testClass.InternalClass.DeepClass.DeepPropertyInt = deepTestInt;
|
||||
testClass.InternalClass.DeepClass.DeepPropertyString = deepTestString;
|
||||
testClass.InternalClass.DeepClass.DeepPropertyDateTime = deepTestDateTime;
|
||||
|
||||
object propertyInt;
|
||||
object propertyString;
|
||||
object propertyDateTime;
|
||||
object fakeObject;
|
||||
|
||||
// Test at first level
|
||||
Assert.True(testClass.GetProperty(out propertyInt, "PropertyInt"), "Couldn't get PropertyInt");
|
||||
Assert.AreEqual(testInt, propertyInt, "PropertyInt wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyString, "PropertyString"), "Couldn't Get PropertyString");
|
||||
Assert.AreEqual(testString, propertyString, "PropertyString wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyDateTime, "PropertyDateTime"), "Couldn't get PropertyDateTime");
|
||||
Assert.AreEqual(testDateTime, propertyDateTime, "PropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.GetProperty(out fakeObject, "FakePropertyName"));
|
||||
|
||||
|
||||
// Test at second level
|
||||
Assert.True(testClass.GetProperty(out propertyInt, "InternalClass", "InternalPropertyInt"), "Couldn't get InternalPropertyInt");
|
||||
Assert.AreEqual(internalTestInt, propertyInt, "InternalPropertyInt wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyString, "InternalClass", "InternalPropertyString"), "Couldn't Get InternalPropertyString");
|
||||
Assert.AreEqual(internalTestString, propertyString, "InternalPropertyString wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyDateTime, "InternalClass", "InternalPropertyDateTime"), "Couldn't get InternalPropertyDateTime");
|
||||
Assert.AreEqual(internalTestDateTime, propertyDateTime, "InternalPropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.GetProperty(out fakeObject, "InternalClass", "FakePropertyName"));
|
||||
|
||||
// Test at third level
|
||||
Assert.True(testClass.GetProperty(out propertyInt, "InternalClass", "DeepClass", "DeepPropertyInt"), "Couldn't get DeepPropertyInt");
|
||||
Assert.AreEqual(deepTestInt, propertyInt, "DeepPropertyInt wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyString, "InternalClass", "DeepClass", "DeepPropertyString"), "Couldn't Get DeepPropertyString");
|
||||
Assert.AreEqual(deepTestString, propertyString, "DeepPropertyString wasn't expected value");
|
||||
Assert.True(testClass.GetProperty(out propertyDateTime, "InternalClass", "DeepClass", "DeepPropertyDateTime"), "Couldn't get DeepPropertyDateTime");
|
||||
Assert.AreEqual(deepTestDateTime, propertyDateTime, "DeepPropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.GetProperty(out fakeObject, "InternalClass", "DeepClass", "FakePropertyName"));
|
||||
|
||||
|
||||
// Test Default/Null Cases
|
||||
var emptyTestClass = new PropertyTestClass();
|
||||
Assert.True(emptyTestClass.GetProperty(out propertyInt, "PropertyInt"), "Couldn't get empty PropertyInt");
|
||||
Assert.AreEqual(default(int), propertyInt, "Empty PropertyInt wasn't expected value");
|
||||
Assert.True(emptyTestClass.GetProperty(out propertyString, "PropertyString"), "Couldn't Get empty PropertyString");
|
||||
Assert.AreEqual(default(string), propertyString, "Empty PropertyString wasn't expected value");
|
||||
Assert.True(emptyTestClass.GetProperty(out propertyDateTime, "PropertyDateTime"), "Couldn't get empty PropertyDateTime");
|
||||
Assert.AreEqual(default(DateTime), propertyDateTime, "Empty PropertyDateTime wasn't expected value");
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetPropertyTest()
|
||||
{
|
||||
var testClass = new PropertyTestClass();
|
||||
|
||||
int testInt = 15;
|
||||
string testString = "TestString";
|
||||
var testDateTime = DateTime.Parse("1987-08-27");
|
||||
|
||||
int internalTestInt = 34;
|
||||
string internalTestString = "InternalTestString";
|
||||
var internalTestDateTime = DateTime.Parse("2011-12-05");
|
||||
|
||||
int deepTestInt = int.MaxValue;
|
||||
string deepTestString = "HelloThere";
|
||||
var deepTestDateTime = DateTime.Parse("1776-07-04");
|
||||
|
||||
// Test at first level
|
||||
Assert.True(testClass.SetProperty(testInt, "PropertyInt"), "Couldn't get PropertyInt");
|
||||
Assert.AreEqual(testInt, testClass.PropertyInt, "PropertyInt wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(testString, "PropertyString"), "Couldn't Get PropertyString");
|
||||
Assert.AreEqual(testString, testClass.PropertyString, "PropertyString wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(testDateTime, "PropertyDateTime"), "Couldn't get PropertyDateTime");
|
||||
Assert.AreEqual(testDateTime, testClass.PropertyDateTime, "PropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.SetProperty("SomeObject", "FakePropertyName"));
|
||||
|
||||
|
||||
// Test at second level
|
||||
Assert.True(testClass.SetProperty(internalTestInt, "InternalClass", "InternalPropertyInt"), "Couldn't get InternalPropertyInt");
|
||||
Assert.AreEqual(internalTestInt, testClass.InternalClass.InternalPropertyInt, "InternalPropertyInt wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(internalTestString, "InternalClass", "InternalPropertyString"), "Couldn't Get InternalPropertyString");
|
||||
Assert.AreEqual(internalTestString, testClass.InternalClass.InternalPropertyString, "InternalPropertyString wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(internalTestDateTime, "InternalClass", "InternalPropertyDateTime"), "Couldn't get InternalPropertyDateTime");
|
||||
Assert.AreEqual(internalTestDateTime, testClass.InternalClass.InternalPropertyDateTime, "InternalPropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.SetProperty("SomeAdditionalObject", "InternalClass", "FakePropertyName"));
|
||||
|
||||
// Test at third level
|
||||
Assert.True(testClass.SetProperty(deepTestInt, "InternalClass", "DeepClass", "DeepPropertyInt"), "Couldn't get DeepPropertyInt");
|
||||
Assert.AreEqual(deepTestInt, testClass.InternalClass.DeepClass.DeepPropertyInt, "DeepPropertyInt wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(deepTestString, "InternalClass", "DeepClass", "DeepPropertyString"), "Couldn't Get DeepPropertyString");
|
||||
Assert.AreEqual(deepTestString, testClass.InternalClass.DeepClass.DeepPropertyString, "DeepPropertyString wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(deepTestDateTime, "InternalClass", "DeepClass", "DeepPropertyDateTime"), "Couldn't get DeepPropertyDateTime");
|
||||
Assert.AreEqual(deepTestDateTime, testClass.InternalClass.DeepClass.DeepPropertyDateTime, "DeepPropertyDateTime wasn't expected value");
|
||||
// Test properties that don't exist
|
||||
Assert.False(testClass.SetProperty("ThisIsAnObjectToo", "InternalClass", "DeepClass", "FakePropertyName"));
|
||||
|
||||
|
||||
// Test Set Default/Null
|
||||
Assert.True(testClass.SetProperty(default(int), "PropertyInt"), "Couldn't get empty PropertyInt");
|
||||
Assert.AreEqual(default(int), testClass.PropertyInt, "Empty PropertyInt wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(default(string), "PropertyString"), "Couldn't Get empty PropertyString");
|
||||
Assert.AreEqual(default(string), testClass.PropertyString, "Empty PropertyString wasn't expected value");
|
||||
Assert.True(testClass.SetProperty(default(DateTime), "PropertyDateTime"), "Couldn't get empty PropertyDateTime");
|
||||
Assert.AreEqual(default(DateTime), testClass.PropertyDateTime, "Empty PropertyDateTime wasn't expected value");
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetPropertyInfoTest()
|
||||
{
|
||||
var testClass = new PropertyTestClass();
|
||||
|
||||
object instance;
|
||||
|
||||
// Test GetPropertyInfo at various levels
|
||||
Assert.AreEqual(testClass.GetType().GetProperty("PropertyString"),
|
||||
testClass.GetPropertyInfo(out instance, "PropertyString"),
|
||||
"First level property not expected value");
|
||||
Assert.AreEqual(testClass, instance, "Unexpected property parent");
|
||||
Assert.AreEqual(testClass.InternalClass.GetType().GetProperty("InternalPropertyString"),
|
||||
testClass.GetPropertyInfo(out instance, "InternalClass", "InternalPropertyString"),
|
||||
"Second level property not expected value");
|
||||
Assert.AreEqual(testClass.InternalClass, instance, "Unexpected property parent");
|
||||
Assert.AreEqual(testClass.InternalClass.DeepClass.GetType().GetProperty("DeepPropertyString"),
|
||||
testClass.GetPropertyInfo(out instance, "InternalClass", "DeepClass", "DeepPropertyString"),
|
||||
"Third level property not expected value");
|
||||
Assert.AreEqual(testClass.InternalClass.DeepClass, instance, "Unexpected property parent");
|
||||
|
||||
// Property that doesn't exits should return null
|
||||
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "DeepClass", "NonExistent"));
|
||||
Assert.IsNull(instance);
|
||||
Assert.IsNull(testClass.GetPropertyInfo(out instance, "FakeFirstLevel", "FakeSecondLevel" , "FakeThridLevel"));
|
||||
Assert.IsNull(instance);
|
||||
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "FakeSecondLevel", "ThirdLevelCanNotBeReal"));
|
||||
Assert.IsNull(instance);
|
||||
Assert.IsNull(testClass.GetPropertyInfo(out instance, "InternalClass", "FakeSecondLevel"));
|
||||
Assert.IsNull(instance);
|
||||
Assert.IsNull(testClass.GetPropertyInfo(out instance, "FakeFirstLevel"));
|
||||
Assert.IsNull(instance);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
43
ICD.Common.Utils.Tests/Extensions/TimeSpanExtensionsTest.cs
Normal file
43
ICD.Common.Utils.Tests/Extensions/TimeSpanExtensionsTest.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class TimeSpanExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void ToReadableStringTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[TestCase(0, 0, 0)]
|
||||
[TestCase(12, 1, 13)]
|
||||
[TestCase(23, 1, 0)]
|
||||
[TestCase(6, -12, 18)]
|
||||
public void AddHoursAndWrapTest(int hours, int addHours, int expectedHours)
|
||||
{
|
||||
Assert.AreEqual(expectedHours, new TimeSpan(hours, 0, 0).AddHoursAndWrap(addHours).Hours);
|
||||
}
|
||||
|
||||
[TestCase(0, 0, 0)]
|
||||
[TestCase(12, 1, 13)]
|
||||
[TestCase(23, 1, 12)]
|
||||
[TestCase(6, -12, 6)]
|
||||
public void AddHoursAndWrap12Hour(int hours, int addHours, int expectedHours)
|
||||
{
|
||||
Assert.AreEqual(expectedHours, new TimeSpan(hours, 0, 0).AddHoursAndWrap12Hour(addHours).Hours);
|
||||
}
|
||||
|
||||
[TestCase(0, 0, 0)]
|
||||
[TestCase(30, 1, 31)]
|
||||
[TestCase(59, 1, 0)]
|
||||
[TestCase(30, -60, 30)]
|
||||
public void AddMinutesAndWrap(int minutes, int addMinutes, int expectedMinutes)
|
||||
{
|
||||
Assert.AreEqual(expectedMinutes, new TimeSpan(0, minutes, 0).AddMinutesAndWrap(addMinutes).Minutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,15 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
[Test]
|
||||
public void GetMinimalInterfacesTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
Type[] interfaces = typeof(ICollection<int>).GetMinimalInterfaces().ToArray();
|
||||
|
||||
Assert.AreEqual(1, interfaces.Length);
|
||||
Assert.AreEqual(typeof(IEnumerable<int>), interfaces[0]);
|
||||
|
||||
interfaces = typeof(IEnumerable<int>).GetMinimalInterfaces().ToArray();
|
||||
|
||||
Assert.AreEqual(1, interfaces.Length);
|
||||
Assert.AreEqual(typeof(IEnumerable), interfaces[0]);
|
||||
}
|
||||
|
||||
[TestCase(typeof(int), "Int32")]
|
||||
@@ -155,6 +163,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?>")]
|
||||
|
||||
@@ -18,5 +18,13 @@ namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetPassword());
|
||||
}
|
||||
|
||||
[TestCase("http://www.test.com/a/b/c", "http://www.test.com/a/b/")]
|
||||
[TestCase("http://www.test.com/a/b/", "http://www.test.com/a/")]
|
||||
[TestCase("http://www.test.com/", "http://www.test.com/")]
|
||||
public void GetParentUri(string uriString, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetParentUri().ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
|
||||
<RootNamespace>ICD.Common.Utils.Tests</RootNamespace>
|
||||
<AssemblyName>ICD.Common.Utils.Tests</AssemblyName>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
@@ -11,18 +11,25 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE;STANDARD</DefineConstants>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
|
||||
<Compile Remove="bin\**" />
|
||||
<EmbeddedResource Remove="bin\**" />
|
||||
<None Remove="bin\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
188
ICD.Common.Utils.Tests/IcdAutoResetEventTest.cs
Normal file
188
ICD.Common.Utils.Tests/IcdAutoResetEventTest.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
class IcdAutoResetEventTest
|
||||
{
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void InitialStateTest(bool initialState)
|
||||
{
|
||||
using (var waitHandleEvent = new IcdAutoResetEvent(initialState))
|
||||
{
|
||||
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultInitialStateTest()
|
||||
{
|
||||
using (var waitHandleEvent = new IcdAutoResetEvent())
|
||||
{
|
||||
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadSetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
countSection.Execute(() => releasedCount++);
|
||||
;
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
|
||||
|
||||
///Auto reset should only release one thread at a time
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(i, countSection.Execute(() => releasedCount),
|
||||
"Incorrect number of threads released");
|
||||
}
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadResetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(true);
|
||||
|
||||
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
|
||||
|
||||
waitHandleEvent.Reset();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
if (waitHandleEvent.WaitOne(100))
|
||||
countSection.Execute(() => releasedCount++);
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(200)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimeoutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne(waitTime * 2);
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimedOutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
bool? returned = null;
|
||||
|
||||
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
returned = waitHandleEvent.WaitOne(waitTime);
|
||||
released = true;
|
||||
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
ThreadingUtils.Sleep(waitTime * 2);
|
||||
|
||||
Assert.True(released, "Thread didn't release after timeout");
|
||||
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
183
ICD.Common.Utils.Tests/IcdManualResetEventTest.cs
Normal file
183
ICD.Common.Utils.Tests/IcdManualResetEventTest.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
class IcdManualResetEventTest
|
||||
{
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void InitialStateTest(bool initialState)
|
||||
{
|
||||
using (var waitHandleEvent = new IcdManualResetEvent(initialState))
|
||||
{
|
||||
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultInitialStateTest()
|
||||
{
|
||||
using (var waitHandleEvent = new IcdManualResetEvent())
|
||||
{
|
||||
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadSetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
countSection.Execute(() => releasedCount++);
|
||||
;
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(500);
|
||||
|
||||
Assert.AreEqual(count, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(5)]
|
||||
[TestCase(15)]
|
||||
public void MultipleThreadResetTest(int count)
|
||||
{
|
||||
int releasedCount = 0;
|
||||
|
||||
SafeCriticalSection countSection = new SafeCriticalSection();
|
||||
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(true);
|
||||
|
||||
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
|
||||
|
||||
waitHandleEvent.Reset();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
if (waitHandleEvent.WaitOne(100))
|
||||
countSection.Execute(() => releasedCount++);
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(200)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne();
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimeoutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
waitHandleEvent.WaitOne(waitTime * 2);
|
||||
released = true;
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(waitTime);
|
||||
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(released, "Thread didn't release after set event");
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestCase(200)]
|
||||
[TestCase(500)]
|
||||
[TestCase(5000)]
|
||||
public void WaitOneTimedOutTest(int waitTime)
|
||||
{
|
||||
bool released = false;
|
||||
bool? returned = null;
|
||||
|
||||
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
returned = waitHandleEvent.WaitOne(waitTime);
|
||||
released = true;
|
||||
|
||||
});
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
|
||||
Assert.False(released, "Thread released when it shouldn't have");
|
||||
|
||||
ThreadingUtils.Sleep(waitTime * 2);
|
||||
|
||||
Assert.True(released, "Thread didn't release after timeout");
|
||||
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
|
||||
|
||||
waitHandleEvent.Set();
|
||||
|
||||
waitHandleEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,11 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(port, new IcdUriBuilder {Port = port}.Port);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void QueryTest(string query)
|
||||
[TestCase("?test", "test")]
|
||||
[TestCase("??test", "?test")]
|
||||
public void QueryTest(string expected, string query)
|
||||
{
|
||||
Assert.AreEqual(query, new IcdUriBuilder {Query = query}.Query);
|
||||
Assert.AreEqual(expected, new IcdUriBuilder {Query = query}.Query);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
@@ -63,14 +64,17 @@ namespace ICD.Common.Utils.Tests
|
||||
|
||||
#endregion
|
||||
|
||||
[TestCase("http://localhost/", null, null, null, null, (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost:80/", null, null, null, null, (ushort)80, null, null, null)]
|
||||
[TestCase("http://username@localhost/", null, null, null, null, (ushort)0, null, null, "username")]
|
||||
[TestCase("http://localhost/", null, null, "password", null, (ushort)0, null, null, null)]
|
||||
[TestCase("https://localhost/", null, null, null, null, (ushort)0, null, "https", null)]
|
||||
[TestCase("http://localhost/test", null, null, null, "test", (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost/test", null, null, null, "/test", (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost//test", null, null, null, "//test", (ushort)0, null, null, null)]
|
||||
[TestCase("/", null, null, null, null, (ushort)0, null, null, null)]
|
||||
[TestCase("http:///", null, null, null, null, (ushort)0, null, "http", null)]
|
||||
[TestCase("http://localhost:80/", null, "localhost", null, null, (ushort)80, null, "http", null)]
|
||||
[TestCase("http://username@localhost/", null, "localhost", null, null, (ushort)0, null, "http", "username")]
|
||||
[TestCase("http://localhost/", null, "localhost", "password", null, (ushort)0, null, "http", null)]
|
||||
[TestCase("https://localhost/", null, "localhost", null, null, (ushort)0, null, "https", null)]
|
||||
[TestCase("http://localhost/test", null, "localhost", null, "test", (ushort)0, null, "http", null)]
|
||||
[TestCase("http://localhost/test", null, "localhost", null, "/test", (ushort)0, null, "http", null)]
|
||||
[TestCase("http://localhost//test", null, "localhost", null, "//test", (ushort)0, null, "http", null)]
|
||||
[TestCase("http://localhost/test?a=b", null, "localhost", null, "/test", (ushort)0, "a=b", "http", null)]
|
||||
[TestCase("http://localhost/test??a=b", null, "localhost", null, "/test", (ushort)0, "?a=b", "http", null)]
|
||||
public void ToStringTest(string expected, string fragment, string address, string password, string path, ushort port,
|
||||
string query, string scheme, string userName)
|
||||
{
|
||||
|
||||
46
ICD.Common.Utils.Tests/Json/DateTimeIsoConverterTest.cs
Normal file
46
ICD.Common.Utils.Tests/Json/DateTimeIsoConverterTest.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Utils.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Json
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class DateTimeIsoConverterTest : AbstractGenericJsonConverterTest
|
||||
{
|
||||
[Test]
|
||||
public override void WriteJsonTest()
|
||||
{
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings
|
||||
{
|
||||
DateParseHandling = DateParseHandling.None
|
||||
};
|
||||
settings.Converters.Add(new DateTimeIsoConverter());
|
||||
|
||||
DateTime dateTime = new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc);
|
||||
string serial = JsonConvert.SerializeObject(dateTime, settings);
|
||||
|
||||
Assert.AreEqual("\"2020-01-02T03:04:05Z\"", serial);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ReadJsonTest()
|
||||
{
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings
|
||||
{
|
||||
DateParseHandling = DateParseHandling.None
|
||||
};
|
||||
settings.Converters.Add(new DateTimeIsoConverter());
|
||||
|
||||
string serial = "\"2020-01-02T03:04:05Z\"";
|
||||
DateTime dateTime = JsonConvert.DeserializeObject<DateTime>(serial, settings);
|
||||
|
||||
Assert.AreEqual(new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc), dateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Json
|
||||
@@ -14,7 +14,7 @@ namespace ICD.Common.Utils.Tests.Json
|
||||
[TestCase(1, typeof(int))]
|
||||
public void ItemTypeTest(object item, Type expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new JsonItemWrapper(item).ItemType);
|
||||
Assert.AreEqual(expected, new JsonItemWrapper(item).Type);
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
@@ -27,19 +27,21 @@ namespace ICD.Common.Utils.Tests.Json
|
||||
[Test]
|
||||
public void WriteTest()
|
||||
{
|
||||
JsonItemWrapper wrapper = new JsonItemWrapper(new List<int> {1, 2, 3});
|
||||
string json = JsonUtils.Serialize(wrapper.Write);
|
||||
const string expected = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}";
|
||||
|
||||
Assert.Inconclusive();
|
||||
JsonItemWrapper wrapper = new JsonItemWrapper(new List<int> {1, 2, 3});
|
||||
string json = JsonConvert.SerializeObject(wrapper);
|
||||
|
||||
Assert.AreEqual(expected, json);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadTest()
|
||||
{
|
||||
const string json = "{\"t\":\"System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]\",\"i\":\"[1,2,3]\"}";
|
||||
const string json = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}";
|
||||
|
||||
JObject jObject = JObject.Parse(json);
|
||||
List<int> wrappedObject = JsonItemWrapper.ReadToObject(jObject) as List<int>;
|
||||
JsonItemWrapper wrapper = JsonConvert.DeserializeObject<JsonItemWrapper>(json);
|
||||
List<int> wrappedObject = wrapper.Item as List<int>;
|
||||
|
||||
Assert.NotNull(wrappedObject);
|
||||
Assert.AreEqual(3, wrappedObject.Count);
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.IO;
|
||||
using ICD.Common.Utils.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Json
|
||||
@@ -11,25 +15,7 @@ namespace ICD.Common.Utils.Tests.Json
|
||||
public sealed class JsonUtilsTest
|
||||
{
|
||||
[Test]
|
||||
public void CacheTypeTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDateTimeTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryParseDateTimeTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PrintTest()
|
||||
public void FormatTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
@@ -141,32 +127,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";
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture, UsedImplicitly]
|
||||
[TestFixture]
|
||||
public sealed class MathUtilsTest
|
||||
{
|
||||
[Test, UsedImplicitly]
|
||||
[Test]
|
||||
public void ClampTest()
|
||||
{
|
||||
Assert.AreEqual(MathUtils.Clamp(-10, 0, 0), 0);
|
||||
@@ -24,9 +23,13 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(MathUtils.Clamp(20, 10, 10), 10);
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
[Test]
|
||||
public void MapRangeTest()
|
||||
{
|
||||
Assert.AreEqual(5, MathUtils.MapRange(-100, 100, 0, 10, 0));
|
||||
Assert.AreEqual(7, MathUtils.MapRange(-100, 100, 0, 10, 50));
|
||||
Assert.AreEqual(10, MathUtils.MapRange(-100, 100, 0, 10, 100));
|
||||
|
||||
Assert.AreEqual(0, MathUtils.MapRange(0, 100, 0, 10, 0));
|
||||
Assert.AreEqual(5, MathUtils.MapRange(0, 100, 0, 10, 50));
|
||||
Assert.AreEqual(10, MathUtils.MapRange(0, 100, 0, 10, 100));
|
||||
@@ -36,7 +39,7 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
[Test]
|
||||
public void GetRangesTest()
|
||||
{
|
||||
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
|
||||
@@ -57,11 +60,21 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(12, ranges[3][1]);
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
[Test]
|
||||
public void RoundToNearestTest()
|
||||
{
|
||||
IEnumerable<int> values = new [] { 0, 15, 30, 45 };
|
||||
Assert.AreEqual(15, MathUtils.RoundToNearest(21, values));
|
||||
}
|
||||
|
||||
[TestCase(10, 10, 0)]
|
||||
[TestCase(-10, 10, 0)]
|
||||
[TestCase(9, 3, 0)]
|
||||
[TestCase(3, 2, 1)]
|
||||
[TestCase(-3, 2, 1)]
|
||||
public void ModulusTest(int value, int mod, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected, MathUtils.Modulus(value, mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,24 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(clique.Contains(6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCliqueSingleNodeTest()
|
||||
{
|
||||
int[] clique = RecursionUtils.GetClique(1, n => s_CliqueGraph[n]).ToArray();
|
||||
|
||||
Assert.AreEqual(4, clique.Length);
|
||||
Assert.IsTrue(clique.Contains(1));
|
||||
Assert.IsTrue(clique.Contains(2));
|
||||
Assert.IsTrue(clique.Contains(3));
|
||||
Assert.IsTrue(clique.Contains(4));
|
||||
|
||||
clique = RecursionUtils.GetClique(5, n => s_CliqueGraph[n]).ToArray();
|
||||
|
||||
Assert.AreEqual(2, clique.Length);
|
||||
Assert.IsTrue(clique.Contains(5));
|
||||
Assert.IsTrue(clique.Contains(6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BreadthFirstSearchTest()
|
||||
{
|
||||
@@ -232,5 +250,40 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual(6, paths[62].ToArray()[1]);
|
||||
Assert.AreEqual(62, paths[62].ToArray()[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BreadthFirstSearchPathsTest()
|
||||
{
|
||||
Dictionary<int, IEnumerable<int>> paths =
|
||||
RecursionUtils.BreadthFirstSearchPaths(1, Graph)
|
||||
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
// Verify we visited all destinations
|
||||
Assert.AreEqual(4, paths.Count);
|
||||
Assert.IsTrue(paths.ContainsKey(1));
|
||||
Assert.IsTrue(paths.ContainsKey(2));
|
||||
Assert.IsTrue(paths.ContainsKey(3));
|
||||
Assert.IsTrue(paths.ContainsKey(4));
|
||||
|
||||
// Verify path for destination 1
|
||||
Assert.AreEqual(1, paths[1].ToArray().Length);
|
||||
Assert.AreEqual(1, paths[1].ToArray()[0]);
|
||||
|
||||
// Verify path for destination 2
|
||||
Assert.AreEqual(2, paths[2].ToArray().Length);
|
||||
Assert.AreEqual(1, paths[2].ToArray()[0]);
|
||||
Assert.AreEqual(2, paths[2].ToArray()[1]);
|
||||
|
||||
// Verify path for destination 3
|
||||
Assert.AreEqual(2, paths[3].ToArray().Length);
|
||||
Assert.AreEqual(1, paths[3].ToArray()[0]);
|
||||
Assert.AreEqual(3, paths[3].ToArray()[1]);
|
||||
|
||||
// Verify path for destination 4
|
||||
Assert.AreEqual(3, paths[4].ToArray().Length);
|
||||
Assert.AreEqual(1, paths[4].ToArray()[0]);
|
||||
Assert.AreEqual(2, paths[4].ToArray()[1]);
|
||||
Assert.AreEqual(4, paths[4].ToArray()[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,30 +37,5 @@ namespace ICD.Common.Utils.Tests
|
||||
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryEnterTest()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
SafeCriticalSection section = new SafeCriticalSection();
|
||||
section.Enter();
|
||||
|
||||
// ReSharper disable once NotAccessedVariable
|
||||
ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => result == 1, 1000));
|
||||
|
||||
section.Leave();
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
result = section.TryEnter() ? 2 : 0;
|
||||
section.Leave();
|
||||
});
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => result == 2, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace ICD.Common.Utils.Tests.Services.Logging
|
||||
public void TimestampTest()
|
||||
{
|
||||
LogItem item = new LogItem(eSeverity.Critical, null);
|
||||
DateTime time = IcdEnvironment.GetLocalTime();
|
||||
DateTime time = IcdEnvironment.GetUtcTime();
|
||||
|
||||
Assert.IsTrue((time - item.Timestamp).TotalSeconds <= 1);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ICD.Common.Properties;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
@@ -24,11 +25,33 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.AreEqual("\x08\x22\x00\x00\x00\x02", output);
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
public void NiceNameTest()
|
||||
[TestCase("FF", new byte[] {0xFF})]
|
||||
[TestCase("01FF", new byte[] { 0x01, 0xFF })]
|
||||
public void HexToBytes(string value, byte[] expected)
|
||||
{
|
||||
string output = StringUtils.NiceName("TodayILiveInTheUSAWithSimon");
|
||||
Assert.AreEqual("Today I Live In The USA With Simon", output);
|
||||
byte[] bytes = StringUtils.HexToBytes(value);
|
||||
Assert.IsTrue(bytes.SequenceEqual(expected));
|
||||
}
|
||||
|
||||
[TestCase("1", 1)]
|
||||
[TestCase("FF", 0xFF)]
|
||||
public void HexToByte(string value, byte expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.HexToByte(value));
|
||||
}
|
||||
|
||||
[TestCase("Test", "Test")]
|
||||
[TestCase("test", "Test")]
|
||||
[TestCase("TodayILiveInTheUSAWithSimon", "Today I Live In The USA With Simon")]
|
||||
[TestCase("CONST_VALUE", "CONST VALUE")]
|
||||
[TestCase("m_PrivateMember", "Private Member")]
|
||||
[TestCase("variableName", "Variable Name")]
|
||||
[TestCase("Comma, Delimited", "Comma, Delimited")]
|
||||
[TestCase("Comma,Delimited", "Comma, Delimited")]
|
||||
public void NiceNameTest(string input, string expected)
|
||||
{
|
||||
string output = StringUtils.NiceName(input);
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
[Test, UsedImplicitly]
|
||||
|
||||
205
ICD.Common.Utils.Tests/ThreadedWorkerQueueTest.cs
Normal file
205
ICD.Common.Utils.Tests/ThreadedWorkerQueueTest.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class ThreadedWorkerQueueTest
|
||||
{
|
||||
[Test]
|
||||
public void BetweenTimeTest()
|
||||
{
|
||||
List<int> callbacks = new List<int>();
|
||||
|
||||
using (ThreadedWorkerQueue<int> queue = new ThreadedWorkerQueue<int>((d) => callbacks.Add(d), true, 1000))
|
||||
{
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Third enqueue did not wait for process to complete");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(3, callbacks.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
[TestCase(1000)]
|
||||
[TestCase(0)]
|
||||
[TestCase(long.MaxValue)]
|
||||
public void BetweenMillisecondsTest(long milliseconds)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>((d) => { }, true, milliseconds))
|
||||
Assert.AreEqual(milliseconds, queue.BetweenTime);
|
||||
}
|
||||
|
||||
[TestCase(5)]
|
||||
[TestCase(0)]
|
||||
[TestCase(30)]
|
||||
public void CountTest(int count)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
queue.Enqueue(1);
|
||||
|
||||
Assert.AreEqual(count, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProcessBetweenTimeTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
Assert.AreEqual(0, processed.Count, "Queue processed item early");
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, processed.Count, "First item not processed");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, processed.Count, "Second item not processed");
|
||||
|
||||
queue.SetRunProcess(false);
|
||||
|
||||
ThreadingUtils.Sleep(2000);
|
||||
|
||||
Assert.AreEqual(2, processed.Count, "Item processed after stopping run process");
|
||||
Assert.AreEqual(1, queue.Count, "Incorrect number of items in queue");
|
||||
|
||||
// Queue lower priority item
|
||||
queue.Enqueue(5, 1);
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(3, processed.Count, "Third item not processed");
|
||||
Assert.AreEqual(5, processed[2], "Dequeued incorrect priority item");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(4, processed.Count, "Didn't process all items");
|
||||
Assert.True(processed.SequenceEqual(new[] { 1, 2, 5, 3 }), "Processed sequence incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlushQueueBetweenTimeTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
|
||||
{
|
||||
Assert.True(queue.WaitForFlush(1), "WaitForFlush on empty queue failed");
|
||||
|
||||
queue.Enqueue(11);
|
||||
queue.Enqueue(21);
|
||||
queue.Enqueue(31);
|
||||
queue.Enqueue(41);
|
||||
queue.Enqueue(51);
|
||||
queue.Enqueue(61);
|
||||
queue.Enqueue(71);
|
||||
queue.Enqueue(81);
|
||||
queue.Enqueue(91);
|
||||
queue.Enqueue(101);
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
|
||||
Assert.False(queue.WaitForFlush(1250), "WaitForFlush didn't time out");
|
||||
Assert.AreEqual(2, processed.Count, "Didn't process correct number of items in time frame");
|
||||
|
||||
Assert.True(queue.WaitForFlush(), "WaitForFlush failed");
|
||||
Assert.AreEqual(10, processed.Count, "Not all items processed");
|
||||
Assert.AreEqual(0, queue.Count, "Queue not empty");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void SetRunProcessTest(bool runProcess)
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, !runProcess))
|
||||
{
|
||||
Assert.AreEqual(!runProcess, queue.RunProcess, "Initial state wrong");
|
||||
queue.SetRunProcess(runProcess);
|
||||
Assert.AreEqual(runProcess, queue.RunProcess, "Didn't set to correct state 1st time");
|
||||
queue.SetRunProcess(!runProcess);
|
||||
Assert.AreEqual(!runProcess, queue.RunProcess, "Didn't set to correct state 2nd time");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
var processed = new List<int>();
|
||||
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false))
|
||||
{
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
Assert.AreEqual(3, queue.Count, "First queue count wrong");
|
||||
|
||||
queue.Enqueue(40);
|
||||
queue.Enqueue(50);
|
||||
queue.Enqueue(60);
|
||||
|
||||
Assert.AreEqual(6, queue.Count, "Second queue count wrong");
|
||||
|
||||
queue.SetRunProcess(true);
|
||||
Assert.True(queue.WaitForFlush(),"Queue didn't flush after processing");
|
||||
|
||||
|
||||
Assert.True(processed.SequenceEqual(new[] { 10, 20, 30, 40, 50, 60 }), "Processed sequence wrong");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => true, 100));
|
||||
|
||||
bool complete = false;
|
||||
DateTime time = IcdEnvironment.GetLocalTime();
|
||||
DateTime time = IcdEnvironment.GetUtcTime();
|
||||
|
||||
ThreadingUtils.SafeInvoke(() =>
|
||||
{
|
||||
@@ -22,15 +22,15 @@ namespace ICD.Common.Utils.Tests
|
||||
});
|
||||
|
||||
Assert.IsTrue(ThreadingUtils.Wait(() => complete, 200));
|
||||
Assert.AreEqual(100, (IcdEnvironment.GetLocalTime() - time).TotalMilliseconds, 20);
|
||||
Assert.AreEqual(100, (IcdEnvironment.GetUtcTime() - time).TotalMilliseconds, 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Sleep()
|
||||
{
|
||||
DateTime now = IcdEnvironment.GetLocalTime();
|
||||
DateTime now = IcdEnvironment.GetUtcTime();
|
||||
ThreadingUtils.Sleep(1000);
|
||||
DateTime now2 = IcdEnvironment.GetLocalTime();
|
||||
DateTime now2 = IcdEnvironment.GetUtcTime();
|
||||
|
||||
Assert.AreEqual(1000, (now2 - now).TotalMilliseconds, 100);
|
||||
}
|
||||
|
||||
17
ICD.Common.Utils.Tests/Timers/IcdStopwatchTest.cs
Normal file
17
ICD.Common.Utils.Tests/Timers/IcdStopwatchTest.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using ICD.Common.Utils.Timers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Timers
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdStopwatchTest
|
||||
{
|
||||
[Test]
|
||||
public void ConstructorTest()
|
||||
{
|
||||
var stopwatch = new IcdStopwatch();
|
||||
ThreadingUtils.Sleep(100);
|
||||
Assert.AreEqual(0, stopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,12 +49,14 @@ namespace ICD.Common.Utils.Tests.Timers
|
||||
[Test]
|
||||
public void TriggerTest()
|
||||
{
|
||||
bool called = false;
|
||||
SafeTimer timer = SafeTimer.Stopped(() => called = true);
|
||||
int called = 0;
|
||||
SafeTimer timer = SafeTimer.Stopped(() => called++);
|
||||
|
||||
timer.Trigger();
|
||||
|
||||
Assert.IsTrue(called);
|
||||
ThreadingUtils.Sleep(50);
|
||||
|
||||
Assert.AreEqual(1, called);
|
||||
|
||||
timer.Dispose();
|
||||
}
|
||||
|
||||
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal file
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Types;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Types
|
||||
{
|
||||
[TestFixture]
|
||||
public class GenericNotifyFlagsChangedTest
|
||||
{
|
||||
[Flags]
|
||||
private enum eTestFlagsEnum
|
||||
{
|
||||
None = 0,
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 4,
|
||||
D = 32,
|
||||
BandC = B | C
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSingleFlags()
|
||||
{
|
||||
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
|
||||
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
|
||||
|
||||
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
|
||||
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
|
||||
|
||||
// Initial State
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add No flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add Flag
|
||||
genericNotify.Data = eTestFlagsEnum.B;
|
||||
Assert.AreEqual(1, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, addedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, genericNotify.Data);
|
||||
|
||||
// Add Another Flag
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, genericNotify.Data);
|
||||
|
||||
// Remove a Flag
|
||||
genericNotify.Data = genericNotify.Data & ~ eTestFlagsEnum.B;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B, removedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, genericNotify.Data);
|
||||
|
||||
// Add Already Existing Flags
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
|
||||
// Clear Flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(2, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, removedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleFlags()
|
||||
{
|
||||
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
|
||||
|
||||
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
|
||||
|
||||
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
|
||||
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
|
||||
|
||||
// Initial State
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add No flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(0, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
|
||||
// Add Flag
|
||||
genericNotify.Data = eTestFlagsEnum.B | eTestFlagsEnum.D;
|
||||
Assert.AreEqual(1, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, addedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Add Another Flag
|
||||
genericNotify.Data |= eTestFlagsEnum.C;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(0, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Remove a Flag
|
||||
genericNotify.Data = eTestFlagsEnum.D;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, removedFlags[0].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.D, genericNotify.Data);
|
||||
|
||||
// Add Already Existing Flags
|
||||
genericNotify.Data |= eTestFlagsEnum.D;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(1, removedFlags.Count);
|
||||
|
||||
// Clear Flags
|
||||
genericNotify.Data = eTestFlagsEnum.None;
|
||||
Assert.AreEqual(2, addedFlags.Count);
|
||||
Assert.AreEqual(2, removedFlags.Count);
|
||||
Assert.AreEqual(eTestFlagsEnum.D, removedFlags[1].Data);
|
||||
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
155
ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs
Normal file
155
ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System.Xml;
|
||||
using ICD.Common.Utils.Xml;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class GenericXmlConverterTest
|
||||
{
|
||||
[XmlConverter(typeof(TestClassConverter))]
|
||||
private sealed class TestClass
|
||||
{
|
||||
public string A { get; set; }
|
||||
public int B { get; set; }
|
||||
}
|
||||
|
||||
private sealed class TestClassConverter : AbstractGenericXmlConverter<TestClass>
|
||||
{
|
||||
private const string ELEMENT_A = "A";
|
||||
private const string ATTRIBUTE_B = "b";
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle the current attribute.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="instance"></param>
|
||||
protected override void ReadAttribute(IcdXmlReader reader, TestClass instance)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case ATTRIBUTE_B:
|
||||
instance.B = int.Parse(reader.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
base.ReadAttribute(reader, instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle the current element.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="instance"></param>
|
||||
protected override void ReadElement(IcdXmlReader reader, TestClass instance)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case ELEMENT_A:
|
||||
instance.A = reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
base.ReadElement(reader, instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to write attributes to the root element.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="value"></param>
|
||||
protected override void WriteAttributes(IcdXmlTextWriter writer, TestClass value)
|
||||
{
|
||||
base.WriteAttributes(writer, value);
|
||||
|
||||
writer.WriteAttributeString(ATTRIBUTE_B, value.B.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to write elements to the writer.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="value"></param>
|
||||
protected override void WriteElements(IcdXmlTextWriter writer, TestClass value)
|
||||
{
|
||||
base.WriteElements(writer, value);
|
||||
|
||||
writer.WriteElementString(ELEMENT_A, value.A);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestElement()
|
||||
{
|
||||
const string xml = @"<Instances>
|
||||
<Instance b=""1"">
|
||||
<A>Test</A>
|
||||
</Instance>
|
||||
</Instances>";
|
||||
|
||||
using (IcdXmlReader reader = new IcdXmlReader(xml))
|
||||
{
|
||||
// Read into the Instances node
|
||||
reader.Read();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
|
||||
// Read into the Instance node
|
||||
reader.Read();
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instance", reader.Name);
|
||||
|
||||
// Deserialize
|
||||
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
|
||||
|
||||
Assert.IsNotNull(instance);
|
||||
Assert.AreEqual("Test", instance.A);
|
||||
Assert.AreEqual(1, instance.B);
|
||||
|
||||
// Deserialization should land on the following node
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyElement()
|
||||
{
|
||||
const string xml = @"<Instances>
|
||||
<Instance b=""1"" />
|
||||
</Instances>";
|
||||
|
||||
using (IcdXmlReader reader = new IcdXmlReader(xml))
|
||||
{
|
||||
// Read into the Instances node
|
||||
reader.Read();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
|
||||
// Read into the Instance node
|
||||
reader.Read();
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
|
||||
Assert.AreEqual("Instance", reader.Name);
|
||||
|
||||
// Deserialize
|
||||
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
|
||||
|
||||
Assert.IsNotNull(instance);
|
||||
Assert.IsNull(instance.A);
|
||||
Assert.AreEqual(1, instance.B);
|
||||
|
||||
// Deserialization should land on the following node
|
||||
reader.SkipInsignificantWhitespace();
|
||||
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
|
||||
Assert.AreEqual("Instances", reader.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
ICD.Common.Utils.Tests/Xml/IcdXmlConvertTest.cs
Normal file
35
ICD.Common.Utils.Tests/Xml/IcdXmlConvertTest.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Xml;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Xml
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdXmlConvertTest
|
||||
{
|
||||
[Test]
|
||||
public void DeserializeArrayGenericTest()
|
||||
{
|
||||
const string xml = @"<A>
|
||||
<B>1</B>
|
||||
<B>2</B>
|
||||
</A>";
|
||||
|
||||
using (IcdXmlReader reader = new IcdXmlReader(xml))
|
||||
{
|
||||
// Read to the first element
|
||||
reader.Read();
|
||||
|
||||
int[] values = IcdXmlConvert.DeserializeArray<int>(reader).ToArray();
|
||||
|
||||
Assert.AreEqual(new[] {1, 2}, values);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeArrayTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
200
ICD.Common.Utils/AnsiUtils.cs
Normal file
200
ICD.Common.Utils/AnsiUtils.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class AnsiUtils
|
||||
{
|
||||
public const string COLOR_RED = "\x1b[31;1m";
|
||||
public const string COLOR_GREEN = "\x1b[32;1m";
|
||||
public const string COLOR_YELLOW = "\x1b[33;1m";
|
||||
public const string COLOR_BLUE = "\x1b[34;1m";
|
||||
public const string COLOR_MAGENTA = "\x1b[35;1m";
|
||||
public const string COLOR_CYAN = "\x1b[36;1m";
|
||||
public const string COLOR_WHITE = "\x1b[37;1m";
|
||||
public const string COLOR_YELLOW_ON_RED_BACKGROUND = "\x1b[93;41m";
|
||||
public const string ANSI_RESET = "\x1b[0m";
|
||||
|
||||
public const string CODE_BLACK = "30";
|
||||
public const string CODE_RED = "31";
|
||||
public const string CODE_GREEN = "32";
|
||||
public const string CODE_YELLOW = "33";
|
||||
public const string CODE_BLUE = "34";
|
||||
public const string CODE_MAGENTA = "35";
|
||||
public const string CODE_CYAN = "36";
|
||||
public const string CODE_WHITE = "37";
|
||||
|
||||
public const string CODE_BRIGHT_BLACK = "30;1";
|
||||
public const string CODE_BRIGHT_RED = "31;1";
|
||||
public const string CODE_BRIGHT_GREEN = "32;1";
|
||||
public const string CODE_BRIGHT_YELLOW = "33;1";
|
||||
public const string CODE_BRIGHT_BLUE = "34;1";
|
||||
public const string CODE_BRIGHT_MAGENTA = "35;1";
|
||||
public const string CODE_BRIGHT_CYAN = "36;1";
|
||||
public const string CODE_BRIGHT_WHITE = "37;1";
|
||||
|
||||
/// <summary>
|
||||
/// Matches ANSI escape codes, e.g. \x1b[31m and \x1b[30;1m
|
||||
/// </summary>
|
||||
public const string ANSI_REGEX = "(?'match'\x01b\\[(?'code'[\\d;]+)m)";
|
||||
|
||||
/// <summary>
|
||||
/// Matches ANSI escape codes to HTML styles.
|
||||
/// Color values are taken from PuTTY.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string> s_PuttyColors =
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{CODE_BLACK, "#000000"},
|
||||
{CODE_RED, "#BB0000"},
|
||||
{CODE_GREEN, "#00BB00"},
|
||||
{CODE_YELLOW, "#BBBB00"},
|
||||
{CODE_BLUE, "#0000BB"},
|
||||
{CODE_MAGENTA, "#BB00BB"},
|
||||
{CODE_CYAN, "#00BBBB"},
|
||||
{CODE_WHITE, "#BBBBBB"},
|
||||
|
||||
{CODE_BRIGHT_BLACK, "#555555"},
|
||||
{CODE_BRIGHT_RED, "#FF5555"},
|
||||
{CODE_BRIGHT_GREEN, "#55FF55"},
|
||||
{CODE_BRIGHT_YELLOW, "#FFFF55"},
|
||||
{CODE_BRIGHT_BLUE, "#5555FF"},
|
||||
{CODE_BRIGHT_MAGENTA, "#FF55FF"},
|
||||
{CODE_BRIGHT_CYAN, "#55FFFF"},
|
||||
{CODE_BRIGHT_WHITE, "#FFFFFF"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color map matching PuTTY.
|
||||
/// </summary>
|
||||
public static IDictionary<string, string> PuttyColors { get { return s_PuttyColors; } }
|
||||
|
||||
#if NETSTANDARD
|
||||
/// <summary>
|
||||
/// Enables ANSI color in the console.
|
||||
/// </summary>
|
||||
public static void EnableAnsiColor()
|
||||
{
|
||||
Pastel.ConsoleExtensions.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables ANSI color in the console.
|
||||
/// </summary>
|
||||
public static void DisableAnsiColor()
|
||||
{
|
||||
Pastel.ConsoleExtensions.Disable();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Removes ANSI control sequences from the string.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static string StripAnsi(string data)
|
||||
{
|
||||
return Regex.Replace(data, ANSI_REGEX, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefixes the given data with an ANSI control sequence and suffixes with a reset sequence.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="ansiSequence"></param>
|
||||
/// <returns></returns>
|
||||
public static string Format(string data, string ansiSequence)
|
||||
{
|
||||
// % needs to be escaped or weird things happen
|
||||
data = string.IsNullOrEmpty(data) ? data : data.Replace("%", "%%");
|
||||
|
||||
return string.Format("{0}{1}{2}", ansiSequence, data, ANSI_RESET);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the given ANSI string into spans.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<AnsiSpan> ToSpans(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
yield break;
|
||||
|
||||
Regex regex = new Regex(ANSI_REGEX);
|
||||
Match match = regex.Match(data);
|
||||
|
||||
// No matches
|
||||
if (!match.Success)
|
||||
yield return new AnsiSpan {Text = data};
|
||||
|
||||
// Find the spans
|
||||
while (match.Success)
|
||||
{
|
||||
// Get the code
|
||||
string code = match.Groups["code"].Value;
|
||||
|
||||
// Get the text
|
||||
Match next = match.NextMatch();
|
||||
int startIndex = match.Index + match.Length;
|
||||
int endIndex = next.Success ? next.Index : data.Length;
|
||||
string text = data.Substring(startIndex, endIndex - startIndex);
|
||||
|
||||
// Build the span
|
||||
if (text.Length > 0)
|
||||
yield return new AnsiSpan { Code = code, Text = text };
|
||||
|
||||
// Loop
|
||||
match = next;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the bright suffix from the code if present, otherwise appends a bright suffix.
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
public static string InvertBright(string code)
|
||||
{
|
||||
return code.EndsWith(";1")
|
||||
? code.Substring(0, code.Length - 2)
|
||||
: code + ";1";
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AnsiSpan
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color value for the code.
|
||||
/// </summary>
|
||||
/// <param name="colors"></param>
|
||||
/// <param name="invertBright"></param>
|
||||
/// <returns></returns>
|
||||
public string GetColor(IDictionary<string, string> colors, bool invertBright)
|
||||
{
|
||||
if (colors == null)
|
||||
throw new ArgumentNullException("colors");
|
||||
|
||||
return GetColor<string>(colors, invertBright);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color value for the code.
|
||||
/// </summary>
|
||||
/// <param name="colors"></param>
|
||||
/// <param name="invertBright"></param>
|
||||
/// <returns></returns>
|
||||
public T GetColor<T>(IDictionary<string, T> colors, bool invertBright)
|
||||
{
|
||||
if (colors == null)
|
||||
throw new ArgumentNullException("colors");
|
||||
|
||||
string code = invertBright ? AnsiUtils.InvertBright(Code) : Code;
|
||||
return colors[code];
|
||||
}
|
||||
}
|
||||
}
|
||||
40
ICD.Common.Utils/AssemblyUtils.cs
Normal file
40
ICD.Common.Utils/AssemblyUtils.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronXmlLinq;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class AssemblyUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the process executable in the default application domain. In other application domains,
|
||||
/// this is the first executable that was executed by ExecuteAssembly(String).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static Assembly GetEntryAssembly()
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
string appDir = InitialParametersClass.ProgramDirectory.ToString();
|
||||
|
||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL)
|
||||
return null;
|
||||
|
||||
string proginfo = IcdFile.ReadToEnd(IcdPath.Combine(appDir, "ProgramInfo.config"), Encoding.UTF8);
|
||||
XDocument doc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n" + proginfo);
|
||||
XElement entry = doc.Descendants("EntryPoint").FirstOrDefault();
|
||||
return entry == null ? null : Assembly.Load(entry.Value);
|
||||
#else
|
||||
return Assembly.GetEntryAssembly();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Services;
|
||||
using ICD.Common.Utils.Services.Logging;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
@@ -20,139 +17,6 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
public static class AttributeUtils
|
||||
{
|
||||
// Avoid caching the same assembly multiple times.
|
||||
private static readonly IcdHashSet<Assembly> s_CachedAssemblies;
|
||||
private static readonly IcdHashSet<Type> s_CachedTypes;
|
||||
|
||||
private static readonly Dictionary<Attribute, Type> s_AttributeToTypeCache;
|
||||
private static readonly Dictionary<Type, IcdHashSet<Attribute>> s_TypeToAttributesCache;
|
||||
|
||||
private static ILoggerService Logger { get { return ServiceProvider.TryGetService<ILoggerService>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
static AttributeUtils()
|
||||
{
|
||||
s_CachedAssemblies = new IcdHashSet<Assembly>();
|
||||
s_CachedTypes = new IcdHashSet<Type>();
|
||||
|
||||
s_AttributeToTypeCache = new Dictionary<Attribute, Type>();
|
||||
s_TypeToAttributesCache = new Dictionary<Type, IcdHashSet<Attribute>>();
|
||||
}
|
||||
|
||||
#region Caching
|
||||
|
||||
/// <summary>
|
||||
/// Pre-emptively caches the given assembly for lookup.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public static bool CacheAssembly(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException("assembly");
|
||||
|
||||
if (s_CachedAssemblies.Contains(assembly))
|
||||
return true;
|
||||
|
||||
s_CachedAssemblies.Add(assembly);
|
||||
|
||||
#if SIMPLSHARP
|
||||
CType[] types;
|
||||
#else
|
||||
Type[] types;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
types = assembly.GetTypes();
|
||||
}
|
||||
#if STANDARD
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
foreach (Exception inner in e.LoaderExceptions)
|
||||
{
|
||||
if (inner is System.IO.FileNotFoundException)
|
||||
{
|
||||
Logger.AddEntry(eSeverity.Error,
|
||||
"{0} failed to cache assembly {1} - Could not find one or more dependencies by path",
|
||||
typeof(AttributeUtils).Name, assembly.GetName().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.AddEntry(eSeverity.Error, inner, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
|
||||
assembly.GetName().Name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
catch (TypeLoadException e)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
Logger.AddEntry(eSeverity.Error, e, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
|
||||
assembly.GetName().Name);
|
||||
#else
|
||||
Logger.AddEntry(eSeverity.Error, e, "{0} failed to cache assembly {1} - could not load type {2}",
|
||||
typeof(AttributeUtils).Name, assembly.GetName().Name, e.TypeName);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var type in types)
|
||||
CacheType(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-emptively caches the given type for lookup.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
#if SIMPLSHARP
|
||||
public static void CacheType(CType type)
|
||||
#else
|
||||
public static void CacheType(Type type)
|
||||
#endif
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (s_CachedTypes.Contains(type))
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
// GetMethods for Open Generic Types is not supported.
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
}
|
||||
// Not sure why this happens :/
|
||||
catch (InvalidProgramException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lookup
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class attributes of the given generic type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetClassAttributes<T>()
|
||||
{
|
||||
return s_AttributeToTypeCache.Select(kvp => kvp.Key)
|
||||
.OfType<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first attribute on the given class type matching the generic type.
|
||||
/// </summary>
|
||||
@@ -161,11 +25,24 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static T GetClassAttribute<T>(Type type)
|
||||
{
|
||||
return GetClassAttribute<T>(type, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first attribute on the given class type matching the generic type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="inherit"></param>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static T GetClassAttribute<T>(Type type, bool inherit)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetClassAttributes<T>(type).FirstOrDefault();
|
||||
return GetClassAttributes<T>(type, inherit).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,39 +56,28 @@ namespace ICD.Common.Utils
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetClassAttributes(type).OfType<T>();
|
||||
return GetClassAttributes<T>(type, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes on the given class.
|
||||
/// Gets the attributes on the given class type matching the generic type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="inherit"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Attribute> GetClassAttributes(Type type)
|
||||
public static IEnumerable<T> GetClassAttributes<T>(Type type, bool inherit)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
CacheType(type);
|
||||
|
||||
IcdHashSet<Attribute> attributes;
|
||||
|
||||
return s_TypeToAttributesCache.TryGetValue(type, out attributes)
|
||||
? attributes.ToArray(attributes.Count)
|
||||
: Enumerable.Empty<Attribute>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type with the given attribute.
|
||||
/// </summary>
|
||||
/// <param name="attribute"></param>
|
||||
/// <returns></returns>
|
||||
public static Type GetClass(Attribute attribute)
|
||||
{
|
||||
if (attribute == null)
|
||||
throw new ArgumentNullException("attribute");
|
||||
|
||||
return s_AttributeToTypeCache[attribute];
|
||||
// ReSharper disable InvokeAsExtensionMethod
|
||||
return ReflectionExtensions.GetCustomAttributes<T>(
|
||||
#if SIMPLSHARP
|
||||
(CType)
|
||||
#endif
|
||||
type, inherit);
|
||||
// ReSharper restore InvokeAsExtensionMethod
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -237,7 +103,5 @@ namespace ICD.Common.Utils
|
||||
.Where(p => ReflectionExtensions.GetCustomAttributes<T>(p, inherit).Any());
|
||||
// ReSharper restore InvokeAsExtensionMethod
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ICD.Common.Utils.Attributes
|
||||
/// <summary>
|
||||
/// AbstractIcdAttribute is the base class for all ICD attributes.
|
||||
/// </summary>
|
||||
public abstract class AbstractIcdAttribute : Attribute
|
||||
public abstract class AbstractIcdAttribute : Attribute, IIcdAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
6
ICD.Common.Utils/Attributes/IIcdAttribute.cs
Normal file
6
ICD.Common.Utils/Attributes/IIcdAttribute.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace ICD.Common.Utils.Attributes
|
||||
{
|
||||
public interface IIcdAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Attributes
|
||||
{
|
||||
@@ -9,90 +12,274 @@ namespace ICD.Common.Utils.Attributes
|
||||
[AttributeUsage(AttributeTargets.Property |
|
||||
AttributeTargets.Field |
|
||||
AttributeTargets.Parameter |
|
||||
AttributeTargets.ReturnValue,
|
||||
AllowMultiple = false,
|
||||
Inherited = true)]
|
||||
AttributeTargets.ReturnValue)]
|
||||
public sealed class RangeAttribute : AbstractIcdAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Remaps from the source numeric min/max to double min/max.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, Func<double, double>> s_Clamp =
|
||||
new Dictionary<Type, Func<double, double>>
|
||||
{
|
||||
// Duh
|
||||
{typeof(double), o => o},
|
||||
|
||||
// Signed
|
||||
{typeof(short), o => o < short.MinValue ? short.MinValue : o > short.MaxValue ? short.MaxValue : o},
|
||||
{typeof(int), o => o < int.MinValue ? int.MinValue : o > int.MaxValue ? int.MaxValue : o},
|
||||
{typeof(long), o => o < long.MinValue ? long.MinValue : o > long.MaxValue ? long.MaxValue : o},
|
||||
{typeof(float),o => o < float.MinValue ? float.MinValue : o > float.MaxValue ? float.MaxValue : o},
|
||||
{typeof(decimal), o => o < (double)decimal.MinValue ? (double)decimal.MinValue : o > (double)decimal.MaxValue ? (double)decimal.MaxValue : o},
|
||||
|
||||
// Unsigned
|
||||
{typeof(ushort), o => o < ushort.MinValue ? ushort.MinValue : o > ushort.MaxValue ? ushort.MaxValue : o},
|
||||
{typeof(uint), o => o < uint.MinValue ? uint.MinValue : o > uint.MaxValue ? uint.MaxValue : o},
|
||||
{typeof(ulong), o => o < ulong.MinValue ? ulong.MinValue : o > ulong.MaxValue ? ulong.MaxValue : o},
|
||||
{typeof(byte), o => o < byte.MinValue ? byte.MinValue : o > byte.MaxValue ? byte.MaxValue : o}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Remaps from the source numeric min/max to double min/max.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, Func<object, double>> s_RemapToDouble =
|
||||
new Dictionary<Type, Func<object, double>>
|
||||
{
|
||||
// Duh
|
||||
{ typeof(double), o => (double)o},
|
||||
|
||||
// Signed - Clamping prevents an overflow due to loss of precision
|
||||
{ typeof(short), o => MathUtils.Clamp(Convert.ToDouble(o) / short.MaxValue, -1, 1) * double.MaxValue},
|
||||
{ typeof(int), o => MathUtils.Clamp(Convert.ToDouble(o) / int.MaxValue, -1, 1) * double.MaxValue},
|
||||
{ typeof(long), o => MathUtils.Clamp(Convert.ToDouble(o) / long.MaxValue, -1, 1) * double.MaxValue},
|
||||
{ typeof(float), o => MathUtils.Clamp(Convert.ToDouble(o) / float.MaxValue, -1, 1) * double.MaxValue},
|
||||
{ typeof(decimal), o => MathUtils.Clamp(Convert.ToDouble(o) / (double)decimal.MaxValue, -1, 1) * double.MaxValue},
|
||||
|
||||
// Unsigned
|
||||
{ typeof(ushort), o => MathUtils.Clamp((Convert.ToDouble(o) / ushort.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
|
||||
{ typeof(uint), o => MathUtils.Clamp((Convert.ToDouble(o) / uint.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
|
||||
{ typeof(ulong), o => MathUtils.Clamp((Convert.ToDouble(o) / ulong.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
|
||||
{ typeof(byte), o => MathUtils.Clamp((Convert.ToDouble(o) / byte.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Remaps from the double min/max to target numeric min/max.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, Func<double, object>> s_RemapFromDouble =
|
||||
new Dictionary<Type, Func<double, object>>
|
||||
{
|
||||
// Duh
|
||||
{typeof(double), v => v},
|
||||
|
||||
// Signed
|
||||
{typeof(short), v => (short)(v / double.MaxValue * short.MaxValue)},
|
||||
{typeof(int), v => (int)(v / double.MaxValue * int.MaxValue)},
|
||||
{typeof(long), v => (long)(v / double.MaxValue * long.MaxValue)},
|
||||
{typeof(float), v => (float)(v / double.MaxValue * float.MaxValue)},
|
||||
{typeof(decimal), v => (decimal)(v / double.MaxValue) * decimal.MaxValue},
|
||||
|
||||
// Unsigned
|
||||
{typeof(ushort), v => (ushort)((v / double.MaxValue + 1) / 2 * ushort.MaxValue)},
|
||||
{typeof(uint), v => (uint)((v / double.MaxValue + 1) / 2 * uint.MaxValue)},
|
||||
{typeof(ulong), v => (ulong)((v / double.MaxValue + 1) / 2 * ulong.MaxValue)},
|
||||
{typeof(byte), v => (byte)((v / double.MaxValue + 1) / 2 * byte.MaxValue)}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the min value of a given numeric type as a double.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, double> s_MinAsDouble =
|
||||
new Dictionary<Type, double>
|
||||
{
|
||||
// Duh
|
||||
{typeof(double), double.MinValue},
|
||||
|
||||
// Signed
|
||||
{typeof(short), Convert.ToDouble(short.MinValue)},
|
||||
{typeof(int), Convert.ToDouble(int.MinValue)},
|
||||
{typeof(long), Convert.ToDouble(long.MinValue)},
|
||||
{typeof(float), Convert.ToDouble(float.MinValue)},
|
||||
{typeof(decimal), Convert.ToDouble(decimal.MinValue)},
|
||||
|
||||
// Unsigned
|
||||
{typeof(ushort), Convert.ToDouble(ushort.MinValue)},
|
||||
{typeof(uint), Convert.ToDouble(uint.MinValue)},
|
||||
{typeof(ulong), Convert.ToDouble(ulong.MinValue)},
|
||||
{typeof(byte), Convert.ToDouble(byte.MinValue)}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the min value of a given numeric type as a double.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, double> s_MaxAsDouble =
|
||||
new Dictionary<Type, double>
|
||||
{
|
||||
// Duh
|
||||
{typeof(double), double.MaxValue},
|
||||
|
||||
// Signed
|
||||
{typeof(short), Convert.ToDouble(short.MaxValue)},
|
||||
{typeof(int), Convert.ToDouble(int.MaxValue)},
|
||||
{typeof(long), Convert.ToDouble(long.MaxValue)},
|
||||
{typeof(float), Convert.ToDouble(float.MaxValue)},
|
||||
{typeof(decimal), Convert.ToDouble(decimal.MaxValue)},
|
||||
|
||||
// Unsigned
|
||||
{typeof(ushort), Convert.ToDouble(ushort.MaxValue)},
|
||||
{typeof(uint), Convert.ToDouble(uint.MaxValue)},
|
||||
{typeof(ulong), Convert.ToDouble(ulong.MaxValue)},
|
||||
{typeof(byte), Convert.ToDouble(byte.MaxValue)}
|
||||
};
|
||||
|
||||
private readonly object m_Min;
|
||||
private readonly object m_Max;
|
||||
|
||||
#region Properties
|
||||
|
||||
[NotNull]
|
||||
public object Min { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the min value for this range.
|
||||
/// </summary>
|
||||
public object Min { get { return m_Min; } }
|
||||
|
||||
[NotNull]
|
||||
public object Max { get; private set; }
|
||||
|
||||
[NotNull]
|
||||
private Type Type { get { return Min.GetType(); } }
|
||||
/// <summary>
|
||||
/// Gets the max value for this range.
|
||||
/// </summary>
|
||||
public object Max { get { return m_Max; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(ushort min, ushort max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(short min, short max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(uint min, uint max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(int min, int max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(ulong min, ulong max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(long min, long max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(float min, float max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(double min, double max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(byte min, byte max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(sbyte min, sbyte max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(decimal min, decimal max)
|
||||
: this((object)min, (object)max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
public RangeAttribute(object min, object max)
|
||||
{
|
||||
if (min == null)
|
||||
throw new ArgumentNullException("min");
|
||||
|
||||
if (max == null)
|
||||
throw new ArgumentNullException("max");
|
||||
|
||||
if (min.GetType() != max.GetType())
|
||||
throw new ArgumentException("Min and Max types do not match");
|
||||
|
||||
if (!min.GetType().IsNumeric())
|
||||
throw new ArgumentException("Given types are not numeric");
|
||||
|
||||
m_Min = min;
|
||||
m_Max = max;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -100,127 +287,239 @@ namespace ICD.Common.Utils.Attributes
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given value is within the range of Min to Max.
|
||||
/// Remaps the numeric value into the min-max range of the target numeric type.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsInRange(object value)
|
||||
[NotNull]
|
||||
public static object Remap([NotNull] object value, [NotNull] Type type)
|
||||
{
|
||||
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 (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (value is ushort)
|
||||
{
|
||||
var castMin = (ushort)Min;
|
||||
var castMax = (ushort)Max;
|
||||
var castVal = (ushort)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
if (value is short)
|
||||
{
|
||||
var castMin = (short)Min;
|
||||
var castMax = (short)Max;
|
||||
var castVal = (short)value;
|
||||
return (castVal >= castMin && castVal <= castMax);
|
||||
}
|
||||
if (!value.GetType().IsNumeric())
|
||||
throw new ArgumentException("Source value is not numeric");
|
||||
|
||||
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.");
|
||||
double intermediate = RemapToDouble(value);
|
||||
return RemapFromDouble(intermediate, type);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(double value)
|
||||
/// <summary>
|
||||
/// Remaps the given numeric value from its min/max range into double min/max range.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static double RemapToDouble([NotNull] object value)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((double)Min, (double)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
Func<object, double> remap;
|
||||
if (!s_RemapToDouble.TryGetValue(value.GetType(), out remap))
|
||||
throw new NotSupportedException("Value type is not supported.");
|
||||
|
||||
return remap(value);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(float value)
|
||||
/// <summary>
|
||||
/// Remaps the given double value from its min/max range into the target type min/max range.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static object RemapFromDouble(double value, [NotNull] Type type)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((float)Min, (float)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
Func<double, object> remap;
|
||||
if (!s_RemapFromDouble.TryGetValue(type, out remap))
|
||||
throw new NotSupportedException("Value type is not supported.");
|
||||
|
||||
return remap(value);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(int value)
|
||||
/// <summary>
|
||||
/// Remaps the given numeric value to the defined min/max.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public object RemapMinMax([NotNull] object value)
|
||||
{
|
||||
return (ushort)MathUtils.MapRange((int)Min, (int)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!value.GetType().IsNumeric())
|
||||
throw new ArgumentException("Source value is not numeric");
|
||||
|
||||
double sourceMin = GetMinAsDouble(value.GetType());
|
||||
double sourceMax = GetMaxAsDouble(value.GetType());
|
||||
|
||||
double targetMin = Convert.ToDouble(Min);
|
||||
double targetMax = Convert.ToDouble(Max);
|
||||
|
||||
double doubleValue = Convert.ToDouble(value);
|
||||
|
||||
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
|
||||
|
||||
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public ushort RemapRangeToUShort(ushort value)
|
||||
/// <summary>
|
||||
/// Clamps the given numeric value into the valid ranges of the target numeric type.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public static object Clamp([NotNull] object value, [NotNull] Type type)
|
||||
{
|
||||
return MathUtils.MapRange((ushort)Min, (ushort)Max, ushort.MinValue, ushort.MaxValue, value);
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
if (!value.GetType().IsNumeric())
|
||||
throw new ArgumentException("Source value is not numeric");
|
||||
|
||||
double doubleValue = Convert.ToDouble(value);
|
||||
double clamped = Clamp(doubleValue, type);
|
||||
|
||||
return Convert.ChangeType(clamped, value.GetType(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given double value into the valid ranges of the target numeric type.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static double Clamp(double value, [NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
Func<double, double> clamp;
|
||||
if (!s_Clamp.TryGetValue(type, out clamp))
|
||||
throw new NotSupportedException("Value type is not supported.");
|
||||
|
||||
return clamp(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given numeric value to the defined min/max then remaps to the target numeric type.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public object ClampMinMaxThenRemap([NotNull] object value, [NotNull] Type type)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
if (!value.GetType().IsNumeric())
|
||||
throw new ArgumentException("Source value is not numeric");
|
||||
|
||||
double min = Convert.ToDouble(Min);
|
||||
double max = Convert.ToDouble(Max);
|
||||
double doubleValue = Convert.ToDouble(value);
|
||||
|
||||
double clamped = MathUtils.Clamp(doubleValue, min, max);
|
||||
object remapped = RemapMinMax(clamped, type);
|
||||
|
||||
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
[NotNull]
|
||||
private object RemapMinMax([NotNull] object value, [NotNull] Type type)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
if (!value.GetType().IsNumeric())
|
||||
throw new ArgumentException("Source value is not numeric");
|
||||
|
||||
double sourceMin = Convert.ToDouble(Min);
|
||||
double sourceMax = Convert.ToDouble(Max);
|
||||
|
||||
double targetMin = GetMinAsDouble(type);
|
||||
double targetMax = GetMaxAsDouble(type);
|
||||
|
||||
double doubleValue = Convert.ToDouble(value);
|
||||
|
||||
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
|
||||
|
||||
return Convert.ChangeType(remapped, type, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the min value for the given numeric type as a double.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private static double GetMinAsDouble([NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
double min;
|
||||
if (!s_MinAsDouble.TryGetValue(type, out min))
|
||||
throw new NotSupportedException("Type is not supported.");
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max value for the given numeric type as a double.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private static double GetMaxAsDouble([NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
if (!type.IsNumeric())
|
||||
throw new ArgumentException("Target type is not numeric");
|
||||
|
||||
double max;
|
||||
if (!s_MaxAsDouble.TryGetValue(type, out max))
|
||||
throw new NotSupportedException("Type is not supported.");
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
153
ICD.Common.Utils/BigEndianBitConverter.cs
Normal file
153
ICD.Common.Utils/BigEndianBitConverter.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class BigEndianBitConverter
|
||||
{
|
||||
private const byte FULL_BYTE = 0xFF;
|
||||
|
||||
public static ushort ToUshort([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((ushort)ToShort(value, startIndex));
|
||||
}
|
||||
|
||||
public static short ToShort([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt16(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(short);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
short result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= (short)(value[i + startIndex] << GetBitShift(i, bytes));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint ToUint([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((uint)ToInt(value, startIndex));
|
||||
}
|
||||
|
||||
public static int ToInt([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt32(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(int);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= value[i + startIndex] << GetBitShift(i, bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ulong ToUlong([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
return unchecked((ulong)ToLong(value, startIndex));
|
||||
}
|
||||
|
||||
public static long ToLong([NotNull] byte[] value, int startIndex)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.ToInt64(value, startIndex);
|
||||
|
||||
const int bytes = sizeof(long);
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (startIndex < 0 || startIndex >= value.Length)
|
||||
throw new ArgumentOutOfRangeException("startIndex");
|
||||
if (startIndex > value.Length - bytes)
|
||||
throw new ArgumentException("Array plus start index too small");
|
||||
|
||||
long result = 0;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
result |= (long)(value[i + startIndex]) << GetBitShift(i, bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(int value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(int);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
public static byte[] GetBytes(uint value)
|
||||
{
|
||||
return unchecked(GetBytes((int)value));
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(short value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(short);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(ushort value)
|
||||
{
|
||||
return unchecked(GetBytes((short)value));
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(long value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
const int total_bytes = sizeof(long);
|
||||
|
||||
byte[] response = new byte[total_bytes];
|
||||
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(ulong value)
|
||||
{
|
||||
return unchecked(GetBytes((long)value));
|
||||
}
|
||||
private static int GetBitShift(int byteNumber, int totalBytes)
|
||||
{
|
||||
return ((totalBytes - 1 - byteNumber) * 8);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
@@ -9,6 +13,9 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> m_KeyToValue;
|
||||
@@ -20,8 +27,10 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValueToKey.Keys; } }
|
||||
|
||||
#endregion
|
||||
@@ -29,10 +38,45 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public BiDictionary()
|
||||
public BiDictionary() :
|
||||
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public BiDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>(valueComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
: this(keyComparer, valueComparer)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException("dict");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dict)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict)
|
||||
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>();
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
@@ -43,24 +87,31 @@ namespace ICD.Common.Utils.Collections
|
||||
m_ValueToKey.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue(TValue value)
|
||||
public bool ContainsValue([NotNull] TValue value)
|
||||
{
|
||||
return m_ValueToKey.ContainsKey(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
public void Add([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ContainsKey(key))
|
||||
@@ -73,14 +124,16 @@ namespace ICD.Common.Utils.Collections
|
||||
m_ValueToKey.Add(value, key);
|
||||
}
|
||||
|
||||
public void Set(TKey key, TValue value)
|
||||
public void Set([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
RemoveKey(key);
|
||||
@@ -89,18 +142,35 @@ namespace ICD.Common.Utils.Collections
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
public TKey GetKey(TValue value)
|
||||
[NotNull]
|
||||
public TKey GetKey([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
return m_ValueToKey[value];
|
||||
}
|
||||
|
||||
public TValue GetValue(TKey key)
|
||||
[NotNull]
|
||||
public TValue GetValue([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue[key];
|
||||
}
|
||||
|
||||
public bool RemoveKey(TKey key)
|
||||
public bool RemoveKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
@@ -112,8 +182,13 @@ namespace ICD.Common.Utils.Collections
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveValue(TValue value)
|
||||
public bool RemoveValue([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!ContainsValue(value))
|
||||
return false;
|
||||
|
||||
@@ -122,13 +197,23 @@ namespace ICD.Common.Utils.Collections
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetKey(TValue value, out TKey key)
|
||||
public bool TryGetKey([NotNull] TValue value, out TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
return m_ValueToKey.TryGetValue(value, out key);
|
||||
}
|
||||
|
||||
@@ -136,9 +221,10 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
#region IDictionary
|
||||
|
||||
TValue IDictionary<TKey, TValue>.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
[NotNull]
|
||||
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
|
||||
bool IDictionary<TKey, TValue>.Remove(TKey key)
|
||||
bool IDictionary<TKey, TValue>.Remove([NotNull] TKey key)
|
||||
{
|
||||
return RemoveKey(key);
|
||||
}
|
||||
@@ -162,7 +248,7 @@ namespace ICD.Common.Utils.Collections
|
||||
return RemoveKey(item.Key);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
|
||||
}
|
||||
@@ -171,11 +257,13 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
#region IEnumerable
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_KeyToValue.GetEnumerator();
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
|
||||
14
ICD.Common.Utils/Collections/INotifyCollectionChanged.cs
Normal file
14
ICD.Common.Utils/Collections/INotifyCollectionChanged.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
// Poor-mans System.Collections.Specialized
|
||||
// Delete when we finally drop Crestron 3-series.
|
||||
public interface INotifyCollectionChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when the contents of the collection change, or the order changes.
|
||||
/// </summary>
|
||||
event EventHandler OnCollectionChanged;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
@@ -10,6 +14,9 @@ namespace ICD.Common.Utils.Collections
|
||||
/// A collection containing only unique items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class IcdHashSet<T> : ICollection<T>
|
||||
{
|
||||
private readonly Dictionary<T, object> m_Dict;
|
||||
@@ -48,7 +55,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public IcdHashSet(IEnumerable<T> items)
|
||||
public IcdHashSet([NotNull] IEnumerable<T> items)
|
||||
: this(EqualityComparer<T>.Default, items)
|
||||
{
|
||||
}
|
||||
@@ -57,7 +64,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdHashSet(IEqualityComparer<T> comparer)
|
||||
public IcdHashSet([NotNull] IEqualityComparer<T> comparer)
|
||||
: this(comparer, Enumerable.Empty<T>())
|
||||
{
|
||||
}
|
||||
@@ -67,7 +74,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="items"></param>
|
||||
public IcdHashSet(IEqualityComparer<T> comparer, IEnumerable<T> items)
|
||||
public IcdHashSet([NotNull] IEqualityComparer<T> comparer, [NotNull] IEnumerable<T> items)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
@@ -90,7 +97,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Union(IEnumerable<T> set)
|
||||
[NotNull]
|
||||
public IcdHashSet<T> Union([NotNull] IEnumerable<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -107,7 +115,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Subtract(IEnumerable<T> set)
|
||||
[NotNull]
|
||||
public IcdHashSet<T> Subtract([NotNull] IEnumerable<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -126,7 +135,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Intersection(IEnumerable<T> set)
|
||||
[NotNull]
|
||||
public IcdHashSet<T> Intersection([NotNull] IEnumerable<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -145,7 +155,8 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> NonIntersection(IEnumerable<T> set)
|
||||
[NotNull]
|
||||
public IcdHashSet<T> NonIntersection([NotNull] IEnumerable<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -169,7 +180,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSubsetOf(IcdHashSet<T> set)
|
||||
public bool IsSubsetOf([NotNull] IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -183,7 +194,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSubsetOf(IcdHashSet<T> set)
|
||||
public bool IsProperSubsetOf([NotNull] IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -197,7 +208,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSupersetOf(IcdHashSet<T> set)
|
||||
public bool IsSupersetOf([NotNull] IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -211,7 +222,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSupersetOf(IcdHashSet<T> set)
|
||||
public bool IsProperSupersetOf([NotNull] IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -225,7 +236,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool SetEquals(IcdHashSet<T> set)
|
||||
public bool SetEquals([NotNull] IcdHashSet<T> set)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException("set");
|
||||
@@ -242,7 +253,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Add(T item)
|
||||
public bool Add([NotNull] T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
@@ -260,7 +271,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Adds the item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
void ICollection<T>.Add(T item)
|
||||
void ICollection<T>.Add([NotNull] T item)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
@@ -269,7 +280,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Adds each of the items in the sequence to the collection.
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public void AddRange(IEnumerable<T> items)
|
||||
public void AddRange([NotNull] IEnumerable<T> items)
|
||||
{
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
@@ -292,7 +303,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(T item)
|
||||
public bool Contains([NotNull] T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
@@ -306,8 +317,11 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Copies the items of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
|
||||
/// </summary>
|
||||
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the items copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of items in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type T cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
public void CopyTo([NotNull] T[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
m_Dict.Keys.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
@@ -318,7 +332,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </returns>
|
||||
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
public bool Remove(T item)
|
||||
public bool Remove([NotNull] T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
@@ -328,6 +342,18 @@ namespace ICD.Common.Utils.Collections
|
||||
return m_Dict.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes each of the items in the sequence from the collection.
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public void RemoveRange([NotNull] IEnumerable<T> items)
|
||||
{
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
m_Dict.RemoveAll(items);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<T>
|
||||
@@ -339,6 +365,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
/// <filterpriority>1</filterpriority>
|
||||
[NotNull]
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_Dict.Keys.GetEnumerator();
|
||||
@@ -351,6 +378,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
|
||||
@@ -1,188 +1,651 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
|
||||
{
|
||||
private readonly List<TKey> m_OrderedKeys;
|
||||
private readonly List<TValue> m_ValuesOrderedByKey;
|
||||
private readonly Dictionary<TKey, TValue> m_Dictionary;
|
||||
private readonly IComparer<TKey> m_Comparer;
|
||||
private readonly IcdOrderedDictionary m_PrivateDictionary;
|
||||
|
||||
#region Properties
|
||||
public int Count { get { return m_PrivateDictionary.Count; } }
|
||||
|
||||
public int Count { get { return m_Dictionary.Count; } }
|
||||
int ICollection.Count { get { return m_PrivateDictionary.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get { return m_Dictionary[key]; }
|
||||
set
|
||||
get
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
{
|
||||
int index = m_OrderedKeys.AddSorted(key, m_Comparer);
|
||||
m_ValuesOrderedByKey.Insert(index, value);
|
||||
}
|
||||
if (m_PrivateDictionary.Contains(key))
|
||||
return (TValue)m_PrivateDictionary[key];
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
m_PrivateDictionary[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
object IDictionary.this[object key]
|
||||
{
|
||||
get { return m_PrivateDictionary[key]; }
|
||||
set { m_PrivateDictionary[key] = value; }
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
List<TKey> keys = new List<TKey>(m_PrivateDictionary.Count);
|
||||
|
||||
foreach (TKey key in m_PrivateDictionary.Keys)
|
||||
{
|
||||
keys.Add(key);
|
||||
}
|
||||
|
||||
// Keys should be put in a ReadOnlyCollection,
|
||||
// but since this is an internal class, for performance reasons,
|
||||
// we choose to avoid creating yet another collection.
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
ICollection IDictionary.Keys { get { return m_PrivateDictionary.Keys; } }
|
||||
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
List<TValue> values = new List<TValue>(m_PrivateDictionary.Count);
|
||||
|
||||
foreach (TValue value in m_PrivateDictionary.Values)
|
||||
values.Add(value);
|
||||
|
||||
// Values should be put in a ReadOnlyCollection,
|
||||
// but since this is an internal class, for performance reasons,
|
||||
// we choose to avoid creating yet another collection.
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
ICollection IDictionary.Values { get { return m_PrivateDictionary.Values; } }
|
||||
|
||||
bool IDictionary.IsFixedSize { get { return ((IDictionary)m_PrivateDictionary).IsFixedSize; } }
|
||||
|
||||
bool IDictionary.IsReadOnly { get { return m_PrivateDictionary.IsReadOnly; } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return ((ICollection)m_PrivateDictionary).IsSynchronized; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return m_PrivateDictionary; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdOrderedDictionary()
|
||||
: this(Comparer<TKey>.Default)
|
||||
{
|
||||
m_PrivateDictionary = new IcdOrderedDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdOrderedDictionary(IComparer<TKey> comparer)
|
||||
: this(comparer, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="equalityComparer"></param>
|
||||
public IcdOrderedDictionary(IComparer<TKey> comparer, IEqualityComparer<TKey> equalityComparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
if (equalityComparer == null)
|
||||
throw new ArgumentNullException("equalityComparer");
|
||||
|
||||
m_Comparer = comparer;
|
||||
m_OrderedKeys = new List<TKey>();
|
||||
m_ValuesOrderedByKey = new List<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dictionary"></param>
|
||||
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
|
||||
: this()
|
||||
{
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException("dictionary");
|
||||
return;
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
m_PrivateDictionary = new IcdOrderedDictionary();
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
|
||||
{
|
||||
m_PrivateDictionary.Add(pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
|
||||
.GetEnumerator();
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_Dictionary.ContainsKey(key))
|
||||
throw new ArgumentException("An item with the same key has already been added.", "key");
|
||||
m_PrivateDictionary.Add(key, value);
|
||||
}
|
||||
|
||||
this[key] = value;
|
||||
public KeyValuePair<TKey, TValue> Get(int index)
|
||||
{
|
||||
DictionaryEntry entry = (DictionaryEntry)m_PrivateDictionary.ObjectsArray[index];
|
||||
return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_ValuesOrderedByKey.Clear();
|
||||
m_Dictionary.Clear();
|
||||
m_PrivateDictionary.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
if (item.Key == null || !m_PrivateDictionary.Contains(item.Key))
|
||||
return false;
|
||||
|
||||
return m_PrivateDictionary[item.Key].Equals(item.Value);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!m_Dictionary.Remove(key))
|
||||
return false;
|
||||
|
||||
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
|
||||
|
||||
m_OrderedKeys.RemoveAt(index);
|
||||
m_ValuesOrderedByKey.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
return m_PrivateDictionary.Contains(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
return m_Dictionary.TryGetValue(key, out value);
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException("array");
|
||||
}
|
||||
|
||||
if (arrayIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("arrayIndex");
|
||||
}
|
||||
|
||||
if (array.Rank > 1 || arrayIndex >= array.Length || array.Length - arrayIndex < m_PrivateDictionary.Count)
|
||||
{
|
||||
throw new ArgumentException("array");
|
||||
}
|
||||
|
||||
int index = arrayIndex;
|
||||
foreach (DictionaryEntry entry in m_PrivateDictionary)
|
||||
{
|
||||
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
foreach (DictionaryEntry entry in m_PrivateDictionary)
|
||||
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value) &&
|
||||
EqualityComparer<TValue>.Default.Equals(value, item.Value);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
|
||||
{
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in this)
|
||||
if (Contains(item))
|
||||
{
|
||||
array.SetValue(kvp, index);
|
||||
index++;
|
||||
m_PrivateDictionary.Remove(item.Key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_PrivateDictionary.Contains(key))
|
||||
{
|
||||
m_PrivateDictionary.Remove(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
bool keyExists = m_PrivateDictionary.Contains(key);
|
||||
value = keyExists ? (TValue)m_PrivateDictionary[key] : default(TValue);
|
||||
|
||||
return keyExists;
|
||||
}
|
||||
|
||||
void IDictionary.Add(object key, object value)
|
||||
{
|
||||
m_PrivateDictionary.Add(key, value);
|
||||
}
|
||||
|
||||
void IDictionary.Clear()
|
||||
{
|
||||
m_PrivateDictionary.Clear();
|
||||
}
|
||||
|
||||
bool IDictionary.Contains(object key)
|
||||
{
|
||||
return m_PrivateDictionary.Contains(key);
|
||||
}
|
||||
|
||||
IDictionaryEnumerator IDictionary.GetEnumerator()
|
||||
{
|
||||
return m_PrivateDictionary.GetEnumerator();
|
||||
}
|
||||
|
||||
void IDictionary.Remove(object key)
|
||||
{
|
||||
m_PrivateDictionary.Remove(key);
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
m_PrivateDictionary.CopyTo(array, index);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IcdOrderedDictionary : IDictionary
|
||||
{
|
||||
private ArrayList m_ObjectsArray;
|
||||
private Hashtable m_ObjectsTable;
|
||||
private readonly int m_InitialCapacity;
|
||||
private readonly IEqualityComparer m_Comparer;
|
||||
private readonly bool m_ReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the table.
|
||||
/// </summary>
|
||||
public int Count { get { return ObjectsArray.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the collection can grow.
|
||||
/// </summary>
|
||||
bool IDictionary.IsFixedSize { get { return m_ReadOnly; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the collection is not read-only
|
||||
/// </summary>
|
||||
public bool IsReadOnly { get { return m_ReadOnly; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this class is not synchronized
|
||||
/// </summary>
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of keys in the table in order.
|
||||
/// </summary>
|
||||
public ICollection Keys { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, true); } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an arrayList of the values in the table
|
||||
/// </summary>
|
||||
public ICollection Values { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, false); } }
|
||||
|
||||
public ArrayList ObjectsArray
|
||||
{
|
||||
get { return m_ObjectsArray ?? (m_ObjectsArray = new ArrayList(m_InitialCapacity)); }
|
||||
}
|
||||
|
||||
private Hashtable ObjectsTable
|
||||
{
|
||||
get { return m_ObjectsTable ?? (m_ObjectsTable = new Hashtable(m_InitialCapacity, m_Comparer)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The SyncRoot object. Not used because IsSynchronized is false
|
||||
/// </summary>
|
||||
object ICollection.SyncRoot { get { return this; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object at the specified index
|
||||
/// </summary>
|
||||
public object this[int index]
|
||||
{
|
||||
get { return ((DictionaryEntry)ObjectsArray[index]).Value; }
|
||||
set
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index < 0 || index >= ObjectsArray.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
|
||||
ObjectsArray[index] = new DictionaryEntry(key, value);
|
||||
ObjectsTable[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
/// <summary>
|
||||
/// Gets or sets the object with the specified key
|
||||
/// </summary>
|
||||
public object this[object key]
|
||||
{
|
||||
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
|
||||
get { return ObjectsTable[key]; }
|
||||
set
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (ObjectsTable.Contains(key))
|
||||
{
|
||||
ObjectsTable[key] = value;
|
||||
ObjectsArray[IndexOfKey(key)] = new DictionaryEntry(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary() : this(0)
|
||||
{
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary(int capacity) : this(capacity, null)
|
||||
{
|
||||
}
|
||||
|
||||
public IcdOrderedDictionary(int capacity, IEqualityComparer comparer)
|
||||
{
|
||||
m_InitialCapacity = capacity;
|
||||
m_Comparer = comparer;
|
||||
}
|
||||
|
||||
private IcdOrderedDictionary(IcdOrderedDictionary dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
throw new ArgumentNullException("dictionary");
|
||||
}
|
||||
|
||||
m_ReadOnly = true;
|
||||
m_ObjectsArray = dictionary.m_ObjectsArray;
|
||||
m_ObjectsTable = dictionary.m_ObjectsTable;
|
||||
m_Comparer = dictionary.m_Comparer;
|
||||
m_InitialCapacity = dictionary.m_InitialCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry to the table with the lowest-available index.
|
||||
/// </summary>
|
||||
public void Add(object key, object value)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
ObjectsTable.Add(key, value);
|
||||
ObjectsArray.Add(new DictionaryEntry(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all elements in the table.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
ObjectsTable.Clear();
|
||||
ObjectsArray.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a readonly IcdOrderedDictionary for the given IcdOrderedDictionary.
|
||||
/// </summary>
|
||||
public IcdOrderedDictionary AsReadOnly()
|
||||
{
|
||||
return new IcdOrderedDictionary(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the key exists in the table, false otherwise.
|
||||
/// </summary>
|
||||
public bool Contains(object key)
|
||||
{
|
||||
return ObjectsTable.Contains(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the table to an array. This will not preserve order.
|
||||
/// </summary>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
ObjectsTable.CopyTo(array, index);
|
||||
}
|
||||
|
||||
private int IndexOfKey(object key)
|
||||
{
|
||||
for (int i = 0; i < ObjectsArray.Count; i++)
|
||||
{
|
||||
object o = ((DictionaryEntry)ObjectsArray[i]).Key;
|
||||
if (m_Comparer != null)
|
||||
{
|
||||
if (m_Comparer.Equals(o, key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o.Equals(key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new object at the given index with the given key.
|
||||
/// </summary>
|
||||
public void Insert(int index, object key, object value)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index > Count || index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
ObjectsTable.Add(key, value);
|
||||
ObjectsArray.Insert(index, new DictionaryEntry(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the entry at the given index.
|
||||
/// </summary>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (index >= Count || index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
|
||||
ObjectsArray.RemoveAt(index);
|
||||
ObjectsTable.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the entry with the given key.
|
||||
/// </summary>
|
||||
public void Remove(object key)
|
||||
{
|
||||
if (m_ReadOnly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
|
||||
int index = IndexOfKey(key);
|
||||
if (index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectsTable.Remove(key);
|
||||
ObjectsArray.RemoveAt(index);
|
||||
}
|
||||
|
||||
#region IDictionary implementation
|
||||
|
||||
/// <internalonly/>
|
||||
public IDictionaryEnumerator GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable implementation
|
||||
|
||||
/// <internalonly/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries
|
||||
/// in the order by index.
|
||||
/// </summary>
|
||||
private sealed class OrderedDictionaryEnumerator : IDictionaryEnumerator
|
||||
{
|
||||
internal const int KEYS = 1;
|
||||
internal const int VALUES = 2;
|
||||
internal const int DICTIONARY_ENTRY = 3;
|
||||
|
||||
private readonly int m_ObjectReturnType;
|
||||
private readonly IEnumerator m_ArrayEnumerator;
|
||||
|
||||
internal OrderedDictionaryEnumerator(ArrayList array, int objectReturnType)
|
||||
{
|
||||
m_ArrayEnumerator = array.GetEnumerator();
|
||||
m_ObjectReturnType = objectReturnType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current DictionaryEntry. This is the same as Entry, but not strongly-typed.
|
||||
/// </summary>
|
||||
public object Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ObjectReturnType == KEYS)
|
||||
{
|
||||
return ((DictionaryEntry)m_ArrayEnumerator.Current).Key;
|
||||
}
|
||||
if (m_ObjectReturnType == VALUES)
|
||||
{
|
||||
return ((DictionaryEntry)m_ArrayEnumerator.Current).Value;
|
||||
}
|
||||
return Entry;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current DictionaryEntry
|
||||
/// </summary>
|
||||
public DictionaryEntry Entry
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DictionaryEntry(((DictionaryEntry)m_ArrayEnumerator.Current).Key,
|
||||
((DictionaryEntry)m_ArrayEnumerator.Current).Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the key of the current DictionaryEntry
|
||||
/// </summary>
|
||||
public object Key { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Key; } }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value of the current DictionaryEntry
|
||||
/// </summary>
|
||||
public object Value { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Moves the enumerator pointer to the next member
|
||||
/// </summary>
|
||||
public bool MoveNext()
|
||||
{
|
||||
return m_ArrayEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumerator pointer to the beginning.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
m_ArrayEnumerator.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OrderedDictionaryKeyValueCollection implements a collection for the Values and Keys properties
|
||||
/// that is "live"- it will reflect changes to the IcdOrderedDictionary on the collection made after the getter
|
||||
/// was called.
|
||||
/// </summary>
|
||||
private sealed class OrderedDictionaryKeyValueCollection : ICollection
|
||||
{
|
||||
private readonly ArrayList m_Objects;
|
||||
private readonly bool m_IsKeys;
|
||||
|
||||
public OrderedDictionaryKeyValueCollection(ArrayList array, bool isKeys)
|
||||
{
|
||||
m_Objects = array;
|
||||
m_IsKeys = isKeys;
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
foreach (object o in m_Objects)
|
||||
{
|
||||
array.SetValue(m_IsKeys ? ((DictionaryEntry)o).Key : ((DictionaryEntry)o).Value, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
int ICollection.Count { get { return m_Objects.Count; } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return m_Objects.SyncRoot; } }
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new OrderedDictionaryEnumerator(m_Objects,
|
||||
m_IsKeys
|
||||
? OrderedDictionaryEnumerator.KEYS
|
||||
: OrderedDictionaryEnumerator.VALUES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
213
ICD.Common.Utils/Collections/IcdSortedDictionary.cs
Normal file
213
ICD.Common.Utils/Collections/IcdSortedDictionary.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class IcdSortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly List<TKey> m_OrderedKeys;
|
||||
private readonly List<TValue> m_ValuesOrderedByKey;
|
||||
private readonly Dictionary<TKey, TValue> m_Dictionary;
|
||||
private readonly IComparer<TKey> m_Comparer;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_Dictionary.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
|
||||
|
||||
[CanBeNull]
|
||||
public TValue this[[NotNull] TKey key]
|
||||
{
|
||||
get { return m_Dictionary[key]; }
|
||||
set
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
Remove(key);
|
||||
Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdSortedDictionary()
|
||||
: this(Comparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer)
|
||||
: this(comparer, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
/// <param name="equalityComparer"></param>
|
||||
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] IEqualityComparer<TKey> equalityComparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
if (equalityComparer == null)
|
||||
throw new ArgumentNullException("equalityComparer");
|
||||
|
||||
m_Comparer = comparer;
|
||||
m_OrderedKeys = new List<TKey>();
|
||||
m_ValuesOrderedByKey = new List<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dictionary"></param>
|
||||
public IcdSortedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
|
||||
: this()
|
||||
{
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException("dictionary");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
|
||||
.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_Dictionary.ContainsKey(key))
|
||||
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
|
||||
|
||||
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
|
||||
m_ValuesOrderedByKey.Insert(index, value);
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_ValuesOrderedByKey.Clear();
|
||||
m_Dictionary.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!m_Dictionary.Remove(key))
|
||||
return false;
|
||||
|
||||
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
|
||||
|
||||
m_OrderedKeys.RemoveAt(index);
|
||||
m_ValuesOrderedByKey.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_Dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value) &&
|
||||
EqualityComparer<TValue>.Default.Equals(value, item.Value);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in this)
|
||||
{
|
||||
array.SetValue(kvp, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,21 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a first-in first-out collection with enhanced insertion features.
|
||||
/// </summary>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
|
||||
{
|
||||
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
|
||||
private readonly IcdSortedDictionary<int, List<T>> m_PriorityToQueue;
|
||||
private int m_Count;
|
||||
|
||||
#region Properties
|
||||
@@ -30,6 +36,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <summary>
|
||||
/// Gets a reference for locking.
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public object SyncRoot { get { return this; } }
|
||||
|
||||
#endregion
|
||||
@@ -39,7 +46,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
public PriorityQueue()
|
||||
{
|
||||
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
|
||||
m_PriorityToQueue = new IcdSortedDictionary<int, List<T>>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
@@ -59,7 +66,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue(T item)
|
||||
public void Enqueue([CanBeNull] T item)
|
||||
{
|
||||
Enqueue(item, int.MaxValue);
|
||||
}
|
||||
@@ -71,16 +78,24 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <param name="item"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue(T item, int priority)
|
||||
public void Enqueue([CanBeNull] T item, int priority)
|
||||
{
|
||||
List<T> queue;
|
||||
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
|
||||
{
|
||||
queue = new List<T>();
|
||||
m_PriorityToQueue[priority] = queue;
|
||||
}
|
||||
m_PriorityToQueue.GetOrAddNew(priority, () => new List<T>())
|
||||
.Add(item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
queue.Add(item);
|
||||
/// <summary>
|
||||
/// Adds the item to the queue with the given priority at the given index.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="position"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue([CanBeNull] T item, int priority, int position)
|
||||
{
|
||||
m_PriorityToQueue.GetOrAddNew(priority, ()=> new List<T>())
|
||||
.Insert(position, item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
@@ -89,30 +104,22 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueFirst(T item)
|
||||
public void EnqueueFirst([CanBeNull] T item)
|
||||
{
|
||||
const int priority = int.MinValue;
|
||||
|
||||
List<T> queue;
|
||||
if (!m_PriorityToQueue.TryGetValue(priority, out queue))
|
||||
{
|
||||
queue = new List<T>();
|
||||
m_PriorityToQueue[priority] = queue;
|
||||
}
|
||||
|
||||
queue.Insert(0, item);
|
||||
m_PriorityToQueue.GetOrAddNew(int.MinValue, () => new List<T>())
|
||||
.Insert(0, item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinant.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove(T item, Func<T, bool> remove)
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove)
|
||||
{
|
||||
if (remove == null)
|
||||
throw new ArgumentNullException("remove");
|
||||
@@ -122,21 +129,40 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinant.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove(T item, Func<T, bool> remove, int priority)
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority)
|
||||
{
|
||||
if (remove == null)
|
||||
throw new ArgumentNullException("remove");
|
||||
|
||||
bool inserted = false;
|
||||
EnqueueRemove(item, remove, priority, false);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue)
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Appends the given item at the end of the given priority level.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinent.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="deDuplicateToEndOfQueue"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority, bool deDuplicateToEndOfQueue)
|
||||
{
|
||||
if (remove == null)
|
||||
throw new ArgumentNullException("remove");
|
||||
|
||||
int lowestMatchingPriority = int.MaxValue;
|
||||
int? firstMatchingIndex = null;
|
||||
|
||||
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue.ToArray())
|
||||
{
|
||||
int[] removeIndices =
|
||||
kvp.Value
|
||||
@@ -144,8 +170,11 @@ namespace ICD.Common.Utils.Collections
|
||||
.Reverse()
|
||||
.ToArray();
|
||||
|
||||
if (removeIndices.Length == 0)
|
||||
continue;
|
||||
if (removeIndices.Any() && kvp.Key < lowestMatchingPriority )
|
||||
{
|
||||
lowestMatchingPriority = kvp.Key;
|
||||
firstMatchingIndex = removeIndices.Last();
|
||||
}
|
||||
|
||||
foreach (int removeIndex in removeIndices)
|
||||
{
|
||||
@@ -153,23 +182,20 @@ namespace ICD.Common.Utils.Collections
|
||||
m_Count--;
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
int insertIndex = removeIndices[0];
|
||||
|
||||
if (insertIndex >= kvp.Value.Count)
|
||||
kvp.Value.Add(item);
|
||||
else
|
||||
kvp.Value.Insert(insertIndex, item);
|
||||
|
||||
m_Count++;
|
||||
|
||||
inserted = true;
|
||||
}
|
||||
if (kvp.Value.Count == 0)
|
||||
m_PriorityToQueue.Remove(kvp.Key);
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
|
||||
if(deDuplicateToEndOfQueue)
|
||||
Enqueue(item, priority);
|
||||
else
|
||||
{
|
||||
if(firstMatchingIndex == null)
|
||||
Enqueue(item, lowestMatchingPriority);
|
||||
else
|
||||
Enqueue(item, lowestMatchingPriority, firstMatchingIndex.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -177,6 +203,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[CanBeNull]
|
||||
public T Dequeue()
|
||||
{
|
||||
T output;
|
||||
@@ -194,30 +221,37 @@ namespace ICD.Common.Utils.Collections
|
||||
[PublicAPI]
|
||||
public bool TryDequeue(out T output)
|
||||
{
|
||||
output = default(T);
|
||||
while (true)
|
||||
{
|
||||
output = default(T);
|
||||
|
||||
KeyValuePair<int, List<T>> kvp;
|
||||
if (!m_PriorityToQueue.TryFirst(out kvp))
|
||||
return false;
|
||||
KeyValuePair<int, List<T>> kvp;
|
||||
if (!m_PriorityToQueue.TryFirst(out kvp))
|
||||
return false;
|
||||
|
||||
int priority = kvp.Key;
|
||||
List<T> queue = kvp.Value;
|
||||
int priority = kvp.Key;
|
||||
List<T> queue = kvp.Value;
|
||||
|
||||
output = queue[0];
|
||||
queue.RemoveAt(0);
|
||||
bool found = queue.TryFirst(out output);
|
||||
if (found)
|
||||
{
|
||||
queue.RemoveAt(0);
|
||||
m_Count--;
|
||||
}
|
||||
|
||||
if (queue.Count == 0)
|
||||
m_PriorityToQueue.Remove(priority);
|
||||
if (queue.Count == 0)
|
||||
m_PriorityToQueue.Remove(priority);
|
||||
|
||||
m_Count--;
|
||||
|
||||
return true;
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator for the items.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_PriorityToQueue.Values
|
||||
@@ -230,13 +264,13 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="index"></param>
|
||||
public void CopyTo(Array array, int index)
|
||||
public void CopyTo([NotNull] Array array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
array.SetValue(item, index++);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -247,6 +281,7 @@ namespace ICD.Common.Utils.Collections
|
||||
/// Gets an enumerator for the items.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Timers;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
|
||||
/// </summary>
|
||||
public sealed class RateLimitedEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised to handle to the next item in the queue.
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
|
||||
|
||||
private readonly SafeTimer m_DequeueTimer;
|
||||
private readonly Queue<T> m_Queue;
|
||||
private readonly SafeCriticalSection m_QueueSection;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the time between dequeues in milliseconds.
|
||||
/// </summary>
|
||||
public long BetweenMilliseconds { get; set; }
|
||||
|
||||
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return true; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return this; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public RateLimitedEventQueue()
|
||||
{
|
||||
m_Queue = new Queue<T>();
|
||||
m_QueueSection = new SafeCriticalSection();
|
||||
|
||||
m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Release resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
OnItemDequeued = null;
|
||||
|
||||
m_DequeueTimer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the given item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Enqueue(item);
|
||||
|
||||
if (m_Queue.Count == 1)
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the queued items.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_DequeueTimer.Stop();
|
||||
m_Queue.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next pulse in the queue.
|
||||
/// </summary>
|
||||
private void SendNext()
|
||||
{
|
||||
if (!m_QueueSection.TryEnter())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (m_Queue.Count == 0)
|
||||
return;
|
||||
|
||||
T item = m_Queue.Peek();
|
||||
|
||||
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
|
||||
|
||||
m_DequeueTimer.Reset(BetweenMilliseconds);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dequeue timer elapses.
|
||||
/// </summary>
|
||||
private void DequeueTimerCallback()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Dequeue();
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable/ICollection
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
299
ICD.Common.Utils/Collections/ReverseLookupDictionary.cs
Normal file
299
ICD.Common.Utils/Collections/ReverseLookupDictionary.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public class ReverseLookupDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> m_KeyToValue;
|
||||
private readonly Dictionary<TValue, IcdHashSet<TKey>> m_ValueToKeys;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_KeyToValue.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
|
||||
|
||||
[NotNull]
|
||||
public ICollection<TValue> Values { get { return m_ValueToKeys.Keys; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ReverseLookupDictionary() :
|
||||
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public ReverseLookupDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
|
||||
m_ValueToKeys = new Dictionary<TValue, IcdHashSet<TKey>>(valueComparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
/// <param name="keyComparer"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
|
||||
: this(keyComparer, valueComparer)
|
||||
{
|
||||
if (dict == null)
|
||||
throw new ArgumentNullException("dict");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in dict)
|
||||
Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict)
|
||||
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_KeyToValue.Clear();
|
||||
m_ValueToKeys.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue([NotNull] TValue value)
|
||||
{
|
||||
return m_ValueToKeys.ContainsKey(value);
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<TValue, IEnumerable<TKey>>> GetReverseDictionary()
|
||||
{
|
||||
// Cast stuff a layer deep cause weird
|
||||
return m_ValueToKeys.Select(kvp => new KeyValuePair<TValue, IEnumerable<TKey>>(kvp.Key, kvp.Value.ToArray(kvp.Value.Count)));
|
||||
}
|
||||
|
||||
public void Add([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ContainsKey(key))
|
||||
throw new ArgumentException("Key is already present in the collection", "key");
|
||||
|
||||
|
||||
m_KeyToValue.Add(key, value);
|
||||
m_ValueToKeys.GetOrAddNew(value, () => new IcdHashSet<TKey>()).Add(key);
|
||||
}
|
||||
|
||||
public void Set([NotNull] TKey key, [NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
RemoveKey(key);
|
||||
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public IEnumerable<TKey> GetKeys([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
return m_ValueToKeys[value];
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public TValue GetValue([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue[key];
|
||||
}
|
||||
|
||||
public bool RemoveKey([NotNull] TKey key)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
TValue value = m_KeyToValue[key];
|
||||
|
||||
m_KeyToValue.Remove(key);
|
||||
|
||||
IcdHashSet<TKey> keys = m_ValueToKeys[value];
|
||||
keys.Remove(key);
|
||||
|
||||
if (keys.Count == 0)
|
||||
m_ValueToKeys.Remove(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the value from the collection, and any keys that were using it
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns>true if items were removed, false if not</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public bool RemoveValue([NotNull] TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!ContainsValue(value))
|
||||
return false;
|
||||
|
||||
IcdHashSet<TKey> keys = m_ValueToKeys[value];
|
||||
|
||||
m_ValueToKeys.Remove(value);
|
||||
|
||||
foreach (TKey key in keys)
|
||||
{
|
||||
m_KeyToValue.Remove(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] TKey key, out TValue value)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return m_KeyToValue.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetKeys([NotNull] TValue value, out IEnumerable<TKey> keys)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
IcdHashSet<TKey> keysInternal;
|
||||
if (m_ValueToKeys.TryGetValue(value, out keysInternal))
|
||||
{
|
||||
keys = keysInternal;
|
||||
return true;
|
||||
}
|
||||
keys = Enumerable.Empty<TKey>();
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary
|
||||
|
||||
[NotNull]
|
||||
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
|
||||
bool IDictionary<TKey, TValue>.Remove([NotNull] TKey key)
|
||||
{
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (m_KeyToValue as IDictionary<TKey, TValue>).Contains(item);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return RemoveKey(item.Key);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable
|
||||
|
||||
[NotNull]
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_KeyToValue.GetEnumerator();
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
@@ -11,13 +13,14 @@ namespace ICD.Common.Utils.Collections
|
||||
/// are removed as new items are added.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContents"></typeparam>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class ScrollQueue<TContents> : IEnumerable<TContents>, ICollection
|
||||
{
|
||||
private readonly LinkedList<TContents> m_Collection;
|
||||
private int m_MaxSize;
|
||||
|
||||
private readonly SafeCriticalSection m_CollectionLock;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
@@ -34,20 +37,23 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
m_MaxSize = value;
|
||||
|
||||
Trim();
|
||||
TContents unused;
|
||||
while (Trim(out unused))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 +70,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,27 +81,20 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_CollectionLock.Execute(() => m_Collection.Clear());
|
||||
m_Collection.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the item to the queue, trims old items that exceed max length.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="removed"></param>
|
||||
/// <returns>Returns true if an item was dequeued.</returns>
|
||||
[PublicAPI]
|
||||
public void Enqueue(TContents item)
|
||||
public bool Enqueue(TContents item, out TContents removed)
|
||||
{
|
||||
m_CollectionLock.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Collection.AddLast(item);
|
||||
Trim();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CollectionLock.Leave();
|
||||
}
|
||||
m_Collection.AddLast(item);
|
||||
return Trim(out removed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -106,18 +104,27 @@ namespace ICD.Common.Utils.Collections
|
||||
[PublicAPI]
|
||||
public TContents Dequeue()
|
||||
{
|
||||
m_CollectionLock.Enter();
|
||||
TContents output = Peek();
|
||||
m_Collection.RemoveFirst();
|
||||
return output;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
TContents output = m_Collection.First.Value;
|
||||
m_Collection.RemoveFirst();
|
||||
return output;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CollectionLock.Leave();
|
||||
}
|
||||
/// <summary>
|
||||
/// Dequeues the next item in the queue. Returns false if the queue is empty.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContents"></typeparam>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool Dequeue(out TContents item)
|
||||
{
|
||||
item = default(TContents);
|
||||
|
||||
if (Count == 0)
|
||||
return false;
|
||||
|
||||
item = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,7 +134,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 +148,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)
|
||||
void ICollection.CopyTo(Array array, 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();
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,19 +167,17 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <summary>
|
||||
/// Removes items that fall outside of the max size.
|
||||
/// </summary>
|
||||
private void Trim()
|
||||
private bool Trim(out TContents removed)
|
||||
{
|
||||
m_CollectionLock.Enter();
|
||||
removed = default(TContents);
|
||||
|
||||
try
|
||||
{
|
||||
while (Count > MaxSize)
|
||||
m_Collection.RemoveFirst();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_CollectionLock.Leave();
|
||||
}
|
||||
if (Count <= MaxSize)
|
||||
return false;
|
||||
|
||||
removed = m_Collection.First.Value;
|
||||
m_Collection.RemoveFirst();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -4,6 +4,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if !SIMPLSHARP
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
@@ -58,9 +61,9 @@ namespace ICD.Common.Utils.Collections
|
||||
m_Comparer = comparer;
|
||||
}
|
||||
|
||||
public int GetHashCode(WeakKeyReference<T> weakKey)
|
||||
public int GetHashCode(WeakKeyReference<T> obj)
|
||||
{
|
||||
return weakKey.HashCode;
|
||||
return obj == null ? 0 : obj.HashCode;
|
||||
}
|
||||
|
||||
// Note: There are actually 9 cases to handle here.
|
||||
@@ -108,6 +111,9 @@ namespace ICD.Common.Utils.Collections
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
#if !SIMPLSHARP
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#endif
|
||||
public sealed class WeakKeyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<WeakKeyReference<TKey>, TValue> m_Dictionary;
|
||||
|
||||
@@ -29,11 +29,12 @@ namespace ICD.Common.Utils.Comparers
|
||||
|
||||
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
throw new ArgumentNullException("x");
|
||||
|
||||
return -1;
|
||||
if (y == null)
|
||||
throw new ArgumentNullException("y");
|
||||
return 1;
|
||||
|
||||
using (IEnumerator<T> firstPos = x.GetEnumerator())
|
||||
{
|
||||
|
||||
33
ICD.Common.Utils/Comparers/UndefinedVersionComparer.cs
Normal file
33
ICD.Common.Utils/Comparers/UndefinedVersionComparer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined Versions have a value of 0.0.-1.-1
|
||||
/// This comparer Maxs Versions to 0.0.0.0
|
||||
/// </summary>
|
||||
public sealed class UndefinedVersionComparer : IComparer<Version>
|
||||
{
|
||||
private static UndefinedVersionComparer s_Instance;
|
||||
|
||||
public static UndefinedVersionComparer Instance
|
||||
{
|
||||
get { return s_Instance = s_Instance ?? new UndefinedVersionComparer(); }
|
||||
}
|
||||
|
||||
public int Compare(Version x, Version y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
|
||||
return x.ClearUndefined()
|
||||
.CompareTo(y.ClearUndefined());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined Versions have a value of 0.0.-1.-1
|
||||
/// This comparer Maxs Versions to 0.0.0.0
|
||||
/// </summary>
|
||||
public sealed class UndefinedVersionEqualityComparer : IEqualityComparer<Version>
|
||||
{
|
||||
private static UndefinedVersionEqualityComparer s_Instance;
|
||||
|
||||
public static UndefinedVersionEqualityComparer Instance
|
||||
{
|
||||
get { return s_Instance = s_Instance ?? new UndefinedVersionEqualityComparer(); }
|
||||
}
|
||||
|
||||
public bool Equals(Version x, Version y)
|
||||
{
|
||||
return x.ClearUndefined()
|
||||
.Equals(y.ClearUndefined());
|
||||
}
|
||||
|
||||
public int GetHashCode(Version version)
|
||||
{
|
||||
return version.ClearUndefined()
|
||||
.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public enum eConsoleColor
|
||||
{
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
YellowOnRed
|
||||
}
|
||||
|
||||
public static class ConsoleColorExtensions
|
||||
{
|
||||
public const string CONSOLE_RED = "\x1B[31;1m";
|
||||
public const string CONSOLE_GREEN = "\x1B[32;1m";
|
||||
public const string CONSOLE_YELLOW = "\x1B[33;1m";
|
||||
public const string CONSOLE_BLUE = "\x1B[34;1m";
|
||||
public const string CONSOLE_MAGENTA = "\x1B[35;1m";
|
||||
public const string CONSOLE_CYAN = "\x1B[36;1m";
|
||||
public const string CONSOLE_WHITE = "\x1B[37;1m";
|
||||
public const string CONSOLE_YELLOW_ON_RED_BACKGROUND = "\x1B[93;41m";
|
||||
public const string CONSOLE_RESET = "\x1B[0m";
|
||||
|
||||
public static string FormatAnsi(this eConsoleColor extends, string data)
|
||||
{
|
||||
return string.Format("{0}{1}{2}", extends.ToAnsiPrefix(), data, CONSOLE_RESET);
|
||||
}
|
||||
|
||||
public static string ToAnsiPrefix(this eConsoleColor extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case eConsoleColor.Red:
|
||||
return CONSOLE_RED;
|
||||
case eConsoleColor.Green:
|
||||
return CONSOLE_GREEN;
|
||||
case eConsoleColor.Yellow:
|
||||
return CONSOLE_YELLOW;
|
||||
case eConsoleColor.Blue:
|
||||
return CONSOLE_BLUE;
|
||||
case eConsoleColor.Magenta:
|
||||
return CONSOLE_MAGENTA;
|
||||
case eConsoleColor.Cyan:
|
||||
return CONSOLE_CYAN;
|
||||
case eConsoleColor.White:
|
||||
return CONSOLE_WHITE;
|
||||
case eConsoleColor.YellowOnRed:
|
||||
return CONSOLE_YELLOW_ON_RED_BACKGROUND;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
|
||||
#if STANDARD
|
||||
|
||||
public static ConsoleColor ToForegroundConsoleColor(this eConsoleColor extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case eConsoleColor.Red:
|
||||
return ConsoleColor.Red;
|
||||
case eConsoleColor.Green:
|
||||
return ConsoleColor.Green;
|
||||
case eConsoleColor.Yellow:
|
||||
case eConsoleColor.YellowOnRed:
|
||||
return ConsoleColor.Yellow;
|
||||
case eConsoleColor.Blue:
|
||||
return ConsoleColor.Blue;
|
||||
case eConsoleColor.Magenta:
|
||||
return ConsoleColor.Magenta;
|
||||
case eConsoleColor.Cyan:
|
||||
return ConsoleColor.Cyan;
|
||||
case eConsoleColor.White:
|
||||
return ConsoleColor.White;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
|
||||
public static ConsoleColor ToBackgroundConsoleColor(this eConsoleColor extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case eConsoleColor.Red:
|
||||
case eConsoleColor.Green:
|
||||
case eConsoleColor.Yellow:
|
||||
case eConsoleColor.Blue:
|
||||
case eConsoleColor.Magenta:
|
||||
case eConsoleColor.Cyan:
|
||||
case eConsoleColor.White:
|
||||
return ConsoleColor.Black;
|
||||
|
||||
case eConsoleColor.YellowOnRed:
|
||||
return ConsoleColor.Red;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
310
ICD.Common.Utils/Csv/Csv.cs
Normal file
310
ICD.Common.Utils/Csv/Csv.cs
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.IO;
|
||||
#if SIMPLSHARP
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Root class that contains static functions for straightforward Csv parsing
|
||||
/// </summary>
|
||||
public static class Csv
|
||||
{
|
||||
/// <summary>
|
||||
/// The default Csv field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_CSV_DELIMITER = ',';
|
||||
|
||||
/// <summary>
|
||||
/// The default Csv text qualifier. This is used to encode strings that contain the field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_CSV_QUALIFIER = '"';
|
||||
|
||||
/// <summary>
|
||||
/// The default TSV (tab delimited file) field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_TSV_DELIMITER = '\t';
|
||||
|
||||
/// <summary>
|
||||
/// The default TSV (tabe delimited file) text qualifier. This is used to encode strings that contain the field delimiter.
|
||||
/// </summary>
|
||||
public const char DEFAULT_TSV_QUALIFIER = '"';
|
||||
|
||||
|
||||
#region Methods to read Csv data
|
||||
/// <summary>
|
||||
/// Parse a Csv stream into IEnumerable<string[]>, while permitting embedded newlines
|
||||
/// </summary>
|
||||
/// <param name="inStream">The stream to read</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An enumerable object that can be examined to retrieve rows from the stream.</returns>
|
||||
public static IEnumerable<string[]> ParseStream(IcdStreamReader inStream, CsvReaderSettings settings)
|
||||
{
|
||||
string line = "";
|
||||
int i = -1;
|
||||
List<string> list = new List<string>();
|
||||
var work = new StringBuilder();
|
||||
|
||||
// Ensure settings are non-null
|
||||
if (settings == null) {
|
||||
settings = CsvReaderSettings.CSV;
|
||||
}
|
||||
|
||||
// Begin reading from the stream
|
||||
while (i < line.Length || !inStream.EndOfStream)
|
||||
{
|
||||
// Consume the next character of data
|
||||
i++;
|
||||
if (i >= line.Length) {
|
||||
var newLine = inStream.ReadLine();
|
||||
line += newLine + settings.LineSeparator;
|
||||
}
|
||||
char c = line[i];
|
||||
|
||||
// Are we at a line separator? If so, yield our work and begin again
|
||||
if (String.Equals(line.Substring(i, settings.LineSeparator.Length), settings.LineSeparator)) {
|
||||
list.Add(work.ToString());
|
||||
yield return list.ToArray();
|
||||
list.Clear();
|
||||
work.Clear();
|
||||
if (inStream.EndOfStream)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Read in next line
|
||||
if (i + settings.LineSeparator.Length >= line.Length)
|
||||
{
|
||||
line = inStream.ReadLine() + settings.LineSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = line.Substring(i + settings.LineSeparator.Length);
|
||||
}
|
||||
i = -1;
|
||||
|
||||
// While starting a field, do we detect a text qualifier?
|
||||
}
|
||||
else if ((c == settings.TextQualifier) && (work.Length == 0))
|
||||
{
|
||||
// Our next task is to find the end of this qualified-text field
|
||||
int p2 = -1;
|
||||
while (p2 < 0) {
|
||||
|
||||
// If we don't see an end in sight, read more from the stream
|
||||
p2 = line.IndexOf(settings.TextQualifier, i + 1);
|
||||
if (p2 < 0) {
|
||||
|
||||
// No text qualifiers yet? Let's read more from the stream and continue
|
||||
work.Append(line.Substring(i + 1));
|
||||
i = -1;
|
||||
var newLine = inStream.ReadLine();
|
||||
if (String.IsNullOrEmpty(newLine) && inStream.EndOfStream)
|
||||
{
|
||||
break;
|
||||
}
|
||||
line = newLine + settings.LineSeparator;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append the text between the qualifiers
|
||||
work.Append(line.Substring(i + 1, p2 - i - 1));
|
||||
i = p2;
|
||||
|
||||
// If the user put in a doubled-up qualifier, e.g. `""`, insert a single one and continue
|
||||
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
|
||||
{
|
||||
work.Append(settings.TextQualifier);
|
||||
i++;
|
||||
p2 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Does this start a new field?
|
||||
}
|
||||
else if (c == settings.FieldDelimiter)
|
||||
{
|
||||
// Is this a null token, and do we permit null tokens?
|
||||
AddToken(list, work, settings);
|
||||
|
||||
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
|
||||
// Checks if the second parameter of the if statement will pass through successfully
|
||||
// e.g. `"bob", "mary", "bill"`
|
||||
if (i + 2 <= line.Length - 1)
|
||||
{
|
||||
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
work.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single row of data from a Csv line into an array of objects, while permitting embedded newlines
|
||||
/// DEPRECATED - Please use ParseStream instead.
|
||||
/// </summary>
|
||||
/// <param name="inStream">The stream to read</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
|
||||
public static string[] ParseMultiLine(IcdStreamReader inStream, CsvReaderSettings settings)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string[] array = null;
|
||||
while (!inStream.EndOfStream)
|
||||
{
|
||||
// Read in a line
|
||||
sb.Append(inStream.ReadLine());
|
||||
|
||||
// Does it parse?
|
||||
string s = sb.ToString();
|
||||
if (TryParseLine(s, out array, settings))
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
// We didn't succeed on the first try - our text must have an embedded newline in it.
|
||||
// Let's assume that we were in the middle of parsing a field when we encountered a newline,
|
||||
// and continue parsing.
|
||||
sb.Append(settings.LineSeparator);
|
||||
}
|
||||
|
||||
// Fails to parse - return the best array we were able to get
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a line from a Csv file and return an array of fields, or null if
|
||||
/// </summary>
|
||||
/// <param name="line">One line of text from a Csv file</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
|
||||
public static string[] ParseLine(string line, CsvReaderSettings settings)
|
||||
{
|
||||
string[] row;
|
||||
TryParseLine(line, out row, settings);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to parse a line of Csv data. Can only return false if an unterminated text qualifier is encountered.
|
||||
/// </summary>
|
||||
/// <returns>False if there was an unterminated text qualifier in the <paramref name="line"/></returns>
|
||||
/// <param name="line">The line of text to parse</param>
|
||||
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
|
||||
/// <param name="row">The array of fields found in the line</param>
|
||||
public static bool TryParseLine(string line, out string[] row, CsvReaderSettings settings)
|
||||
{
|
||||
// Ensure settings are non-null
|
||||
if (settings == null) settings = CsvReaderSettings.CSV;
|
||||
|
||||
// Okay, let's begin parsing
|
||||
List<string> list = new List<string>();
|
||||
var work = new StringBuilder();
|
||||
for (int i = 0; i < line.Length; i++)
|
||||
{
|
||||
char c = line[i];
|
||||
|
||||
// If we are starting a new field, is this field text qualified?
|
||||
if ((c == settings.TextQualifier) && (work.Length == 0))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int p2 = line.IndexOf(settings.TextQualifier, i + 1);
|
||||
|
||||
// If no closing qualifier is found, this string is broken; return failure.
|
||||
if (p2 < 0)
|
||||
{
|
||||
work.Append(line.Substring(i + 1));
|
||||
list.Add(work.ToString());
|
||||
row = list.ToArray();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Append this qualified string
|
||||
work.Append(line.Substring(i + 1, p2 - i - 1));
|
||||
i = p2;
|
||||
|
||||
// If this is a double quote, keep going!
|
||||
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
|
||||
{
|
||||
work.Append(settings.TextQualifier);
|
||||
i++;
|
||||
|
||||
// otherwise, this is a single qualifier, we're done
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Does this start a new field?
|
||||
}
|
||||
else if (c == settings.FieldDelimiter)
|
||||
{
|
||||
// Is this a null token, and do we permit null tokens?
|
||||
AddToken(list, work, settings);
|
||||
|
||||
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
|
||||
// Checks if the second parameter of the if statement will pass through successfully
|
||||
// e.g. "bob", "mary", "bill"
|
||||
if (i + 2 <= line.Length - 1)
|
||||
{
|
||||
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
work.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// We always add the last work as an element. That means `alice,bob,charlie,` will be four items long.
|
||||
AddToken(list, work, settings);
|
||||
row = list.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a single token to the list
|
||||
/// </summary>
|
||||
/// <param name="list">List.</param>
|
||||
/// <param name="work">Work.</param>
|
||||
/// <param name="settings">Settings.</param>
|
||||
private static void AddToken(List<string> list, StringBuilder work, CsvReaderSettings settings)
|
||||
{
|
||||
var s = work.ToString();
|
||||
if (settings.AllowNull && String.Equals(s, settings.NullToken, StringComparison.Ordinal))
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(s);
|
||||
}
|
||||
work.Length = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
100
ICD.Common.Utils/Csv/CsvReader.cs
Normal file
100
ICD.Common.Utils/Csv/CsvReader.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
public sealed class CsvReader : IEnumerable<string[]>, IDisposable
|
||||
{
|
||||
private readonly CsvReaderSettings m_Settings;
|
||||
private readonly IcdStreamReader m_Instream;
|
||||
|
||||
#region Public Variables
|
||||
|
||||
/// <summary>
|
||||
/// If the first row in the file is a header row, this will be populated
|
||||
/// </summary>
|
||||
private string[] m_Headers;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Construct a new Csv reader off a streamed source
|
||||
/// </summary>
|
||||
/// <param name="source">The stream source</param>
|
||||
/// <param name="settings">The Csv settings to use for this reader (Default: Csv)</param>
|
||||
public CsvReader(IcdStreamReader source, [CanBeNull] CsvReaderSettings settings)
|
||||
{
|
||||
m_Instream = source;
|
||||
m_Settings = settings ?? CsvReaderSettings.CSV;
|
||||
|
||||
// Do we need to parse headers?
|
||||
if (m_Settings.HeaderRowIncluded)
|
||||
{
|
||||
m_Headers = NextLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Headers = m_Settings.AssumedHeaders != null ? m_Settings.AssumedHeaders.ToArray() : null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterate through a Csv File
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return Lines().GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
IEnumerator<string[]> IEnumerable<string[]>.GetEnumerator()
|
||||
{
|
||||
return Lines().GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterate through all lines in this Csv file
|
||||
/// </summary>
|
||||
/// <returns>An array of all data columns in the line</returns>
|
||||
public IEnumerable<string[]> Lines()
|
||||
{
|
||||
return Csv.ParseStream(m_Instream, m_Settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the next line from the file.
|
||||
/// DEPRECATED -
|
||||
/// </summary>
|
||||
/// <returns>One line from the file.</returns>
|
||||
public string[] NextLine()
|
||||
{
|
||||
return Csv.ParseMultiLine(m_Instream, m_Settings);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Disposal
|
||||
/// <summary>
|
||||
/// Close our resources - specifically, the stream reader
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Instream.Dispose();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
105
ICD.Common.Utils/Csv/CsvReaderSettings.cs
Normal file
105
ICD.Common.Utils/Csv/CsvReaderSettings.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 2006 - 2018 Ted Spence, http://tedspence.com
|
||||
* License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Home page: https://github.com/tspence/csharp-csv-reader
|
||||
*/
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Csv
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings to configure how a Csv file is parsed
|
||||
/// </summary>
|
||||
public sealed class CsvReaderSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor picks Csv as the default
|
||||
/// </summary>
|
||||
public CsvReaderSettings()
|
||||
{
|
||||
FieldDelimiter = ',';
|
||||
TextQualifier = '"';
|
||||
ForceQualifiers = false;
|
||||
LineSeparator = IcdEnvironment.NewLine;
|
||||
NullToken = null;
|
||||
AllowNull = false;
|
||||
IgnoreDimensionErrors = true;
|
||||
AssumedHeaders = null;
|
||||
HeaderRowIncluded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character used to delimit individual fields in the Csv.
|
||||
/// </summary>
|
||||
public char FieldDelimiter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character used to enclose fields that contain the delimiter character.
|
||||
/// </summary>
|
||||
public char TextQualifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The separator used to indicate the end of a line in the Csv file.
|
||||
/// </summary>
|
||||
public string LineSeparator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to enclose all fields in the text qualifier character.
|
||||
/// </summary>
|
||||
public bool ForceQualifiers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to allow nulls to be rendered.
|
||||
/// Csv files by default do not permit null fields. If this field is set to true, all non-null fields
|
||||
/// will be enclosed by the text qualifier
|
||||
/// </summary>
|
||||
public bool AllowNull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If AllowNull is set to true, this token will be used to represent NULL values.
|
||||
/// </summary>
|
||||
public string NullToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first line of the Csv file will include the names of each field.
|
||||
/// </summary>
|
||||
public bool HeaderRowIncluded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If HeaderRowIncluded is false, use these values for the headers
|
||||
/// </summary>
|
||||
public List<string> AssumedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to allow parsing for files where each row has a different number of fields
|
||||
/// </summary>
|
||||
public bool IgnoreDimensionErrors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this value to true to ignore header errors when deserializing
|
||||
/// </summary>
|
||||
public bool IgnoreHeaderErrors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Standard comma-separated value (Csv) file settings
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings CSV = new CsvReaderSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Standard comma-separated value (Csv) file settings that permit rendering of NULL values
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings CSV_PERMIT_NULL = new CsvReaderSettings
|
||||
{
|
||||
AllowNull = true,
|
||||
NullToken = "NULL"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Standard tab-separated value (TSV) file settings
|
||||
/// </summary>
|
||||
public static readonly CsvReaderSettings TSV = new CsvReaderSettings
|
||||
{
|
||||
FieldDelimiter = '\t'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
@@ -11,44 +10,49 @@ namespace ICD.Common.Utils.Csv
|
||||
private const string DOUBLE_QUOTE_MARK = "\"\"";
|
||||
|
||||
private readonly IcdTextWriter m_Writer;
|
||||
private readonly CsvWriterSettings m_Settings;
|
||||
|
||||
private readonly string m_Seperator;
|
||||
private readonly string m_LineTerminator;
|
||||
private readonly bool m_AlwaysEscape;
|
||||
|
||||
/// <summary>
|
||||
/// Are we currently at the beginning of a new line?
|
||||
/// </summary>
|
||||
private bool m_NewLine;
|
||||
|
||||
#region Properties
|
||||
|
||||
private string Separator { get { return m_Settings.InsertSpaceAfterComma ? ", " : ","; } }
|
||||
|
||||
private string LineTerminator { get { return m_Settings.NewLineSequence; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public CsvWriter(IcdTextWriter writer, bool spaceAfterComma, bool alwaysEscape, string newline, params string[] header)
|
||||
public CsvWriter([NotNull] IcdTextWriter writer,
|
||||
[NotNull] CsvWriterSettings settings)
|
||||
{
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
if (settings == null)
|
||||
throw new ArgumentNullException("settings");
|
||||
|
||||
m_NewLine = true;
|
||||
m_Writer = writer;
|
||||
m_Seperator = spaceAfterComma ? ", " : ",";
|
||||
m_AlwaysEscape = alwaysEscape;
|
||||
m_LineTerminator = newline;
|
||||
|
||||
if(header.Any())
|
||||
AppendRow(header);
|
||||
m_Settings = settings;
|
||||
}
|
||||
|
||||
~CsvWriter()
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose();
|
||||
m_Writer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls ToString() for each item and adds the row to the builder.
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
[PublicAPI]
|
||||
public void AppendRow(params object[] row)
|
||||
{
|
||||
foreach (object value in row)
|
||||
AppendValue(value);
|
||||
AppendNewline();
|
||||
}
|
||||
#region Methods
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the row to the builder.
|
||||
@@ -82,9 +86,9 @@ namespace ICD.Common.Utils.Csv
|
||||
value = value ?? string.Empty;
|
||||
|
||||
if (!m_NewLine)
|
||||
m_Writer.WrappedTextWriter.Write(m_Seperator);
|
||||
m_Writer.WrappedTextWriter.Write(Separator);
|
||||
|
||||
if (m_AlwaysEscape || value.Contains(","))
|
||||
if (m_Settings.AlwaysEscapeEveryValue || value.Contains(","))
|
||||
{
|
||||
value = value.Replace(QUOTATION_MARK, DOUBLE_QUOTE_MARK);
|
||||
|
||||
@@ -107,31 +111,11 @@ namespace ICD.Common.Utils.Csv
|
||||
[PublicAPI]
|
||||
public void AppendNewline()
|
||||
{
|
||||
m_Writer.WrappedTextWriter.Write(m_LineTerminator);
|
||||
m_Writer.WrappedTextWriter.Write(LineTerminator);
|
||||
|
||||
m_NewLine = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Writer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <param name="header"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header)
|
||||
{
|
||||
return new CsvWriter(writer,
|
||||
settings.InsertSpaceAfterComma,
|
||||
settings.AlwaysEscapeEveryValue,
|
||||
settings.NewLineSequence,
|
||||
header);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
ICD.Common.Utils/CultureInfo.sqlite
Normal file
BIN
ICD.Common.Utils/CultureInfo.sqlite
Normal file
Binary file not shown.
48
ICD.Common.Utils/DateTimeUtils.cs
Normal file
48
ICD.Common.Utils/DateTimeUtils.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class DateTimeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the hour in 24 hour format to 12 hour format (1 through 12).
|
||||
/// </summary>
|
||||
/// <param name="hour"></param>
|
||||
/// <returns></returns>
|
||||
public static int To12Hour(int hour)
|
||||
{
|
||||
return MathUtils.Modulus(hour + 11, 12) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DateTime from the given number of milliseconds since the epoch (1970-01-01T00:00:00Z)
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">milliseconds since the epoch</param>
|
||||
/// <returns></returns>
|
||||
public static DateTime FromEpochMilliseconds(long milliseconds)
|
||||
{
|
||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DateTime from the given number of seconds since the epoch (1970-01-01T00:00:00Z)
|
||||
/// </summary>
|
||||
/// <param name="seconds">seconds since the epoch</param>
|
||||
/// <returns></returns>
|
||||
public static DateTime FromEpochSeconds(long seconds)
|
||||
{
|
||||
return FromEpochMilliseconds(seconds * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a DateTime for the given ISO-8601 string.
|
||||
/// </summary>
|
||||
/// <param name="iso"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime FromIso8601(string iso)
|
||||
{
|
||||
return DateTime.Parse(iso, null, DateTimeStyles.RoundtripKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
ICD.Common.Utils/Email/EmailValidation.cs
Normal file
14
ICD.Common.Utils/Email/EmailValidation.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
{
|
||||
public static class EmailValidation
|
||||
{
|
||||
private const string EMAIL_REGEX = @"(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|""(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*"")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])";
|
||||
|
||||
public static bool IsValidEmailAddress(string emailAddress)
|
||||
{
|
||||
return emailAddress != null && Regex.IsMatch(emailAddress, EMAIL_REGEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
#if STANDARD
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Net.Mail;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Email
|
||||
@@ -126,7 +125,7 @@ namespace ICD.Common.Utils.Email
|
||||
|
||||
public static class MailErrorCodeUtils
|
||||
{
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code)
|
||||
{
|
||||
switch (code)
|
||||
@@ -185,8 +184,7 @@ namespace ICD.Common.Utils.Email
|
||||
throw new ArgumentOutOfRangeException(nameof(code), code, null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if SIMPLSHARP
|
||||
#else
|
||||
public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code)
|
||||
{
|
||||
switch (code)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
#if SIMPLSHARP
|
||||
@@ -13,16 +14,21 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
public static class EnumUtils
|
||||
{
|
||||
private static readonly Dictionary<Type, object> s_EnumValuesCache;
|
||||
private static readonly Dictionary<Type, Dictionary<int, object>> s_EnumFlagsCache;
|
||||
private static readonly Dictionary<Type, object[]> s_EnumValuesCache;
|
||||
private static readonly SafeCriticalSection s_EnumValuesCacheSection;
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<object, object[]>> s_EnumFlagsCache;
|
||||
private static readonly SafeCriticalSection s_EnumFlagsCacheSection;
|
||||
|
||||
/// <summary>
|
||||
/// Static constructor.
|
||||
/// </summary>
|
||||
static EnumUtils()
|
||||
{
|
||||
s_EnumValuesCache = new Dictionary<Type, object>();
|
||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
|
||||
s_EnumValuesCache = new Dictionary<Type, object[]>();
|
||||
s_EnumValuesCacheSection = new SafeCriticalSection();
|
||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<object, object[]>>();
|
||||
s_EnumFlagsCacheSection = new SafeCriticalSection();
|
||||
}
|
||||
|
||||
#region Validation
|
||||
@@ -58,7 +64,7 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static bool IsEnum<T>(T value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
return value != null && IsEnumType(value.GetType());
|
||||
}
|
||||
|
||||
@@ -123,6 +129,30 @@ namespace ICD.Common.Utils
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of the values in the enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetNames<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetNames(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of the values in the enumeration.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetNames([NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValues(type).Select(v => v.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration.
|
||||
/// </summary>
|
||||
@@ -131,46 +161,51 @@ namespace ICD.Common.Utils
|
||||
public static IEnumerable<T> GetValues<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
// Reflection is slow and this method is called a lot, so we cache the results.
|
||||
object cache;
|
||||
if (!s_EnumValuesCache.TryGetValue(type, out cache))
|
||||
{
|
||||
cache = GetValuesUncached<T>().ToArray();
|
||||
s_EnumValuesCache[type] = cache;
|
||||
}
|
||||
|
||||
return cache as T[];
|
||||
return GetValues(typeof(T)).Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// Gets the values from an enumeration.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<int> GetValues(Type type)
|
||||
public static IEnumerable<object> GetValues(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type);
|
||||
// Reflection is slow and this method is called a lot, so we cache the results.
|
||||
return s_EnumValuesCacheSection.Execute(() => s_EnumValuesCache.GetOrAddNew(type, () => GetValuesUncached(type).ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// Gets the values from an enumeration except the 0 value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> GetValuesUncached<T>()
|
||||
public static IEnumerable<T> GetValuesExceptNone<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetValuesUncached(typeof(T)).Select(i => (T)(object)i);
|
||||
return GetValuesExceptNone(typeof(T)).Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration except the 0 value.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetValuesExceptNone([NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValues(type).Where(v => (int)v != 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<int> GetValuesUncached(Type type)
|
||||
private static IEnumerable<object> GetValuesUncached(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
@@ -182,34 +217,10 @@ namespace ICD.Common.Utils
|
||||
#if SIMPLSHARP
|
||||
.GetCType()
|
||||
#else
|
||||
.GetTypeInfo()
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
||||
.Select(x => x.GetValue(null))
|
||||
.Cast<int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration except the 0 value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetValuesExceptNone<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetFlagsExceptNone<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values from an enumeration except the 0 value without performing any caching. This is slow because of reflection.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<int> GetValuesExceptNone(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValues(type).Except(0);
|
||||
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
||||
.Select(x => x.GetValue(null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -330,24 +341,22 @@ namespace ICD.Common.Utils
|
||||
public static IEnumerable<T> GetFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
Type type = typeof(T);
|
||||
int valueInt = (int)(object)value;
|
||||
return GetFlags(typeof(T), value).Cast<T>();
|
||||
}
|
||||
|
||||
Dictionary<int, object> cache;
|
||||
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
|
||||
{
|
||||
cache = new Dictionary<int, object>();
|
||||
s_EnumFlagsCache[type] = cache;
|
||||
}
|
||||
|
||||
object flags;
|
||||
if (!cache.TryGetValue(valueInt, out flags))
|
||||
{
|
||||
flags = GetValues<T>().Where(e => HasFlag(value, e)).ToArray();
|
||||
cache[valueInt] = flags;
|
||||
}
|
||||
|
||||
return flags as T[];
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetFlags(Type type, object value)
|
||||
{
|
||||
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache
|
||||
.GetOrAddNew(type, () => new Dictionary<object, object[]>())
|
||||
.GetOrAddNew(value, () => GetValues(type)
|
||||
.Where(f => !HasMultipleFlags((int)f) && HasFlag(value, f))
|
||||
.ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,8 +367,7 @@ namespace ICD.Common.Utils
|
||||
public static IEnumerable<T> GetFlagsExceptNone<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
T allValue = GetFlagsAllValue<T>();
|
||||
return GetFlagsExceptNone(allValue);
|
||||
return GetFlagsExceptNone(typeof(T)).Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -371,7 +379,35 @@ namespace ICD.Common.Utils
|
||||
public static IEnumerable<T> GetFlagsExceptNone<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return GetFlags(value).Except(default(T));
|
||||
return GetFlagsExceptNone(typeof(T), value).Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum type except 0.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetFlagsExceptNone([NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
object allValue = GetFlagsAllValue(type);
|
||||
return GetFlagsExceptNone(type, allValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum except 0.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<object> GetFlagsExceptNone([NotNull] Type type, object value)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetFlags(type, value).Where(f => (int)f != 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -412,7 +448,20 @@ namespace ICD.Common.Utils
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type).Aggregate(0, (current, value) => current | value);
|
||||
return GetValues(type).Aggregate(0, (current, value) => current | (int)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enum value of the given type with the inverse of the flags set
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T GetInverseFlags<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
int output = GetFlagsAllValue(typeof(T)) & ~(int)(object)value;
|
||||
return (T)Enum.ToObject(typeof(T), output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -428,6 +477,17 @@ namespace ICD.Common.Utils
|
||||
return HasFlags(value, flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlag(object value, object flag)
|
||||
{
|
||||
return HasFlag((int)value, (int)flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
@@ -717,5 +777,39 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Formatting
|
||||
|
||||
/// <summary>
|
||||
/// Builds a comma delimited string of the defined enum flags, followed by the numeric remainder.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToStringUndefined<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (!IsFlagsEnum<T>())
|
||||
return value.ToString();
|
||||
|
||||
long remainder = (int)(object)value;
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
string format = "{0}";
|
||||
|
||||
foreach (T flag in GetFlagsExceptNone(value))
|
||||
{
|
||||
output.AppendFormat(format, flag);
|
||||
remainder -= (int)(object)flag;
|
||||
format = ", {0}";
|
||||
}
|
||||
|
||||
if (remainder != 0)
|
||||
output.AppendFormat(", {0}", remainder);
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public sealed class BoolEventArgs : GenericEventArgs<bool>
|
||||
{
|
||||
@@ -20,4 +24,18 @@
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class BoolEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<BoolEventArgs> extends, object sender, bool data)
|
||||
{
|
||||
extends.Raise(sender, new BoolEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using ICD.Common.Properties;
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
@@ -9,4 +11,18 @@ namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class CharEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<CharEventArgs> extends, object sender, char data)
|
||||
{
|
||||
extends.Raise(sender, new CharEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
@@ -13,4 +15,18 @@ namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class DateTimeEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<DateTimeEventArgs> extends, object sender, DateTime data)
|
||||
{
|
||||
extends.Raise(sender, new DateTimeEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
ICD.Common.Utils/EventArguments/DateTimeNullableEventArgs.cs
Normal file
31
ICD.Common.Utils/EventArguments/DateTimeNullableEventArgs.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public sealed class DateTimeNullableEventArgs : GenericEventArgs<DateTime?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public DateTimeNullableEventArgs(DateTime? data) : base(data)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class DateTimeNullableEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<DateTimeNullableEventArgs> extends, object sender, DateTime? data)
|
||||
{
|
||||
extends.Raise(sender, new DateTimeNullableEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public sealed class FloatEventArgs : GenericEventArgs<float>
|
||||
{
|
||||
@@ -11,4 +15,18 @@
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class FloatEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<FloatEventArgs> extends, object sender, float data)
|
||||
{
|
||||
extends.Raise(sender, new FloatEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public class GenericEventArgs<T> : EventArgs, IGenericEventArgs<T>
|
||||
{
|
||||
private readonly T m_Data;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped data associated with the event.
|
||||
/// </summary>
|
||||
public T Data { get; private set; }
|
||||
object IGenericEventArgs.Data { get { return Data; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped data associated with the event.
|
||||
/// </summary>
|
||||
public T Data { get { return m_Data; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
@@ -15,7 +24,21 @@ namespace ICD.Common.Utils.EventArguments
|
||||
/// <param name="data"></param>
|
||||
public GenericEventArgs(T data)
|
||||
{
|
||||
Data = data;
|
||||
m_Data = data;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GenericEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise<T>([CanBeNull]this EventHandler<GenericEventArgs<T>> extends, object sender, T data)
|
||||
{
|
||||
extends.Raise(sender, new GenericEventArgs<T>(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public interface IGenericEventArgs<T>
|
||||
public interface IGenericEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the wrapped data associated with the event.
|
||||
/// </summary>
|
||||
T Data { get; }
|
||||
object Data { get; }
|
||||
}
|
||||
|
||||
public interface IGenericEventArgs<T> : IGenericEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the wrapped data associated with the event.
|
||||
/// </summary>
|
||||
new T Data { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public sealed class IntEventArgs : GenericEventArgs<int>
|
||||
{
|
||||
@@ -10,4 +14,18 @@
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class IntEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<IntEventArgs> extends, object sender, int data)
|
||||
{
|
||||
extends.Raise(sender, new IntEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
public sealed class StringEventArgs : GenericEventArgs<string>
|
||||
{
|
||||
@@ -19,4 +23,18 @@
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<StringEventArgs> extends, object sender, string data)
|
||||
{
|
||||
extends.Raise(sender, new StringEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using ICD.Common.Properties;
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
@@ -13,4 +15,18 @@ namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class UShortEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<UShortEventArgs> extends, object sender, ushort data)
|
||||
{
|
||||
extends.Raise(sender, new UShortEventArgs(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.EventArguments
|
||||
{
|
||||
@@ -26,4 +27,19 @@ namespace ICD.Common.Utils.EventArguments
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class XmlRecursionEventArgsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="outer"></param>
|
||||
/// <param name="path"></param>
|
||||
public static void Raise([CanBeNull]this EventHandler<XmlRecursionEventArgs> extends, object sender, string outer, string[] path)
|
||||
{
|
||||
extends.Raise(sender, new XmlRecursionEventArgs(outer, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static string GetPath(this Assembly extends)
|
||||
public static string GetPath([NotNull]this Assembly extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -29,17 +29,16 @@ namespace ICD.Common.Utils.Extensions
|
||||
#endif
|
||||
.CodeBase;
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
#if STANDARD
|
||||
#if !SIMPLSHARP
|
||||
if (string.IsNullOrEmpty(path))
|
||||
path = extends.Location;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
const string prefix = @"file:/";
|
||||
if (path != null && path.StartsWith(prefix))
|
||||
{
|
||||
const string prefix = @"file:///";
|
||||
if (path.StartsWith(prefix))
|
||||
path = path.Substring(prefix.Length);
|
||||
Uri uri = new Uri(path);
|
||||
path = uri.LocalPath;
|
||||
}
|
||||
|
||||
return IcdFile.Exists(path) ? path : null;
|
||||
@@ -51,7 +50,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static DateTime GetCreationTime(this Assembly extends)
|
||||
public static DateTime GetCreationTime([NotNull]this Assembly extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -66,7 +65,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string GetInformationalVersion(this Assembly extends)
|
||||
public static string GetInformationalVersion([NotNull]this Assembly extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -85,7 +84,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="version"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryGetInformationalVersion(this Assembly extends, out string version)
|
||||
public static bool TryGetInformationalVersion([NotNull]this Assembly extends, out string version)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
45
ICD.Common.Utils/Extensions/CollectionExtensions.cs
Normal file
45
ICD.Common.Utils/Extensions/CollectionExtensions.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the collection and adds the given range of items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="range"></param>
|
||||
public static void SetRange<T>([NotNull] this ICollection<T> extends, [NotNull] IEnumerable<T> range)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (range == null)
|
||||
throw new ArgumentNullException("range");
|
||||
|
||||
extends.Clear();
|
||||
extends.AddRange(range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given range of items to the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="range"></param>
|
||||
public static void AddRange<T>([NotNull] this ICollection<T> extends, [NotNull] IEnumerable<T> range)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (range == null)
|
||||
throw new ArgumentNullException("range");
|
||||
|
||||
foreach (T item in range)
|
||||
extends.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
ICD.Common.Utils/Extensions/CultureInfoExtensions.cs
Normal file
91
ICD.Common.Utils/Extensions/CultureInfoExtensions.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class CultureInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the given culture uses a 24 hour time format.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Uses24HourFormat([NotNull]this CultureInfo extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.DateTimeFormat.ShortTimePattern.Contains("H");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the time patterns for the given culture to use 12 hour time.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
public static void ConvertTo12HourCulture([NotNull]this CultureInfo extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.IsReadOnly)
|
||||
throw new InvalidOperationException("Culture is readonly");
|
||||
|
||||
DateTimeFormatInfo dateTimeFormat = extends.DateTimeFormat;
|
||||
|
||||
dateTimeFormat.FullDateTimePattern = To12HourPattern(dateTimeFormat.FullDateTimePattern);
|
||||
dateTimeFormat.LongTimePattern = To12HourPattern(dateTimeFormat.LongTimePattern);
|
||||
dateTimeFormat.ShortTimePattern = To12HourPattern(dateTimeFormat.ShortTimePattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the time patterns for the given culture to use 24 hour time.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
public static void ConvertTo24HourCulture([NotNull]this CultureInfo extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.IsReadOnly)
|
||||
throw new InvalidOperationException("Culture is readonly");
|
||||
|
||||
DateTimeFormatInfo dateTimeFormat = extends.DateTimeFormat;
|
||||
|
||||
dateTimeFormat.FullDateTimePattern = To24HourPattern(dateTimeFormat.FullDateTimePattern);
|
||||
dateTimeFormat.LongTimePattern = To24HourPattern(dateTimeFormat.LongTimePattern);
|
||||
dateTimeFormat.ShortTimePattern = To24HourPattern(dateTimeFormat.ShortTimePattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given format pattern to use 24 hour time.
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
private static string To24HourPattern(string pattern)
|
||||
{
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
pattern = pattern.Replace(" tt", "");
|
||||
|
||||
return pattern.Replace("h", "H");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given format pattern to use 12 hour time.
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
private static string To12HourPattern(string pattern)
|
||||
{
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
if (!pattern.Contains("t"))
|
||||
pattern = pattern + " tt";
|
||||
|
||||
return pattern.Replace("H", "h");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
@@ -37,14 +39,29 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, params DateTime[] times)
|
||||
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, [NotNull] params DateTime[] times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("times");
|
||||
|
||||
return target.NextEarliestTime(inclusive, (IEnumerable<DateTime>)times);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest DateTime to the target time that is greater than the target time
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? NextEarliestTime(this DateTime target, bool inclusive, [NotNull] IEnumerable<DateTime> times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("times");
|
||||
|
||||
DateTime earliestTime;
|
||||
bool success = times.OrderBy(dt => dt).TryFirst(dt => inclusive ? target <= dt : target < dt, out earliestTime);
|
||||
return success ? earliestTime : (DateTime?) null;
|
||||
return success ? earliestTime : (DateTime?)null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,14 +71,168 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, params DateTime[] times)
|
||||
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, [NotNull] params DateTime[] times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("null");
|
||||
throw new ArgumentNullException("times");
|
||||
|
||||
return target.PreviousLatestTime(inclusive, (IEnumerable<DateTime>)times);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest DateTime to the target time that is less than the target time
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="inclusive">Whether or not to include times equal to the target time</param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime? PreviousLatestTime(this DateTime target, bool inclusive, [NotNull] IEnumerable<DateTime> times)
|
||||
{
|
||||
if (times == null)
|
||||
throw new ArgumentNullException("times");
|
||||
|
||||
DateTime latestTime;
|
||||
bool success = times.OrderByDescending(dt => dt).TryFirst(dt => inclusive ? target >= dt : target > dt, out latestTime);
|
||||
return success ? latestTime : (DateTime?) null;
|
||||
return success ? latestTime : (DateTime?)null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DateTime representing the very start of the current day.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime StartOfDay(this DateTime extends)
|
||||
{
|
||||
return new DateTime(extends.Year, extends.Month, extends.Day, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DateTime representing the very end of the current day.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime EndOfDay(this DateTime extends)
|
||||
{
|
||||
return extends.StartOfDay() + new TimeSpan(24, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the give date in ISO-8601 format.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToIso(this DateTime extends)
|
||||
{
|
||||
return extends.ToString("o");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the given date in unix timestamp format.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static double ToUnixTimestamp(this DateTime extends)
|
||||
{
|
||||
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
TimeSpan diff = extends.ToUniversalTime() - origin;
|
||||
return Math.Floor(diff.TotalSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of years to the date, and checks if the day is still valid (basically only for leap days).
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="years"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddYearsAndWrap(this DateTime extends, int years)
|
||||
{
|
||||
years += extends.Year;
|
||||
|
||||
// need to check days in month due to leap year
|
||||
int daysInMonth = DateTime.DaysInMonth(years, extends.Month);
|
||||
int day = daysInMonth < extends.Day ? daysInMonth : extends.Day;
|
||||
return new DateTime(years, extends.Month, day, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of months to the date, wrapping every 12 months, and lowering the day in the month is not valid.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="months"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddMonthsAndWrap(this DateTime extends, int months)
|
||||
{
|
||||
months = MathUtils.Modulus(months + extends.Month - 1, 12) + 1;
|
||||
int daysInMonth = DateTime.DaysInMonth(extends.Year, months);
|
||||
int day = daysInMonth < extends.Day ? daysInMonth : extends.Day;
|
||||
|
||||
return new DateTime(extends.Year, months, day, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of days to the date, wrapping when the number of days exceeds the days in a month.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="days"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddDaysAndWrap(this DateTime extends, int days)
|
||||
{
|
||||
days = MathUtils.Modulus(days + extends.Day - 1, DateTime.DaysInMonth(extends.Year, extends.Month)) + 1;
|
||||
return new DateTime(extends.Year, extends.Month, days, extends.Hour, extends.Minute, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of hours to the time, wrapping every 24 hours without modifying the day.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="hours"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddHoursAndWrap(this DateTime extends, int hours)
|
||||
{
|
||||
hours = MathUtils.Modulus(hours + extends.Hour, 24);
|
||||
return new DateTime(extends.Year, extends.Month, extends.Day, hours, extends.Minute, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of hours to the time, wrapping within the current 12 hour span, without modifying the day.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="hours"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddHoursAndWrap12Hour(this DateTime extends, int hours)
|
||||
{
|
||||
int currentHour = extends.Hour;
|
||||
bool am = extends.Hour < 12;
|
||||
|
||||
int current12Hour = MathUtils.Modulus(currentHour, 12);
|
||||
int new12Hour = MathUtils.Modulus(current12Hour + hours, 12);
|
||||
|
||||
return am
|
||||
? new DateTime(extends.Year, extends.Month, extends.Day, new12Hour, extends.Minute, extends.Second, extends.Millisecond)
|
||||
: new DateTime(extends.Year, extends.Month, extends.Day, new12Hour + 12, extends.Minute, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given number of minutes to the time, wrapping every 60 minutes without modifying the hour.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="minutes"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime AddMinutesAndWrap(this DateTime extends, int minutes)
|
||||
{
|
||||
minutes = MathUtils.Modulus(minutes + extends.Minute, 60);
|
||||
return new DateTime(extends.Year, extends.Month, extends.Day, extends.Hour, minutes, extends.Second, extends.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total number of seconds since DateTime.MinValue
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetTotalSeconds(this DateTime extends)
|
||||
{
|
||||
return (extends - DateTime.MinValue).TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,29 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes the key from the dictionary, outputting the value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Remove<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key, out TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.TryGetValue(key, out value) && extends.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all of the given keys from the dictionary.
|
||||
/// </summary>
|
||||
@@ -14,11 +37,15 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="keys"></param>
|
||||
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TKey> keys)
|
||||
public static void RemoveAll<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<TKey> keys)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (keys == null)
|
||||
throw new ArgumentNullException("keys");
|
||||
|
||||
foreach (TKey key in keys)
|
||||
extends.Remove(key);
|
||||
}
|
||||
@@ -32,7 +59,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="value"></param>
|
||||
/// <returns>False if value is not found in the dictionary.</returns>
|
||||
[PublicAPI]
|
||||
public static bool RemoveValue<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
public static bool RemoveValue<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -49,7 +76,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
[PublicAPI]
|
||||
public static void RemoveAllValues<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
public static void RemoveAllValues<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -68,13 +95,15 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
[PublicAPI]
|
||||
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
|
||||
public static TValue GetDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.GetDefault(key, default(TValue));
|
||||
@@ -90,13 +119,16 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key, TValue defaultValue)
|
||||
public static TValue GetDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key,
|
||||
TValue defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
TValue value;
|
||||
@@ -112,16 +144,16 @@ 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)
|
||||
public static TValue GetOrAddDefault<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key, TValue defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
TValue value = extends.GetDefault(key, defaultValue);
|
||||
@@ -130,6 +162,65 @@ namespace ICD.Common.Utils.Extensions
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the key is present in the dictionary return the value, otherwise add a new value to the dictionary and return it.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key)
|
||||
where TValue : new()
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.GetOrAddNew(key, () => ReflectionUtils.CreateInstance<TValue>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the key is present in the dictionary return the value, otherwise add a new value to the dictionary and return it.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="valueFunc"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetOrAddNew<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] TKey key,
|
||||
[NotNull] Func<TValue> valueFunc)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (valueFunc == null)
|
||||
throw new ArgumentNullException("valueFunc");
|
||||
|
||||
TValue value;
|
||||
if (!extends.TryGetValue(key, out value))
|
||||
{
|
||||
value = valueFunc();
|
||||
extends.Add(key, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for the given value.
|
||||
/// </summary>
|
||||
@@ -140,7 +231,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The value does not exist in the dictionary.</exception>
|
||||
[PublicAPI]
|
||||
public static TKey GetKey<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
public static TKey GetKey<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -163,7 +254,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool TryGetKey<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value, out TKey key)
|
||||
public static bool TryGetKey<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends, TValue value,
|
||||
out TKey key)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -180,7 +272,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<TKey> GetKeys<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
public static IEnumerable<TKey> GetKeys<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -198,8 +291,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool Update<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IEnumerable<KeyValuePair<TKey, TValue>> other)
|
||||
public static bool Update<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -220,9 +313,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool Update<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IEnumerable<KeyValuePair<TKey, TValue>> other,
|
||||
IEqualityComparer<TValue> comparer)
|
||||
public static bool Update<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> other,
|
||||
[NotNull] IEqualityComparer<TValue> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -230,6 +323,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
bool change = false;
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> pair in other)
|
||||
@@ -253,7 +349,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -274,8 +371,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getKey"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TValue> items,
|
||||
Func<TValue, TKey> getKey)
|
||||
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<TValue> items,
|
||||
[NotNull] Func<TValue, TKey> getKey)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -299,8 +397,9 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getValue"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TKey> items,
|
||||
Func<TKey, TValue> getValue)
|
||||
public static void AddRange<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<TKey> items,
|
||||
[NotNull] Func<TKey, TValue> getValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -323,8 +422,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this Dictionary<TKey, TValue> extends,
|
||||
IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
public static void AddRange<TKey, TValue>([NotNull] this Dictionary<TKey, TValue> extends,
|
||||
[NotNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -345,12 +444,15 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IDictionary<TKey, TValue> other)
|
||||
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IDictionary<TKey, TValue> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
return extends.DictionaryEqual(other, EqualityComparer<TValue>.Default);
|
||||
}
|
||||
|
||||
@@ -364,13 +466,16 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="valueComparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IDictionary<TKey, TValue> other,
|
||||
IEqualityComparer<TValue> valueComparer)
|
||||
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IDictionary<TKey, TValue> other,
|
||||
[NotNull] IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (valueComparer == null)
|
||||
throw new ArgumentNullException("valueComparer");
|
||||
|
||||
@@ -387,20 +492,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="valueComparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IDictionary<TKey, TValue> other,
|
||||
Func<TValue, TValue, bool> valueComparer)
|
||||
public static bool DictionaryEqual<TKey, TValue>([NotNull] this IDictionary<TKey, TValue> extends,
|
||||
[NotNull] IDictionary<TKey, TValue> other,
|
||||
[NotNull] Func<TValue, TValue, bool> valueComparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (valueComparer == null)
|
||||
throw new ArgumentNullException("valueComparer");
|
||||
|
||||
if (extends == other)
|
||||
return true;
|
||||
if (other == null)
|
||||
return false;
|
||||
if (extends.Count != other.Count)
|
||||
return false;
|
||||
|
||||
@@ -412,6 +518,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (!valueComparer(kvp.Value, secondValue))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -424,7 +531,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> OrderByKey<TKey, TValue>(
|
||||
this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -440,7 +547,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<TValue> OrderValuesByKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
public static IEnumerable<TValue> OrderValuesByKey<TKey, TValue>(
|
||||
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -456,7 +564,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
public static Dictionary<TValue, List<TKey>> ToInverse<TKey, TValue>(
|
||||
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -477,5 +586,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns an enumerable of KeyValuePairs back into a dictionary
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
|
||||
[NotNull] this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
@@ -32,7 +34,51 @@ namespace ICD.Common.Utils.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum value as a
|
||||
/// Returns these enum flags, excluding the other enum flags.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T ExcludeFlags<T>(this T extends, T other)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return EnumUtils.ExcludeFlags(extends, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns these enum flags, including the other enum flags.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T IncludeFlags<T>(this T extends, T other)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return EnumUtils.IncludeFlags(extends, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum, with the other flags set or unset
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
public static T SetFlags<T>(this T extends, T other, bool set)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return set ?
|
||||
EnumUtils.IncludeFlags(extends, other) :
|
||||
EnumUtils.ExcludeFlags(extends, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum value as a ushort.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
@@ -43,5 +89,37 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
return (ushort)(object)extends;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a comma delimited string of the defined enum flags, followed by the numeric remainder.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToStringUndefined<T>(this T extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return EnumUtils.ToStringUndefined(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the next defined enum value for the enum type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static T CycleNext<T>(this T extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (EnumUtils.IsFlagsEnum(typeof(T)) && !EnumUtils.HasSingleFlag(extends))
|
||||
throw new InvalidOperationException(string.Format("Cannot cycle enum with multiple flags - {0}", extends));
|
||||
|
||||
IEnumerable<T> values = EnumUtils.GetValues<T>();
|
||||
IList<T> list = values as IList<T> ?? values.ToArray();
|
||||
|
||||
int index = list.BinarySearch(extends);
|
||||
|
||||
return index == list.Count - 1 ? list[0] : list[index + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
@@ -17,7 +13,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
public static void Raise(this EventHandler extends, object sender)
|
||||
public static void Raise([CanBeNull] this EventHandler extends, object sender)
|
||||
{
|
||||
if (extends != null)
|
||||
extends(sender, EventArgs.Empty);
|
||||
@@ -30,7 +26,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void Raise<T>(this EventHandler<T> extends, object sender, T args)
|
||||
public static void Raise<T>([CanBeNull]this EventHandler<T> extends, object sender, [NotNull]T args)
|
||||
where T : EventArgs
|
||||
{
|
||||
if (args == null)
|
||||
@@ -39,22 +35,5 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends != null)
|
||||
extends(sender, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross-platform shim for getting MethodInfo for the delegate.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo(this Delegate extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
#if SIMPLSHARP
|
||||
return extends.GetMethod();
|
||||
#else
|
||||
return extends.Method;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
using System;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the object value.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (serializer == null)
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
if (converter == null)
|
||||
throw new ArgumentNullException("converter");
|
||||
|
||||
JObject jObject = JObject.FromObject(value, serializer);
|
||||
jObject.WriteTo(extends, converter);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
// 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>
|
||||
/// Gets the current value as a Type.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Type GetValueAsType(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string value = extends.GetValueAsString();
|
||||
return Type.GetType(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an unsigned integer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static uint GetValueAsUInt(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (uint)(long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an integer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int GetValueAsInt(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (int)(long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string GetValueAsString(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.String || extends.TokenType == JsonToken.Null)
|
||||
return extends.Value as string;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.String);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a bool.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool GetValueAsBool(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Boolean)
|
||||
return (bool)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Boolean);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an enum.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T GetValueAsEnum<T>(this JsonReader extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.String)
|
||||
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
|
||||
|
||||
return (T)(object)extends.GetValueAsInt();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="reader"></param>
|
||||
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
return extends.DeserializeArray(reader, (s, r) => extends.Deserialize<TItem>(reader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="read"></param>
|
||||
public static IEnumerable<TItem> DeserializeArray<TItem>(this JsonSerializer extends, JsonReader reader,
|
||||
Func<JsonSerializer, JsonReader, TItem> read)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (read == null)
|
||||
throw new ArgumentNullException("read");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return Enumerable.Empty<TItem>();
|
||||
|
||||
if (reader.TokenType != JsonToken.StartArray)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
|
||||
|
||||
return DeserializeArrayIterator(extends, reader, read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="read"></param>
|
||||
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
|
||||
Func<JsonSerializer, JsonReader, TItem> read)
|
||||
{
|
||||
// Step into the first value
|
||||
reader.Read();
|
||||
|
||||
while (reader.TokenType != JsonToken.EndArray)
|
||||
{
|
||||
TItem output = read(serializer, reader);
|
||||
yield return output;
|
||||
|
||||
// Read out of the last value
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
328
ICD.Common.Utils/Extensions/JsonReaderExtensions.cs
Normal file
328
ICD.Common.Utils/Extensions/JsonReaderExtensions.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for working with JSON.
|
||||
/// </summary>
|
||||
public static class JsonReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
JsonSerializer serializer =
|
||||
#if SIMPLSHARP
|
||||
new JsonSerializer();
|
||||
#else
|
||||
JsonSerializer.CreateDefault();
|
||||
#endif
|
||||
return serializer.Deserialize<T>(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads through the current object token and calls the callback for each property value.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="readPropertyValue"></param>
|
||||
public static void ReadObject([NotNull] this JsonReader extends, [NotNull] JsonSerializer serializer,
|
||||
[NotNull] Action<string, JsonReader, JsonSerializer> readPropertyValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a Type.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
[CanBeNull]
|
||||
public static Type GetValueAsType([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Null)
|
||||
return null;
|
||||
|
||||
string value = extends.GetValueAsString();
|
||||
return Type.GetType(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an unsigned integer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static uint GetValueAsUInt([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (uint)(long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an integer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int GetValueAsInt([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (int)(long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a long.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static long GetValueAsLong([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an unsigned long.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static ulong GetValueAsULong([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (ulong)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a float.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static float GetValueAsFloat([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Float || extends.TokenType == JsonToken.Integer)
|
||||
return (float)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a double.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static double GetValueAsDouble([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Float || extends.TokenType == JsonToken.Integer)
|
||||
return (double)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string GetValueAsString([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
#if !SIMPLSHARP
|
||||
// Newer versions of NewtonSoft try to be helpful and interpret strings as DateTimes without any consideration for different DateTime formats.
|
||||
if (extends.TokenType == JsonToken.Date && extends.DateParseHandling != DateParseHandling.None)
|
||||
throw new InvalidOperationException("DateParseHandling needs to be set to None");
|
||||
#endif
|
||||
|
||||
if (!extends.TokenType.IsPrimitive())
|
||||
throw new FormatException("Expected primitive token type but got " + extends.TokenType);
|
||||
|
||||
return extends.Value == null ? null : extends.Value.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a bool.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool GetValueAsBool([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Boolean)
|
||||
return (bool)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value,
|
||||
JsonToken.Boolean);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an enum.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T GetValueAsEnum<T>([NotNull] this JsonReader extends)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.String)
|
||||
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
|
||||
|
||||
return (T)(object)extends.GetValueAsInt();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a guid.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Guid GetValueAsGuid([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string stringValue = extends.GetValueAsString();
|
||||
return new Guid(stringValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a date.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime GetValueAsDateTime([NotNull] this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
#if !SIMPLSHARP
|
||||
// Newer NewtonSoft tries to be helpful by assuming that anything that looks like a DateTime must be a date.
|
||||
if (extends.DateParseHandling != DateParseHandling.None)
|
||||
return (DateTime)extends.Value;
|
||||
#endif
|
||||
/*
|
||||
"\"\\/Date(1335205592410)\\/\"" .NET JavaScriptSerializer
|
||||
"\"\\/Date(1335205592410-0500)\\/\"" .NET DataContractJsonSerializer
|
||||
"2012-04-23T18:25:43.511Z" JavaScript built-in JSON object
|
||||
"2012-04-21T18:25:43-05:00" ISO 8601
|
||||
*/
|
||||
|
||||
string serial = extends.GetValueAsString();
|
||||
|
||||
Match match;
|
||||
if (RegexUtils.Matches(serial, @"Date\((?'date'\d+)(?'zone'(-|\+)\d+)?\)", out match))
|
||||
{
|
||||
long ms = long.Parse(match.Groups["date"].Value);
|
||||
DateTime dateTime = DateTimeUtils.FromEpochMilliseconds(ms);
|
||||
if (!match.Groups["zone"].Success)
|
||||
return dateTime;
|
||||
|
||||
// No TimeZoneInfo in CF, so now things get gross
|
||||
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
|
||||
serial = dateTime.ToIso() + match.Groups["zone"].Value;
|
||||
}
|
||||
|
||||
return DateTimeUtils.FromIso8601(serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
340
ICD.Common.Utils/Extensions/JsonSerializerExtensions.cs
Normal file
340
ICD.Common.Utils/Extensions/JsonSerializerExtensions.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class JsonSerializerExtensions
|
||||
{
|
||||
private const string PROPERTY_KEY = "k";
|
||||
private const string PROPERTY_VALUE = "v";
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="reader"></param>
|
||||
[CanBeNull]
|
||||
public static IEnumerable<TItem> DeserializeArray<TItem>([NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonReader reader)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
return extends.DeserializeArray(reader, (s, r) => extends.Deserialize<TItem>(reader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="read"></param>
|
||||
[CanBeNull]
|
||||
public static IEnumerable<TItem> DeserializeArray<TItem>([NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonReader reader,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TItem> read)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (read == null)
|
||||
throw new ArgumentNullException("read");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return null;
|
||||
|
||||
if (reader.TokenType != JsonToken.StartArray)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray,
|
||||
reader.TokenType));
|
||||
|
||||
// ToArray to ensure everything gets read before moving onto the next token
|
||||
return DeserializeArrayIterator(extends, reader, read).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="read"></param>
|
||||
[NotNull]
|
||||
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(
|
||||
[NotNull] JsonSerializer serializer, [NotNull] JsonReader reader,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TItem> read)
|
||||
{
|
||||
// Step into the first value
|
||||
reader.Read();
|
||||
|
||||
while (reader.TokenType != JsonToken.EndArray)
|
||||
{
|
||||
TItem output = read(serializer, reader);
|
||||
yield return output;
|
||||
|
||||
// Step out of the value
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
[CanBeNull]
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(
|
||||
[NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonReader reader)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
return extends.DeserializeDict(reader,
|
||||
(s, r) => s.Deserialize<TKey>(r),
|
||||
(s, r) => s.Deserialize<TValue>(r));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="readKey"></param>
|
||||
/// <param name="readValue"></param>
|
||||
[CanBeNull]
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDict<TKey, TValue>(
|
||||
[NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonReader reader,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TKey> readKey,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TValue> readValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (readKey == null)
|
||||
throw new ArgumentNullException("readKey");
|
||||
|
||||
if (readValue == null)
|
||||
throw new ArgumentNullException("readValue");
|
||||
|
||||
return extends.DeserializeArray(reader, (s, r) => s.DeserializeKeyValuePair(r, readKey, readValue));
|
||||
}
|
||||
|
||||
public static KeyValuePair<TKey, TValue> DeserializeKeyValuePair<TKey, TValue>(
|
||||
[NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonReader reader,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TKey> readKey,
|
||||
[NotNull] Func<JsonSerializer, JsonReader, TValue> readValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (readKey == null)
|
||||
throw new ArgumentNullException("readKey");
|
||||
|
||||
if (readValue == null)
|
||||
throw new ArgumentNullException("readValue");
|
||||
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject,
|
||||
reader.TokenType));
|
||||
|
||||
TKey key = default(TKey);
|
||||
TValue value = default(TValue);
|
||||
|
||||
// Step into the first property
|
||||
reader.Read();
|
||||
|
||||
while (reader.TokenType != JsonToken.EndObject)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.PropertyName)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.PropertyName,
|
||||
reader.TokenType));
|
||||
|
||||
string propertyName = (string)reader.Value;
|
||||
|
||||
// Step into the value
|
||||
reader.Read();
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case PROPERTY_KEY:
|
||||
key = readKey(extends, reader);
|
||||
break;
|
||||
|
||||
case PROPERTY_VALUE:
|
||||
value = readValue(extends, reader);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatException(string.Format("Unexpected property {0}", reader.Value));
|
||||
}
|
||||
|
||||
// Step out of the value
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
return new KeyValuePair<TKey, TValue>(key, value);
|
||||
}
|
||||
|
||||
/// <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>([NotNull] this JsonSerializer extends, [NotNull] JsonWriter writer,
|
||||
[CanBeNull] IEnumerable<TItem> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
extends.SerializeArray(writer, items, (s, w, item) => s.Serialize(w, item));
|
||||
}
|
||||
|
||||
/// <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>([NotNull] this JsonSerializer extends, [NotNull] JsonWriter writer,
|
||||
[CanBeNull] IEnumerable<TItem> items,
|
||||
[NotNull] Action<JsonSerializer, JsonWriter, TItem> write)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
if (write == null)
|
||||
throw new ArgumentNullException("write");
|
||||
|
||||
if (items == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteStartArray();
|
||||
{
|
||||
foreach (TItem item in items)
|
||||
write(extends, writer, item);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the given sequence of items 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 SerializeDict<TKey, TValue>([NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonWriter writer,
|
||||
[CanBeNull] IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
extends.SerializeDict(writer, items,
|
||||
(s, w, k) => s.Serialize(w, k),
|
||||
(s, w, v) => s.Serialize(w, v));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the given sequence of items to the writer.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="writeKey"></param>
|
||||
/// <param name="writeValue"></param>
|
||||
public static void SerializeDict<TKey, TValue>([NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonWriter writer,
|
||||
[CanBeNull] IEnumerable<KeyValuePair<TKey, TValue>> items,
|
||||
[NotNull] Action<JsonSerializer, JsonWriter, TKey> writeKey,
|
||||
[NotNull] Action<JsonSerializer, JsonWriter, TValue> writeValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
if (writeKey == null)
|
||||
throw new ArgumentNullException("writeKey");
|
||||
|
||||
if (writeValue == null)
|
||||
throw new ArgumentNullException("writeValue");
|
||||
|
||||
extends.SerializeArray(writer, items, (s, w, kvp) => s.SerializeKeyValuePair(w, kvp, writeKey, writeValue));
|
||||
}
|
||||
|
||||
public static void SerializeKeyValuePair<TKey, TValue>(
|
||||
[NotNull] this JsonSerializer extends,
|
||||
[NotNull] JsonWriter writer, KeyValuePair<TKey, TValue> kvp,
|
||||
[NotNull] Action<JsonSerializer, JsonWriter, TKey> writeKey,
|
||||
[NotNull] Action<JsonSerializer, JsonWriter, TValue> writeValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException("writer");
|
||||
|
||||
if (writeKey == null)
|
||||
throw new ArgumentNullException("writeKey");
|
||||
|
||||
if (writeValue == null)
|
||||
throw new ArgumentNullException("writeValue");
|
||||
|
||||
writer.WriteStartObject();
|
||||
{
|
||||
writer.WritePropertyName(PROPERTY_KEY);
|
||||
writeKey(extends, writer, kvp.Key);
|
||||
|
||||
writer.WritePropertyName(PROPERTY_VALUE);
|
||||
writeValue(extends, writer, kvp.Value);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
50
ICD.Common.Utils/Extensions/JsonTokenExtensions.cs
Normal file
50
ICD.Common.Utils/Extensions/JsonTokenExtensions.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class JsonTokenExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the JsonToken respresents a single data value
|
||||
/// rather than a complex type such as an object or an array.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPrimitive(this JsonToken extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case JsonToken.None:
|
||||
case JsonToken.StartObject:
|
||||
case JsonToken.StartArray:
|
||||
case JsonToken.StartConstructor:
|
||||
case JsonToken.EndObject:
|
||||
case JsonToken.EndArray:
|
||||
case JsonToken.EndConstructor:
|
||||
case JsonToken.Undefined:
|
||||
return false;
|
||||
|
||||
case JsonToken.PropertyName:
|
||||
case JsonToken.Comment:
|
||||
case JsonToken.Raw:
|
||||
case JsonToken.Integer:
|
||||
case JsonToken.Float:
|
||||
case JsonToken.String:
|
||||
case JsonToken.Boolean:
|
||||
case JsonToken.Null:
|
||||
case JsonToken.Date:
|
||||
case JsonToken.Bytes:
|
||||
return true;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
ICD.Common.Utils/Extensions/JsonWriterExtensions.cs
Normal file
158
ICD.Common.Utils/Extensions/JsonWriterExtensions.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
#if NETFRAMEWORK
|
||||
extern alias RealNewtonsoft;
|
||||
using RealNewtonsoft.Newtonsoft.Json;
|
||||
#else
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class JsonWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the DateTime as an ISO-8601 string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="dateTime"></param>
|
||||
public static void WriteDateTime([NotNull] this JsonWriter extends, DateTime dateTime)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string iso = dateTime.ToIso();
|
||||
|
||||
// Remove redundant ms
|
||||
iso = iso.Replace(".0000000", "");
|
||||
|
||||
extends.WriteValue(iso);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the type value.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="type"></param>
|
||||
[PublicAPI]
|
||||
public static void WriteType([NotNull] this JsonWriter extends, [CanBeNull] Type type)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
extends.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
string name = type.GetMinimalName();
|
||||
extends.WriteValue(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, object value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, string value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, DateTime value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteDateTime(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, bool value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, int value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, Guid value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the property name and value to the writer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void WriteProperty([NotNull]this JsonWriter extends, string propertyName, Type value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.WritePropertyName(propertyName);
|
||||
extends.WriteType(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Comparers;
|
||||
|
||||
@@ -11,14 +10,159 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// </summary>
|
||||
public static class ListExtensions
|
||||
{
|
||||
#region Add Sorted
|
||||
|
||||
/// <summary>
|
||||
/// Adds the items into a sorted list.
|
||||
/// Attempts to add the item to the sorted list.
|
||||
/// Returns false if the item already exists in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool AddSorted<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.AddSorted(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to add the item to the sorted list.
|
||||
/// Returns false if the item already exists in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TProp"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static bool AddSorted<T, TProp>([NotNull] this IList<T> extends, T item,
|
||||
[NotNull] Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
return extends.AddSorted(item, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to add the item to the sorted list.
|
||||
/// Returns false if the item already exists in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool AddSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
int index = extends.BinarySearch(item, comparer);
|
||||
if (index >= 0)
|
||||
return false;
|
||||
|
||||
index = ~index;
|
||||
extends.Insert(index, item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Remove Sorted
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the item from the sorted list.
|
||||
/// Returns false if the item does not exist in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool RemoveSorted<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.RemoveSorted(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the item from the sorted list.
|
||||
/// Returns false if the item does not exist in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TProp"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static bool RemoveSorted<T, TProp>([NotNull] this IList<T> extends, T item,
|
||||
[NotNull] Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
return extends.RemoveSorted(item, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the item from the sorted list.
|
||||
/// Returns false if the item does not exist in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool RemoveSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
int index = extends.BinarySearch(item, comparer);
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
extends.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert Sorted
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the items into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T>(this List<T> extends, IEnumerable<T> items)
|
||||
public static void InsertSorted<T>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -26,18 +170,19 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
extends.AddSorted(items, Comparer<T>.Default);
|
||||
extends.InsertSorted(items, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the items into a sorted list.
|
||||
/// Inserts the items into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="comparer"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T>(this List<T> extends, IEnumerable<T> items, IComparer<T> comparer)
|
||||
public static void InsertSorted<T>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items,
|
||||
[NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -48,11 +193,11 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
items.ForEach(i => extends.AddSorted(i, comparer));
|
||||
items.ForEach(i => extends.InsertSorted(i, comparer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the items into a sorted list.
|
||||
/// Inserts the items into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TProp"></typeparam>
|
||||
@@ -60,7 +205,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="items"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static void AddSorted<T, TProp>(this List<T> extends, IEnumerable<T> items, Func<T, TProp> predicate)
|
||||
public static void InsertSorted<T, TProp>([NotNull] this IList<T> extends, [NotNull] IEnumerable<T> items,
|
||||
[NotNull] Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -72,33 +218,55 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
extends.AddSorted(items, comparer);
|
||||
extends.InsertSorted(items, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item into a sorted list.
|
||||
/// Inserts the item into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public static int AddSorted<T>(this List<T> extends, T item)
|
||||
public static int InsertSorted<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.AddSorted(item, Comparer<T>.Default);
|
||||
return extends.InsertSorted(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item into a sorted list.
|
||||
/// Inserts the item into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TProp"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static int InsertSorted<T, TProp>([NotNull] this IList<T> extends, T item,
|
||||
[NotNull] Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
return extends.InsertSorted(item, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the item into a sorted list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
[PublicAPI]
|
||||
public static int AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer)
|
||||
public static int InsertSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -115,8 +283,48 @@ namespace ICD.Common.Utils.Extensions
|
||||
return index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Contains Sorted
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item into a sorted list.
|
||||
/// Returns true if the sorted list contains the given item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool ContainsSorted<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.ContainsSorted(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the sorted list contains the given item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool ContainsSorted<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return extends.BinarySearch(item, comparer) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the sorted list contains the given item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TProp"></typeparam>
|
||||
@@ -124,7 +332,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="item"></param>
|
||||
/// <param name="predicate"></param>
|
||||
[PublicAPI]
|
||||
public static int AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate)
|
||||
public static bool ContainsSorted<T, TProp>([NotNull] this IList<T> extends, T item,
|
||||
[NotNull] Func<T, TProp> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -133,48 +342,77 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
PredicateComparer<T, TProp> comparer = new PredicateComparer<T, TProp>(predicate);
|
||||
return extends.AddSorted(item, comparer);
|
||||
return extends.ContainsSorted(item, comparer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Binary Search
|
||||
|
||||
/// <summary>
|
||||
/// Pads the list to the given total length.
|
||||
/// Returns the index of the item in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="totalLength"></param>
|
||||
[PublicAPI]
|
||||
public static void PadRight<T>(this List<T> extends, int totalLength)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (totalLength < 0)
|
||||
throw new ArgumentOutOfRangeException("totalLength", "totalLength must be greater or equal to 0");
|
||||
|
||||
extends.PadRight(totalLength, default(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads the list to the given total length with the given item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="totalLength"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static void PadRight<T>(this List<T> extends, int totalLength, T item)
|
||||
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (totalLength < 0)
|
||||
throw new ArgumentOutOfRangeException("totalLength", "totalLength must be greater or equal to 0");
|
||||
|
||||
int pad = totalLength - extends.Count;
|
||||
if (pad <= 0)
|
||||
return;
|
||||
|
||||
extends.AddRange(Enumerable.Repeat(item, pad));
|
||||
return extends.BinarySearch(item, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the item in the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item, [NotNull] IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
// Array
|
||||
T[] array = extends as T[];
|
||||
if (array != null)
|
||||
return Array.BinarySearch(array, 0, array.Length, item, comparer);
|
||||
|
||||
// List
|
||||
List<T> list = extends as List<T>;
|
||||
if (list != null)
|
||||
return list.BinarySearch(item, comparer);
|
||||
|
||||
// IList
|
||||
int lo = 0;
|
||||
int hi = extends.Count - 1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = lo + ((hi - lo) >> 1);
|
||||
int order = comparer.Compare(extends[i], item);
|
||||
|
||||
if (order == 0)
|
||||
return i;
|
||||
|
||||
if (order < 0)
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
|
||||
return ~lo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user