using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reflection; using Artemis.Storage.Entities.Plugins; using McMaster.NETCore.Plugins; using Ninject; namespace Artemis.Core { /// /// Represents a plugin /// public class Plugin : CorePropertyChanged, IDisposable { private readonly List _features; private bool _isEnabled; internal Plugin(PluginInfo info, DirectoryInfo directory) { Info = info; Directory = directory; _features = new List(); } /// /// Gets the plugin GUID /// public Guid Guid => Info.Guid; /// /// Gets the plugin info related to this plugin /// public PluginInfo Info { get; } /// /// The plugins root directory /// public DirectoryInfo Directory { get; } /// /// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins /// public IPluginConfigurationDialog? ConfigurationDialog { get; set; } /// /// Indicates whether the user enabled the plugin or not /// public bool IsEnabled { get => _isEnabled; private set => SetAndNotify(ref _isEnabled, value); } /// /// Gets a read-only collection of all features this plugin provides /// public ReadOnlyCollection Features => _features.AsReadOnly(); /// /// The assembly the plugin code lives in /// public Assembly? Assembly { get; internal set; } /// /// Gets the plugin bootstrapper /// public IPluginBootstrapper? Bootstrapper { get; internal set; } /// /// The Ninject kernel of the plugin /// public IKernel? Kernel { get; internal set; } /// /// The PluginLoader backing this plugin /// internal PluginLoader? PluginLoader { get; set; } /// /// The entity representing the plugin /// internal PluginEntity Entity { get; set; } /// /// Resolves the relative path provided in the parameter to an absolute path /// /// The path to resolve /// An absolute path pointing to the provided relative path public string? ResolveRelativePath(string path) { return path == null ? null : Path.Combine(Directory.FullName, path); } /// /// Looks up the instance of the feature of type /// Note: This method only returns instances of enabled features /// /// The type of feature to find /// If found, the instance of the feature public T? GetFeature() where T : PluginFeature { return _features.FirstOrDefault(i => i is T) as T; } /// public override string ToString() { return Info.ToString(); } internal void ApplyToEntity() { Entity.Id = Guid; Entity.IsEnabled = IsEnabled; } internal void AddFeature(PluginFeature feature) { feature.Plugin = this; _features.Add(feature); OnFeatureAdded(new PluginFeatureEventArgs(feature)); } internal void RemoveFeature(PluginFeature feature) { if (feature.IsEnabled) throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin"); _features.Remove(feature); feature.Dispose(); OnFeatureRemoved(new PluginFeatureEventArgs(feature)); } internal void SetEnabled(bool enable) { if (IsEnabled == enable) return; if (!enable && Features.Any(e => e.IsEnabled)) throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features"); IsEnabled = enable; if (enable) { Bootstrapper?.Enable(this); OnEnabled(); } else { Bootstrapper?.Disable(this); OnDisabled(); } } public void Dispose() { foreach (PluginFeature feature in Features) feature.Dispose(); Kernel?.Dispose(); PluginLoader?.Dispose(); _features.Clear(); SetEnabled(false); } #region Events /// /// Occurs when the plugin is enabled /// public event EventHandler? Enabled; /// /// Occurs when the plugin is disabled /// public event EventHandler? Disabled; /// /// Occurs when an feature is loaded and added to the plugin /// public event EventHandler? FeatureAdded; /// /// Occurs when an feature is disabled and removed from the plugin /// public event EventHandler? FeatureRemoved; /// /// Invokes the Enabled event /// protected virtual void OnEnabled() { Enabled?.Invoke(this, EventArgs.Empty); } /// /// Invokes the Disabled event /// protected virtual void OnDisabled() { Disabled?.Invoke(this, EventArgs.Empty); } /// /// Invokes the FeatureAdded event /// protected virtual void OnFeatureAdded(PluginFeatureEventArgs e) { FeatureAdded?.Invoke(this, e); } /// /// Invokes the FeatureRemoved event /// protected virtual void OnFeatureRemoved(PluginFeatureEventArgs e) { FeatureRemoved?.Invoke(this, e); } #endregion } }