/
FilteredCatalog.Traversal.cs
124 lines (108 loc) · 4.93 KB
/
FilteredCatalog.Traversal.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// 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;
namespace System.ComponentModel.Composition.Hosting
{
public partial class FilteredCatalog
{
/// <summary>
/// Creates a new instance of the <see cref="FilteredCatalog"/> that conatains all the parts from the original filtered catalog and all their dependencies.
/// </summary>
/// <returns></returns>
public FilteredCatalog IncludeDependencies()
{
return IncludeDependencies(i => i.Cardinality == ImportCardinality.ExactlyOne);
}
/// <summary>
/// Creates a new instance of the <see cref="FilteredCatalog"/> that conatains all the parts from the original filtered catalog and all their dependencies that
/// can be reached via imports that match the specified filter.
/// </summary>
/// <param name="importFilter">The import filter.</param>
/// <returns></returns>
public FilteredCatalog IncludeDependencies(Func<ImportDefinition, bool> importFilter)
{
Requires.NotNull(importFilter, nameof(importFilter));
ThrowIfDisposed();
return Traverse(new DependenciesTraversal(this, importFilter));
}
/// <summary>
/// Creates a new instance of the <see cref="FilteredCatalog"/> that conatains all the parts from the original filtered catalog and all their dependents.
/// </summary>
/// <returns></returns>
public FilteredCatalog IncludeDependents()
{
return IncludeDependents(i => i.Cardinality == ImportCardinality.ExactlyOne);
}
/// <summary>
/// Creates a new instance of the <see cref="FilteredCatalog"/> that conatains all the parts from the original filtered catalog and all their dependents that
/// can be reached via imports that match the specified filter.
/// </summary>
/// <param name="importFilter">The import filter.</param>
/// <returns></returns>
public FilteredCatalog IncludeDependents(Func<ImportDefinition, bool> importFilter)
{
Requires.NotNull(importFilter, nameof(importFilter));
ThrowIfDisposed();
return Traverse(new DependentsTraversal(this, importFilter));
}
private FilteredCatalog Traverse(IComposablePartCatalogTraversal traversal)
{
ArgumentNullException.ThrowIfNull(traversal);
// we make sure that the underlyiong catalog cannot change while we are doing the trasversal
// After thaty traversal is done, the freeze is lifted, and the catalog is free to change, but the changes
// cannot affect partitioning
FreezeInnerCatalog();
try
{
traversal.Initialize();
var traversalClosure = GetTraversalClosure(_innerCatalog.Where(_filter), traversal);
return new FilteredCatalog(_innerCatalog, traversalClosure.Contains);
}
finally
{
UnfreezeInnerCatalog();
}
}
private static HashSet<ComposablePartDefinition> GetTraversalClosure(IEnumerable<ComposablePartDefinition> parts, IComposablePartCatalogTraversal traversal)
{
ArgumentNullException.ThrowIfNull(traversal);
var traversedParts = new HashSet<ComposablePartDefinition>();
GetTraversalClosure(parts, traversedParts, traversal);
return traversedParts;
}
private static void GetTraversalClosure(IEnumerable<ComposablePartDefinition> parts, HashSet<ComposablePartDefinition> traversedParts, IComposablePartCatalogTraversal traversal)
{
foreach (var part in parts)
{
if (traversedParts.Add(part))
{
if (traversal.TryTraverse(part, out IEnumerable<ComposablePartDefinition>? partsToTraverse))
{
GetTraversalClosure(partsToTraverse, traversedParts, traversal);
}
}
}
}
private void FreezeInnerCatalog()
{
if (_innerCatalog is INotifyComposablePartCatalogChanged innerNotifyCatalog)
{
innerNotifyCatalog.Changing += ThrowOnRecomposition;
}
}
private void UnfreezeInnerCatalog()
{
if (_innerCatalog is INotifyComposablePartCatalogChanged innerNotifyCatalog)
{
innerNotifyCatalog.Changing -= ThrowOnRecomposition;
}
}
private static void ThrowOnRecomposition(object? sender, ComposablePartCatalogChangeEventArgs e)
{
throw new ChangeRejectedException();
}
}
}