Expose read-only collections in PluginManager instead of full collections

This commit is contained in:
John Gross
2017-09-21 22:30:48 -07:00
parent 1f4197ce67
commit b7f2a62b3c
5 changed files with 72 additions and 15 deletions

View File

@@ -14,12 +14,12 @@ namespace Torch.API.Managers
/// <summary> /// <summary>
/// Fired when plugins are loaded. /// Fired when plugins are loaded.
/// </summary> /// </summary>
event Action<ICollection<ITorchPlugin>> PluginsLoaded; event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
/// <summary> /// <summary>
/// Collection of loaded plugins. /// Collection of loaded plugins.
/// </summary> /// </summary>
IDictionary<Guid, ITorchPlugin> Plugins { get; } IReadOnlyDictionary<Guid, ITorchPlugin> Plugins { get; }
/// <summary> /// <summary>
/// Updates all loaded plugins. /// Updates all loaded plugins.

View File

@@ -29,7 +29,7 @@ namespace Torch.Server.ViewModels
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded; pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
} }
private void PluginManager_PluginsLoaded(ICollection<ITorchPlugin> obj) private void PluginManager_PluginsLoaded(IReadOnlyCollection<ITorchPlugin> obj)
{ {
Plugins.Clear(); Plugins.Clear();
foreach (var plugin in obj) foreach (var plugin in obj)

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Torch
{
public static class ICollectionExtensions
{
/// <summary>
/// Returns a read-only wrapped <see cref="ICollection{T}"/>
/// </summary>
public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source);
}
/// <summary>
/// Returns a read-only wrapped <see cref="IList{T}"/>
/// </summary>
public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return source as IReadOnlyList<T> ?? new ReadOnlyCollection<T>(source);
}
/// <summary>
/// Returns a read-only wrapped <see cref="IDictionary{TKey, TValue}"/>
/// </summary>
public static IReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return source as IReadOnlyDictionary<TKey, TValue> ?? new ReadOnlyDictionary<TKey, TValue>(source);
}
sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
{
private readonly ICollection<T> _source;
public ReadOnlyCollectionAdapter(ICollection<T> source)
{
_source = source;
}
public int Count => _source.Count;
public IEnumerator<T> GetEnumerator() => _source.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@@ -25,13 +26,14 @@ namespace Torch.Managers
private static Logger _log = LogManager.GetLogger(nameof(PluginManager)); private static Logger _log = LogManager.GetLogger(nameof(PluginManager));
private const string MANIFEST_NAME = "manifest.xml"; private const string MANIFEST_NAME = "manifest.xml";
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
private readonly ObservableDictionary<Guid, ITorchPlugin> _plugins = new ObservableDictionary<Guid, ITorchPlugin>();
[Dependency] [Dependency]
private CommandManager _commandManager; private CommandManager _commandManager;
/// <inheritdoc /> /// <inheritdoc />
public IDictionary<Guid, ITorchPlugin> Plugins { get; } = new ObservableDictionary<Guid, ITorchPlugin>(); public IReadOnlyDictionary<Guid, ITorchPlugin> Plugins => _plugins.AsReadOnly();
public event Action<ICollection<ITorchPlugin>> PluginsLoaded; public event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
public PluginManager(ITorchBase torchInstance) : base(torchInstance) public PluginManager(ITorchBase torchInstance) : base(torchInstance)
{ {
@@ -44,7 +46,7 @@ namespace Torch.Managers
/// </summary> /// </summary>
public void UpdatePlugins() public void UpdatePlugins()
{ {
foreach (var plugin in Plugins.Values) foreach (var plugin in _plugins.Values)
plugin.Update(); plugin.Update();
} }
@@ -53,10 +55,10 @@ namespace Torch.Managers
/// </summary> /// </summary>
public override void Detach() public override void Detach()
{ {
foreach (var plugin in Plugins.Values) foreach (var plugin in _plugins.Values)
plugin.Dispose(); plugin.Dispose();
Plugins.Clear(); _plugins.Clear();
} }
public void LoadPlugins() public void LoadPlugins()
@@ -75,9 +77,9 @@ namespace Torch.Managers
continue; continue;
} }
if (Plugins.ContainsKey(manifest.Guid)) if (_plugins.ContainsKey(manifest.Guid))
{ {
_log.Error($"The GUID provided by {manifest.Name} ({item}) is already in use by {Plugins[manifest.Guid].Name}"); _log.Error($"The GUID provided by {manifest.Name} ({item}) is already in use by {_plugins[manifest.Guid].Name}");
continue; continue;
} }
@@ -87,9 +89,9 @@ namespace Torch.Managers
LoadPluginFromFolder(path); LoadPluginFromFolder(path);
} }
Plugins.ForEach(x => x.Value.Init(Torch)); _plugins.ForEach(x => x.Value.Init(Torch));
_log.Info($"Loaded {Plugins.Count} plugins."); _log.Info($"Loaded {_plugins.Count} plugins.");
PluginsLoaded?.Invoke(Plugins.Values); PluginsLoaded?.Invoke(_plugins.Values.AsReadOnly());
} }
private void DownloadPluginUpdates() private void DownloadPluginUpdates()
@@ -307,14 +309,14 @@ namespace Torch.Managers
plugin.Manifest = manifest; plugin.Manifest = manifest;
plugin.StoragePath = Torch.Config.InstancePath; plugin.StoragePath = Torch.Config.InstancePath;
plugin.Torch = Torch; plugin.Torch = Torch;
Plugins.Add(manifest.Guid, plugin); _plugins.Add(manifest.Guid, plugin);
_commandManager.RegisterPluginCommands(plugin); _commandManager.RegisterPluginCommands(plugin);
} }
/// <inheritdoc cref="IEnumerable.GetEnumerator"/> /// <inheritdoc cref="IEnumerable.GetEnumerator"/>
public IEnumerator<ITorchPlugin> GetEnumerator() public IEnumerator<ITorchPlugin> GetEnumerator()
{ {
return Plugins.Values.GetEnumerator(); return _plugins.Values.GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()

View File

@@ -157,6 +157,7 @@
<Compile Include="ChatMessage.cs" /> <Compile Include="ChatMessage.cs" />
<Compile Include="Collections\ObservableList.cs" /> <Compile Include="Collections\ObservableList.cs" />
<Compile Include="Extensions\DispatcherExtensions.cs" /> <Compile Include="Extensions\DispatcherExtensions.cs" />
<Compile Include="Extensions\ICollectionExtensions.cs" />
<Compile Include="Managers\DependencyManager.cs" /> <Compile Include="Managers\DependencyManager.cs" />
<Compile Include="Managers\PatchManager\AssemblyMemory.cs" /> <Compile Include="Managers\PatchManager\AssemblyMemory.cs" />
<Compile Include="Managers\PatchManager\DecoratedMethod.cs" /> <Compile Include="Managers\PatchManager\DecoratedMethod.cs" />