copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / AttributedModel / AttributedPartCreationInfo.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Collections.Generic;\r
6 using System.ComponentModel.Composition.Diagnostics;\r
7 using System.ComponentModel.Composition.Hosting;\r
8 using System.ComponentModel.Composition.Primitives;\r
9 using System.ComponentModel.Composition.ReflectionModel;\r
10 using System.Linq;\r
11 using System.Reflection;\r
12 using Microsoft.Internal;\r
13 using Microsoft.Internal.Collections;\r
14 \r
15 namespace System.ComponentModel.Composition.AttributedModel\r
16 {\r
17     internal class AttributedPartCreationInfo : IReflectionPartCreationInfo\r
18     {\r
19         private readonly Type _type;\r
20         private readonly bool _ignoreConstructorImports = false;\r
21         private readonly ICompositionElement _origin;\r
22         private PartCreationPolicyAttribute _partCreationPolicy = null;\r
23         private ConstructorInfo _constructor;\r
24         private IEnumerable<ExportDefinition> _exports;\r
25         private IEnumerable<ImportDefinition> _imports;\r
26         private HashSet<string> _contractNamesOnNonInterfaces;\r
27 \r
28         public AttributedPartCreationInfo(Type type, PartCreationPolicyAttribute partCreationPolicy, bool ignoreConstructorImports, ICompositionElement origin)\r
29         {\r
30             Assumes.NotNull(type);\r
31             this._type = type;\r
32             this._ignoreConstructorImports = ignoreConstructorImports;\r
33             this._partCreationPolicy = partCreationPolicy;\r
34             this._origin = origin;\r
35         }\r
36 \r
37         public Type GetPartType()\r
38         {\r
39             return this._type;\r
40         }\r
41 \r
42         public Lazy<Type> GetLazyPartType()\r
43         {\r
44             return new Lazy<Type>(this.GetPartType, false);\r
45         }\r
46 \r
47         public ConstructorInfo GetConstructor()\r
48         {\r
49             if (this._constructor == null && !this._ignoreConstructorImports)\r
50             {\r
51                 this._constructor = SelectPartConstructor(this._type);\r
52             }\r
53             return this._constructor;\r
54         }\r
55 \r
56         public IDictionary<string, object> GetMetadata()\r
57         {\r
58             return this._type.GetPartMetadataForType(this.CreationPolicy);\r
59         }\r
60 \r
61         public IEnumerable<ExportDefinition> GetExports()\r
62         {\r
63             DiscoverExportsAndImports();\r
64             return this._exports;\r
65         }\r
66 \r
67         public IEnumerable<ImportDefinition> GetImports()\r
68         {\r
69             DiscoverExportsAndImports();\r
70             return this._imports;\r
71         }\r
72 \r
73         public bool IsDisposalRequired\r
74         {\r
75             get\r
76             {\r
77                 return typeof(IDisposable).IsAssignableFrom(this.GetPartType());\r
78             }\r
79         }\r
80 \r
81         public bool IsPartDiscoverable()\r
82         {\r
83             if (this._type.IsAttributeDefined<PartNotDiscoverableAttribute>())\r
84             {\r
85                 CompositionTrace.DefinitionMarkedWithPartNotDiscoverableAttribute(this._type);\r
86                 return false;\r
87             }\r
88 \r
89             if (this._type.ContainsGenericParameters)\r
90             {\r
91                 CompositionTrace.DefinitionContainsGenericsParameters(this._type);\r
92                 return false;\r
93             }\r
94 \r
95             if (!HasExports())\r
96             {\r
97                 CompositionTrace.DefinitionContainsNoExports(this._type);\r
98                 return false;\r
99             }\r
100 \r
101             return true;\r
102         }\r
103 \r
104         private bool HasExports()\r
105         {\r
106             return GetExportMembers(this._type).Any() ||\r
107                    GetInheritedExports(this._type).Any();\r
108         }\r
109 \r
110         string ICompositionElement.DisplayName\r
111         {\r
112             get { return this.GetDisplayName(); }\r
113         }\r
114 \r
115         ICompositionElement ICompositionElement.Origin\r
116         {\r
117             get { return this._origin; }\r
118         }\r
119 \r
120         public override string ToString()\r
121         {\r
122             return GetDisplayName();\r
123         }\r
124 \r
125         private string GetDisplayName()\r
126         {\r
127             return this.GetPartType().GetDisplayName();\r
128         }\r
129 \r
130         private CreationPolicy CreationPolicy\r
131         {\r
132             get\r
133             {\r
134                 if (this._partCreationPolicy == null)\r
135                 {\r
136                     this._partCreationPolicy = this._type.GetFirstAttribute<PartCreationPolicyAttribute>() ?? PartCreationPolicyAttribute.Default;\r
137                 }\r
138                 return this._partCreationPolicy.CreationPolicy;\r
139             }\r
140         }\r
141 \r
142         private static ConstructorInfo SelectPartConstructor(Type type)\r
143         {\r
144             Assumes.NotNull(type);\r
145 \r
146             if (type.IsAbstract)\r
147             {\r
148                 return null;\r
149             }\r
150 \r
151             // Only deal with non-static constructors\r
152             BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;\r
153 \r
154             ConstructorInfo[] constructors = type.GetConstructors(flags);\r
155 \r
156             // Should likely only happen for static or abstract types\r
157             if (constructors.Length == 0)\r
158             {\r
159                 return null;\r
160             }\r
161 \r
162             // Optimize single default constructor.\r
163             if (constructors.Length == 1 && constructors[0].GetParameters().Length == 0)\r
164             {\r
165                 return constructors[0];\r
166             }\r
167 \r
168             // Select the marked constructor if there is exactly one marked\r
169             IEnumerable<ConstructorInfo> importingConstructors = constructors.Where(\r
170                 ctor => ctor.IsAttributeDefined<ImportingConstructorAttribute>());\r
171 \r
172             switch (importingConstructors.GetCardinality())\r
173             {\r
174                 case EnumerableCardinality.One:\r
175                     {\r
176                         return importingConstructors.First();\r
177                     }\r
178 \r
179                 case EnumerableCardinality.TwoOrMore:\r
180                     {\r
181                         // Return null, the part will error on instantiation.\r
182                         return null;\r
183                     }\r
184             }\r
185 \r
186             // If there are no marked constructors then select the default constructor\r
187             IEnumerable<ConstructorInfo> defaultConstructors = constructors.Where(\r
188                 ctor => ctor.GetParameters().Length == 0);\r
189 \r
190             // There should only ever be zero or one default constructors  \r
191             return defaultConstructors.SingleOrDefault();\r
192         }\r
193 \r
194         private void DiscoverExportsAndImports()\r
195         {\r
196             if (this._exports != null)\r
197             {\r
198                 return;\r
199             }\r
200 \r
201             this._exports = GetExportDefinitions();\r
202             this._imports = GetImportDefinitions();\r
203         }\r
204 \r
205         private IEnumerable<ExportDefinition> GetExportDefinitions()\r
206         {\r
207             List<ExportDefinition> exports = new List<ExportDefinition>();\r
208 \r
209             this._contractNamesOnNonInterfaces = new HashSet<string>();\r
210 \r
211             // GetExportMembers should only contain the type itself along with the members declared on it, \r
212             // it should not contain any base types, members on base types or interfaces on the type.\r
213             foreach (MemberInfo member in GetExportMembers(this._type))\r
214             {\r
215                 foreach (ExportAttribute exportAttribute in member.GetAttributes<ExportAttribute>())\r
216                 {\r
217                     var attributedExportDefinition = new AttributedExportDefinition(this, member, exportAttribute);\r
218 \r
219                     if (exportAttribute.GetType() == CompositionServices.InheritedExportAttributeType)\r
220                     {\r
221                         // Any InheritedExports on the type itself are contributed during this pass \r
222                         // and we need to do the book keeping for those.\r
223                         if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))\r
224                         {\r
225                             exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));\r
226                             this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);\r
227                         }\r
228                     }\r
229                     else\r
230                     {\r
231                         exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));\r
232                     }\r
233                 }\r
234             }\r
235 \r
236             // GetInheritedExports should only contain InheritedExports on base types or interfaces.\r
237             // The order of types returned here is important because it is used as a \r
238             // priority list of which InhertedExport to choose if multiple exists with \r
239             // the same contract name. Therefore ensure that we always return the types\r
240             // in the hiearchy from most derived to the lowest base type, followed\r
241             // by all the interfaces that this type implements.\r
242             foreach (Type type in GetInheritedExports(this._type))\r
243             {\r
244                 foreach (InheritedExportAttribute exportAttribute in type.GetAttributes<InheritedExportAttribute>())\r
245                 {\r
246                     var attributedExportDefinition = new AttributedExportDefinition(this, type, exportAttribute);\r
247 \r
248                     if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))\r
249                     {\r
250                         exports.Add(new ReflectionMemberExportDefinition(type.ToLazyMember(), attributedExportDefinition, this));\r
251 \r
252                         if (!type.IsInterface)\r
253                         {\r
254                             this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);\r
255                         }\r
256                     }\r
257                 }\r
258             }\r
259 \r
260             this._contractNamesOnNonInterfaces = null; // No need to hold this state around any longer\r
261 \r
262             return exports;\r
263         }\r
264 \r
265         private IEnumerable<MemberInfo> GetExportMembers(Type type)\r
266         {\r
267             BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public |\r
268                 BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;\r
269 \r
270             // If the type is abstract only find local static exports\r
271             if (type.IsAbstract)\r
272             {\r
273                 flags &= ~BindingFlags.Instance;\r
274             }\r
275             else if (IsExport(type))\r
276             {\r
277                 yield return type;\r
278             }\r
279 \r
280             // Walk the fields \r
281             foreach (var member in type.GetFields(flags))\r
282             {\r
283                 if (IsExport(member))\r
284                 {\r
285                     yield return member;\r
286                 }\r
287             }\r
288 \r
289             // Walk the properties \r
290             foreach (var member in type.GetProperties(flags))\r
291             {\r
292                 if (IsExport(member))\r
293                 {\r
294                     yield return member;\r
295                 }\r
296             }\r
297 \r
298             // Walk the methods \r
299             foreach (var member in type.GetMethods(flags))\r
300             {\r
301                 if (IsExport(member))\r
302                 {\r
303                     yield return member;\r
304                 }\r
305             }\r
306         }\r
307 \r
308         private IEnumerable<Type> GetInheritedExports(Type type)\r
309         {\r
310             // If the type is abstract we aren't interested in type level exports\r
311             if (type.IsAbstract)\r
312             {\r
313                 yield break;\r
314             }\r
315 \r
316             // The order of types returned here is important because it is used as a \r
317             // priority list of which InhertedExport to choose if multiple exists with \r
318             // the same contract name. Therefore ensure that we always return the types\r
319             // in the hiearchy from most derived to the lowest base type, followed\r
320             // by all the interfaces that this type implements.\r
321 \r
322             Type currentType = type.BaseType;\r
323 \r
324             if (currentType == null)\r
325             {\r
326                 yield break;\r
327             }\r
328 \r
329             // Stopping at object instead of null to help with performance. It is a noticable performance\r
330             // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.\r
331             while (currentType != CompositionServices.ObjectType)\r
332             {\r
333                 if (IsInheritedExport(currentType))\r
334                 {\r
335                     yield return currentType;\r
336                 }\r
337                 currentType = currentType.BaseType;\r
338             }\r
339 \r
340             foreach (Type iface in type.GetInterfaces())\r
341             {\r
342                 if (IsInheritedExport(iface))\r
343                 {\r
344                     yield return iface;\r
345                 }\r
346             }\r
347         }\r
348 \r
349         private static bool IsExport(ICustomAttributeProvider attributeProvider)\r
350         {\r
351             return attributeProvider.IsAttributeDefined<ExportAttribute>(false);\r
352         }\r
353 \r
354         private static bool IsInheritedExport(ICustomAttributeProvider attributedProvider)\r
355         {\r
356             return attributedProvider.IsAttributeDefined<InheritedExportAttribute>(false);\r
357         }\r
358 \r
359         private IEnumerable<ImportDefinition> GetImportDefinitions()\r
360         {\r
361             List<ImportDefinition> imports = new List<ImportDefinition>();\r
362 \r
363             foreach (MemberInfo member in GetImportMembers(this._type))\r
364             {\r
365                 ReflectionMemberImportDefinition importDefinition = AttributedModelDiscovery.CreateMemberImportDefinition(member, this);\r
366                 imports.Add(importDefinition);\r
367             }\r
368 \r
369             var constructor = this.GetConstructor();\r
370 \r
371             if (constructor != null)\r
372             {\r
373                 foreach (ParameterInfo parameter in constructor.GetParameters())\r
374                 {\r
375                     ReflectionParameterImportDefinition importDefinition = AttributedModelDiscovery.CreateParameterImportDefinition(parameter, this);\r
376                     imports.Add(importDefinition);\r
377                 }\r
378             }\r
379 \r
380             return imports;\r
381         }\r
382 \r
383         private IEnumerable<MemberInfo> GetImportMembers(Type type)\r
384         {\r
385             if (type.IsAbstract)\r
386             {\r
387                 yield break;\r
388             }\r
389 \r
390             foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type))\r
391             {\r
392                 yield return member;\r
393             }\r
394 \r
395             // Walk up the type chain until you hit object.\r
396             if (type.BaseType != null)\r
397             {\r
398                 Type baseType = type.BaseType;\r
399 \r
400                 // Stopping at object instead of null to help with performance. It is a noticable performance\r
401                 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.\r
402                 while (baseType != CompositionServices.ObjectType)\r
403                 {\r
404                     foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType))\r
405                     {\r
406                         yield return member;\r
407                     }\r
408                     baseType = baseType.BaseType;\r
409                 }\r
410             }\r
411         }\r
412 \r
413         private IEnumerable<MemberInfo> GetDeclaredOnlyImportMembers(Type type)\r
414         {\r
415             BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;\r
416 \r
417             // Walk the fields \r
418             foreach (var member in type.GetFields(flags))\r
419             {\r
420                 if (IsImport(member))\r
421                 {\r
422                     yield return member;\r
423                 }\r
424             }\r
425 \r
426             // Walk the properties \r
427             foreach (var member in type.GetProperties(flags))\r
428             {\r
429                 if (IsImport(member))\r
430                 {\r
431                     yield return member;\r
432                 }\r
433             }\r
434         }\r
435 \r
436         private static bool IsImport(ICustomAttributeProvider attributeProvider)\r
437         {\r
438             return attributeProvider.IsAttributeDefined<IAttributedImport>(false);\r
439         }\r
440     }\r
441 }\r