1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 using System.Collections.Generic;
5 using System.ComponentModel.Composition.Primitives;
7 using System.Diagnostics;
8 using System.Diagnostics.CodeAnalysis;
10 namespace System.ComponentModel.Composition.Hosting
12 public partial class FilteredCatalog
15 /// Implementation of IComposablePartTraversal supporting the Dependents traveral pattern.
16 /// The implementation is optimized for a situation when the traversal is expected to be rather short-lived - that is,
17 /// if the chains of dependecies are rather small. To achieve that we do a very minimal structure prep upfront - merely creating a contract-based
18 /// index of imports - and the verify the full match of imports during the traversal. Given that most parts have a very few imports this should perform well.
20 internal sealed class DependentsTraversal : IComposablePartCatalogTraversal
22 private readonly IEnumerable<ComposablePartDefinition> _parts;
23 private readonly Func<ImportDefinition, bool> _importFilter;
24 private Dictionary<string, List<ComposablePartDefinition>>? _importersIndex;
26 public DependentsTraversal(FilteredCatalog catalog, Func<ImportDefinition, bool> importFilter)
28 ArgumentNullException.ThrowIfNull(catalog);
29 ArgumentNullException.ThrowIfNull(importFilter);
31 _parts = catalog._innerCatalog;
32 _importFilter = importFilter;
35 public void Initialize()
37 BuildImportersIndex();
40 private void BuildImportersIndex()
42 _importersIndex = new Dictionary<string, List<ComposablePartDefinition>>();
43 foreach (ComposablePartDefinition part in _parts)
45 foreach (var import in part.ImportDefinitions)
47 foreach (var contractName in import.GetCandidateContractNames(part))
49 AddToImportersIndex(contractName, part);
55 private void AddToImportersIndex(string contractName, ComposablePartDefinition part)
57 if (!_importersIndex!.TryGetValue(contractName, out List<ComposablePartDefinition>? parts))
59 parts = new List<ComposablePartDefinition>();
60 _importersIndex.Add(contractName, parts);
65 public bool TryTraverse(ComposablePartDefinition part, [NotNullWhen(true)] out IEnumerable<ComposablePartDefinition>? reachableParts)
67 reachableParts = null;
68 List<ComposablePartDefinition>? reachablePartList = null;
70 Debug.Assert(_importersIndex != null);
71 // Go through all part exports
72 foreach (ExportDefinition export in part.ExportDefinitions)
74 // Find all parts that we know will import each export
75 List<ComposablePartDefinition>? candidateReachableParts = null;
76 if (_importersIndex.TryGetValue(export.ContractName, out candidateReachableParts))
78 // find if they actually match
79 foreach (var candidateReachablePart in candidateReachableParts)
81 foreach (ImportDefinition import in candidateReachablePart.ImportDefinitions.Where(_importFilter))
83 if (import.IsImportDependentOnPart(part, export, part.IsGeneric() != candidateReachablePart.IsGeneric()))
85 if (reachablePartList == null)
87 reachablePartList = new List<ComposablePartDefinition>();
89 reachablePartList.Add(candidateReachablePart);
96 reachableParts = reachablePartList;
97 return (reachableParts != null);