1c7f01b32e870c27887414aef1d20f30f413a9bd
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System.Collections.Generic;
5 using System.ComponentModel.Composition.Primitives;
6 using System.Linq;
7 using System.Diagnostics;
8 using System.Diagnostics.CodeAnalysis;
9
10 namespace System.ComponentModel.Composition.Hosting
11 {
12     public partial class FilteredCatalog
13     {
14         /// <summary>
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.
19         /// </summary>
20         internal sealed class DependentsTraversal : IComposablePartCatalogTraversal
21         {
22             private readonly IEnumerable<ComposablePartDefinition> _parts;
23             private readonly Func<ImportDefinition, bool> _importFilter;
24             private Dictionary<string, List<ComposablePartDefinition>>? _importersIndex;
25
26             public DependentsTraversal(FilteredCatalog catalog, Func<ImportDefinition, bool> importFilter)
27             {
28                 ArgumentNullException.ThrowIfNull(catalog);
29                 ArgumentNullException.ThrowIfNull(importFilter);
30
31                 _parts = catalog._innerCatalog;
32                 _importFilter = importFilter;
33             }
34
35             public void Initialize()
36             {
37                 BuildImportersIndex();
38             }
39
40             private void BuildImportersIndex()
41             {
42                 _importersIndex = new Dictionary<string, List<ComposablePartDefinition>>();
43                 foreach (ComposablePartDefinition part in _parts)
44                 {
45                     foreach (var import in part.ImportDefinitions)
46                     {
47                         foreach (var contractName in import.GetCandidateContractNames(part))
48                         {
49                             AddToImportersIndex(contractName, part);
50                         }
51                     }
52                 }
53             }
54
55             private void AddToImportersIndex(string contractName, ComposablePartDefinition part)
56             {
57                 if (!_importersIndex!.TryGetValue(contractName, out List<ComposablePartDefinition>? parts))
58                 {
59                     parts = new List<ComposablePartDefinition>();
60                     _importersIndex.Add(contractName, parts);
61                 }
62                 parts.Add(part);
63             }
64
65             public bool TryTraverse(ComposablePartDefinition part, [NotNullWhen(true)] out IEnumerable<ComposablePartDefinition>? reachableParts)
66             {
67                 reachableParts = null;
68                 List<ComposablePartDefinition>? reachablePartList = null;
69
70                 Debug.Assert(_importersIndex != null);
71                 // Go through all part exports
72                 foreach (ExportDefinition export in part.ExportDefinitions)
73                 {
74                     // Find all parts that we know will import each export
75                     List<ComposablePartDefinition>? candidateReachableParts = null;
76                     if (_importersIndex.TryGetValue(export.ContractName, out candidateReachableParts))
77                     {
78                         // find if they actually match
79                         foreach (var candidateReachablePart in candidateReachableParts)
80                         {
81                             foreach (ImportDefinition import in candidateReachablePart.ImportDefinitions.Where(_importFilter))
82                             {
83                                 if (import.IsImportDependentOnPart(part, export, part.IsGeneric() != candidateReachablePart.IsGeneric()))
84                                 {
85                                     if (reachablePartList == null)
86                                     {
87                                         reachablePartList = new List<ComposablePartDefinition>();
88                                     }
89                                     reachablePartList.Add(candidateReachablePart);
90                                 }
91                             }
92                         }
93                     }
94                 }
95
96                 reachableParts = reachablePartList;
97                 return (reachableParts != null);
98             }
99         }
100     }
101 }