PageRenderTime 58ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/danipen/mono
C# | 522 lines | 391 code | 80 blank | 51 comment | 85 complexity | a0bbfc8eaf5b8ff84e5d0983124759aa 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 string GetContractNameFromExport(this MemberInfo member, ExportAttribute export)
  46. {
  47. if (!string.IsNullOrEmpty(export.ContractName))
  48. {
  49. return export.ContractName;
  50. }
  51. if (export.ContractType != null)
  52. {
  53. return AttributedModelServices.GetContractName(export.ContractType);
  54. }
  55. if (member.MemberType == MemberTypes.Method)
  56. {
  57. return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
  58. }
  59. return AttributedModelServices.GetContractName(member.GetDefaultTypeFromMember());
  60. }
  61. internal static string GetTypeIdentityFromExport(this MemberInfo member, ExportAttribute export)
  62. {
  63. if (export.ContractType != null)
  64. {
  65. return AttributedModelServices.GetTypeIdentity(export.ContractType);
  66. }
  67. if (member.MemberType == MemberTypes.Method)
  68. {
  69. return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
  70. }
  71. return AttributedModelServices.GetTypeIdentity(member.GetDefaultTypeFromMember());
  72. }
  73. internal static Type GetContractTypeFromImport(this IAttributedImport import, ImportType importType)
  74. {
  75. if (import.ContractType != null)
  76. {
  77. return import.ContractType;
  78. }
  79. return importType.ContractType;
  80. }
  81. internal static string GetContractNameFromImport(this IAttributedImport import, ImportType importType)
  82. {
  83. if (!string.IsNullOrEmpty(import.ContractName))
  84. {
  85. return import.ContractName;
  86. }
  87. Type contractType = import.GetContractTypeFromImport(importType);
  88. return AttributedModelServices.GetContractName(contractType);
  89. }
  90. internal static string GetTypeIdentityFromImport(this IAttributedImport import, ImportType importType)
  91. {
  92. Type contractType = import.GetContractTypeFromImport(importType);
  93. // For our importers we treat object as not having a type identity
  94. if (contractType == CompositionServices.ObjectType)
  95. {
  96. return null;
  97. }
  98. return AttributedModelServices.GetTypeIdentity(contractType);
  99. }
  100. internal static IDictionary<string, object> GetPartMetadataForType(this Type type, CreationPolicy creationPolicy)
  101. {
  102. IDictionary<string, object> dictionary = new Dictionary<string, object>(StringComparers.MetadataKeyNames);
  103. if (creationPolicy != CreationPolicy.Any)
  104. {
  105. dictionary.Add(CompositionConstants.PartCreationPolicyMetadataName, creationPolicy);
  106. }
  107. foreach (PartMetadataAttribute partMetadata in type.GetAttributes<PartMetadataAttribute>())
  108. {
  109. if (reservedMetadataNames.Contains(partMetadata.Name, StringComparers.MetadataKeyNames)
  110. || dictionary.ContainsKey(partMetadata.Name))
  111. {
  112. // Perhaps we should log an error here so that people know this value is being ignored.
  113. continue;
  114. }
  115. dictionary.Add(partMetadata.Name, partMetadata.Value);
  116. }
  117. if (dictionary.Count == 0)
  118. {
  119. return MetadataServices.EmptyMetadata;
  120. }
  121. else
  122. {
  123. return dictionary;
  124. }
  125. }
  126. internal static void TryExportMetadataForMember(this MemberInfo member, out IDictionary<string, object> dictionary)
  127. {
  128. dictionary = new Dictionary<string, object>();
  129. foreach (var attr in member.GetAttributes<Attribute>())
  130. {
  131. var provider = attr as ExportMetadataAttribute;
  132. if (provider != null)
  133. {
  134. if (reservedMetadataNames.Contains(provider.Name, StringComparers.MetadataKeyNames))
  135. {
  136. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
  137. }
  138. // we pass "null" for valueType which would make it inferred. We don;t have additional type information when metadata
  139. // goes through the ExportMetadataAttribute path
  140. if (!dictionary.TryContributeMetadataValue(provider.Name, provider.Value, null, provider.IsMultiple))
  141. {
  142. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), provider.Name);
  143. }
  144. }
  145. else
  146. {
  147. Type attrType = attr.GetType();
  148. if ((attrType != CompositionServices.ExportAttributeType) && attrType.IsAttributeDefined<MetadataAttributeAttribute>(true))
  149. {
  150. bool allowsMultiple = false;
  151. AttributeUsageAttribute usage = attrType.GetFirstAttribute<AttributeUsageAttribute>(true);
  152. if (usage != null)
  153. {
  154. allowsMultiple = usage.AllowMultiple;
  155. }
  156. foreach (PropertyInfo pi in attrType.GetProperties())
  157. {
  158. if (pi.DeclaringType == CompositionServices.ExportAttributeType || pi.DeclaringType == CompositionServices.AttributeType)
  159. {
  160. // Don't contribute metadata properies from the base attribute types.
  161. continue;
  162. }
  163. if (reservedMetadataNames.Contains(pi.Name, StringComparers.MetadataKeyNames))
  164. {
  165. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);
  166. }
  167. object value = pi.GetValue(attr, null);
  168. if (value != null && !IsValidAttributeType(value.GetType()))
  169. {
  170. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_MetadataContainsValueWithInvalidType, pi.GetDisplayName(), value.GetType().GetDisplayName());
  171. }
  172. if (!dictionary.TryContributeMetadataValue(pi.Name, value, pi.PropertyType, allowsMultiple))
  173. {
  174. throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), pi.Name);
  175. }
  176. }
  177. }
  178. }
  179. }
  180. // Need Keys.ToArray because we alter the dictionary in the loop
  181. foreach (var key in dictionary.Keys.ToArray())
  182. {
  183. var list = dictionary[key] as MetadataList;
  184. if (list != null)
  185. {
  186. dictionary[key] = list.ToArray();
  187. }
  188. }
  189. return;
  190. }
  191. private static bool TryContributeMetadataValue(this IDictionary<string, object> dictionary, string name, object value, Type valueType, bool allowsMultiple)
  192. {
  193. object metadataValue;
  194. if (!dictionary.TryGetValue(name, out metadataValue))
  195. {
  196. if (allowsMultiple)
  197. {
  198. var list = new MetadataList();
  199. list.Add(value, valueType);
  200. value = list;
  201. }
  202. dictionary.Add(name, value);
  203. }
  204. else
  205. {
  206. var list = metadataValue as MetadataList;
  207. if (!allowsMultiple || list == null)
  208. {
  209. // Either single value already found when should be multiple
  210. // or a duplicate name already exists
  211. dictionary.Remove(name);
  212. return false;
  213. }
  214. list.Add(value, valueType);
  215. }
  216. return true;
  217. }
  218. private class MetadataList
  219. {
  220. private Type _arrayType = null;
  221. private bool _containsNulls = false;
  222. private static readonly Type ObjectType = typeof(object);
  223. private static readonly Type TypeType = typeof(Type);
  224. private Collection<object> _innerList = new Collection<object>();
  225. public void Add(object item, Type itemType)
  226. {
  227. this._containsNulls |= (item == null);
  228. // if we've been passed typeof(object), we basically have no type inmformation
  229. if (itemType == ObjectType)
  230. {
  231. itemType = null;
  232. }
  233. // if we have no type information, get it from the item, if we can
  234. if ((itemType == null) && (item != null))
  235. {
  236. itemType = item.GetType();
  237. }
  238. // Types are special, because the are abstract classes, so if the item casts to Type, we assume System.Type
  239. if (item is Type)
  240. {
  241. itemType = TypeType;
  242. }
  243. // only try to call this if we got a meaningful type
  244. if (itemType != null)
  245. {
  246. this.InferArrayType(itemType);
  247. }
  248. this._innerList.Add(item);
  249. }
  250. private void InferArrayType(Type itemType)
  251. {
  252. Assumes.NotNull(itemType);
  253. if (this._arrayType == null)
  254. {
  255. // this is the first typed element we've been given, it sets the type of the array
  256. this._arrayType = itemType;
  257. }
  258. else
  259. {
  260. // if there's a disagreement on the array type, we flip to Object
  261. // 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
  262. // in metadata right now, it's a moot point
  263. if (this._arrayType != itemType)
  264. {
  265. this._arrayType = ObjectType;
  266. }
  267. }
  268. }
  269. public Array ToArray()
  270. {
  271. if (this._arrayType == null)
  272. {
  273. // if the array type has not been set, assume Object
  274. this._arrayType = ObjectType;
  275. }
  276. else if (this._containsNulls && this._arrayType.IsValueType)
  277. {
  278. // if the array type is a value type and we have seen nulls, then assume Object
  279. this._arrayType = ObjectType;
  280. }
  281. Array array = Array.CreateInstance(this._arrayType, this._innerList.Count);
  282. for(int i = 0; i < array.Length; i++)
  283. {
  284. array.SetValue(this._innerList[i], i);
  285. }
  286. return array;
  287. }
  288. }
  289. //UNDONE: Need to add these warnings somewhere...Dev10:472538 should address this.
  290. //internal static CompositionResult MatchRequiredMetadata(this IDictionary<string, object> metadata, IEnumerable<string> requiredMetadata, string contractName)
  291. //{
  292. // Assumes.IsTrue(metadata != null);
  293. // var result = CompositionResult.SucceededResult;
  294. // var missingMetadata = (requiredMetadata == null) ? null : requiredMetadata.Except<string>(metadata.Keys);
  295. // if (missingMetadata != null && missingMetadata.Any())
  296. // {
  297. // result = result.MergeIssue(
  298. // CompositionError.CreateIssueAsWarning(CompositionErrorId.RequiredMetadataNotFound,
  299. // Strings.RequiredMetadataNotFound,
  300. // contractName,
  301. // string.Join(", ", missingMetadata.ToArray())));
  302. // return new CompositionResult(false, result.Issues);
  303. // }
  304. // return result;
  305. //}
  306. internal static IEnumerable<KeyValuePair<string, Type>> GetRequiredMetadata(Type metadataViewType)
  307. {
  308. if ((metadataViewType == null) ||
  309. ExportServices.IsDefaultMetadataViewType(metadataViewType) ||
  310. ExportServices.IsDictionaryConstructorViewType(metadataViewType) ||
  311. !metadataViewType.IsInterface)
  312. {
  313. return Enumerable.Empty<KeyValuePair<string, Type>>();
  314. }
  315. // A metadata view is required to be an Intrerface, and therefore only properties are allowed
  316. List<PropertyInfo> properties = metadataViewType.GetAllProperties().
  317. Where(property => property.GetFirstAttribute<DefaultValueAttribute>() == null).
  318. ToList();
  319. // NOTE : this is a carefully found balance between eager and delay-evaluation - the properties are filtered once and upfront
  320. // whereas the key/Type pairs are created every time. The latter is fine as KVPs are structs and as such copied on access regardless.
  321. // This also allows us to avoid creation of List<KVP> which - at least according to FxCop - leads to isues with NGEN
  322. return properties.Select(property => new KeyValuePair<string, Type>(property.Name, property.PropertyType));
  323. }
  324. internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
  325. {
  326. try
  327. {
  328. engine.SatisfyImports(part);
  329. }
  330. catch (CompositionException ex)
  331. {
  332. throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
  333. }
  334. try
  335. {
  336. return part.GetExportedValue(definition);
  337. }
  338. catch (ComposablePartException ex)
  339. {
  340. throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);
  341. }
  342. }
  343. internal static bool IsRecomposable(this ComposablePart part)
  344. {
  345. return part.ImportDefinitions.Any(import => import.IsRecomposable);
  346. }
  347. internal static CompositionResult<T> TryInvoke<T>(Func<T> action)
  348. {
  349. try
  350. {
  351. T value = action();
  352. return new CompositionResult<T>(value);
  353. }
  354. catch (CompositionException ex)
  355. {
  356. return new CompositionResult<T>(ex.Errors);
  357. }
  358. }
  359. internal static CompositionResult TryInvoke(Action action)
  360. {
  361. try
  362. {
  363. action();
  364. return CompositionResult.SucceededResult;
  365. }
  366. catch (CompositionException ex)
  367. {
  368. return new CompositionResult(ex.Errors);
  369. }
  370. }
  371. internal static CompositionResult TryFire<TEventArgs>(EventHandler<TEventArgs> _delegate, object sender, TEventArgs e)
  372. where TEventArgs : EventArgs
  373. {
  374. CompositionResult result = CompositionResult.SucceededResult;
  375. foreach (EventHandler<TEventArgs> _subscriber in _delegate.GetInvocationList())
  376. {
  377. try
  378. {
  379. _subscriber.Invoke(sender, e);
  380. }
  381. catch (CompositionException ex)
  382. {
  383. result = result.MergeErrors(ex.Errors);
  384. }
  385. }
  386. return result;
  387. }
  388. internal static CreationPolicy GetRequiredCreationPolicy(this ImportDefinition definition)
  389. {
  390. ContractBasedImportDefinition contractDefinition = definition as ContractBasedImportDefinition;
  391. if (contractDefinition != null)
  392. {
  393. return contractDefinition.RequiredCreationPolicy;
  394. }
  395. return CreationPolicy.Any;
  396. }
  397. /// <summary>
  398. /// Returns a value indicating whether cardinality is
  399. /// <see cref="ImportCardinality.ZeroOrOne"/> or
  400. /// <see cref="ImportCardinality.ExactlyOne"/>.
  401. /// </summary>
  402. internal static bool IsAtMostOne(this ImportCardinality cardinality)
  403. {
  404. return cardinality == ImportCardinality.ZeroOrOne || cardinality == ImportCardinality.ExactlyOne;
  405. }
  406. private static bool IsValidAttributeType(Type type)
  407. {
  408. return IsValidAttributeType(type, true);
  409. }
  410. private static bool IsValidAttributeType(Type type, bool arrayAllowed)
  411. {
  412. Assumes.NotNull(type);
  413. // Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3.
  414. // One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  415. if (type.IsPrimitive)
  416. {
  417. return true;
  418. }
  419. if (type == typeof(string))
  420. {
  421. return true;
  422. }
  423. // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility
  424. if (type.IsEnum && type.IsVisible)
  425. {
  426. return true;
  427. }
  428. if (typeof(Type).IsAssignableFrom(type))
  429. {
  430. return true;
  431. }
  432. // Single-dimensional arrays of the above types.
  433. if (arrayAllowed && type.IsArray &&
  434. type.GetArrayRank() == 1 &&
  435. IsValidAttributeType(type.GetElementType(), false))
  436. {
  437. return true;
  438. }
  439. return false;
  440. }
  441. }
  442. }