-
Notifications
You must be signed in to change notification settings - Fork 74
/
Types.cs
1649 lines (1437 loc) · 52.6 KB
/
Types.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Waher.Events;
namespace Waher.Runtime.Inventory
{
/// <summary>
/// Static class that dynamically manages types and interfaces available in the runtime environment.
/// </summary>
public static class Types
{
private static readonly SortedDictionary<string, Type> types = new SortedDictionary<string, Type>();
private static readonly SortedDictionary<string, SortedDictionary<string, Type>> typesPerInterface = new SortedDictionary<string, SortedDictionary<string, Type>>();
private static readonly SortedDictionary<string, SortedDictionary<string, Type>> typesPerNamespace = new SortedDictionary<string, SortedDictionary<string, Type>>();
private static readonly SortedDictionary<string, SortedDictionary<string, bool>> namespacesPerNamespace = new SortedDictionary<string, SortedDictionary<string, bool>>();
private static readonly SortedDictionary<string, bool> rootNamespaces = new SortedDictionary<string, bool>();
private static readonly SortedDictionary<string, object> qualifiedNames = new SortedDictionary<string, object>();
private static readonly Dictionary<string, object> moduleParameters = new Dictionary<string, object>();
private static readonly Dictionary<string, MethodInfo> tryParseMethods = new Dictionary<string, MethodInfo>();
private static readonly Dictionary<Type, ConstructorInfo> defaultConstructors = new Dictionary<Type, ConstructorInfo>();
private static Assembly[] assemblies = null;
private static IModule[] modules = null;
private static readonly Type[] noTypes = new Type[0];
private static readonly object[] noParameters = new object[0];
private static readonly object synchObject = new object();
private static bool isInitialized = false;
static Types()
{
Log.Terminating += OnProcessExit;
}
/// <summary>
/// Gets a type, given its full name.
/// </summary>
/// <param name="FullName">Full name of type.</param>
/// <returns>Type, if found, null otherwise.</returns>
public static Type GetType(string FullName)
{
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (types.TryGetValue(FullName, out Type Result))
return Result;
}
if (FullName.EndsWith("[]"))
{
Type ElementType = GetType(FullName.Substring(0, FullName.Length - 2));
if (ElementType is null)
return null;
return ElementType.MakeArrayType();
}
return null;
}
/// <summary>
/// Checks if <paramref name="FullName"/> references a type in the inventory.
/// </summary>
/// <param name="FullName">Full name</param>
/// <returns>If <paramref name="FullName"/> references a type.</returns>
public static bool IsType(string FullName)
{
return !(GetType(FullName) is null);
}
private static Exception NotInitializedException()
{
return new Exception("Inventory engine not initialized properly. Make sure to call Waher.Runtime.Inventory.Types.Initialize() or Waher.Runtime.Inventory.Loader.TypesLoader.Initialize() first.");
}
/// <summary>
/// Gets all types implementing a given interface.
/// </summary>
/// <param name="InterfaceFullName">Full name of interface.</param>
/// <returns>Types implementing the interface.</returns>
public static Type[] GetTypesImplementingInterface(string InterfaceFullName)
{
Type[] Result;
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (!typesPerInterface.TryGetValue(InterfaceFullName, out SortedDictionary<string, Type> Types))
return new Type[0];
Result = new Type[Types.Count];
Types.Values.CopyTo(Result, 0);
}
return Result;
}
/// <summary>
/// Gets all types implementing a given interface.
/// </summary>
/// <param name="Interface">Interface</param>
/// <returns>Types implementing the interface.</returns>
public static Type[] GetTypesImplementingInterface(Type Interface)
{
return GetTypesImplementingInterface(Interface.FullName);
}
/// <summary>
/// Gets all types in a namespace. (Types in sub-namespaces are not included.)
/// </summary>
/// <param name="Namespace">Namespace.</param>
/// <returns>Types in the namespace.</returns>
public static Type[] GetTypesInNamespace(string Namespace)
{
Type[] Result;
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (!typesPerNamespace.TryGetValue(Namespace, out SortedDictionary<string, Type> Types))
return new Type[0];
Result = new Type[Types.Count];
Types.Values.CopyTo(Result, 0);
}
return Result;
}
/// <summary>
/// Gets the assembly reference of the first type found in a namespace.
/// </summary>
/// <param name="Namespace">Namespace.</param>
/// <returns>Assembly reference of first type found in a namespace. If no such type was found, null is returned.</returns>
public static Assembly GetFirstAssemblyReferenceInNamespace(string Namespace)
{
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (!typesPerNamespace.TryGetValue(Namespace, out SortedDictionary<string, Type> Types))
return null;
foreach (Type T in Types.Values)
return T.GetTypeInfo().Assembly;
}
return null;
}
/// <summary>
/// Gets an array of root namespaces.
/// </summary>
/// <returns>Array of root namespaces.</returns>
public static string[] GetRootNamespaces()
{
string[] Result;
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
Result = new string[rootNamespaces.Count];
rootNamespaces.Keys.CopyTo(Result, 0);
}
return Result;
}
/// <summary>
/// Checks if a name is a root namespace.
/// </summary>
/// <param name="Name">Name to check.</param>
/// <returns>If the name is a root namespace.</returns>
public static bool IsRootNamespace(string Name)
{
lock (synchObject)
{
return rootNamespaces.ContainsKey(Name);
}
}
/// <summary>
/// Gets an array of sub-namespaces to a given namespace.
/// </summary>
/// <param name="Namespace">Namespace</param>
/// <returns>Array of sub-namespaces.</returns>
public static string[] GetSubNamespaces(string Namespace)
{
string[] Result;
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (!namespacesPerNamespace.TryGetValue(Namespace, out SortedDictionary<string, bool> Namespaces))
return new string[0];
Result = new string[Namespaces.Count];
Namespaces.Keys.CopyTo(Result, 0);
}
return Result;
}
/// <summary>
/// Checks if a local name in <paramref name="LocalName"/> represents a subnamespace from the point of view of the namespace
/// in <paramref name="Namespace"/>.
/// </summary>
/// <param name="Namespace">Namespace.</param>
/// <param name="LocalName">Local name.</param>
/// <returns>If the local name represents a subnamespace.</returns>
public static bool IsSubNamespace(string Namespace, string LocalName)
{
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
if (!namespacesPerNamespace.TryGetValue(Namespace, out SortedDictionary<string, bool> Namespaces))
return false;
return Namespaces.ContainsKey(Namespace + "." + LocalName);
}
}
/// <summary>
/// Gets an array (possibly null) of qualified names relating to an unqualified name.
/// </summary>
/// <param name="UnqualifiedName">Unqualified name.</param>
/// <param name="QualifiedNames">Array of qualified names (null if none)</param>
/// <returns>If the unqualified name was recognized.</returns>
public static bool TryGetQualifiedNames(string UnqualifiedName, out string[] QualifiedNames)
{
lock (synchObject)
{
if (!qualifiedNames.TryGetValue(UnqualifiedName, out object Obj))
{
QualifiedNames = null;
return false;
}
if (Obj is string[] A)
{
QualifiedNames = A;
return true;
}
if (Obj is string s)
{
QualifiedNames = new string[] { s };
qualifiedNames[UnqualifiedName] = QualifiedNames;
return true;
}
if (Obj is SortedDictionary<string, bool> Sorted)
{
QualifiedNames = new string[Sorted.Count];
Sorted.Keys.CopyTo(QualifiedNames, 0);
qualifiedNames[UnqualifiedName] = QualifiedNames;
return true;
}
QualifiedNames = new string[] { Obj.ToString() };
qualifiedNames[UnqualifiedName] = QualifiedNames;
return true;
}
}
/// <summary>
/// Invalidates type caches. This method should be called after having loaded assemblies dynamically, to make sure any types,
/// interfaces and namespaces in the newly loaded assemblies are included.
/// </summary>
public static void Invalidate()
{
lock (synchObject)
{
isInitialized = false;
types.Clear();
typesPerInterface.Clear();
typesPerNamespace.Clear();
namespacesPerNamespace.Clear();
rootNamespaces.Clear();
qualifiedNames.Clear();
tryParseMethods.Clear();
}
EventHandler h = OnInvalidated;
if (!(h is null))
{
try
{
h(typeof(Types), EventArgs.Empty);
}
catch (Exception ex)
{
Log.Critical(ex);
}
}
}
/// <summary>
/// Event raised when the type cache has been invalidated. Can be used by code that themselves cache results and need to be updated
/// after new types are available.
/// </summary>
public static event EventHandler OnInvalidated = null;
private static void OnProcessExit(object Sender, EventArgs e)
{
StopAllModules();
}
/// <summary>
/// Stops all modules.
/// </summary>
public static Task StopAllModules()
{
return StopAllModules(null);
}
/// <summary>
/// Stops all modules.
/// </summary>
/// <param name="Order">Order in which modules should be stopped.
/// Default order is the reverse starting order, if no other order is provided.</param>
public static async Task StopAllModules(IComparer<IModule> Order)
{
if (isInitialized)
{
IModule[] Modules = (IModule[])Types.Modules?.Clone();
if (!(Modules is null))
{
if (Order is null)
Array.Reverse(Modules);
else
Array.Sort(Modules, Order);
foreach (IModule Module in Modules)
{
try
{
await Module.Stop();
}
catch (Exception ex)
{
Log.Critical(ex);
}
}
modules = new IModule[0];
}
lock (moduleParameters)
{
moduleParameters.Clear();
}
}
}
/// <summary>
/// Loaded modules.
/// </summary>
public static IModule[] Modules
{
get
{
lock (synchObject)
{
if (!isInitialized)
throw NotInitializedException();
return modules;
}
}
}
/// <summary>
/// Gets an array of loaded modules.
/// </summary>
/// <returns>Array of loaded modules.</returns>
public static IModule[] GetLoadedModules()
{
return GetLoadedModules(null);
}
/// <summary>
/// Gets an array of loaded modules.
/// </summary>
/// <param name="Order">Optional sort order of modules.</param>
/// <returns>Array of loaded modules.</returns>
public static IModule[] GetLoadedModules(IComparer<IModule> Order)
{
List<IModule> Modules = new List<IModule>();
IModule Module;
TypeInfo TI;
foreach (Type T in GetTypesImplementingInterface(typeof(IModule)))
{
TI = T.GetTypeInfo();
if (TI.IsAbstract || TI.IsInterface || TI.IsGenericTypeDefinition)
continue;
try
{
Module = (IModule)Instantiate(T);
Modules.Add(Module);
}
catch (Exception ex)
{
Log.Error("Unable to start module: " + ex.Message, T.FullName);
}
}
if (Order is null)
Order = new DependencyOrder();
Modules.Sort(Order);
return Modules.ToArray();
}
/// <summary>
/// Starts all loaded modules.
/// </summary>
/// <param name="Timeout">Timeout, in milliseconds.</param>
/// <returns>If all modules have been successfully started (true), or if at least one has not been
/// started within the time period defined by <paramref name="Timeout"/>.</returns>
public static Task<bool> StartAllModules(int Timeout)
{
return StartAllModules(Timeout, null);
}
/// <summary>
/// Starts all loaded modules.
/// </summary>
/// <param name="Timeout">Timeout, in milliseconds.</param>
/// <param name="Order">Order in which modules should be started.</param>
/// <returns>If all modules have been successfully started (true), or if at least one has not been
/// started within the time period defined by <paramref name="Timeout"/>.</returns>
public static async Task<bool> StartAllModules(int Timeout, IComparer<IModule> Order)
{
if (modules is null || modules.Length == 0)
{
IModule[] Modules = GetLoadedModules(Order);
bool Ok = true;
foreach (IModule Module2 in Modules)
{
try
{
if (!await StartModule(Module2, Timeout)) // 1 min timeout
Ok = false;
}
catch (Exception ex)
{
Log.Critical(ex);
Ok = false;
}
}
modules = Modules;
return Ok;
}
else
return true;
}
private static async Task<bool> StartModule(IModule Module, int TimeoutMilliseconds)
{
Type T = Module.GetType();
try
{
Log.Informational("Starting module.", T.FullName);
Task<int> TimeoutTask = Timeout(TimeoutMilliseconds);
Task<int> StartTask = StartModule2(Module);
Task<int> First = await Task.WhenAny<int>(StartTask, TimeoutTask);
if (await First != 0)
{
Log.Warning("Starting module takes too long time. Startup continues in the background.", T.FullName);
return false;
}
else
{
Log.Informational("Module started.", T.FullName);
return true;
}
}
catch (Exception ex)
{
Log.Error("Unable to start module: " + ex.Message, T.FullName);
return false;
}
}
private static async Task<int> StartModule2(IModule Module)
{
await Module.Start();
return 0;
}
private static async Task<int> Timeout(int Milliseconds)
{
await Task.Delay(Milliseconds);
return 1;
}
/// <summary>
/// Contains an empty array of types.
/// </summary>
public static Type[] NoTypes => noTypes;
/// <summary>
/// Contains an empty array of parameter values.
/// </summary>
public static object[] NoParameters => noParameters;
/// <summary>
/// Sets a module parameter. This parameter value will be accessible to modules when they are loaded.
/// </summary>
/// <param name="Name">Parameter name.</param>
/// <param name="Value">Parameter value.</param>
/// <exception cref="ArgumentException">If a module parameter with the same name is already defined.</exception>
/// <remarks>A module parameter that is disposed will still be available, but it can be reset by
/// another module parameter instance using the same name. The method to check if a parameter is
/// disposed, is if the object instance has a public property named "Disposeed" returning a bool
/// value indicating if the object has been disposed or not.</remarks>
public static void SetModuleParameter(string Name, object Value)
{
lock (moduleParameters)
{
if (moduleParameters.TryGetValue(Name, out object Value2) && !Value.Equals(Value2))
{
Type T = Value2.GetType();
PropertyInfo PI = T.GetRuntimeProperty("Disposed");
if (PI is null || PI.PropertyType != typeof(bool) || !(bool)PI.GetValue(Value2))
throw new ArgumentException("Module parameter already defined: " + Name, nameof(Name));
}
moduleParameters[Name] = Value;
}
}
/// <summary>
/// Tries to get a module parameter value.
/// </summary>
/// <param name="Name">Name of module parameter.</param>
/// <param name="Value">Value of module parameter.</param>
/// <returns>If a module parameter with the same name was found.</returns>
public static bool TryGetModuleParameter(string Name, out object Value)
{
lock (moduleParameters)
{
return moduleParameters.TryGetValue(Name, out Value);
}
}
/// <summary>
/// If the inventory has been initialized.
/// </summary>
public static bool IsInitialized => isInitialized;
/// <summary>
/// Initializes the inventory engine, registering types and interfaces available in <paramref name="Assemblies"/>.
/// </summary>
public static void Initialize(params Assembly[] Assemblies)
{
SortedDictionary<string, Type> Types;
SortedDictionary<string, Type> LastTypes = null;
Dictionary<string, Type> TypeNameAliases = null;
Dictionary<string, Assembly> NamespaceAliases = null;
IEnumerable<Type> AssemblyTypes;
Assembly A;
string InterfaceName;
string TypeName;
string Namespace;
string ParentNamespace;
string LastNamespace = string.Empty;
int i;
lock (synchObject)
{
if (isInitialized)
{
List<Assembly> NewAssemblies = new List<Assembly>();
List<Assembly> AllAssemblies = new List<Assembly>();
AllAssemblies.AddRange(assemblies);
foreach (Assembly Assembly in Assemblies)
{
if (!AllAssemblies.Contains(Assembly))
{
NewAssemblies.Add(Assembly);
AllAssemblies.Add(Assembly);
}
}
assemblies = AllAssemblies.ToArray();
Assemblies = NewAssemblies.ToArray();
}
else
{
CheckIncluded(ref Assemblies, typeof(Types).GetTypeInfo().Assembly);
CheckIncluded(ref Assemblies, typeof(int).GetTypeInfo().Assembly);
if (Array.IndexOf(Assemblies, A = typeof(Types).GetTypeInfo().Assembly) < 0)
{
int c = Assemblies.Length;
Array.Resize(ref Assemblies, c + 1);
Assemblies[c] = A;
}
assemblies = Assemblies;
}
foreach (Assembly Assembly in Assemblies)
{
foreach (NamespaceAliasAttribute Alias in Assembly.GetCustomAttributes<NamespaceAliasAttribute>())
{
if (NamespaceAliases is null)
NamespaceAliases = new Dictionary<string, Assembly>();
if (NamespaceAliases.ContainsKey(Alias.Namespace))
Log.Error("Namespace alias already registered.", Alias.Namespace, Assembly.FullName);
else
NamespaceAliases[Alias.Namespace] = Assembly;
}
try
{
AssemblyTypes = Assembly.ExportedTypes;
}
catch (ReflectionTypeLoadException ex)
{
foreach (Exception ex2 in ex.LoaderExceptions)
Log.Critical(ex2);
continue;
}
catch (Exception ex)
{
Log.Critical(ex, Assembly.FullName);
continue;
}
foreach (Type Type in AssemblyTypes)
{
TypeName = Type.FullName;
i = TypeName.LastIndexOf('`');
if (i > 0 && int.TryParse(TypeName.Substring(i + 1), out int j))
TypeName = TypeName.Substring(0, i);
types[TypeName] = Type;
i = TypeName.LastIndexOf('.');
if (i >= 0)
RegisterQualifiedName(TypeName.Substring(i + 1), TypeName);
try
{
TypeInfo TI = Type.GetTypeInfo();
foreach (Type Interface in TI.ImplementedInterfaces)
{
InterfaceName = Interface.FullName;
if (InterfaceName is null)
continue; // Generic interface.
if (!typesPerInterface.TryGetValue(InterfaceName, out Types))
{
Types = new SortedDictionary<string, Type>();
typesPerInterface[InterfaceName] = Types;
}
Types[TypeName] = Type;
}
foreach (TypeAliasAttribute Alias in TI.GetCustomAttributes<TypeAliasAttribute>(false))
{
if (TypeNameAliases is null)
TypeNameAliases = new Dictionary<string, Type>();
if (TypeNameAliases.ContainsKey(Alias.TypeName))
Log.Error("Type alias already registered.", Alias.TypeName, Type.FullName);
else
TypeNameAliases[Alias.TypeName] = Type;
}
}
catch (Exception)
{
// Implemented interfaces might not be accessible.
}
Namespace = Type.Namespace;
if (!(Namespace is null))
{
if (Namespace == LastNamespace)
Types = LastTypes;
else
{
if (!typesPerNamespace.TryGetValue(Namespace, out Types))
{
Types = new SortedDictionary<string, Type>();
typesPerNamespace[Namespace] = Types;
i = Namespace.LastIndexOf('.');
while (i >= 0)
{
RegisterQualifiedName(Namespace.Substring(i + 1), Namespace);
ParentNamespace = Namespace.Substring(0, i);
if (!namespacesPerNamespace.TryGetValue(ParentNamespace, out SortedDictionary<string, bool> Namespaces))
{
Namespaces = new SortedDictionary<string, bool>();
namespacesPerNamespace[ParentNamespace] = Namespaces;
}
else
{
if (Namespaces.ContainsKey(Namespace))
break;
}
Namespaces[Namespace] = true;
Namespace = ParentNamespace;
i = Namespace.LastIndexOf('.');
}
if (i < 0)
{
rootNamespaces[Namespace] = true;
RegisterQualifiedName(Namespace, Namespace);
}
}
LastNamespace = Namespace;
LastTypes = Types;
}
Types[TypeName] = Type;
}
}
}
if (!(TypeNameAliases is null))
{
foreach (KeyValuePair<string, Type> P in TypeNameAliases)
{
if (types.TryGetValue(P.Key, out Type T))
Log.Error("Type alias conflicts with registered type.", P.Key, T.FullName);
else
types[P.Key] = P.Value;
}
}
if (!(NamespaceAliases is null))
{
foreach (KeyValuePair<string, Assembly> P in NamespaceAliases)
{
LinkedList<KeyValuePair<string, string>> CheckNamespaces = new LinkedList<KeyValuePair<string, string>>();
CheckNamespaces.AddLast(new KeyValuePair<string, string>(P.Key, P.Value.GetName().Name));
while (!(CheckNamespaces.First is null))
{
string MapToNamespace = CheckNamespaces.First.Value.Value;
string Alias = CheckNamespaces.First.Value.Key;
CheckNamespaces.RemoveFirst();
if (typesPerNamespace.TryGetValue(MapToNamespace, out Types))
{
foreach (Type T in Types.Values)
{
string TypeAlias = T.FullName.Replace(MapToNamespace, Alias);
if (types.TryGetValue(TypeAlias, out Type T2))
Log.Error("Type alias conflicts with registered type.", TypeAlias, T2.FullName);
else
types[TypeAlias] = T;
}
}
if (namespacesPerNamespace.TryGetValue(MapToNamespace, out SortedDictionary<string, bool> Namespaces))
{
foreach (string Subnamespace in Namespaces.Keys)
{
i = Subnamespace.LastIndexOf('.');
if (i >= 0)
CheckNamespaces.AddLast(new KeyValuePair<string, string>(Alias + Subnamespace.Substring(i), Subnamespace));
}
}
}
}
}
isInitialized = true;
}
}
private static void RegisterQualifiedName(string UnqualifiedName, string QualifiedName)
{
if (qualifiedNames.TryGetValue(UnqualifiedName, out object Obj))
{
if (!(Obj is SortedDictionary<string, bool> Qualified))
{
if (Obj is string s)
{
Qualified = new SortedDictionary<string, bool>()
{
{ s, true }
};
}
else if (Obj is string[] A)
{
Qualified = new SortedDictionary<string, bool>();
foreach (string s2 in A)
Qualified[s2] = true;
}
else
{
Qualified = new SortedDictionary<string, bool>()
{
{ Obj.ToString(), true }
};
}
qualifiedNames[UnqualifiedName] = Qualified;
}
Qualified[QualifiedName] = true;
}
else
qualifiedNames[UnqualifiedName] = QualifiedName;
}
private static void CheckIncluded(ref Assembly[] Assemblies, Assembly A)
{
if (Array.IndexOf(Assemblies, A) < 0)
{
int c = Assemblies.Length;
Array.Resize(ref Assemblies, c + 1);
Assemblies[c] = A;
}
}
/// <summary>
/// Assemblies in the inventory.
/// </summary>
public static Assembly[] Assemblies => assemblies;
/// <summary>
/// Creates an object of a given type, given its full name.
/// </summary>
/// <param name="TypeName">Full type name.</param>
/// <param name="Parameters">Parameters to pass on to the constructor.</param>
/// <returns>Created object.</returns>
/// <exception cref="ArgumentException">If no type with the given name exists.</exception>
public static object CreateObject(string TypeName, params object[] Parameters)
{
Type T = GetType(TypeName)
?? throw new ArgumentException("Type not loaded: " + TypeName, nameof(TypeName));
return Activator.CreateInstance(T, Parameters);
}
/// <summary>
/// Gets a property value (or field value) from an object.
/// </summary>
/// <param name="Object">Object instance.</param>
/// <param name="PropertyName">Name of property (or field).</param>
/// <returns>Property (or field) value.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="Object"/> is null.</exception>
/// <exception cref="ArgumentException">If there is no property or field with the given name.</exception>
public static object GetProperty(object Object, string PropertyName)
{
if (Object is null)
throw new ArgumentNullException(nameof(Object));
Type T = Object.GetType();
PropertyInfo PI = T.GetRuntimeProperty(PropertyName);
if (!(PI is null))
{
if (PI.CanRead && PI.GetMethod.IsPublic)
return PI.GetValue(Object);
else
throw new ArgumentException("Property not readable or accessible.", nameof(PropertyName));
}
FieldInfo FI = T.GetRuntimeField(PropertyName);
if (!(FI is null))
{
if (FI.IsPublic)
return FI.GetValue(Object);
else
throw new ArgumentException("Field not accessible.", nameof(PropertyName));
}
throw new ArgumentException("Property (or field) not found.", nameof(PropertyName));
}
/// <summary>
/// Sets a property value (or field value) in an object.
/// </summary>
/// <param name="Object">Object instance.</param>
/// <param name="PropertyName">Name of property (or field).</param>
/// <param name="Value">Value to set.</param>
/// <exception cref="ArgumentNullException">If <paramref name="Object"/> is null.</exception>
/// <exception cref="ArgumentException">If there is no property or field with the given name.</exception>
public static void SetProperty(object Object, string PropertyName, object Value)
{
if (Object is null)
throw new ArgumentNullException(nameof(Object));
Type T = Object.GetType();
PropertyInfo PI = T.GetRuntimeProperty(PropertyName);
if (!(PI is null))
{
if (PI.CanWrite && PI.SetMethod.IsPublic)
PI.SetValue(Object, Value);
else
throw new ArgumentException("Property not writable or accessible.", nameof(PropertyName));
}
else
{
FieldInfo FI = T.GetRuntimeField(PropertyName);
if (!(FI is null))
{
if (FI.IsPublic)
FI.SetValue(Object, Value);
else
throw new ArgumentException("Field not accessible.", nameof(PropertyName));
}
else
throw new ArgumentException("Property (or field) not found.", nameof(PropertyName));
}
}
/// <summary>
/// Calls a method on an object.
/// </summary>
/// <param name="Object">Object instance.</param>
/// <param name="MethodName">Name of method.</param>
/// <param name="Arguments">Arguments to pass on to method.</param>
/// <returns>Result</returns>
/// <exception cref="ArgumentNullException">If <paramref name="Object"/> is null.</exception>
/// <exception cref="ArgumentException">If there is no method with the given name and argument types.</exception>
public static object Call(object Object, string MethodName, params object[] Arguments)
{
if (Object is null)
throw new ArgumentNullException(nameof(Object));
Type T = Object.GetType();
return Call(Object, T, MethodName, Arguments);
}
private static object Call(object Object, Type T, string MethodName, params object[] Arguments)
{
int i, c = Arguments.Length;
Type[] ArgumentTypes = new Type[c];
bool HasNull = false;
object Arg;
for (i = 0; i < c; i++)
{
Arg = Arguments[i];
if (Arg is null)
{
HasNull = true;
break;
}
else
ArgumentTypes[i] = Arg.GetType();
}
if (HasNull)
{
foreach (MethodInfo MI in T.GetRuntimeMethods())
{
if (!MI.IsPublic)
continue;