1
// -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
5 using System
.Collections
.Generic
;
6 using System
.ComponentModel
.Composition
.AttributedModel
;
7 using System
.ComponentModel
.Composition
.Primitives
;
8 using System
.ComponentModel
.Composition
.ReflectionModel
;
9 using System
.Diagnostics
;
10 using System
.Diagnostics
.CodeAnalysis
;
11 using System
.Globalization
;
14 using System
.Threading
;
15 using Microsoft
.Internal
;
17 namespace System
.ComponentModel
.Composition
.Hosting
20 /// An immutable ComposablePartCatalog created from a type array or a list of managed types. This class is threadsafe.
23 [DebuggerTypeProxy(typeof(ComposablePartCatalogDebuggerProxy
))]
24 public class TypeCatalog
: ComposablePartCatalog
, ICompositionElement
26 private readonly object _thisLock
= new object();
27 private Type
[] _types
= null;
28 private volatile IQueryable
<ComposablePartDefinition
> _queryableParts
;
29 private volatile bool _isDisposed
= false;
30 private readonly ICompositionElement _definitionOrigin
;
31 private readonly Lazy
<IDictionary
<string, List
<ComposablePartDefinition
>>> _contractPartIndex
;
34 /// Initializes a new instance of the <see cref="TypeCatalog"/> class
35 /// with the specified types.
37 /// <param name="types">
38 /// An <see cref="Array"/> of attributed <see cref="Type"/> objects to add to the
39 /// <see cref="TypeCatalog"/>.
41 /// <exception cref="ArgumentNullException">
42 /// <paramref name="types"/> is <see langword="null"/>.
44 /// <exception cref="ArgumentException">
45 /// <paramref name="types"/> contains an element that is <see langword="null"/>.
49 /// <paramref name="types"/> contains an element that was loaded in the Reflection-only context.
51 public TypeCatalog(params Type
[] types
)
52 : this(types
, (ICompositionElement
)null)
57 /// Initializes a new instance of the <see cref="TypeCatalog"/> class
58 /// with the specified types.
60 /// <param name="types">
61 /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add
62 /// to the <see cref="TypeCatalog"/>.
64 /// <exception cref="ArgumentNullException">
65 /// <paramref name="types"/> is <see langword="null"/>.
67 /// <exception cref="ArgumentException">
68 /// <paramref name="types"/> contains an element that is <see langword="null"/>.
72 /// <paramref name="types"/> contains an element that was loaded in the reflection-only context.
74 public TypeCatalog(IEnumerable
<Type
> types
)
75 : this(types
, (ICompositionElement
)null)
79 internal TypeCatalog(IEnumerable
<Type
> types
, ICompositionElement definitionOrigin
)
81 Requires
.NotNull(types
, "types");
83 foreach (Type type
in types
)
87 throw ExceptionBuilder
.CreateContainsNullElement("types");
90 if (type
.Assembly
.ReflectionOnly
)
92 throw new ArgumentException(string.Format(CultureInfo
.CurrentCulture
, Strings
.Argument_ElementReflectionOnlyType
, "types"), "types");
97 this._types
= types
.ToArray();
98 this._definitionOrigin
= definitionOrigin
?? this;
100 this._contractPartIndex
= new Lazy
<IDictionary
<string, List
<ComposablePartDefinition
>>>(this.CreateIndex
, true);
102 this._contractPartIndex
= new Lazy
<IDictionary
<string, List
<ComposablePartDefinition
>>>(this.CreateIndex
);
108 /// Gets the part definitions of the catalog.
111 /// A <see cref="IQueryable{T}"/> of <see cref="ComposablePartDefinition"/> objects of the
112 /// <see cref="TypeCatalog"/>.
114 /// <exception cref="ObjectDisposedException">
115 /// The <see cref="TypeCatalog"/> has been disposed of.
117 public override IQueryable
<ComposablePartDefinition
> Parts
121 this.ThrowIfDisposed();
123 return this.PartsInternal
;
128 /// Gets the display name of the type catalog.
131 /// A <see cref="String"/> containing a human-readable display name of the <see cref="TypeCatalog"/>.
133 [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
134 string ICompositionElement
.DisplayName
136 get { return this.GetDisplayName(); }
140 /// Gets the composition element from which the type catalog originated.
143 /// This property always returns <see langword="null"/>.
145 [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
146 ICompositionElement ICompositionElement
.Origin
151 private IQueryable
<ComposablePartDefinition
> PartsInternal
155 if (this._queryableParts
== null)
157 lock (this._thisLock
)
159 if (this._queryableParts
== null)
161 Assumes
.NotNull(this._types
);
163 var collection
= new List
<ComposablePartDefinition
>();
164 foreach (Type type
in this._types
)
166 var definition
= AttributedModelDiscovery
.CreatePartDefinitionIfDiscoverable(type
, _definitionOrigin
);
167 if (definition
!= null)
169 collection
.Add(definition
);
172 IQueryable
<ComposablePartDefinition
> queryableParts
= collection
.AsQueryable();
173 Thread
.MemoryBarrier();
176 this._queryableParts
= queryableParts
;
181 return this._queryableParts
;
186 /// Returns the export definitions that match the constraint defined by the specified definition.
188 /// <param name="definition">
189 /// The <see cref="ImportDefinition"/> that defines the conditions of the
190 /// <see cref="ExportDefinition"/> objects to return.
193 /// An <see cref="IEnumerable{T}"/> of <see cref="Tuple{T1, T2}"/> containing the
194 /// <see cref="ExportDefinition"/> objects and their associated
195 /// <see cref="ComposablePartDefinition"/> for objects that match the constraint defined
196 /// by <paramref name="definition"/>.
198 /// <exception cref="ArgumentNullException">
199 /// <paramref name="definition"/> is <see langword="null"/>.
201 /// <exception cref="ObjectDisposedException">
202 /// The <see cref="ComposablePartCatalog"/> has been disposed of.
205 /// <note type="inheritinfo">
206 /// Overriders of this property should never return <see langword="null"/>, if no
207 /// <see cref="ExportDefinition"/> match the conditions defined by
208 /// <paramref name="definition"/>, return an empty <see cref="IEnumerable{T}"/>.
211 public override IEnumerable
<Tuple
<ComposablePartDefinition
, ExportDefinition
>> GetExports(ImportDefinition definition
)
213 this.ThrowIfDisposed();
215 Requires
.NotNull(definition
, "definition");
217 IEnumerable
<ComposablePartDefinition
> candidateParts
= this.GetCandidateParts(definition
);
218 if (candidateParts
== null)
220 return Enumerable
.Empty
<Tuple
<ComposablePartDefinition
, ExportDefinition
>>();
223 var exports
= new List
<Tuple
<ComposablePartDefinition
, ExportDefinition
>>();
224 foreach (var part
in candidateParts
)
226 foreach (var export
in part
.ExportDefinitions
)
228 if (definition
.IsConstraintSatisfiedBy(export
))
230 exports
.Add(new Tuple
<ComposablePartDefinition
, ExportDefinition
>(part
, export
));
237 private IEnumerable
<ComposablePartDefinition
> GetCandidateParts(ImportDefinition definition
)
239 string contractName
= definition
.ContractName
;
241 // Empty string represents a non-contract based import and thus the constraint needs
242 // to be applied to all the possible exports in this catalog.
243 if (string.IsNullOrEmpty(contractName
))
245 return this.PartsInternal
;
248 List
<ComposablePartDefinition
> candidateParts
= null;
249 if (this._contractPartIndex
.Value
.TryGetValue(contractName
, out candidateParts
))
251 return candidateParts
;
259 private IDictionary
<string, List
<ComposablePartDefinition
>> CreateIndex()
261 Dictionary
<string, List
<ComposablePartDefinition
>> index
= new Dictionary
<string, List
<ComposablePartDefinition
>>(StringComparers
.ContractName
);
263 foreach (var part
in this.PartsInternal
)
265 foreach (string contractName
in part
.ExportDefinitions
.Select(export
=> export
.ContractName
).Distinct())
267 List
<ComposablePartDefinition
> contractParts
= null;
268 if (!index
.TryGetValue(contractName
, out contractParts
))
270 contractParts
= new List
<ComposablePartDefinition
>();
271 index
.Add(contractName
, contractParts
);
273 contractParts
.Add(part
);
280 /// Returns a string representation of the type catalog.
283 /// A <see cref="String"/> containing the string representation of the <see cref="TypeCatalog"/>.
285 public override string ToString()
287 return this.GetDisplayName();
290 protected override void Dispose(bool disposing
)
294 this._isDisposed
= true;
297 base.Dispose(disposing
);
300 private string GetDisplayName()
302 return String
.Format(CultureInfo
.CurrentCulture
,
303 Strings
.TypeCatalog_DisplayNameFormat
,
305 this.GetTypesDisplay());
308 private string GetTypesDisplay()
310 int count
= this.PartsInternal
.Count();
313 return Strings
.TypeCatalog_Empty
;
316 const int displayCount
= 2;
317 StringBuilder builder
= new StringBuilder();
318 foreach (ReflectionComposablePartDefinition definition
in this.PartsInternal
.Take(displayCount
))
320 if (builder
.Length
> 0)
322 builder
.Append(CultureInfo
.CurrentCulture
.TextInfo
.ListSeparator
);
326 builder
.Append(definition
.GetPartType().GetDisplayName());
329 if (count
> displayCount
)
330 { // Add an elipse to indicate that there
331 // are more types than actually listed
332 builder
.Append(CultureInfo
.CurrentCulture
.TextInfo
.ListSeparator
);
333 builder
.Append(" ...");
336 return builder
.ToString();
339 private void ThrowIfDisposed()
341 if (this._isDisposed
)
343 throw ExceptionBuilder
.CreateObjectDisposed(this);