PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.ComponentModel.Composition.4.5/src/ComponentModel/System/ComponentModel/Composition/Hosting/CompositionServices.cs

https://bitbucket.org/danipen/mono
C# | 662 lines | 506 code | 97 blank | 59 comment | 119 complexity | d494ae7a576b8961516c91f233bee03a MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. // -----------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // -----------------------------------------------------------------------
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.ComponentModel.Composition;
  8. using System.ComponentModel.Composition.AttributedModel;
  9. using System.ComponentModel.Composition.Primitives;
  10. using System.Globalization;
  11. using System.Linq;
  12. using System.Reflection;
  13. using Microsoft.Internal;
  14. using Microsoft.Internal.Collections;
  15. using System.Collections.ObjectModel;
  16. using System.ComponentModel.Composition.ReflectionModel;
  17. namespace System.ComponentModel.Composition.Hosting
  18. {
  19. internal static class CompositionServices
  20. {
  21. internal static readonly Type InheritedExportAttributeType = typeof(InheritedExportAttribute);
  22. internal static readonly Type ExportAttributeType = typeof(ExportAttribute);
  23. internal static readonly Type AttributeType = typeof(Attribute);
  24. internal static readonly Type ObjectType = typeof(object);
  25. private static readonly string[] reservedMetadataNames = new string[]
  26. {
  27. CompositionConstants.PartCreationPolicyMetadataName
  28. };
  29. internal static Type GetDefaultTypeFromMember(this MemberInfo member)
  30. {
  31. Assumes.NotNull(member);
  32. switch (member.MemberType)
  33. {
  34. case MemberTypes.Property:
  35. return ((PropertyInfo)member).PropertyType;
  36. case MemberTypes.NestedType:
  37. case MemberTypes.TypeInfo:
  38. return ((Type)member);
  39. case MemberTypes.Field:
  40. default:
  41. Assumes.IsTrue(member.MemberType == MemberTypes.Field);
  42. return ((FieldInfo)member).FieldType;
  43. }
  44. }
  45. internal static Type AdjustSpecifiedTypeIdentityType(this Type specifiedContractType, MemberInfo member)
  46. {
  47. if (member.MemberType == MemberTypes.Method)
  48. {
  49. return specifiedContractType;
  50. }
  51. else
  52. {
  53. return specifiedContractType.AdjustSpecifiedTypeIdentityType(member.GetDefaultTypeFromMember());
  54. }
  55. }
  56. internal static Type AdjustSpecifiedTypeIdentityType(this Type specifiedContractType, Type memberType)
  57. {
  58. Assumes.NotNull(specifiedContractType);
  59. if ((memberType != null) && memberType.IsGenericType && specifiedContractType.IsGenericType)
  60. {
  61. // if the memeber type is closed and the specified contract type is open and they have exatly the same number of parameters
  62. // we will close the specfied contract type
  63. if (specifiedContractType.ContainsGenericParameters && !memberType.ContainsGenericParameters)
  64. {
  65. var typeGenericArguments = memberType.GetGenericArguments();
  66. var metadataTypeGenericArguments = specifiedContractType.GetGenericArguments();
  67. if (typeGenericArguments.Length == metadataTypeGenericArguments.Length)
  68. {
  69. return specifiedContractType.MakeGenericType(typeGenericArguments);
  70. }
  71. }
  72. // if both member type and the contract type are open generic types, make sure that their parameters are ordered the same way
  73. else if(specifiedContractType.ContainsGenericParameters && memberType.ContainsGenericParameters)
  74. {
  75. var memberGenericParameters = memberType.GetPureGenericParameters();
  76. if (specifiedContractType.GetPureGenericArity() == memberGenericParameters.Count)
  77. {
  78. return specifiedContractType.GetGenericTypeDefinition().MakeGenericType(memberGenericParameters.ToArray());
  79. }
  80. }
  81. }
  82. return specifiedContractType;
  83. }
  84. private static string AdjustTypeIdentity(string originalTypeIdentity, Type typeIdentityType)
  85. {
  86. return GenericServices.GetGenericName(originalTypeIdentity, GenericServices.GetGenericParametersOrder(typeIdentityType), GenericServices.GetPureGenericArity(typeIdentityType));
  87. }
  88. internal static void GetContractInfoFromExport(this MemberInfo member, ExportAttribute export, out Type typeIdentityType, out string contractName)
  89. {
  90. typeIdentityType = member.GetTypeIdentityTypeFromExport(export);
  91. if (!string.IsNullOrEmpty(export.ContractName))
  92. {
  93. contractName = export.ContractName;
  94. }
  95. else
  96. {
  97. contractName = member.GetTypeIdentityFromExport(typeIdentityType);
  98. }
  99. }
  100. internal static string GetTypeIdentityFromExport(this MemberInfo member, Type typeIdentityType)
  101. {
  102. if (typeIdentityType != null)
  103. {
  104. string typeIdentity = AttributedModelServices.GetTypeIdentity(typeIdentityType);
  105. if (typeIdentityType.ContainsGenericParameters)
  106. {
  107. typeIdentity = AdjustTypeIdentity(typeIdentity, typeIdentityType);
  108. }
  109. return typeIdentity;
  110. }
  111. else
  112. {
  113. MethodInfo method = member as MethodInfo;
  114. Assumes.NotNull(method);
  115. return AttributedModelServices.GetTypeIdentity(method);
  116. }
  117. }
  118. private static Type GetTypeIdentityTypeFromExport(this MemberInfo member, ExportAttribute export)
  119. {
  120. if (export.ContractType != null)
  121. {
  122. return export.ContractType.AdjustSpecifiedTypeIdentityType(member);
  123. }
  124. else
  125. {
  126. return (member.MemberType != MemberTypes.Method) ? member.GetDefaultTypeFromMember() : null;
  127. }
  128. }
  129. internal static bool IsContractNameSameAsTypeIdentity(this ExportAttribute export)
  130. {
  131. return string.IsNullOrEmpty(export.ContractName);
  132. }
  133. internal static Type GetContractTypeFromImport(this IAttributedImport import, ImportType importType)
  134. {
  135. if (import.ContractType != null)
  136. {
  137. return import.ContractType.AdjustSpecifiedTypeIdentityType(importType.ContractType);
  138. }
  139. return importType.ContractType;
  140. }
  141. internal static string GetContractNameFromImport(this IAttributedImport import, ImportType importType)
  142. {
  143. if (!string.IsNullOrEmpty(import.ContractName))
  144. {
  145. return import.ContractName;
  146. }
  147. Type contractType = import.GetContractTypeFromImport(importType);
  148. return AttributedModelServices.GetContractName(contractType);
  149. }
  150. internal static string GetTypeIdentityFromImport(this IAttributedImport import, ImportType importType)
  151. {
  152. Type contractType = import.GetContractTypeFromImport(importType);
  153. // For our importers we treat object as not having a type identity
  154. if (contractType == CompositionServices.ObjectType)
  155. {
  156. return null;
  157. }
  158. return AttributedModelServices.GetTypeIdentity(contractType);
  159. }
  160. internal static IDictionary<string, object> GetPartMetadataForType(this Type type, CreationPolicy creationPolicy)
  161. {
  162. IDictionary<string, object> dictionary = new Dictionary<string, object>(StringComparers.MetadataKeyNames);
  163. if (creationPolicy != CreationPolicy.Any)
  164. {
  165. dictionary.Add(CompositionConstants.PartCreationPolicyMetadataName, creationPolicy);
  166. }
  167. foreach (PartMetadataAttribute partMetadata in type.GetAttributes<PartMetadataAttribute>())
  168. {
  169. if (reservedMetadataNames.Contains(partMetadata.Name, StringComparers.MetadataKeyNames)
  170. || dictionary.ContainsKey(partMetadata.Name))
  171. {
  172. // Perhaps we should log an error here so that people know this value is being ignored.
  173. continue;
  174. }
  175. dictionary.Add(partMetadata.Name, partMetadata.Value);
  176. }
  177. // metadata for generic types
  178. if (type.ContainsGenericParameters)
  179. {
  180. // Register the part as generic
  181. dictionary.Add(CompositionConstants.IsGenericPartMetadataName, true);
  182. // Add arity
  183. Type[] genericArguments = type.GetGenericArguments();
  184. dictionary.Add(CompositionConstants.GenericPartArityMetadataName, genericArguments.Length);
  185. // add constraints
  186. bool hasConstraints = false;
  187. object[] genericParameterConstraints = new object[genericArguments.Length];
  188. GenericParameterAttributes[] genericParameterAttributes = new GenericParameterAttributes[genericArguments.Length];
  189. for (int i=0; i< genericArguments.Length ; i++)
  190. {
  191. Type genericArgument = genericArguments[i];
  192. Type[] constraints = genericArgument.GetGenericParameterConstraints();
  193. if (constraints.Length == 0)
  194. {
  195. constraints = null;
  196. }
  197. GenericParameterAttributes attributes = genericArgument.GenericParameterAttributes;
  198. if ((constraints != null) || (attributes != GenericParameterAttributes.None))
  199. {
  200. genericParameterConstraints[i] = constraints;
  201. genericParameterAttributes[i] = attributes;
  202. hasConstraints = true;
  203. }
  204. }
  205. if (hasConstraints)
  206. {
  207. dictionary.Add(CompositionConstants.GenericParameterConstraintsMetadataName, genericParameterConstraints);
  208. dictionary.Add(CompositionConstants.GenericParameterAttributesMetadataName, genericParameterAttributes);
  209. }
  210. }
  211. if (dictionary.Count == 0)
  212. {
  213. return MetadataServices.EmptyMetadata;
  214. }
  215. else
  216. {
  217. return dictionary;
  218. }
  219. }
  220. internal static void TryExportMetadataForMember(this MemberInfo member, out IDictionary<string, object> dictionary)
  221. {
  222. dictionary = new Dictionary<string, object>();
  223. foreach (var attr in member.GetAttributes<Attribute>())
  224. {
  225. var provider = attr as ExportMetadataAttribute;
  226. if (provider != null)
  227. {
  228. if (reservedMetadataNames.Contains(provider.Name, StringComparers.MetadataKeyNames))
  229. {
  230. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
  231. }
  232. // we pass "null" for valueType which would make it inferred. We don;t have additional type information when metadata
  233. // goes through the ExportMetadataAttribute path
  234. if (!dictionary.TryContributeMetadataValue(provider.Name, provider.Value, null, provider.IsMultiple))
  235. {
  236. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), provider.Name);
  237. }
  238. }
  239. else
  240. {
  241. Type attrType = attr.GetType();
  242. if ((attrType != CompositionServices.ExportAttributeType) && attrType.IsAttributeDefined<MetadataAttributeAttribute>(true))
  243. {
  244. bool allowsMultiple = false;
  245. AttributeUsageAttribute usage = attrType.GetFirstAttribute<AttributeUsageAttribute>(true);
  246. if (usage != null)
  247. {
  248. allowsMultiple = usage.AllowMultiple;
  249. }
  250. foreach (PropertyInfo pi in attrType.GetProperties())
  251. {
  252. if (pi.DeclaringType == CompositionServices.ExportAttributeType || pi.DeclaringType == CompositionServices.AttributeType)
  253. {
  254. // Don't contribute metadata properies from the base attribute types.
  255. continue;
  256. }
  257. if (reservedMetadataNames.Contains(pi.Name, StringComparers.MetadataKeyNames))
  258. {
  259. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
  260. }
  261. object value = pi.GetValue(attr, null);
  262. if (value != null && !IsValidAttributeType(value.GetType()))
  263. {
  264. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_MetadataContainsValueWithInvalidType, pi.GetDisplayName(), value.GetType().GetDisplayName());
  265. }
  266. if (!dictionary.TryContributeMetadataValue(pi.Name, value, pi.PropertyType, allowsMultiple))
  267. {
  268. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), pi.Name);
  269. }
  270. }
  271. }
  272. }
  273. }
  274. // Need Keys.ToArray because we alter the dictionary in the loop
  275. foreach (var key in dictionary.Keys.ToArray())
  276. {
  277. var list = dictionary[key] as MetadataList;
  278. if (list != null)
  279. {
  280. dictionary[key] = list.ToArray();
  281. }
  282. }
  283. return;
  284. }
  285. private static bool TryContributeMetadataValue(this IDictionary<string, object> dictionary, string name, object value, Type valueType, bool allowsMultiple)
  286. {
  287. object metadataValue;
  288. if (!dictionary.TryGetValue(name, out metadataValue))
  289. {
  290. if (allowsMultiple)
  291. {
  292. var list = new MetadataList();
  293. list.Add(value, valueType);
  294. value = list;
  295. }
  296. dictionary.Add(name, value);
  297. }
  298. else
  299. {
  300. var list = metadataValue as MetadataList;
  301. if (!allowsMultiple || list == null)
  302. {
  303. // Either single value already found when should be multiple
  304. // or a duplicate name already exists
  305. dictionary.Remove(name);
  306. return false;
  307. }
  308. list.Add(value, valueType);
  309. }
  310. return true;
  311. }
  312. private class MetadataList
  313. {
  314. private Type _arrayType = null;
  315. private bool _containsNulls = false;
  316. private static readonly Type ObjectType = typeof(object);
  317. private static readonly Type TypeType = typeof(Type);
  318. private Collection<object> _innerList = new Collection<object>();
  319. public void Add(object item, Type itemType)
  320. {
  321. this._containsNulls |= (item == null);
  322. // if we've been passed typeof(object), we basically have no type inmformation
  323. if (itemType == ObjectType)
  324. {
  325. itemType = null;
  326. }
  327. // if we have no type information, get it from the item, if we can
  328. if ((itemType == null) && (item != null))
  329. {
  330. itemType = item.GetType();
  331. }
  332. // Types are special, because the are abstract classes, so if the item casts to Type, we assume System.Type
  333. if (item is Type)
  334. {
  335. itemType = TypeType;
  336. }
  337. // only try to call this if we got a meaningful type
  338. if (itemType != null)
  339. {
  340. this.InferArrayType(itemType);
  341. }
  342. this._innerList.Add(item);
  343. }
  344. private void InferArrayType(Type itemType)
  345. {
  346. Assumes.NotNull(itemType);
  347. if (this._arrayType == null)
  348. {
  349. // this is the first typed element we've been given, it sets the type of the array
  350. this._arrayType = itemType;
  351. }
  352. else
  353. {
  354. // if there's a disagreement on the array type, we flip to Object
  355. // 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
  356. // in metadata right now, it's a moot point
  357. if (this._arrayType != itemType)
  358. {
  359. this._arrayType = ObjectType;
  360. }
  361. }
  362. }
  363. public Array ToArray()
  364. {
  365. if (this._arrayType == null)
  366. {
  367. // if the array type has not been set, assume Object
  368. this._arrayType = ObjectType;
  369. }
  370. else if (this._containsNulls && this._arrayType.IsValueType)
  371. {
  372. // if the array type is a value type and we have seen nulls, then assume Object
  373. this._arrayType = ObjectType;
  374. }
  375. Array array = Array.CreateInstance(this._arrayType, this._innerList.Count);
  376. for(int i = 0; i < array.Length; i++)
  377. {
  378. array.SetValue(this._innerList[i], i);
  379. }
  380. return array;
  381. }
  382. }
  383. //UNDONE: Need to add these warnings somewhere...Dev10:472538 should address this.
  384. //internal static CompositionResult MatchRequiredMetadata(this IDictionary<string, object> metadata, IEnumerable<string> requiredMetadata, string contractName)
  385. //{
  386. // Assumes.IsTrue(metadata != null);
  387. // var result = CompositionResult.SucceededResult;
  388. // var missingMetadata = (requiredMetadata == null) ? null : requiredMetadata.Except<string>(metadata.Keys);
  389. // if (missingMetadata != null && missingMetadata.Any())
  390. // {
  391. // result = result.MergeIssue(
  392. // CompositionError.CreateIssueAsWarning(CompositionErrorId.RequiredMetadataNotFound,
  393. // Strings.RequiredMetadataNotFound,
  394. // contractName,
  395. // string.Join(", ", missingMetadata.ToArray())));
  396. // return new CompositionResult(false, result.Issues);
  397. // }
  398. // return result;
  399. //}
  400. internal static IEnumerable<KeyValuePair<string, Type>> GetRequiredMetadata(Type metadataViewType)
  401. {
  402. if ((metadataViewType == null) ||
  403. ExportServices.IsDefaultMetadataViewType(metadataViewType) ||
  404. ExportServices.IsDictionaryConstructorViewType(metadataViewType) ||
  405. !metadataViewType.IsInterface)
  406. {
  407. return Enumerable.Empty<KeyValuePair<string, Type>>();
  408. }
  409. // A metadata view is required to be an Intrerface, and therefore only properties are allowed
  410. List<PropertyInfo> properties = metadataViewType.GetAllProperties().
  411. Where(property => property.GetFirstAttribute<DefaultValueAttribute>() == null).
  412. ToList();
  413. // NOTE : this is a carefully found balance between eager and delay-evaluation - the properties are filtered once and upfront
  414. // whereas the key/Type pairs are created every time. The latter is fine as KVPs are structs and as such copied on access regardless.
  415. // This also allows us to avoid creation of List<KVP> which - at least according to FxCop - leads to isues with NGEN
  416. return properties.Select(property => new KeyValuePair<string, Type>(property.Name, property.PropertyType));
  417. }
  418. internal static IDictionary<string, object> GetImportMetadata(ImportType importType, IAttributedImport attributedImport)
  419. {
  420. return GetImportMetadata(importType.ContractType, attributedImport);
  421. }
  422. internal static IDictionary<string, object> GetImportMetadata(Type type, IAttributedImport attributedImport)
  423. {
  424. Dictionary<string, object> metadata = null;
  425. if (type.IsGenericType)
  426. {
  427. metadata = new Dictionary<string, object>();
  428. if (type.ContainsGenericParameters)
  429. {
  430. metadata[CompositionConstants.GenericImportParametersOrderMetadataName] = GenericServices.GetGenericParametersOrder(type);
  431. }
  432. else
  433. {
  434. metadata[CompositionConstants.GenericContractMetadataName] = ContractNameServices.GetTypeIdentity(type.GetGenericTypeDefinition());
  435. metadata[CompositionConstants.GenericParametersMetadataName] = type.GetGenericArguments();
  436. }
  437. }
  438. // Default value is ImportSource.Any
  439. if(attributedImport != null && attributedImport.Source != ImportSource.Any)
  440. {
  441. if(metadata == null)
  442. {
  443. metadata = new Dictionary<string, object>();
  444. }
  445. metadata[CompositionConstants.ImportSourceMetadataName] = attributedImport.Source;
  446. }
  447. if(metadata != null)
  448. {
  449. return metadata.AsReadOnly();
  450. }
  451. else
  452. {
  453. return MetadataServices.EmptyMetadata;
  454. }
  455. }
  456. internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
  457. {
  458. if (engine != null)
  459. {
  460. try
  461. {
  462. engine.SatisfyImports(part);
  463. }
  464. catch (CompositionException ex)
  465. {
  466. throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
  467. }
  468. }
  469. try
  470. {
  471. return part.GetExportedValue(definition);
  472. }
  473. catch (ComposablePartException ex)
  474. {
  475. throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
  476. }
  477. }
  478. internal static bool IsRecomposable(this ComposablePart part)
  479. {
  480. return part.ImportDefinitions.Any(import => import.IsRecomposable);
  481. }
  482. internal static CompositionResult TryInvoke(Action action)
  483. {
  484. try
  485. {
  486. action();
  487. return CompositionResult.SucceededResult;
  488. }
  489. catch (CompositionException ex)
  490. {
  491. return new CompositionResult(ex.Errors);
  492. }
  493. }
  494. internal static CompositionResult TryFire<TEventArgs>(EventHandler<TEventArgs> _delegate, object sender, TEventArgs e)
  495. where TEventArgs : EventArgs
  496. {
  497. CompositionResult result = CompositionResult.SucceededResult;
  498. foreach (EventHandler<TEventArgs> _subscriber in _delegate.GetInvocationList())
  499. {
  500. try
  501. {
  502. _subscriber.Invoke(sender, e);
  503. }
  504. catch (CompositionException ex)
  505. {
  506. result = result.MergeErrors(ex.Errors);
  507. }
  508. }
  509. return result;
  510. }
  511. internal static CreationPolicy GetRequiredCreationPolicy(this ImportDefinition definition)
  512. {
  513. ContractBasedImportDefinition contractDefinition = definition as ContractBasedImportDefinition;
  514. if (contractDefinition != null)
  515. {
  516. return contractDefinition.RequiredCreationPolicy;
  517. }
  518. return CreationPolicy.Any;
  519. }
  520. /// <summary>
  521. /// Returns a value indicating whether cardinality is
  522. /// <see cref="ImportCardinality.ZeroOrOne"/> or
  523. /// <see cref="ImportCardinality.ExactlyOne"/>.
  524. /// </summary>
  525. internal static bool IsAtMostOne(this ImportCardinality cardinality)
  526. {
  527. return cardinality == ImportCardinality.ZeroOrOne || cardinality == ImportCardinality.ExactlyOne;
  528. }
  529. private static bool IsValidAttributeType(Type type)
  530. {
  531. return IsValidAttributeType(type, true);
  532. }
  533. private static bool IsValidAttributeType(Type type, bool arrayAllowed)
  534. {
  535. Assumes.NotNull(type);
  536. // Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3.
  537. // One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  538. if (type.IsPrimitive)
  539. {
  540. return true;
  541. }
  542. if (type == typeof(string))
  543. {
  544. return true;
  545. }
  546. // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility
  547. if (type.IsEnum && type.IsVisible)
  548. {
  549. return true;
  550. }
  551. if (typeof(Type).IsAssignableFrom(type))
  552. {
  553. return true;
  554. }
  555. // Single-dimensional arrays of the above types.
  556. if (arrayAllowed && type.IsArray &&
  557. type.GetArrayRank() == 1 &&
  558. IsValidAttributeType(type.GetElementType(), false))
  559. {
  560. return true;
  561. }
  562. return false;
  563. }
  564. }
  565. }