update MEF to preview 9
[mcs.git] / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / Hosting / TypeCatalog.cs
blobcabc1e5dc5cad6258cede56c98350286cac578cd
1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
4 using System;
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;
12 using System.Linq;
13 using System.Text;
14 using System.Threading;
15 using Microsoft.Internal;
17 namespace System.ComponentModel.Composition.Hosting
19 /// <summary>
20 /// An immutable ComposablePartCatalog created from a type array or a list of managed types. This class is threadsafe.
21 /// It is Disposable.
22 /// </summary>
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;
33 /// <summary>
34 /// Initializes a new instance of the <see cref="TypeCatalog"/> class
35 /// with the specified types.
36 /// </summary>
37 /// <param name="types">
38 /// An <see cref="Array"/> of attributed <see cref="Type"/> objects to add to the
39 /// <see cref="TypeCatalog"/>.
40 /// </param>
41 /// <exception cref="ArgumentNullException">
42 /// <paramref name="types"/> is <see langword="null"/>.
43 /// </exception>
44 /// <exception cref="ArgumentException">
45 /// <paramref name="types"/> contains an element that is <see langword="null"/>.
46 /// <para>
47 /// -or-
48 /// </para>
49 /// <paramref name="types"/> contains an element that was loaded in the Reflection-only context.
50 /// </exception>
51 public TypeCatalog(params Type[] types)
52 : this(types, (ICompositionElement)null)
56 /// <summary>
57 /// Initializes a new instance of the <see cref="TypeCatalog"/> class
58 /// with the specified types.
59 /// </summary>
60 /// <param name="types">
61 /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add
62 /// to the <see cref="TypeCatalog"/>.
63 /// </param>
64 /// <exception cref="ArgumentNullException">
65 /// <paramref name="types"/> is <see langword="null"/>.
66 /// </exception>
67 /// <exception cref="ArgumentException">
68 /// <paramref name="types"/> contains an element that is <see langword="null"/>.
69 /// <para>
70 /// -or-
71 /// </para>
72 /// <paramref name="types"/> contains an element that was loaded in the reflection-only context.
73 /// </exception>
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)
85 if (type == null)
87 throw ExceptionBuilder.CreateContainsNullElement("types");
89 #if !SILVERLIGHT
90 if (type.Assembly.ReflectionOnly)
92 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Argument_ElementReflectionOnlyType, "types"), "types");
94 #endif
97 this._types = types.ToArray();
98 this._definitionOrigin = definitionOrigin ?? this;
99 #if !SILVERLIGHT
100 this._contractPartIndex = new Lazy<IDictionary<string, List<ComposablePartDefinition>>>(this.CreateIndex, true);
101 #else
102 this._contractPartIndex = new Lazy<IDictionary<string, List<ComposablePartDefinition>>>(this.CreateIndex);
103 #endif
107 /// <summary>
108 /// Gets the part definitions of the catalog.
109 /// </summary>
110 /// <value>
111 /// A <see cref="IQueryable{T}"/> of <see cref="ComposablePartDefinition"/> objects of the
112 /// <see cref="TypeCatalog"/>.
113 /// </value>
114 /// <exception cref="ObjectDisposedException">
115 /// The <see cref="TypeCatalog"/> has been disposed of.
116 /// </exception>
117 public override IQueryable<ComposablePartDefinition> Parts
121 this.ThrowIfDisposed();
123 return this.PartsInternal;
127 /// <summary>
128 /// Gets the display name of the type catalog.
129 /// </summary>
130 /// <value>
131 /// A <see cref="String"/> containing a human-readable display name of the <see cref="TypeCatalog"/>.
132 /// </value>
133 [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
134 string ICompositionElement.DisplayName
136 get { return this.GetDisplayName(); }
139 /// <summary>
140 /// Gets the composition element from which the type catalog originated.
141 /// </summary>
142 /// <value>
143 /// This property always returns <see langword="null"/>.
144 /// </value>
145 [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
146 ICompositionElement ICompositionElement.Origin
148 get { return null; }
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();
175 this._types = null;
176 this._queryableParts = queryableParts;
181 return this._queryableParts;
185 /// <summary>
186 /// Returns the export definitions that match the constraint defined by the specified definition.
187 /// </summary>
188 /// <param name="definition">
189 /// The <see cref="ImportDefinition"/> that defines the conditions of the
190 /// <see cref="ExportDefinition"/> objects to return.
191 /// </param>
192 /// <returns>
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"/>.
197 /// </returns>
198 /// <exception cref="ArgumentNullException">
199 /// <paramref name="definition"/> is <see langword="null"/>.
200 /// </exception>
201 /// <exception cref="ObjectDisposedException">
202 /// The <see cref="ComposablePartCatalog"/> has been disposed of.
203 /// </exception>
204 /// <remarks>
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}"/>.
209 /// </note>
210 /// </remarks>
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));
234 return exports;
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;
253 else
255 return null;
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);
276 return index;
279 /// <summary>
280 /// Returns a string representation of the type catalog.
281 /// </summary>
282 /// <returns>
283 /// A <see cref="String"/> containing the string representation of the <see cref="TypeCatalog"/>.
284 /// </returns>
285 public override string ToString()
287 return this.GetDisplayName();
290 protected override void Dispose(bool disposing)
292 if (disposing)
294 this._isDisposed = true;
297 base.Dispose(disposing);
300 private string GetDisplayName()
302 return String.Format(CultureInfo.CurrentCulture,
303 Strings.TypeCatalog_DisplayNameFormat,
304 this.GetType().Name,
305 this.GetTypesDisplay());
308 private string GetTypesDisplay()
310 int count = this.PartsInternal.Count();
311 if (count == 0)
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);
323 builder.Append(" ");
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);