GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
ActivatorHelper.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using DocumentFormat.OpenXml.Bibliography;
18using MimeKit;
19using System;
20using System.Collections;
21using System.Collections.Generic;
22using System.Linq;
23using System.Reflection;
24using System.Text;
25using System.Threading.Tasks;
26
27namespace GenerallyPositive
28{
29 // written by grok
35 public static class ActivatorHelper
36 {
41 public static T CreateInstance<T>(int elementCount = 0)
42 {
43 return (T)CreateInstance(typeof(T), elementCount);
44 }
45
46 // Original method with object return type
47 /*
48 public static object CreateInstance(Type type, int elementCount = 0)
49 {
50 // ensure any interface passed in is converted to a real class with a public constructor
51 type = ConverterHelper.GetConcreteType(type);
52 Type factoryType = typeof(GPAL);
53
54 // Handle array & list<T> types
55 // Handle arrays and List<T> generically
56 bool isArray = type.IsArray;
57 bool isList = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
58
59 if (isArray || isList)
60 {
61 Type elementType = isArray ? type.GetElementType() : type.GetGenericArguments()[0];
62 if (elementType == null)
63 {
64 string stackTrace = Environment.StackTrace;
65 Exception ex = new InvalidOperationException($"Cannot determine element type for type [{type.Name}].\n\nStack trace:\n{stackTrace}");
66 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{type.Name}]", null, Enums.GPALObjectType.None, ex);
67 return null;
68 }
69
70 object collectionInstance = isArray
71 ? Array.CreateInstance(elementType, elementCount)
72 : Activator.CreateInstance(type);
73
74 IList list = isList ? (IList)collectionInstance : null;
75 Array array = isArray ? (Array)collectionInstance : null;
76
77 if (elementType.Name.StartsWith("GPAL", StringComparison.OrdinalIgnoreCase))
78 {
79 string baseName = elementType.IsGenericType ? elementType.Name.Split('`')[0] : elementType.Name;
80 string propertyName = baseName.StartsWith("GPAL") ? baseName.Substring(4) : baseName;
81 var property = factoryType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static);
82
83 if (property == null)
84 {
85 string stackTrace = Environment.StackTrace;
86 Exception ex = new InvalidOperationException($"Factory getter [GPAL.{propertyName}] not found for type [{elementType.Name}].\n\nStack trace:\n{stackTrace}");
87 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{elementType.Name}]", null, Enums.GPALObjectType.None, ex);
88 return null;
89 }
90
91 Type factoryReturnType = property.PropertyType;
92 if (!elementType.IsAssignableFrom(factoryReturnType) && !factoryReturnType.IsAssignableFrom(elementType))
93 {
94 string stackTrace = Environment.StackTrace;
95 Exception ex = new InvalidOperationException(
96 $"Factory getter [GPAL.{propertyName}] returns type [{factoryReturnType.Name}] which is not compatible with requested type [{elementType.Name}].\n\nStack trace:\n{stackTrace}"
97 );
98 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{elementType.Name}]", null, Enums.GPALObjectType.None, ex);
99 return null;
100 }
101
102 for (int i = 0; i < elementCount; i++)
103 {
104 object elementInstance = property.GetValue(null);
105 if (isArray)
106 array.SetValue(elementInstance, i);
107 else
108 list.Add(elementInstance);
109 }
110 }
111
112 return collectionInstance;
113 }
114
115 // Handle non-array, non-list types
116 if (type.Name.StartsWith("GPAL", StringComparison.OrdinalIgnoreCase))
117 {
118 string baseName = type.IsGenericType ? type.Name.Split('`')[0] : type.Name;
119 string propertyName = baseName.StartsWith("GPAL") ? baseName.Substring(4) : baseName;
120 var property = factoryType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static);
121 if (property == null)
122 {
123 string stackTrace = Environment.StackTrace;
124 Exception ex = new InvalidOperationException($"Factory getter [GPAL.{propertyName}] not found for type [{type.Name}].\n\nStack trace:\n{stackTrace}");
125 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{type.Name}]", null, Enums.GPALObjectType.None, ex);
126 return null;
127 }
128
129 Type factoryReturnType = property.PropertyType;
130 if (!type.IsAssignableFrom(factoryReturnType) && !factoryReturnType.IsAssignableFrom(type))
131 {
132 string stackTrace = Environment.StackTrace;
133 Exception ex = new InvalidOperationException(
134 $"Factory getter [GPAL.{propertyName}] returns type [{factoryReturnType.Name}] which is not compatible with requested type [{type.Name}].\n\nStack trace:\n{stackTrace}"
135 );
136 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{type.Name}]", null, Enums.GPALObjectType.None, ex);
137 return null;
138 }
139
140 return property.GetValue(null);
141 }
142
143 return Activator.CreateInstance(type);
144 }
145 */
150 public static dynamic CreateInstance(Type type, int elementCount = 0)
151 {
152 // Ensure any interface passed in is converted to a real class with a public constructor
153 type = ConverterHelper.GetConcreteType(type);
154 Type factoryType = typeof(GPAL);
155
156 bool isArray = type.IsArray;
157 bool isList = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
158
159 if (isArray || isList)
160 {
161 Type elementType = isArray ? type.GetElementType() : type.GetGenericArguments()[0];
162 if (elementType == null)
163 {
164 string stackTrace = Environment.StackTrace;
165 Exception ex = new InvalidOperationException($"Cannot determine element type for type [{type.Name}].\n\nStack trace:\n{stackTrace}");
166 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{type.Name}]", null, Enums.GPALObjectType.None, ex);
167 return null;
168 }
169
170 object collectionInstance = isArray
171 ? Array.CreateInstance(elementType, elementCount)
172 : Activator.CreateInstance(type);
173
174 IList list = isList ? (IList)collectionInstance : null;
175 Array array = isArray ? (Array)collectionInstance : null;
176
177 if (elementType.Name.StartsWith("GPAL", StringComparison.OrdinalIgnoreCase))
178 {
179 var property = GetGPALFactoryProperty(elementType);
180 if (property == null) return null;
181
182 for (int i = 0; i < elementCount; i++)
183 {
184 object elementInstance = property.GetValue(null);
185 if (isArray)
186 array.SetValue(elementInstance, i);
187 else
188 list.Add(elementInstance);
189 }
190 }
191
192 return collectionInstance;
193 }
194
195 // Handle single GPAL-based objects
196 if (type.Name.StartsWith("GPAL", StringComparison.OrdinalIgnoreCase))
197 {
198 var property = GetGPALFactoryProperty(type);
199 if (property == null) return null;
200
201 return property.GetValue(null);
202 }
203
204 // Fallback to parameterless constructor
205 // Try parameterless constructor first
206 var paramlessCtor = type.GetConstructor(Type.EmptyTypes);
207 if (paramlessCtor != null)
208 {
209 return paramlessCtor.Invoke(null);
210 }
211
212 // Find constructor with fewest parameters
213 if (type.IsValueType)
214 {
215 return Activator.CreateInstance(type); // Default value for value types (e.g., false for bool)
216 }
217
218 var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
219 if (constructors.Length == 0)
220 {
221 GPAL.PublishSimpleEvent(Enums.GPALEventType.ERROR, $"No constructors found for type [{type.FullName}]. Don't know what you want to do.");
222 throw new InvalidOperationException($"No constructors found for type [{type.FullName}]");
223 }
224
225 var targetCtor = constructors.OrderBy(c => c.GetParameters().Length).First();
226 var parameters = targetCtor.GetParameters();
227 var args = new object[parameters.Length];
228
229 // Provide default values for parameters
230 for (int i = 0; i < parameters.Length; i++)
231 {
232 var paramType = parameters[i].ParameterType;
233 if (paramType == typeof(string))
234 args[i] = null;
235 else if (paramType.IsValueType)
236 args[i] = Activator.CreateInstance(paramType);
237 else if (paramType.IsGenericType && (paramType.GetGenericTypeDefinition() == typeof(Dictionary<,>) || paramType.GetGenericTypeDefinition() == typeof(List<>)))
238 args[i] = Activator.CreateInstance(paramType);
239 else
240 args[i] = null;
241 }
242
243 return targetCtor.Invoke(args);
244 }
248 private static PropertyInfo GetGPALFactoryProperty(Type targetType)
249 {
250 if (targetType == null) return null;
251
252 string baseName = targetType.IsGenericType
253 ? targetType.Name.Split('`')[0]
254 : targetType.Name;
255
256 string propertyName = baseName.StartsWith("GPAL", StringComparison.OrdinalIgnoreCase)
257 ? baseName.Substring(4)
258 : baseName;
259
260 Type factoryType = typeof(GPAL);
261 var property = factoryType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
262
263 if (property == null)
264 {
265 string stackTrace = Environment.StackTrace;
266 Exception ex = new InvalidOperationException(
267 $"Factory getter [GPAL.{propertyName}] not found for type [{targetType.Name}].\n\nStack trace:\n{stackTrace}"
268 );
269 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{targetType.Name}]", null, Enums.GPALObjectType.None, ex);
270 return null;
271 }
272
273 Type factoryReturnType = property.PropertyType;
274 if (!targetType.IsAssignableFrom(factoryReturnType) && !factoryReturnType.IsAssignableFrom(targetType))
275 {
276 string stackTrace = Environment.StackTrace;
277 Exception ex = new InvalidOperationException(
278 $"Factory getter [GPAL.{propertyName}] returns type [{factoryReturnType.Name}] which is not compatible with requested type [{targetType.Name}].\n\nStack trace:\n{stackTrace}"
279 );
280 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Unable to instantiate a [{targetType.Name}]", null, Enums.GPALObjectType.None, ex);
281 return null;
282 }
283
284 return property;
285 }
286 }
287}
288