1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections;
\r
6 using System.Collections.Generic;
\r
7 using System.ComponentModel.Composition;
\r
8 using System.ComponentModel.Composition.AttributedModel;
\r
9 using System.ComponentModel.Composition.Primitives;
\r
10 using System.Globalization;
\r
12 using System.Reflection;
\r
13 using Microsoft.Internal;
\r
14 using Microsoft.Internal.Collections;
\r
15 using System.Collections.ObjectModel;
\r
16 using System.ComponentModel.Composition.ReflectionModel;
\r
18 namespace System.ComponentModel.Composition.Hosting
\r
20 internal static class CompositionServices
\r
22 internal static readonly Type InheritedExportAttributeType = typeof(InheritedExportAttribute);
\r
23 internal static readonly Type ExportAttributeType = typeof(ExportAttribute);
\r
24 internal static readonly Type AttributeType = typeof(Attribute);
\r
25 internal static readonly Type ObjectType = typeof(object);
\r
27 private static readonly string[] reservedMetadataNames = new string[]
\r
29 CompositionConstants.PartCreationPolicyMetadataName
\r
32 internal static Type GetDefaultTypeFromMember(this MemberInfo member)
\r
34 Assumes.NotNull(member);
\r
36 switch (member.MemberType)
\r
38 case MemberTypes.Property:
\r
39 return ((PropertyInfo)member).PropertyType;
\r
41 case MemberTypes.NestedType:
\r
42 case MemberTypes.TypeInfo:
\r
43 return ((Type)member);
\r
45 case MemberTypes.Field:
\r
47 Assumes.IsTrue(member.MemberType == MemberTypes.Field);
\r
48 return ((FieldInfo)member).FieldType;
\r
52 internal static string GetContractNameFromExport(this MemberInfo member, ExportAttribute export)
\r
54 if (!string.IsNullOrEmpty(export.ContractName))
\r
56 return export.ContractName;
\r
59 if (export.ContractType != null)
\r
61 return AttributedModelServices.GetContractName(export.ContractType);
\r
64 if (member.MemberType == MemberTypes.Method)
\r
66 return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
\r
69 return AttributedModelServices.GetContractName(member.GetDefaultTypeFromMember());
\r
72 internal static string GetTypeIdentityFromExport(this MemberInfo member, ExportAttribute export)
\r
74 if (export.ContractType != null)
\r
76 return AttributedModelServices.GetTypeIdentity(export.ContractType);
\r
79 if (member.MemberType == MemberTypes.Method)
\r
81 return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
\r
84 return AttributedModelServices.GetTypeIdentity(member.GetDefaultTypeFromMember());
\r
87 internal static Type GetContractTypeFromImport(this IAttributedImport import, ImportType importType)
\r
89 if (import.ContractType != null)
\r
91 return import.ContractType;
\r
94 return importType.ContractType;
\r
97 internal static string GetContractNameFromImport(this IAttributedImport import, ImportType importType)
\r
99 if (!string.IsNullOrEmpty(import.ContractName))
\r
101 return import.ContractName;
\r
104 Type contractType = import.GetContractTypeFromImport(importType);
\r
106 return AttributedModelServices.GetContractName(contractType);
\r
109 internal static string GetTypeIdentityFromImport(this IAttributedImport import, ImportType importType)
\r
111 Type contractType = import.GetContractTypeFromImport(importType);
\r
113 // For our importers we treat object as not having a type identity
\r
114 if (contractType == CompositionServices.ObjectType)
\r
119 return AttributedModelServices.GetTypeIdentity(contractType);
\r
122 internal static IDictionary<string, object> GetPartMetadataForType(this Type type, CreationPolicy creationPolicy)
\r
124 IDictionary<string, object> dictionary = new Dictionary<string, object>(StringComparers.MetadataKeyNames);
\r
126 if (creationPolicy != CreationPolicy.Any)
\r
128 dictionary.Add(CompositionConstants.PartCreationPolicyMetadataName, creationPolicy);
\r
131 foreach (PartMetadataAttribute partMetadata in type.GetAttributes<PartMetadataAttribute>())
\r
133 if (reservedMetadataNames.Contains(partMetadata.Name, StringComparers.MetadataKeyNames)
\r
134 || dictionary.ContainsKey(partMetadata.Name))
\r
136 // Perhaps we should log an error here so that people know this value is being ignored.
\r
140 dictionary.Add(partMetadata.Name, partMetadata.Value);
\r
143 if (dictionary.Count == 0)
\r
145 return MetadataServices.EmptyMetadata;
\r
153 internal static void TryExportMetadataForMember(this MemberInfo member, out IDictionary<string, object> dictionary)
\r
155 dictionary = new Dictionary<string, object>();
\r
157 foreach (var attr in member.GetAttributes<Attribute>())
\r
159 var provider = attr as ExportMetadataAttribute;
\r
161 if (provider != null)
\r
163 if (reservedMetadataNames.Contains(provider.Name, StringComparers.MetadataKeyNames))
\r
165 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
\r
168 // we pass "null" for valueType which would make it inferred. We don;t have additional type information when metadata
\r
169 // goes through the ExportMetadataAttribute path
\r
170 if (!dictionary.TryContributeMetadataValue(provider.Name, provider.Value, null, provider.IsMultiple))
\r
172 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), provider.Name);
\r
177 Type attrType = attr.GetType();
\r
178 if ((attrType != CompositionServices.ExportAttributeType) && attrType.IsAttributeDefined<MetadataAttributeAttribute>(true))
\r
180 bool allowsMultiple = false;
\r
181 AttributeUsageAttribute usage = attrType.GetFirstAttribute<AttributeUsageAttribute>(true);
\r
185 allowsMultiple = usage.AllowMultiple;
\r
188 foreach (PropertyInfo pi in attrType.GetProperties())
\r
190 if (pi.DeclaringType == CompositionServices.ExportAttributeType || pi.DeclaringType == CompositionServices.AttributeType)
\r
192 // Don't contribute metadata properies from the base attribute types.
\r
196 if (reservedMetadataNames.Contains(pi.Name, StringComparers.MetadataKeyNames))
\r
198 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
\r
201 object value = pi.GetValue(attr, null);
\r
203 if (value != null && !IsValidAttributeType(value.GetType()))
\r
205 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_MetadataContainsValueWithInvalidType, pi.GetDisplayName(), value.GetType().GetDisplayName());
\r
208 if (!dictionary.TryContributeMetadataValue(pi.Name, value, pi.PropertyType, allowsMultiple))
\r
210 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), pi.Name);
\r
217 // Need Keys.ToArray because we alter the dictionary in the loop
\r
218 foreach (var key in dictionary.Keys.ToArray())
\r
220 var list = dictionary[key] as MetadataList;
\r
223 dictionary[key] = list.ToArray();
\r
230 private static bool TryContributeMetadataValue(this IDictionary<string, object> dictionary, string name, object value, Type valueType, bool allowsMultiple)
\r
232 object metadataValue;
\r
233 if (!dictionary.TryGetValue(name, out metadataValue))
\r
235 if (allowsMultiple)
\r
237 var list = new MetadataList();
\r
238 list.Add(value, valueType);
\r
242 dictionary.Add(name, value);
\r
246 var list = metadataValue as MetadataList;
\r
247 if (!allowsMultiple || list == null)
\r
249 // Either single value already found when should be multiple
\r
250 // or a duplicate name already exists
\r
251 dictionary.Remove(name);
\r
255 list.Add(value, valueType);
\r
260 private class MetadataList
\r
262 private Type _arrayType = null;
\r
263 private bool _containsNulls = false;
\r
264 private static readonly Type ObjectType = typeof(object);
\r
265 private static readonly Type TypeType = typeof(Type);
\r
266 private Collection<object> _innerList = new Collection<object>();
\r
268 public void Add(object item, Type itemType)
\r
270 this._containsNulls |= (item == null);
\r
272 // if we've been passed typeof(object), we basically have no type inmformation
\r
273 if (itemType == ObjectType)
\r
278 // if we have no type information, get it from the item, if we can
\r
279 if ((itemType == null) && (item != null))
\r
281 itemType = item.GetType();
\r
284 // Types are special, because the are abstract classes, so if the item casts to Type, we assume System.Type
\r
287 itemType = TypeType;
\r
290 // only try to call this if we got a meaningful type
\r
291 if (itemType != null)
\r
293 this.InferArrayType(itemType);
\r
296 this._innerList.Add(item);
\r
299 private void InferArrayType(Type itemType)
\r
301 Assumes.NotNull(itemType);
\r
303 if (this._arrayType == null)
\r
305 // this is the first typed element we've been given, it sets the type of the array
\r
306 this._arrayType = itemType;
\r
310 // if there's a disagreement on the array type, we flip to Object
\r
311 // NOTE : we can try to do better in the future to find common base class, but given that we support very limited set of types
\r
312 // in metadata right now, it's a moot point
\r
313 if (this._arrayType != itemType)
\r
315 this._arrayType = ObjectType;
\r
320 public Array ToArray()
\r
322 if (this._arrayType == null)
\r
324 // if the array type has not been set, assume Object
\r
325 this._arrayType = ObjectType;
\r
327 else if (this._containsNulls && this._arrayType.IsValueType)
\r
329 // if the array type is a value type and we have seen nulls, then assume Object
\r
330 this._arrayType = ObjectType;
\r
333 Array array = Array.CreateInstance(this._arrayType, this._innerList.Count);
\r
335 for(int i = 0; i < array.Length; i++)
\r
337 array.SetValue(this._innerList[i], i);
\r
343 //UNDONE: Need to add these warnings somewhere...Dev10:472538 should address this.
\r
344 //internal static CompositionResult MatchRequiredMetadata(this IDictionary<string, object> metadata, IEnumerable<string> requiredMetadata, string contractName)
\r
346 // Assumes.IsTrue(metadata != null);
\r
348 // var result = CompositionResult.SucceededResult;
\r
350 // var missingMetadata = (requiredMetadata == null) ? null : requiredMetadata.Except<string>(metadata.Keys);
\r
351 // if (missingMetadata != null && missingMetadata.Any())
\r
353 // result = result.MergeIssue(
\r
354 // CompositionError.CreateIssueAsWarning(CompositionErrorId.RequiredMetadataNotFound,
\r
355 // Strings.RequiredMetadataNotFound,
\r
357 // string.Join(", ", missingMetadata.ToArray())));
\r
359 // return new CompositionResult(false, result.Issues);
\r
365 internal static IEnumerable<KeyValuePair<string, Type>> GetRequiredMetadata(Type metadataViewType)
\r
367 if ((metadataViewType == null) ||
\r
368 ExportServices.IsDefaultMetadataViewType(metadataViewType) ||
\r
369 ExportServices.IsDictionaryConstructorViewType(metadataViewType) ||
\r
370 !metadataViewType.IsInterface)
\r
372 return Enumerable.Empty<KeyValuePair<string, Type>>();
\r
375 // A metadata view is required to be an Intrerface, and therefore only properties are allowed
\r
376 List<PropertyInfo> properties = metadataViewType.GetAllProperties().
\r
377 Where(property => property.GetFirstAttribute<DefaultValueAttribute>() == null).
\r
380 // NOTE : this is a carefully found balance between eager and delay-evaluation - the properties are filtered once and upfront
\r
381 // whereas the key/Type pairs are created every time. The latter is fine as KVPs are structs and as such copied on access regardless.
\r
382 // This also allows us to avoid creation of List<KVP> which - at least according to FxCop - leads to isues with NGEN
\r
383 return properties.Select(property => new KeyValuePair<string, Type>(property.Name, property.PropertyType));
\r
386 internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
\r
390 engine.SatisfyImports(part);
\r
392 catch (CompositionException ex)
\r
394 throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
\r
399 return part.GetExportedValue(definition);
\r
401 catch (ComposablePartException ex)
\r
403 throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
\r
407 internal static bool IsRecomposable(this ComposablePart part)
\r
409 return part.ImportDefinitions.Any(import => import.IsRecomposable);
\r
412 internal static CompositionResult<T> TryInvoke<T>(Func<T> action)
\r
416 T value = action();
\r
417 return new CompositionResult<T>(value);
\r
419 catch (CompositionException ex)
\r
421 return new CompositionResult<T>(ex.Errors);
\r
425 internal static CompositionResult TryInvoke(Action action)
\r
430 return CompositionResult.SucceededResult;
\r
432 catch (CompositionException ex)
\r
434 return new CompositionResult(ex.Errors);
\r
438 internal static CompositionResult TryFire<TEventArgs>(EventHandler<TEventArgs> _delegate, object sender, TEventArgs e)
\r
439 where TEventArgs : EventArgs
\r
441 CompositionResult result = CompositionResult.SucceededResult;
\r
442 foreach (EventHandler<TEventArgs> _subscriber in _delegate.GetInvocationList())
\r
446 _subscriber.Invoke(sender, e);
\r
448 catch (CompositionException ex)
\r
450 result = result.MergeErrors(ex.Errors);
\r
457 internal static CreationPolicy GetRequiredCreationPolicy(this ImportDefinition definition)
\r
459 ContractBasedImportDefinition contractDefinition = definition as ContractBasedImportDefinition;
\r
461 if (contractDefinition != null)
\r
463 return contractDefinition.RequiredCreationPolicy;
\r
466 return CreationPolicy.Any;
\r
470 /// Returns a value indicating whether cardinality is
\r
471 /// <see cref="ImportCardinality.ZeroOrOne"/> or
\r
472 /// <see cref="ImportCardinality.ExactlyOne"/>.
\r
474 internal static bool IsAtMostOne(this ImportCardinality cardinality)
\r
476 return cardinality == ImportCardinality.ZeroOrOne || cardinality == ImportCardinality.ExactlyOne;
\r
479 private static bool IsValidAttributeType(Type type)
\r
481 return IsValidAttributeType(type, true);
\r
484 private static bool IsValidAttributeType(Type type, bool arrayAllowed)
\r
486 Assumes.NotNull(type);
\r
487 // Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3.
\r
489 // One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
\r
490 if (type.IsPrimitive)
\r
495 if (type == typeof(string))
\r
500 // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility
\r
501 if (type.IsEnum && type.IsVisible)
\r
506 if (typeof(Type).IsAssignableFrom(type))
\r
511 // Single-dimensional arrays of the above types.
\r
512 if (arrayAllowed && type.IsArray &&
\r
513 type.GetArrayRank() == 1 &&
\r
514 IsValidAttributeType(type.GetElementType(), false))
\r