|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using Microsoft.Internal.Collections;
namespace System.ComponentModel.Composition.Hosting
{
public partial class ImportEngine
{
/// <summary>
/// Used by the <see cref="ImportEngine"/> to effiecently store and retrieve the list of parts
/// that will be affected by changes to exports. This allows the <see cref="ImportEngine"/> to properly
/// block breaking changes and also recompose imports as appropriate.
/// </summary>
private sealed class RecompositionManager
{
private readonly WeakReferenceCollection<PartManager> _partsToIndex = new WeakReferenceCollection<PartManager>();
private readonly WeakReferenceCollection<PartManager> _partsToUnindex = new WeakReferenceCollection<PartManager>();
private readonly Dictionary<string, WeakReferenceCollection<PartManager>> _partManagerIndex = new Dictionary<string, WeakReferenceCollection<PartManager>>();
public void AddPartToIndex(PartManager partManager)
{
_partsToIndex.Add(partManager);
}
public void AddPartToUnindex(PartManager partManager)
{
_partsToUnindex.Add(partManager);
}
public List<PartManager> GetAffectedParts(IEnumerable<string> changedContractNames)
{
UpdateImportIndex();
List<PartManager> parts = new List<PartManager>();
parts.AddRange(GetPartsImporting(ImportDefinition.EmptyContractName));
foreach (string contractName in changedContractNames)
{
parts.AddRange(GetPartsImporting(contractName));
}
return parts;
}
public static IEnumerable<ImportDefinition> GetAffectedImports(ComposablePart part, IEnumerable<ExportDefinition> changedExports)
{
return part.ImportDefinitions.Where(import => IsAffectedImport(import, changedExports));
}
private static bool IsAffectedImport(ImportDefinition import, IEnumerable<ExportDefinition> changedExports)
{
// This could be more efficient still if the export definitions were indexed by contract name,
// only worth revisiting if we need to squeeze more performance out of recomposition
foreach (var export in changedExports)
{
if (import.IsConstraintSatisfiedBy(export))
{
return true;
}
}
return false;
}
public IEnumerable<PartManager> GetPartsImporting(string contractName)
{
if (!_partManagerIndex.TryGetValue(contractName, out WeakReferenceCollection<PartManager>? partManagerList))
{
return Enumerable.Empty<PartManager>();
}
return partManagerList.AliveItemsToList();
}
private void AddIndexEntries(PartManager partManager)
{
foreach (string contractName in partManager.GetImportedContractNames())
{
if (!_partManagerIndex.TryGetValue(contractName, out WeakReferenceCollection<PartManager>? indexEntries))
{
indexEntries = new WeakReferenceCollection<PartManager>();
_partManagerIndex.Add(contractName, indexEntries);
}
if (!indexEntries.Contains(partManager))
{
indexEntries.Add(partManager);
}
}
}
private void RemoveIndexEntries(PartManager partManager)
{
foreach (string contractName in partManager.GetImportedContractNames())
{
if (_partManagerIndex.TryGetValue(contractName, out WeakReferenceCollection<PartManager>? indexEntries))
{
indexEntries.Remove(partManager);
var aliveItems = indexEntries.AliveItemsToList();
if (aliveItems.Count == 0)
{
_partManagerIndex.Remove(contractName);
}
}
}
}
private void UpdateImportIndex()
{
var partsToIndex = _partsToIndex.AliveItemsToList();
_partsToIndex.Clear();
List<PartManager?> partsToUnindex = _partsToUnindex.AliveItemsToList()!;
_partsToUnindex.Clear();
if (partsToIndex.Count == 0 && partsToUnindex.Count == 0)
{
return;
}
foreach (var partManager in partsToIndex)
{
var index = partsToUnindex.IndexOf(partManager);
// If the same part is being added and removed we can ignore both
if (index >= 0)
{
partsToUnindex[index] = null;
}
else
{
AddIndexEntries(partManager);
}
}
foreach (var partManager in partsToUnindex)
{
if (partManager != null)
{
RemoveIndexEntries(partManager);
}
}
}
}
}
}
|