797 lines
27 KiB
C#
797 lines
27 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using JD.Collections;
|
|
using JD.Collections.Extensions;
|
|
using JD.Kernel.StaticHelpers;
|
|
using JD.LogService;
|
|
using JD.TeaBoxAddinSystem.Addins.Description;
|
|
using JD.TeaBoxAddinSystem.Addins.Description.Conditions;
|
|
using JD.TeaBoxAddinSystem.Addins.Description.FileReferences;
|
|
|
|
namespace JD.TeaBoxAddinSystem.Addins.Internal
|
|
{
|
|
/// <summary>
|
|
/// Responsible for managing the current session addins.
|
|
/// </summary>
|
|
internal class AddinSessionService
|
|
{
|
|
#region Inner Types
|
|
|
|
private delegate void TypeSetReferenceValidationDelegate(string reference);
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly Dictionary<string, Addin> m_loadedAddins = new Dictionary<string, Addin>(); // Addin full id -> addin
|
|
|
|
private readonly Dictionary<string, ExtensionTypeSet> m_extensionTypeSets = new Dictionary<string, ExtensionTypeSet>(); // Type name -> type
|
|
private readonly Dictionary<string, ConditionType> m_conditionTypes = new Dictionary<string, ConditionType>(); // Type name -> type
|
|
|
|
private readonly Dictionary<string, ExtensionPoint> m_extensionPoints = new Dictionary<string, ExtensionPoint>(); // Path -> extension point
|
|
private readonly MultiDictionary<string, RuntimeAssembly> m_loadedAssemblies = new MultiDictionary<string, RuntimeAssembly>(StringComparer.OrdinalIgnoreCase); // Assembly Name -> Loaded assembly. Needed for correct deserialization in framework (assembly resolving).
|
|
|
|
private readonly DependencyManager m_dependencies = new DependencyManager();
|
|
private readonly ConditionManager m_conditionManager = new ConditionManager();
|
|
private readonly ChangedHandlerManager m_handlerManager = new ChangedHandlerManager();
|
|
private readonly TreeNode m_extensionTree = new TreeNode();
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Loaded addins.
|
|
/// </summary>
|
|
public ICollection<Addin> LoadedAddins
|
|
{
|
|
get
|
|
{
|
|
return m_loadedAddins.Values;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loaded extension type sets.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, ExtensionTypeSet> ExtensionTypeSets
|
|
{
|
|
get
|
|
{
|
|
return m_extensionTypeSets.AsReadOnly();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loaded condition types.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, ConditionType> ConditionTypes
|
|
{
|
|
get
|
|
{
|
|
return m_conditionTypes.AsReadOnly();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loaded extension points.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, ExtensionPoint> ExtensionPoints
|
|
{
|
|
get
|
|
{
|
|
return m_extensionPoints.AsReadOnly();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension tree.
|
|
/// </summary>
|
|
public TreeNode ExtensionTree
|
|
{
|
|
get
|
|
{
|
|
return m_extensionTree;
|
|
}
|
|
}
|
|
|
|
internal ConditionManager ConditionManager
|
|
{
|
|
get
|
|
{
|
|
return m_conditionManager;
|
|
}
|
|
}
|
|
|
|
internal ChangedHandlerManager ChangedHandlerManager
|
|
{
|
|
get
|
|
{
|
|
return m_handlerManager;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
#region General Methods
|
|
|
|
public bool IsAddinLoaded(Addin addin)
|
|
{
|
|
if (addin == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Addin loadedAddin;
|
|
return m_loadedAddins.TryGetValue(addin.FullId, out loadedAddin) && loadedAddin == addin;
|
|
}
|
|
|
|
public bool IsModuleLoaded(Module module)
|
|
{
|
|
if (module == null || module.Addin == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!IsAddinLoaded(module.Addin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (module.IsMainModule)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach(AddinReference dependency in module.Dependencies)
|
|
{
|
|
if (!ResolveDependency(dependency))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers the given <paramref name="handler"/> on the <see cref="ExtensionNode"/> at the given <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <param name="handler"></param>
|
|
public void AddExtensionChangedHandler(string path, EventHandler<ExtensionNodeChangedEventArgs> handler)
|
|
{
|
|
Throw.IfEmpty(path, "path");
|
|
Throw.IfNull(handler, "handler");
|
|
|
|
path = ExtensionPoint.NormalizePath(path);
|
|
ChangedHandlerManager.Add(path, handler);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregisters the given <paramref name="handler"/> on the <see cref="ExtensionNode"/> at the given <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <param name="handler"></param>
|
|
public void RemoveExtensionChangedHandler(string path, EventHandler<ExtensionNodeChangedEventArgs> handler)
|
|
{
|
|
Throw.IfEmpty(path, "path");
|
|
Throw.IfNull(handler, "handler");
|
|
|
|
path = ExtensionPoint.NormalizePath(path);
|
|
ChangedHandlerManager.Remove(path, handler);
|
|
}
|
|
|
|
internal ExtensionType FindExtensionType(ExtensionTypeSet typeSet, string typeName)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(typeName));
|
|
|
|
// Check in immediate types
|
|
if (typeSet.ExtensionTypes.Contains(typeName))
|
|
{
|
|
return typeSet.ExtensionTypes[typeName];
|
|
}
|
|
|
|
// Check references.
|
|
foreach (string reference in typeSet.ExtensionTypeSets)
|
|
{
|
|
if (ExtensionTypeSets.ContainsKey(reference))
|
|
{
|
|
// Recurse
|
|
return FindExtensionType(ExtensionTypeSets[reference], typeName);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal ConditionType FindConditionType(string typeName)
|
|
{
|
|
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(typeName));
|
|
|
|
// Check in immediate types
|
|
ConditionType conditionType;
|
|
ConditionTypes.TryGetValue(typeName, out conditionType);
|
|
return conditionType;
|
|
}
|
|
|
|
internal IEnumerable<Module> GetLoadedDependencies(Module module)
|
|
{
|
|
System.Diagnostics.Debug.Assert(module != null);
|
|
foreach (AddinReference dependency in module.Dependencies)
|
|
{
|
|
Addin addin;
|
|
if (m_loadedAddins.TryGetValue(dependency.Name, out addin))
|
|
{
|
|
yield return addin.MainModule;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal System.Reflection.Assembly GetAssembly(string assemblyName)
|
|
{
|
|
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(assemblyName));
|
|
|
|
string simpleAssemblyName = assemblyName.ToLowerInvariant();
|
|
|
|
int commaIndex = simpleAssemblyName.IndexOf(',');
|
|
if (commaIndex > 0)
|
|
{
|
|
simpleAssemblyName = simpleAssemblyName.Substring(0, commaIndex);
|
|
}
|
|
|
|
foreach (RuntimeAssembly runtimeAssembly in m_loadedAssemblies.Values)
|
|
{
|
|
if (runtimeAssembly.AssemblyName.ToLowerInvariant().Contains(simpleAssemblyName))
|
|
{
|
|
System.Reflection.Assembly assembly = runtimeAssembly.Assembly;
|
|
if (assembly != null && string.Equals(assembly.FullName, assemblyName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return assembly;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Loading
|
|
|
|
public bool LoadAddin(Addin addin)
|
|
{
|
|
Throw.IfNull(addin, "addin");
|
|
|
|
if (IsAddinLoaded(addin))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (m_loadedAddins.ContainsKey(addin.FullId))
|
|
{
|
|
throw new InvalidOperationException($"Another addin with the id {addin.FullId} is already loaded.");
|
|
}
|
|
|
|
// We need to proceed in two passes to be "error-safe", i.e. in case of an error, the session service state must be reverted to what it was before the call.
|
|
// So we verify everything first and then proceed with the actual operation.
|
|
|
|
// Verification step
|
|
if (!ValidateAddin(addin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Actual loading starts here
|
|
if (!LoadAddinImpl(addin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PostLoad(addin);
|
|
return true;
|
|
}
|
|
|
|
private bool ValidateAddin(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
bool encounteredError = false;
|
|
|
|
// Check strong dependencies
|
|
foreach (AddinReference dependency in addin.MainModule.Dependencies)
|
|
{
|
|
if (!ResolveDependency(dependency))
|
|
{
|
|
AddinManager.Log(new Error(LogCategory.Error, "Cannot load addin {0} because it has an unresolved dependency: {1}", addin.FullId, dependency));
|
|
encounteredError = true;
|
|
}
|
|
}
|
|
|
|
if (encounteredError)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
encounteredError |= CheckNoExistingElement("extension type set", m_extensionTypeSets, addin.ExtensionTypeSets, delegate(ExtensionTypeSet typeSet)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
return typeSet.Name;
|
|
});
|
|
|
|
encounteredError |= CheckNoExistingElement("condition type", m_conditionTypes, addin.ConditionTypes, delegate(ConditionType conditionType)
|
|
{
|
|
System.Diagnostics.Debug.Assert(conditionType != null);
|
|
return conditionType.Name;
|
|
});
|
|
|
|
encounteredError |= CheckNoExistingElement("extension point", m_extensionPoints, addin.ExtensionPoints, delegate(ExtensionPoint extensionPoint)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extensionPoint != null);
|
|
return extensionPoint.Path;
|
|
});
|
|
|
|
return !encounteredError;
|
|
}
|
|
|
|
private static bool CheckNoExistingElement<T>(string elementType, IDictionary<string, T> dictionary, IEnumerable<T> elements, Converter<T, string> converter) where T : AddinObject
|
|
{
|
|
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(elementType));
|
|
System.Diagnostics.Debug.Assert(dictionary != null);
|
|
System.Diagnostics.Debug.Assert(elements != null);
|
|
System.Diagnostics.Debug.Assert(converter != null);
|
|
|
|
bool encounteredError = false;
|
|
|
|
foreach (T element in elements)
|
|
{
|
|
string key = converter(element);
|
|
|
|
T existingElement;
|
|
if (dictionary.TryGetValue(key, out existingElement))
|
|
{
|
|
LogNodeTypeError(elementType, key, element, existingElement);
|
|
encounteredError = true;
|
|
}
|
|
}
|
|
|
|
return encounteredError;
|
|
}
|
|
|
|
private static void LogNodeTypeError(string nodeType, string name, AddinObject element, AddinObject existingElement)
|
|
{
|
|
element.LogError("Cannot load addin {0} because an {1} named {2} is already provided by the addin {3} at {4}",
|
|
element.Addin.FullId, nodeType, name, existingElement.Addin.FullId, existingElement.FileLocation);
|
|
}
|
|
|
|
private bool ResolveDependency(AddinReference reference)
|
|
{
|
|
System.Diagnostics.Debug.Assert(reference != null);
|
|
|
|
// Check if an addin is loaded with the correct name
|
|
Addin loadedAddin;
|
|
if (!m_loadedAddins.TryGetValue(reference.Name, out loadedAddin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check version
|
|
return reference.Validate(loadedAddin.Manifest.Version);
|
|
}
|
|
|
|
private bool LoadAddinImpl(Addin addin)
|
|
{
|
|
// Add extension type sets
|
|
foreach (ExtensionTypeSet typeSet in addin.ExtensionTypeSets)
|
|
{
|
|
InsertExtensionTypeSet(typeSet);
|
|
}
|
|
|
|
// Add condition types
|
|
foreach (ConditionType conditionType in addin.ConditionTypes)
|
|
{
|
|
InsertConditionType(conditionType);
|
|
}
|
|
|
|
// Add extension points
|
|
foreach (ExtensionPoint extensionPoint in addin.ExtensionPoints)
|
|
{
|
|
InsertExtensionPoint(extensionPoint);
|
|
}
|
|
|
|
// Load modules
|
|
LoadModules(addin);
|
|
|
|
m_loadedAddins.Add(addin.FullId, addin);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void PostLoad(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
// Validate global extension type set references (they are validated in post-load because they might refer to other type sets that were just loaded)
|
|
foreach (ExtensionTypeSet typeSet in addin.ExtensionTypeSets)
|
|
{
|
|
ValidateExtensionTypeSetReferences(addin, typeSet, delegate(string reference)
|
|
{
|
|
typeSet.LogWarning("Extension type set {0} in addin {1} references an invalid type set {2}", typeSet.Name, addin.FullId, reference);
|
|
});
|
|
}
|
|
|
|
// Validate extension type set references on extension points
|
|
foreach (ExtensionPoint point in addin.ExtensionPoints)
|
|
{
|
|
ValidateExtensionTypeSetReferences(addin, point.ExtensionTypeSet, delegate(string reference)
|
|
{
|
|
point.LogWarning("Extension point {0} in addin {1} references an invalid type set {2}", point.Path, addin.FullId, reference);
|
|
});
|
|
}
|
|
|
|
// Active optional dependent modules
|
|
ActivateOptionalModules(addin);
|
|
|
|
// For each loaded extension point, trigger changed event
|
|
foreach (ExtensionPoint point in addin.ExtensionPoints)
|
|
{
|
|
System.Diagnostics.Debug.Assert(ExtensionTree[point.Path] != null);
|
|
ChangedHandlerManager.OnChanged(new ExtensionNodeChangedEventArgs(ExtensionTree[point.Path]));
|
|
}
|
|
}
|
|
|
|
private void ValidateExtensionTypeSetReferences(Addin addin, ExtensionTypeSet typeSet, TypeSetReferenceValidationDelegate validationDelegate)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
System.Diagnostics.Debug.Assert(validationDelegate != null);
|
|
|
|
foreach (string reference in typeSet.ExtensionTypeSets)
|
|
{
|
|
if (!ExtensionTypeSets.ContainsKey(reference))
|
|
{
|
|
validationDelegate(reference);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadModules(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
LoadModule(addin.MainModule);
|
|
foreach (Module module in addin.Modules)
|
|
{
|
|
LoadModule(module);
|
|
}
|
|
}
|
|
|
|
private void ActivateOptionalModules(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
foreach (Module module in m_dependencies.GetWeakDependencies(addin.FullId))
|
|
{
|
|
ActivateModule(module);
|
|
}
|
|
}
|
|
|
|
private void LoadModule(Module module)
|
|
{
|
|
// Register module
|
|
m_dependencies.RegisterModule(module);
|
|
|
|
ActivateModule(module);
|
|
}
|
|
|
|
private void ActivateModule(Module module)
|
|
{
|
|
System.Diagnostics.Debug.Assert(module != null);
|
|
|
|
// Resolve dependencies
|
|
bool resolved = true;
|
|
foreach (AddinReference dependency in module.Dependencies)
|
|
{
|
|
if (!ResolveDependency(dependency))
|
|
{
|
|
System.Diagnostics.Debug.Assert(!module.IsMainModule);
|
|
System.Diagnostics.Debug.Assert(module.Addin != null);
|
|
AddinManager.Log(new Message("Will not load optional module in addin {0} because its dependency {1} cannot be resolved", module.Addin.FullId, dependency) { Importance = MessageImportance.Low });
|
|
resolved = false;
|
|
}
|
|
}
|
|
|
|
if (!resolved)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Register assemblies
|
|
foreach (AssemblyFileReference assemblyReference in module.Assemblies)
|
|
{
|
|
m_loadedAssemblies.Add(assemblyReference.Runtime.AssemblyName, assemblyReference.Runtime);
|
|
}
|
|
|
|
// Load extensions
|
|
foreach (Extension extension in module.Extensions)
|
|
{
|
|
LoadExtension(extension);
|
|
}
|
|
}
|
|
|
|
private void LoadExtension(Extension extension)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extension != null);
|
|
|
|
// Find the path in the tree
|
|
TreeNode node = ExtensionTree[extension.Path];
|
|
|
|
if (node == null || node.AllowedTypes == null)
|
|
{
|
|
extension.LogWarning("Cannot load extension {0} because the path is not extensible", extension.Path);
|
|
return;
|
|
}
|
|
|
|
var extensionPoint = node.ExtensionPoint;
|
|
if (extensionPoint != null && extensionPoint.IsDeprecated)
|
|
{
|
|
extension.LogWarning("Extension {0} is deprecated: {1}", extension.Path, extensionPoint.DeprecationMessage);
|
|
}
|
|
|
|
node.LoadExtension(extension);
|
|
}
|
|
|
|
private void InsertExtensionTypeSet(ExtensionTypeSet typeSet)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
System.Diagnostics.Debug.Assert(!m_extensionTypeSets.ContainsKey(typeSet.Name));
|
|
m_extensionTypeSets.Add(typeSet.Name, typeSet);
|
|
}
|
|
|
|
private void InsertConditionType(ConditionType conditionType)
|
|
{
|
|
System.Diagnostics.Debug.Assert(conditionType != null);
|
|
System.Diagnostics.Debug.Assert(!m_conditionTypes.ContainsKey(conditionType.Name));
|
|
m_conditionTypes.Add(conditionType.Name, conditionType);
|
|
}
|
|
|
|
private void InsertExtensionPoint(ExtensionPoint extensionPoint)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extensionPoint != null);
|
|
System.Diagnostics.Debug.Assert(!m_extensionPoints.ContainsKey(extensionPoint.Path));
|
|
m_extensionPoints.Add(extensionPoint.Path, extensionPoint);
|
|
|
|
TreeNode extensionPathNode = ExtensionTree.GetNode(extensionPoint.Path, true);
|
|
System.Diagnostics.Debug.Assert(extensionPathNode.ExtensionPoint == null);
|
|
extensionPathNode.AllowedTypes = extensionPoint;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Unloading
|
|
|
|
public bool UnloadAddin(Addin addin)
|
|
{
|
|
Throw.IfNull(addin, "addin");
|
|
|
|
// Make sure we can unload the addin
|
|
if (!ValidateUnloadAddin(addin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return UnloadAddinImpl(addin);
|
|
}
|
|
|
|
private bool ValidateUnloadAddin(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
if (!IsAddinLoaded(addin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check dependent assemblies.
|
|
if (m_dependencies.HasStrongDependencies(addin.FullId))
|
|
{
|
|
AddinManager.Log(new Error(LogCategory.Error, "Cannot unload addin {0} because there are addins that directly depend on it", addin.FullId));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool UnloadAddinImpl(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
// Deactivate dependent modules
|
|
DeactivateOptionalModules(addin);
|
|
|
|
// Unload modules
|
|
UnloadModules(addin);
|
|
|
|
// Remove extension points
|
|
foreach (ExtensionPoint extensionPoint in addin.ExtensionPoints)
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_extensionPoints.ContainsKey(extensionPoint.Path) && m_extensionPoints[extensionPoint.Path] == extensionPoint);
|
|
m_extensionPoints.Remove(extensionPoint.Path);
|
|
|
|
RemoveExtensionPoint(extensionPoint);
|
|
}
|
|
|
|
// Remove condition types
|
|
foreach (ConditionType conditionType in addin.ConditionTypes)
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_conditionTypes.ContainsKey(conditionType.Name) && m_conditionTypes[conditionType.Name] == conditionType);
|
|
m_conditionTypes.Remove(conditionType.Name);
|
|
}
|
|
|
|
// Remove extension type sets
|
|
foreach (ExtensionTypeSet typeSet in addin.ExtensionTypeSets)
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_extensionTypeSets.ContainsKey(typeSet.Name) && m_extensionTypeSets[typeSet.Name] == typeSet);
|
|
m_extensionTypeSets.Remove(typeSet.Name);
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(m_loadedAddins.ContainsKey(addin.FullId));
|
|
System.Diagnostics.Debug.Assert(m_loadedAddins[addin.FullId] == addin);
|
|
m_loadedAddins.Remove(addin.FullId);
|
|
|
|
// For each unloaded extension point, trigger an OnChanged event after unloading.
|
|
foreach (ExtensionPoint point in addin.ExtensionPoints)
|
|
{
|
|
ChangedHandlerManager.OnChanged(new ExtensionNodeChangedEventArgs(point.Path));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void UnloadModules(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
foreach (Module module in addin.Modules)
|
|
{
|
|
UnloadModule(module);
|
|
}
|
|
UnloadModule(addin.MainModule);
|
|
}
|
|
|
|
private void DeactivateOptionalModules(Addin addin)
|
|
{
|
|
System.Diagnostics.Debug.Assert(addin != null);
|
|
|
|
foreach (Module module in m_dependencies.GetWeakDependencies(addin.FullId))
|
|
{
|
|
DeactivateModule(module);
|
|
}
|
|
}
|
|
|
|
private void UnloadModule(Module module)
|
|
{
|
|
System.Diagnostics.Debug.Assert(module != null);
|
|
|
|
DeactivateModule(module);
|
|
|
|
// Unregister module
|
|
m_dependencies.UnregisterModule(module);
|
|
}
|
|
|
|
private void DeactivateModule(Module module)
|
|
{
|
|
System.Diagnostics.Debug.Assert(module != null);
|
|
|
|
// Unload extensions
|
|
foreach (Extension extension in module.Extensions)
|
|
{
|
|
UnloadExtension(extension);
|
|
}
|
|
|
|
// Unregister assemblies
|
|
foreach (AssemblyFileReference assemblyReference in module.Assemblies)
|
|
{
|
|
m_loadedAssemblies.Remove(assemblyReference.Runtime.AssemblyName, assemblyReference.Runtime);
|
|
}
|
|
}
|
|
|
|
private void UnloadExtension(Extension extension)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extension != null);
|
|
|
|
// Find the path in the tree
|
|
TreeNode node = ExtensionTree[extension.Path];
|
|
|
|
if (node != null) // It's possible this extension could not be loaded for some reason.
|
|
{
|
|
node.UnloadExtension(extension);
|
|
}
|
|
}
|
|
|
|
public void UnloadAllAddins(bool destroyHandlers)
|
|
{
|
|
/*// Big, complicated loop to unload addins correctly.
|
|
int lastCount = LoadedAddins.Count;
|
|
while (LoadedAddins.Count > 0)
|
|
{
|
|
foreach (Addin addin in new List<Addin>(LoadedAddins))
|
|
{
|
|
if (GetDependentAddins(addin).Count == 0)
|
|
{
|
|
UnloadAddin(addin);
|
|
}
|
|
}
|
|
|
|
if (LoadedAddins.Count == lastCount)
|
|
{
|
|
throw new InvalidProgramException("Circular addin dependency detected");
|
|
}
|
|
|
|
lastCount = LoadedAddins.Count;
|
|
}*/
|
|
|
|
// Trigger a changed event for all unloaded extension points.
|
|
List<string> removedExtensionPointPaths = new List<string>();
|
|
if (!destroyHandlers)
|
|
{
|
|
foreach (ExtensionPoint point in m_extensionPoints.Values)
|
|
{
|
|
System.Diagnostics.Debug.Assert(ExtensionTree[point.Path] != null);
|
|
System.Diagnostics.Debug.Assert(ExtensionTree[point.Path].ExtensionPoint != null);
|
|
removedExtensionPointPaths.Add(point.Path);
|
|
}
|
|
}
|
|
|
|
m_loadedAddins.Clear();
|
|
m_extensionPoints.Clear();
|
|
m_extensionTypeSets.Clear();
|
|
m_conditionTypes.Clear();
|
|
m_extensionTree.Clear();
|
|
m_dependencies.Clear();
|
|
m_conditionManager.Clear();
|
|
|
|
foreach (string extensionPointPath in removedExtensionPointPaths)
|
|
{
|
|
ChangedHandlerManager.OnChanged(new ExtensionNodeChangedEventArgs(extensionPointPath));
|
|
}
|
|
|
|
if (destroyHandlers)
|
|
{
|
|
ChangedHandlerManager.Clear();
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(m_loadedAddins.Count == 0);
|
|
}
|
|
|
|
private void RemoveExtensionPoint(ExtensionPoint extensionPoint)
|
|
{
|
|
TreeNode extensionNode = ExtensionTree[extensionPoint.Path];
|
|
System.Diagnostics.Debug.Assert(extensionNode != null);
|
|
System.Diagnostics.Debug.Assert(extensionNode.ExtensionPoint == extensionPoint);
|
|
extensionNode.AllowedTypes = null;
|
|
RemoveTreeNodeIfPossible(extensionNode);
|
|
}
|
|
|
|
private void RemoveTreeNodeIfPossible(TreeNode node)
|
|
{
|
|
System.Diagnostics.Debug.Assert(node != null);
|
|
|
|
if (node.ChildNodes.Count > 0 || node.Parent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TreeNode parent = node.Parent;
|
|
parent.ChildNodesInternal.Remove(node);
|
|
RemoveTreeNodeIfPossible(parent);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|