25 private static bool initialized;
26 private static readonly
object _lock =
new object();
28 private static readonly Dictionary<string, PlugInInstance> plugInClasses =
new Dictionary<string, PlugInInstance>();
29 private static readonly Dictionary<string, List<PlugInInstance>> queuePlugInClasses =
new Dictionary<string, List<PlugInInstance>>();
44 LoadReferencedAssemblies();
53 [ExcludeFromCodeCoverage]
54 private static void LoadReferencedAssemblies()
56 Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
59 foreach (Assembly assembly
in loadedAssemblies)
61 if (!assembly.IsDynamic && !
string.IsNullOrEmpty(assembly.Location))
65 RegisterPlugIns(assembly);
69 System.Diagnostics.Debug.WriteLine($
"Failed to register plugins from {assembly.Location}: {ex.Message}");
75 IEnumerable<string> allLoadedPaths = loadedAssemblies
76 .Where(a => !a.IsDynamic && !
string.IsNullOrEmpty(a.Location))
77 .Select(a => a.Location);
78 HashSet<string> loadedPaths =
new HashSet<string>(allLoadedPaths, StringComparer.InvariantCultureIgnoreCase);
84 List<string> referencedPaths;
87 referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory,
"*.dll")
88 .Where(path => !loadedPaths.Contains(path))
93 System.Diagnostics.Debug.WriteLine($
"Failed to enumerate assemblies in {AppDomain.CurrentDomain.BaseDirectory}: {ex.Message}");
97 foreach (
string path
in referencedPaths)
101 Assembly assembly = Assembly.LoadFrom(path);
102 RegisterPlugIns(assembly);
106 System.Diagnostics.Debug.WriteLine($
"Failed to load assembly: {path} - {ex.Message}");
116 internal static void InjectPlugins(List<Type> pluginTypes)
121 IEnumerable<Type> replacingPluginTypes = pluginTypes
122 .Where(t => t.GetCustomAttribute<NanoXlsxPlugInAttribute>() !=
null);
125 IEnumerable<Type> queuePluginTypes = pluginTypes
126 .Where(t => t.GetCustomAttributes<NanoXlsxQueuePlugInAttribute>().Any());
129 HandleReplacingPlugIns(replacingPluginTypes);
130 HandleQueuePlugIns(queuePluginTypes);
139 internal static void DisposePlugins()
143 plugInClasses.Clear();
144 queuePlugInClasses.Clear();
153 [ExcludeFromCodeCoverage]
154 private static void RegisterPlugIns(Assembly assembly)
156 IEnumerable<Type> replacingPlugInTypes = GetAssemblyPlugInsByType(assembly, typeof(NanoXlsxPlugInAttribute));
157 IEnumerable<Type> queuePlugInTypes = GetAssemblyPlugInsByType(assembly, typeof(NanoXlsxQueuePlugInAttribute));
158 HandleReplacingPlugIns(replacingPlugInTypes);
159 HandleQueuePlugIns(queuePlugInTypes);
169 [ExcludeFromCodeCoverage]
170 private static List<Type> GetAssemblyPlugInsByType(Assembly assembly, Type attributeType)
172 List<Type> plugInTypes =
new List<Type>();
173 Type plugInInterface = typeof(IPlugin);
174 Type[] allTypes = assembly.GetTypes();
176 for (
int i = 0; i < allTypes.Length; i++)
178 Type type = allTypes[i];
179 if (type.IsClass && !type.IsAbstract &&
180 plugInInterface.IsAssignableFrom(type) &&
181 type.GetCustomAttribute(attributeType) !=
null)
183 plugInTypes.Add(type);
193 [ExcludeFromCodeCoverage]
194 private static void HandleReplacingPlugIns(IEnumerable<Type> plugInTypes)
196 foreach (Type plugInType
in plugInTypes)
198 IEnumerable<NanoXlsxPlugInAttribute> attributes = plugInType.GetCustomAttributes<NanoXlsxPlugInAttribute>();
199 foreach (NanoXlsxPlugInAttribute attribute
in attributes)
201 if (plugInClasses.ContainsKey(attribute.
PlugInUUID))
209 else if (!plugInClasses.ContainsKey(attribute.
PlugInUUID))
221 private static void HandleQueuePlugIns(IEnumerable<Type> queuePlugInTypes)
223 foreach (Type plugInType
in queuePlugInTypes)
225 IEnumerable<NanoXlsxQueuePlugInAttribute> attributes = plugInType.GetCustomAttributes<NanoXlsxQueuePlugInAttribute>();
226 foreach (var attribute
in attributes)
228 if (!queuePlugInClasses.TryGetValue(attribute.QueueUUID, out var value))
230 value =
new List<PlugInInstance>();
231 queuePlugInClasses.Add(attribute.QueueUUID, value);
234 value.Add(
new PlugInInstance(attribute.PlugInUUID, attribute.PlugInOrder, plugInType));
238 foreach (KeyValuePair<
string, List<PlugInInstance>> entry
in queuePlugInClasses)
240 entry.Value.Sort((a, b) => a.Order.CompareTo(b.Order));
253 internal static T GetPlugIn<T>(
string plugInUUID, T fallBackInstance)
255 if (plugInClasses.TryGetValue(plugInUUID, out var plugIn))
257 return (T)Activator.CreateInstance(plugIn.Type,
true);
261 return fallBackInstance;
270 internal static bool HasQueuePlugins(
string queueUUID)
272 return queuePlugInClasses.TryGetValue(queueUUID, out var list) && list.Count > 0;
285 internal static T GetNextQueuePlugIn<T>(
string queueUUID,
string lastPlugInUUID, out
string currentPlugInUUID)
286 where T :
class, IPlugin
288 return GetNextQueuePlugIn<T>(queueUUID, lastPlugInUUID, out currentPlugInUUID,
null);
300 internal static T GetNextQueuePlugIn<T>(
string queueUUID,
string lastPlugInUUID, out
string currentPlugInUUID, IBaseWriter baseWriter)
301 where T :
class, IPlugin
303 currentPlugInUUID =
null;
304 if (!queuePlugInClasses.TryGetValue(queueUUID, out var plugInList) || plugInList.Count == 0)
310 if (lastPlugInUUID !=
null)
312 int lastIndex = plugInList.FindIndex(p => p.UUID == lastPlugInUUID);
318 startIndex = lastIndex + 1;
321 Type requestedType = typeof(T);
322 for (
int i = startIndex; i < plugInList.Count; i++)
324 PlugInInstance plugIn = plugInList[i];
325 if (!requestedType.IsAssignableFrom(plugIn.Type))
330 object instance =
null;
331 if (baseWriter !=
null)
333 ConstructorInfo contextualConstructor = plugIn.Type
334 .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
335 .FirstOrDefault(constructor =>
337 ParameterInfo[] parameters = constructor.GetParameters();
338 return parameters.Length == 1 && parameters[0].ParameterType == typeof(IBaseWriter);
340 if (contextualConstructor !=
null)
342 instance = contextualConstructor.Invoke(
new object[] { baseWriter });
346 if (instance ==
null)
348 instance = Activator.CreateInstance(plugIn.Type,
true);
351 currentPlugInUUID = plugIn.UUID;
361 private sealed class PlugInInstance
366 public string UUID {
get;
private set; }
370 public int Order {
get;
private set; }
375 public Type Type {
get;
private set; }
383 internal PlugInInstance(
string uuid,
int order, Type type)