-
Notifications
You must be signed in to change notification settings - Fork 45
/
InterceptingCatalog.cs
338 lines (282 loc) · 14.1 KB
/
InterceptingCatalog.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
namespace MefContrib.Hosting.Interception
{
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using MefContrib.Hosting.Interception.Configuration;
/// <summary>
/// Defines a catalog which enables interception on its parts.
/// </summary>
public class InterceptingCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged
{
private readonly object synchRoot = new object();
private readonly ComposablePartCatalog interceptedCatalog;
private readonly IInterceptionConfiguration configuration;
private readonly IDictionary<ComposablePartDefinition, InnerPartDefinition> innerParts;
private IQueryable<ComposablePartDefinition> innerPartsQueryable;
/// <summary>
/// Initializes a new instance of the <see cref="InterceptingCatalog"/> class.
/// </summary>
/// <param name="interceptedCatalog">Catalog to be intercepted.</param>
/// <param name="configuration">Interception configuration.</param>
public InterceptingCatalog(ComposablePartCatalog interceptedCatalog, IInterceptionConfiguration configuration)
{
if (interceptedCatalog == null) throw new ArgumentNullException("interceptedCatalog");
if (configuration == null) throw new ArgumentNullException("configuration");
this.interceptedCatalog = interceptedCatalog;
this.configuration = configuration;
this.innerParts = new Dictionary<ComposablePartDefinition, InnerPartDefinition>();
InitializeRecomposition();
InitializeHandlers();
}
private void InitializeHandlers()
{
foreach(var handler in this.configuration.ExportHandlers)
{
handler.Initialize(this.interceptedCatalog);
}
foreach (var handler in this.configuration.PartHandlers)
{
handler.Initialize(this.interceptedCatalog);
handler.Changed += HandlePartHandlerChanged;
}
}
private void InitializeRecomposition()
{
var interceptedCatalogNotifyChange = interceptedCatalog as INotifyComposablePartCatalogChanged;
if (interceptedCatalogNotifyChange != null)
{
interceptedCatalogNotifyChange.Changing += HandleInterceptedCatalogChanging;
}
}
private void HandleInterceptedCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)
{
Recompose(e.AddedDefinitions, e.RemovedDefinitions, e.AtomicComposition);
}
private void HandlePartHandlerChanged(object sender, PartHandlerChangedEventArgs e)
{
Recompose(e.AddedDefinitions, e.RemovedDefinitions, null);
}
private void EnsurePartsInitialized()
{
if (GetParts() == null)
{
throw new InvalidOperationException();
}
}
private void Recompose(IEnumerable<ComposablePartDefinition> added, IEnumerable<ComposablePartDefinition> removed, AtomicComposition outerComposition)
{
EnsurePartsInitialized();
var addedInnerPartDefinitions = added.Select(GetPart);
var removedInnerPartDefinitions = removed.Select(def => innerParts[def]);
using (var composition = new AtomicComposition(outerComposition))
{
var addedDefinitions = addedInnerPartDefinitions.Select(p => p.Definition).ToList();
var removedDefinitions = removedInnerPartDefinitions.Select(p => p.Definition).ToList();
composition.AddCompleteAction(() => OnChanged(
addedDefinitions,
removedDefinitions,
null));
OnChanging(
addedDefinitions,
removedDefinitions,
composition);
foreach (var innerPart in addedInnerPartDefinitions)
{
innerParts.Add(innerPart.Original, innerPart);
}
foreach (var removedDefinition in removedInnerPartDefinitions)
{
innerParts.Remove(removedDefinition.Original);
}
composition.Complete();
}
}
/// <summary>
/// Gets a list of export definitions that match the constraint defined
/// by the specified <see cref="ImportDefinition"/> object.
/// </summary>
/// <returns>
/// A collection of <see cref="T:System.Tuple`2"/> containing the <see cref="ExportDefinition"/>
/// objects and their associated <see cref="ComposablePartDefinition"/> objects for objects that match the constraint specified by <paramref name="definition"/>.
/// </returns>
/// <param name="definition">The conditions of the <see cref="ExportDefinition"/>
/// objects to be returned.</param>
/// <exception cref="ObjectDisposedException">The <see cref="ComposablePartCatalog"/> object has been disposed of.</exception>
/// <exception cref="ArgumentNullException"><paramref name="definition"/> is null.</exception>
public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
{
if (definition == null) throw new ArgumentNullException("definition");
var exports = base.GetExports(definition);
foreach (var handler in this.configuration.ExportHandlers)
{
exports = handler.GetExports(definition, exports);
}
return exports;
}
/// <summary>
/// Gets the part definitions that are contained in the catalog.
/// </summary>
/// <returns>
/// The <see cref="ComposablePartDefinition"/> contained in the <see cref="ComposablePartCatalog"/>.
/// </returns>
/// <exception cref="ObjectDisposedException">The <see cref="ComposablePartCatalog"/> object has been disposed of.</exception>
public override IQueryable<ComposablePartDefinition> Parts
{
get { return GetParts(); }
}
private IQueryable<ComposablePartDefinition> GetParts()
{
if (this.innerPartsQueryable == null)
{
lock (this.synchRoot)
{
if (this.innerPartsQueryable == null)
{
IEnumerable<ComposablePartDefinition> parts =
new List<ComposablePartDefinition>(this.interceptedCatalog.Parts);
foreach (var handler in this.configuration.PartHandlers)
{
parts = handler.GetParts(parts);
}
foreach (var innerPart in parts.Select(GetPart))
{
this.innerParts.Add(innerPart.Original, innerPart);
}
this.innerPartsQueryable = this.innerParts.Values.Select(p => p.Definition).AsQueryable();
}
}
}
return this.innerPartsQueryable;
}
private InnerPartDefinition GetPart(ComposablePartDefinition partDefinition)
{
var interceptor = GetInterceptor(partDefinition);
if (interceptor == null)
{
// If the part is not being intercepted, suppress interception
// by returning the original part
return new InnerPartDefinition(partDefinition);
}
var innerPart = new InnerPartDefinition(
partDefinition,
new InterceptingComposablePartDefinition(partDefinition, interceptor));
return innerPart;
}
private IExportedValueInterceptor GetInterceptor(ComposablePartDefinition partDefinition)
{
var interceptors = new List<IExportedValueInterceptor>(this.configuration.Interceptors);
var partInterceptors = from criteria in this.configuration.InterceptionCriteria
where criteria.Predicate(partDefinition)
select criteria.Interceptor;
interceptors.AddRange(partInterceptors);
if (interceptors.Count == 0) return null;
if (interceptors.Count == 1) return interceptors[0];
return GetCompositeInterceptor(interceptors);
}
#region INotifyComposablePartCatalogChanged Implementation
/// <summary>
/// Occurs when a <see cref="ComposablePartCatalog"/> has changed.
/// </summary>
public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;
/// <summary>
/// Occurs when a <see cref="ComposablePartCatalog"/> is changing.
/// </summary>
public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;
/// <summary>
/// Fires the <see cref="Changing"/> event.
/// </summary>
/// <param name="addedDefinitions">The collection of added <see cref="ComposablePartDefinition"/> instances.</param>
/// <param name="removedDefinitions">The collection of removed <see cref="ComposablePartDefinition"/> instances.</param>
/// <param name="composition"><see cref="AtomicComposition"/> instance.</param>
protected virtual void OnChanging(IEnumerable<ComposablePartDefinition> addedDefinitions, IEnumerable<ComposablePartDefinition> removedDefinitions, AtomicComposition composition)
{
if (Changing != null)
{
if (addedDefinitions == null) addedDefinitions = Enumerable.Empty<ComposablePartDefinition>();
if (removedDefinitions == null) removedDefinitions = Enumerable.Empty<ComposablePartDefinition>();
var args = new ComposablePartCatalogChangeEventArgs(
addedDefinitions, removedDefinitions, composition);
Changing(this, args);
}
}
/// <summary>
/// Fires the <see cref="Changed"/> event.
/// </summary>
/// <param name="addedDefinitions">The collection of added <see cref="ComposablePartDefinition"/> instances.</param>
/// <param name="removedDefinitions">The collection of removed <see cref="ComposablePartDefinition"/> instances.</param>
/// <param name="composition"><see cref="AtomicComposition"/> instance.</param>
protected virtual void OnChanged(IEnumerable<ComposablePartDefinition> addedDefinitions, IEnumerable<ComposablePartDefinition> removedDefinitions, AtomicComposition composition)
{
if (Changed != null)
{
if (addedDefinitions == null) addedDefinitions = Enumerable.Empty<ComposablePartDefinition>();
if (removedDefinitions == null) removedDefinitions = Enumerable.Empty<ComposablePartDefinition>();
var args = new ComposablePartCatalogChangeEventArgs(
addedDefinitions, removedDefinitions, composition);
Changed(this, args);
}
}
#endregion
/// <summary>
/// Method called in order to aggregate two or more <see cref="IExportedValueInterceptor"/> instances
/// into a single interceptor. By default, the <see cref="CompositeValueInterceptor"/> instance is created.
/// </summary>
/// <param name="interceptors">Interceptors to be aggregated.</param>
/// <returns>New instance of the <see cref="IExportedValueInterceptor"/>.</returns>
protected virtual IExportedValueInterceptor GetCompositeInterceptor(
IEnumerable<IExportedValueInterceptor> interceptors)
{
return new CompositeValueInterceptor(interceptors.ToArray());
}
private class InnerPartDefinition
{
public InnerPartDefinition(ComposablePartDefinition original)
{
if (original == null)
{
throw new ArgumentNullException("original");
}
Original = original;
}
public InnerPartDefinition(ComposablePartDefinition original, ComposablePartDefinition intercepting)
{
if (original == null)
{
throw new ArgumentNullException("original");
}
if (intercepting == null)
{
throw new ArgumentNullException("intercepting");
}
Original = original;
Intercepting = intercepting;
}
public ComposablePartDefinition Original { get; private set; }
private ComposablePartDefinition Intercepting { get; set; }
public ComposablePartDefinition Definition
{
get { return Intercepting ?? Original; }
}
public bool Equals(InnerPartDefinition other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Original, Original);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (InnerPartDefinition)) return false;
return Equals((InnerPartDefinition) obj);
}
public override int GetHashCode()
{
return Original.GetHashCode();
}
}
}
}