Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[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             // NOTE : in most cases both of these will be null or not null at the same time\r
197             // the only situation when that is not the case is when there was a failure during the previous discovery\r
198             // and one of them ended up not being set. In that case we will force the discovery again so that the same exception is thrown.\r
199             if ((this._exports != null) && (this._imports != null))\r
200             {\r
201                 return;\r
202             }\r
203 \r
204             this._exports = GetExportDefinitions();\r
205             this._imports = GetImportDefinitions();\r
206         }\r
207 \r
208         private IEnumerable<ExportDefinition> GetExportDefinitions()\r
209         {\r
210             List<ExportDefinition> exports = new List<ExportDefinition>();\r
211 \r
212             this._contractNamesOnNonInterfaces = new HashSet<string>();\r
213 \r
214             // GetExportMembers should only contain the type itself along with the members declared on it, \r
215             // it should not contain any base types, members on base types or interfaces on the type.\r
216             foreach (MemberInfo member in GetExportMembers(this._type))\r
217             {\r
218                 foreach (ExportAttribute exportAttribute in member.GetAttributes<ExportAttribute>())\r
219                 {\r
220                     var attributedExportDefinition = new AttributedExportDefinition(this, member, exportAttribute);\r
221 \r
222                     if (exportAttribute.GetType() == CompositionServices.InheritedExportAttributeType)\r
223                     {\r
224                         // Any InheritedExports on the type itself are contributed during this pass \r
225                         // and we need to do the book keeping for those.\r
226                         if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))\r
227                         {\r
228                             exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));\r
229                             this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);\r
230                         }\r
231                     }\r
232                     else\r
233                     {\r
234                         exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));\r
235                     }\r
236                 }\r
237             }\r
238 \r
239             // GetInheritedExports should only contain InheritedExports on base types or interfaces.\r
240             // The order of types returned here is important because it is used as a \r
241             // priority list of which InhertedExport to choose if multiple exists with \r
242             // the same contract name. Therefore ensure that we always return the types\r
243             // in the hiearchy from most derived to the lowest base type, followed\r
244             // by all the interfaces that this type implements.\r
245             foreach (Type type in GetInheritedExports(this._type))\r
246             {\r
247                 foreach (InheritedExportAttribute exportAttribute in type.GetAttributes<InheritedExportAttribute>())\r
248                 {\r
249                     var attributedExportDefinition = new AttributedExportDefinition(this, type, exportAttribute);\r
250 \r
251                     if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))\r
252                     {\r
253                         exports.Add(new ReflectionMemberExportDefinition(type.ToLazyMember(), attributedExportDefinition, this));\r
254 \r
255                         if (!type.IsInterface)\r
256                         {\r
257                             this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);\r
258                         }\r
259                     }\r
260                 }\r
261             }\r
262 \r
263             this._contractNamesOnNonInterfaces = null; // No need to hold this state around any longer\r
264 \r
265             return exports;\r
266         }\r
267 \r
268         private IEnumerable<MemberInfo> GetExportMembers(Type type)\r
269         {\r
270             BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public |\r
271                 BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;\r
272 \r
273             // If the type is abstract only find local static exports\r
274             if (type.IsAbstract)\r
275             {\r
276                 flags &= ~BindingFlags.Instance;\r
277             }\r
278             else if (IsExport(type))\r
279             {\r
280                 yield return type;\r
281             }\r
282 \r
283             // Walk the fields \r
284             foreach (var member in type.GetFields(flags))\r
285             {\r
286                 if (IsExport(member))\r
287                 {\r
288                     yield return member;\r
289                 }\r
290             }\r
291 \r
292             // Walk the properties \r
293             foreach (var member in type.GetProperties(flags))\r
294             {\r
295                 if (IsExport(member))\r
296                 {\r
297                     yield return member;\r
298                 }\r
299             }\r
300 \r
301             // Walk the methods \r
302             foreach (var member in type.GetMethods(flags))\r
303             {\r
304                 if (IsExport(member))\r
305                 {\r
306                     yield return member;\r
307                 }\r
308             }\r
309         }\r
310 \r
311         private IEnumerable<Type> GetInheritedExports(Type type)\r
312         {\r
313             // If the type is abstract we aren't interested in type level exports\r
314             if (type.IsAbstract)\r
315             {\r
316                 yield break;\r
317             }\r
318 \r
319             // The order of types returned here is important because it is used as a \r
320             // priority list of which InhertedExport to choose if multiple exists with \r
321             // the same contract name. Therefore ensure that we always return the types\r
322             // in the hiearchy from most derived to the lowest base type, followed\r
323             // by all the interfaces that this type implements.\r
324 \r
325             Type currentType = type.BaseType;\r
326 \r
327             if (currentType == null)\r
328             {\r
329                 yield break;\r
330             }\r
331             \r
332             // Stopping at object instead of null to help with performance. It is a noticable performance\r
333             // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.\r
334             // We also need the null check in case we're passed a type that doesn't live in the runtime context.\r
335             while (currentType != null && currentType != CompositionServices.ObjectType)\r
336             {\r
337                 if (IsInheritedExport(currentType))\r
338                 {\r
339                     yield return currentType;\r
340                 }\r
341                 currentType = currentType.BaseType;\r
342             }\r
343 \r
344             foreach (Type iface in type.GetInterfaces())\r
345             {\r
346                 if (IsInheritedExport(iface))\r
347                 {\r
348                     yield return iface;\r
349                 }\r
350             }\r
351         }\r
352 \r
353         private static bool IsExport(ICustomAttributeProvider attributeProvider)\r
354         {\r
355             return attributeProvider.IsAttributeDefined<ExportAttribute>(false);\r
356         }\r
357 \r
358         private static bool IsInheritedExport(ICustomAttributeProvider attributedProvider)\r
359         {\r
360             return attributedProvider.IsAttributeDefined<InheritedExportAttribute>(false);\r
361         }\r
362 \r
363         private IEnumerable<ImportDefinition> GetImportDefinitions()\r
364         {\r
365             List<ImportDefinition> imports = new List<ImportDefinition>();\r
366 \r
367             foreach (MemberInfo member in GetImportMembers(this._type))\r
368             {\r
369                 ReflectionMemberImportDefinition importDefinition = AttributedModelDiscovery.CreateMemberImportDefinition(member, this);\r
370                 imports.Add(importDefinition);\r
371             }\r
372 \r
373             var constructor = this.GetConstructor();\r
374 \r
375             if (constructor != null)\r
376             {\r
377                 foreach (ParameterInfo parameter in constructor.GetParameters())\r
378                 {\r
379                     ReflectionParameterImportDefinition importDefinition = AttributedModelDiscovery.CreateParameterImportDefinition(parameter, this);\r
380                     imports.Add(importDefinition);\r
381                 }\r
382             }\r
383 \r
384             return imports;\r
385         }\r
386 \r
387         private IEnumerable<MemberInfo> GetImportMembers(Type type)\r
388         {\r
389             if (type.IsAbstract)\r
390             {\r
391                 yield break;\r
392             }\r
393 \r
394             foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type))\r
395             {\r
396                 yield return member;\r
397             }\r
398 \r
399             // Walk up the type chain until you hit object.\r
400             if (type.BaseType != null)\r
401             {\r
402                 Type baseType = type.BaseType;\r
403 \r
404                 // Stopping at object instead of null to help with performance. It is a noticable performance\r
405                 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.\r
406                 // We also need the null check in case we're passed a type that doesn't live in the runtime context.\r
407                 while (baseType != null && baseType != CompositionServices.ObjectType)\r
408                 {\r
409                     foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType))\r
410                     {\r
411                         yield return member;\r
412                     }\r
413                     baseType = baseType.BaseType;\r
414                 }\r
415             }\r
416         }\r
417 \r
418         private IEnumerable<MemberInfo> GetDeclaredOnlyImportMembers(Type type)\r
419         {\r
420             BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;\r
421 \r
422             // Walk the fields \r
423             foreach (var member in type.GetFields(flags))\r
424             {\r
425                 if (IsImport(member))\r
426                 {\r
427                     yield return member;\r
428                 }\r
429             }\r
430 \r
431             // Walk the properties \r
432             foreach (var member in type.GetProperties(flags))\r
433             {\r
434                 if (IsImport(member))\r
435                 {\r
436                     yield return member;\r
437                 }\r
438             }\r
439         }\r
440 \r
441         private static bool IsImport(ICustomAttributeProvider attributeProvider)\r
442         {\r
443             return attributeProvider.IsAttributeDefined<IAttributedImport>(false);\r
444         }\r
445     }\r
446 }\r