Remove excessive shortcut key matching in ToolStrip
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / Hosting / CompositionServices.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\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
11 using System.Linq;\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
17 \r
18 namespace System.ComponentModel.Composition.Hosting\r
19 {\r
20     internal static class CompositionServices\r
21     {\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
26 \r
27         private static readonly string[] reservedMetadataNames = new string[]\r
28         {\r
29             CompositionConstants.PartCreationPolicyMetadataName\r
30         };  \r
31 \r
32         internal static Type GetDefaultTypeFromMember(this MemberInfo member)\r
33         {\r
34             Assumes.NotNull(member);\r
35 \r
36             switch (member.MemberType)\r
37             {\r
38                 case MemberTypes.Property:\r
39                     return ((PropertyInfo)member).PropertyType;\r
40 \r
41                 case MemberTypes.NestedType:\r
42                 case MemberTypes.TypeInfo:\r
43                     return ((Type)member);\r
44 \r
45                 case MemberTypes.Field:\r
46                 default:\r
47                     Assumes.IsTrue(member.MemberType == MemberTypes.Field);\r
48                     return ((FieldInfo)member).FieldType;\r
49             }\r
50         }\r
51 \r
52         internal static string GetContractNameFromExport(this MemberInfo member, ExportAttribute export)\r
53         {\r
54             if (!string.IsNullOrEmpty(export.ContractName))\r
55             {\r
56                 return export.ContractName;\r
57             }\r
58 \r
59             if (export.ContractType != null)\r
60             {\r
61                 return AttributedModelServices.GetContractName(export.ContractType);\r
62             }\r
63 \r
64             if (member.MemberType == MemberTypes.Method)\r
65             {\r
66                 return AttributedModelServices.GetTypeIdentity((MethodInfo)member);\r
67             }\r
68 \r
69             return AttributedModelServices.GetContractName(member.GetDefaultTypeFromMember());\r
70         }\r
71 \r
72         internal static string GetTypeIdentityFromExport(this MemberInfo member, ExportAttribute export)\r
73         {\r
74             if (export.ContractType != null)\r
75             {\r
76                 return AttributedModelServices.GetTypeIdentity(export.ContractType);\r
77             }\r
78 \r
79             if (member.MemberType == MemberTypes.Method)\r
80             {\r
81                 return AttributedModelServices.GetTypeIdentity((MethodInfo)member);\r
82             }\r
83 \r
84             return AttributedModelServices.GetTypeIdentity(member.GetDefaultTypeFromMember());\r
85         }\r
86 \r
87         internal static Type GetContractTypeFromImport(this IAttributedImport import, ImportType importType)\r
88         {\r
89             if (import.ContractType != null)\r
90             {\r
91                 return import.ContractType;\r
92             }\r
93 \r
94             return importType.ContractType;\r
95         }\r
96 \r
97         internal static string GetContractNameFromImport(this IAttributedImport import, ImportType importType)\r
98         {\r
99             if (!string.IsNullOrEmpty(import.ContractName))\r
100             {\r
101                 return import.ContractName;\r
102             }\r
103 \r
104             Type contractType = import.GetContractTypeFromImport(importType);\r
105 \r
106             return AttributedModelServices.GetContractName(contractType); \r
107         }\r
108 \r
109         internal static string GetTypeIdentityFromImport(this IAttributedImport import, ImportType importType)\r
110         {\r
111             Type contractType = import.GetContractTypeFromImport(importType);\r
112 \r
113             // For our importers we treat object as not having a type identity\r
114             if (contractType == CompositionServices.ObjectType)\r
115             {\r
116                 return null;\r
117             }\r
118 \r
119             return AttributedModelServices.GetTypeIdentity(contractType); \r
120         }\r
121 \r
122         internal static IDictionary<string, object> GetPartMetadataForType(this Type type, CreationPolicy creationPolicy)\r
123         {\r
124             IDictionary<string, object> dictionary = new Dictionary<string, object>(StringComparers.MetadataKeyNames);\r
125 \r
126             if (creationPolicy != CreationPolicy.Any)\r
127             {\r
128                 dictionary.Add(CompositionConstants.PartCreationPolicyMetadataName, creationPolicy);\r
129             }\r
130 \r
131             foreach (PartMetadataAttribute partMetadata in type.GetAttributes<PartMetadataAttribute>())\r
132             {\r
133                 if (reservedMetadataNames.Contains(partMetadata.Name, StringComparers.MetadataKeyNames) \r
134                     || dictionary.ContainsKey(partMetadata.Name))\r
135                 {\r
136                     // Perhaps we should log an error here so that people know this value is being ignored.\r
137                     continue;\r
138                 }\r
139 \r
140                 dictionary.Add(partMetadata.Name, partMetadata.Value);\r
141             }\r
142 \r
143             if (dictionary.Count == 0)\r
144             {\r
145                 return MetadataServices.EmptyMetadata;\r
146             }\r
147             else\r
148             {\r
149                 return dictionary;\r
150             }\r
151         }\r
152 \r
153         internal static void TryExportMetadataForMember(this MemberInfo member, out IDictionary<string, object> dictionary)\r
154         {\r
155             dictionary = new Dictionary<string, object>();\r
156 \r
157             foreach (var attr in member.GetAttributes<Attribute>())\r
158             {\r
159                 var provider = attr as ExportMetadataAttribute;\r
160 \r
161                 if (provider != null)\r
162                 {\r
163                     if (reservedMetadataNames.Contains(provider.Name, StringComparers.MetadataKeyNames))\r
164                     {\r
165                         throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);\r
166                     }\r
167 \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
171                     {\r
172                         throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), provider.Name);\r
173                     }\r
174                 }\r
175                 else\r
176                 {\r
177                     Type attrType = attr.GetType();\r
178                     if ((attrType != CompositionServices.ExportAttributeType) && attrType.IsAttributeDefined<MetadataAttributeAttribute>(true))\r
179                     {\r
180                         bool allowsMultiple = false;\r
181                         AttributeUsageAttribute usage = attrType.GetFirstAttribute<AttributeUsageAttribute>(true);\r
182 \r
183                         if (usage != null)\r
184                         {\r
185                             allowsMultiple = usage.AllowMultiple;\r
186                         }\r
187 \r
188                         foreach (PropertyInfo pi in attrType.GetProperties())\r
189                         {\r
190                             if (pi.DeclaringType == CompositionServices.ExportAttributeType || pi.DeclaringType == CompositionServices.AttributeType)\r
191                             {\r
192                                 // Don't contribute metadata properies from the base attribute types.\r
193                                 continue;\r
194                             }\r
195                  \r
196                             if (reservedMetadataNames.Contains(pi.Name, StringComparers.MetadataKeyNames))\r
197                             {\r
198                                 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name);\r
199                             }\r
200 \r
201                             object value = pi.GetValue(attr, null);\r
202 \r
203                             if (value != null && !IsValidAttributeType(value.GetType()))\r
204                             {\r
205                                 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_MetadataContainsValueWithInvalidType, pi.GetDisplayName(), value.GetType().GetDisplayName());\r
206                             }\r
207 \r
208                             if (!dictionary.TryContributeMetadataValue(pi.Name, value, pi.PropertyType, allowsMultiple))\r
209                             {\r
210                                 throw ExceptionBuilder.CreateDiscoveryException(Strings.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), pi.Name);\r
211                             }\r
212                     }\r
213                     }\r
214                 }\r
215             }\r
216 \r
217             // Need Keys.ToArray because we alter the dictionary in the loop\r
218             foreach (var key in dictionary.Keys.ToArray())\r
219             {\r
220                 var list = dictionary[key] as MetadataList;\r
221                 if (list != null)\r
222                 {\r
223                     dictionary[key] = list.ToArray();\r
224                 }\r
225             }\r
226 \r
227             return;\r
228         }\r
229 \r
230         private static bool TryContributeMetadataValue(this IDictionary<string, object> dictionary, string name, object value, Type valueType, bool allowsMultiple)\r
231         {\r
232             object metadataValue;\r
233             if (!dictionary.TryGetValue(name, out metadataValue))\r
234             {\r
235                 if (allowsMultiple)\r
236                 {\r
237                     var list = new MetadataList();\r
238                     list.Add(value, valueType);\r
239                     value = list;\r
240                 }\r
241 \r
242                 dictionary.Add(name, value);\r
243             }\r
244             else\r
245             {\r
246                 var list = metadataValue as MetadataList;\r
247                 if (!allowsMultiple || list == null)\r
248                 {\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
252                     return false;\r
253                 }\r
254 \r
255                 list.Add(value, valueType);\r
256             }\r
257             return true;\r
258         }\r
259 \r
260         private class MetadataList\r
261         {\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
267 \r
268             public void Add(object item, Type itemType)\r
269             {\r
270                 this._containsNulls |= (item == null);\r
271 \r
272                 // if we've been passed typeof(object), we basically have no type inmformation\r
273                 if (itemType == ObjectType)\r
274                 {\r
275                     itemType = null;\r
276                 }\r
277 \r
278                 // if we have no type information, get it from the item, if we can\r
279                 if ((itemType == null) && (item != null))\r
280                 {\r
281                     itemType = item.GetType();\r
282                 }\r
283 \r
284                 // Types are special, because the are abstract classes, so if the item casts to Type, we assume System.Type\r
285                 if (item is Type)\r
286                 {\r
287                     itemType = TypeType;\r
288                 }\r
289 \r
290                 // only try to call this if we got a meaningful type\r
291                 if (itemType != null)\r
292                 {\r
293                     this.InferArrayType(itemType);\r
294                 }\r
295 \r
296                 this._innerList.Add(item);\r
297             }\r
298 \r
299             private void InferArrayType(Type itemType)\r
300             {\r
301                 Assumes.NotNull(itemType);\r
302 \r
303                 if (this._arrayType == null)\r
304                 {\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
307                 }\r
308                 else\r
309                 {\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
314                     {\r
315                         this._arrayType = ObjectType;\r
316                     }\r
317                 }\r
318             }\r
319 \r
320             public Array ToArray()\r
321             {\r
322                 if (this._arrayType == null)\r
323                 {\r
324                     // if the array type has not been set, assume Object \r
325                     this._arrayType = ObjectType;\r
326                 }\r
327                 else if (this._containsNulls && this._arrayType.IsValueType)\r
328                 {\r
329                     // if the array type is a value type and we have seen nulls, then assume Object\r
330                     this._arrayType = ObjectType;\r
331                 }\r
332 \r
333                 Array array = Array.CreateInstance(this._arrayType, this._innerList.Count);\r
334 \r
335                 for(int i = 0; i < array.Length; i++)\r
336                 {\r
337                     array.SetValue(this._innerList[i], i);\r
338                 }\r
339                 return array;\r
340             }\r
341         }\r
342 \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
345         //{\r
346         //    Assumes.IsTrue(metadata != null);\r
347 \r
348         //    var result = CompositionResult.SucceededResult;\r
349 \r
350         //    var missingMetadata = (requiredMetadata == null) ? null : requiredMetadata.Except<string>(metadata.Keys);\r
351         //    if (missingMetadata != null && missingMetadata.Any())\r
352         //    {\r
353         //        result = result.MergeIssue(\r
354         //            CompositionError.CreateIssueAsWarning(CompositionErrorId.RequiredMetadataNotFound,\r
355         //            Strings.RequiredMetadataNotFound,\r
356         //            contractName,\r
357         //            string.Join(", ", missingMetadata.ToArray())));\r
358 \r
359         //        return new CompositionResult(false, result.Issues);\r
360         //    }\r
361 \r
362         //    return result;\r
363         //}\r
364 \r
365         internal static IEnumerable<KeyValuePair<string, Type>> GetRequiredMetadata(Type metadataViewType)\r
366         {\r
367             if ((metadataViewType == null) ||\r
368                 ExportServices.IsDefaultMetadataViewType(metadataViewType) ||\r
369                 ExportServices.IsDictionaryConstructorViewType(metadataViewType) ||\r
370                 !metadataViewType.IsInterface)\r
371             {\r
372                 return Enumerable.Empty<KeyValuePair<string, Type>>();\r
373             }\r
374 \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
378                 ToList();\r
379 \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
384         }\r
385 \r
386         internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)\r
387         {\r
388             try\r
389             {\r
390                 engine.SatisfyImports(part);\r
391             }\r
392             catch (CompositionException ex)\r
393             {\r
394                 throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);\r
395             }\r
396 \r
397             try\r
398             {\r
399                 return part.GetExportedValue(definition);\r
400             }\r
401             catch (ComposablePartException ex)\r
402             {\r
403                 throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex);\r
404             }\r
405         }\r
406         \r
407         internal static bool IsRecomposable(this ComposablePart part)\r
408         {\r
409             return part.ImportDefinitions.Any(import => import.IsRecomposable);\r
410         }\r
411 \r
412         internal static CompositionResult<T> TryInvoke<T>(Func<T> action)\r
413         {\r
414             try\r
415             {\r
416                 T value = action();\r
417                 return new CompositionResult<T>(value);\r
418             }\r
419             catch (CompositionException ex)\r
420             {\r
421                 return new CompositionResult<T>(ex.Errors);\r
422             }\r
423         }\r
424 \r
425         internal static CompositionResult TryInvoke(Action action)\r
426         {\r
427             try\r
428             {\r
429                 action();\r
430                 return CompositionResult.SucceededResult;\r
431             }\r
432             catch (CompositionException ex)\r
433             {\r
434                 return new CompositionResult(ex.Errors);\r
435             }\r
436         }\r
437 \r
438         internal static CompositionResult TryFire<TEventArgs>(EventHandler<TEventArgs> _delegate, object sender, TEventArgs e)\r
439             where TEventArgs : EventArgs\r
440         {\r
441             CompositionResult result = CompositionResult.SucceededResult;\r
442             foreach (EventHandler<TEventArgs> _subscriber in _delegate.GetInvocationList())\r
443             {\r
444                 try\r
445                 {\r
446                     _subscriber.Invoke(sender, e);\r
447                 }\r
448                 catch (CompositionException ex)\r
449                 {\r
450                     result = result.MergeErrors(ex.Errors);\r
451                 }\r
452             }\r
453 \r
454             return result;\r
455         }\r
456 \r
457         internal static CreationPolicy GetRequiredCreationPolicy(this ImportDefinition definition)\r
458         {\r
459             ContractBasedImportDefinition contractDefinition = definition as ContractBasedImportDefinition;\r
460 \r
461             if (contractDefinition != null)\r
462             {\r
463                 return contractDefinition.RequiredCreationPolicy;\r
464             }\r
465 \r
466             return CreationPolicy.Any;\r
467         }\r
468 \r
469         /// <summary>\r
470         ///     Returns a value indicating whether cardinality is \r
471         ///     <see cref="ImportCardinality.ZeroOrOne"/> or \r
472         ///     <see cref="ImportCardinality.ExactlyOne"/>.\r
473         /// </summary>\r
474         internal static bool IsAtMostOne(this ImportCardinality cardinality)\r
475         {\r
476             return cardinality == ImportCardinality.ZeroOrOne || cardinality == ImportCardinality.ExactlyOne;\r
477         }\r
478 \r
479         private static bool IsValidAttributeType(Type type)\r
480         {\r
481             return IsValidAttributeType(type, true);\r
482         }\r
483 \r
484         private static bool IsValidAttributeType(Type type, bool arrayAllowed)\r
485         {\r
486             Assumes.NotNull(type);\r
487             // Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3.\r
488 \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
491             {\r
492                 return true;\r
493             }\r
494 \r
495             if (type == typeof(string))\r
496             {\r
497                 return true;\r
498             }\r
499 \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
502             {\r
503                 return true;\r
504             }\r
505 \r
506             if (typeof(Type).IsAssignableFrom(type))\r
507             {\r
508                 return true;\r
509             }\r
510 \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
515             {\r
516                 return true;\r
517             }\r
518 \r
519             return false;\r
520         }\r
521     }\r
522 }\r