using Disco.Data.Repository; using Newtonsoft.Json; using System; using System.Linq; namespace Disco.Services.Plugins { public class PluginFeatureManifest { public string Id { get; set; } public string Name { get; set; } public string TypeName { get; set; } public bool PrimaryFeature { get; set; } [JsonProperty] private string CategoryTypeName { get; set; } [JsonIgnore] public PluginManifest PluginManifest { get; private set; } [JsonIgnore] internal Type Type { get; private set; } [JsonIgnore] public Type CategoryType { get; private set; } internal bool Initialize(DiscoDataContext Database, PluginManifest pluginManifest) { PluginManifest = pluginManifest; if (Type == null) Type = PluginManifest.PluginAssembly.GetType(TypeName, true, true); if (CategoryType == null) CategoryType = Type.GetType(CategoryTypeName, true, true); using (var instance = CreateInstance()) { instance.Initialize(Database); } PluginsLog.LogInitializedPluginFeature(PluginManifest, this); return true; } public PluginFeature CreateInstance() { var i = (PluginFeature)Activator.CreateInstance(Type); i.Manifest = this; return i; } public CategoryType CreateInstance() where CategoryType : PluginFeature { if (typeof(CategoryType).IsAssignableFrom(Type)) { var i = (CategoryType)Activator.CreateInstance(Type); i.Manifest = this; return i; } else throw new InvalidOperationException($"The feature [{Type.Name}] cannot be cast into type [{typeof(CategoryType).Name}]"); } /// /// Uses reflection to build a Plugin Manifest /// /// Assembly containing a plugin /// A plugin manifest for the first encountered plugin within the assembly public static PluginFeatureManifest FromPluginFeatureType(Type featureType, PluginManifest pluginManifest) { var featureAttribute = (PluginFeatureAttribute)featureType.GetCustomAttributes(typeof(PluginFeatureAttribute), false).FirstOrDefault(); if (featureAttribute == null) throw new ArgumentException($"Plugin Feature found [{featureType.Name}], but no PluginFeatureAttribute found", "featureType"); var featureId = featureAttribute.Id; var featureName = featureAttribute.Name; var featurePrimary = featureAttribute.PrimaryFeature; // Determine Feature Category var featureCategoryType = featureType.BaseType; if (featureCategoryType == null) throw new ArgumentException($"Plugin Feature found [{featureType.Name}], but has no Base Type to determine its Category", "featureType"); // Handle Generic-Type Features (Only use base-feature, not generic parameters) if (featureCategoryType.IsGenericType) featureCategoryType = featureCategoryType.GetGenericTypeDefinition(); if (featureCategoryType == typeof(PluginFeature) || !typeof(PluginFeature).IsAssignableFrom(featureCategoryType)) throw new ArgumentException($"Plugin Feature found [{featureType.Name}], but its Base Type is not a valid Feature Category Type (Base Feature or not assignable)", "featureType"); var featureCategoryAttribute = (PluginFeatureCategoryAttribute)featureCategoryType.GetCustomAttributes(typeof(PluginFeatureCategoryAttribute), false).FirstOrDefault(); if (featureCategoryAttribute == null) throw new ArgumentException($"Plugin Feature found [{featureType.Name}], but its Base Type is not a valid Feature Category Type (no attribute)", "featureType"); return new PluginFeatureManifest() { PluginManifest = pluginManifest, Id = featureId, Name = featureName, PrimaryFeature = featurePrimary, Type = featureType, TypeName = featureType.FullName, CategoryType = featureCategoryType, CategoryTypeName = featureCategoryType.FullName }; } } }