Remove excessive shortcut key matching in ToolStrip
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / Primitives / ContractBasedImportDefinition.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.Hosting;\r
7 using System.Diagnostics.CodeAnalysis;\r
8 using System.Linq;\r
9 using System.Linq.Expressions;\r
10 using Microsoft.Internal;\r
11 using System.Globalization;\r
12 \r
13 namespace System.ComponentModel.Composition.Primitives\r
14 {\r
15     /// <summary>\r
16     ///     Represents a contract name and metadata-based import \r
17     ///     required by a <see cref="ComposablePart"/> object.\r
18     /// </summary>\r
19     public class ContractBasedImportDefinition : ImportDefinition\r
20     {\r
21         // Unlike contract name, required metadata has a sensible default; set it to an empty \r
22         // enumerable, so that derived definitions only need to override ContractName by default.\r
23         private readonly IEnumerable<KeyValuePair<string, Type>> _requiredMetadata = Enumerable.Empty<KeyValuePair<string, Type>>();\r
24         private Expression<Func<ExportDefinition, bool>> _constraint;\r
25         private readonly CreationPolicy _requiredCreationPolicy = CreationPolicy.Any;\r
26         private readonly string _requiredTypeIdentity = null;\r
27         private bool _isRequiredMetadataValidated = false;\r
28 \r
29         /// <summary>\r
30         ///     Initializes a new instance of the <see cref="ContractBasedImportDefinition"/> class.\r
31         /// </summary>\r
32         /// <remarks>\r
33         ///     <note type="inheritinfo">\r
34         ///         Derived types calling this constructor can optionally override the \r
35         ///         <see cref="ImportDefinition.ContractName"/>, <see cref="RequiredTypeIdentity"/>,\r
36         ///         <see cref="RequiredMetadata"/>, <see cref="ImportDefinition.Cardinality"/>, \r
37         ///         <see cref="ImportDefinition.IsPrerequisite"/>, <see cref="ImportDefinition.IsRecomposable"/> \r
38         ///         and <see cref="RequiredCreationPolicy"/> properties.\r
39         ///     </note>\r
40         /// </remarks>\r
41         protected ContractBasedImportDefinition()\r
42         {\r
43         }\r
44 \r
45         /// <summary>\r
46         ///     Initializes a new instance of the <see cref="ContractBasedImportDefinition"/> class \r
47         ///     with the specified contract name, required metadataq, cardinality, value indicating \r
48         ///     if the import definition is recomposable and a value indicating if the import definition \r
49         ///     is a prerequisite.\r
50         /// </summary>\r
51         /// <param name="contractName">\r
52         ///     A <see cref="String"/> containing the contract name of the \r
53         ///     <see cref="Export"/> required by the <see cref="ContractBasedImportDefinition"/>.\r
54         /// </param>\r
55         /// <param name="requiredTypeIdentity">\r
56         ///     The type identity of the export type expected. Use <see cref="AttributedModelServices.GetTypeIdentity(Type)"/>\r
57         ///     to generate a type identity for a given type. If no specific type is required pass <see langword="null"/>.\r
58         /// </param>\r
59         /// <param name="requiredMetadata">\r
60         ///     An <see cref="IEnumerable{T}"/> of <see cref="String"/> objects containing\r
61         ///     the metadata names of the <see cref="Export"/> required by the \r
62         ///     <see cref="ContractBasedImportDefinition"/>; or <see langword="null"/> to\r
63         ///     set the <see cref="RequiredMetadata"/> property to an empty <see cref="IEnumerable{T}"/>.\r
64         /// </param>\r
65         /// <param name="cardinality">\r
66         ///     One of the <see cref="ImportCardinality"/> values indicating the \r
67         ///     cardinality of the <see cref="Export"/> objects required by the\r
68         ///     <see cref="ContractBasedImportDefinition"/>.\r
69         /// </param>\r
70         /// <param name="isRecomposable">\r
71         ///     <see langword="true"/> if the <see cref="ContractBasedImportDefinition"/> can be satisfied \r
72         ///     multiple times throughout the lifetime of a <see cref="ComposablePart"/>, otherwise, \r
73         ///     <see langword="false"/>.\r
74         /// </param>\r
75         /// <param name="isPrerequisite">\r
76         ///     <see langword="true"/> if the <see cref="ContractBasedImportDefinition"/> is required to be \r
77         ///     satisfied before a <see cref="ComposablePart"/> can start producing exported \r
78         ///     objects; otherwise, <see langword="false"/>.\r
79         /// </param>\r
80         /// <param name="requiredCreationPolicy">\r
81         ///     A value indicating that the importer requires a specific <see cref="CreationPolicy"/> for \r
82         ///     the exports used to satisfy this import. If no specific <see cref="CreationPolicy"/> is needed\r
83         ///     pass the default <see cref="CreationPolicy.Any"/>.\r
84         /// </param>\r
85         /// <exception cref="ArgumentNullException">\r
86         ///     <paramref name="contractName"/> is <see langword="null"/>.\r
87         /// </exception>\r
88         /// <exception cref="ArgumentException">\r
89         ///     <paramref name="contractName"/> is an empty string ("").\r
90         ///     <para>\r
91         ///         -or-\r
92         ///     </para>\r
93         ///     <paramref name="requiredMetadata"/> contains an element that is <see langword="null"/>.\r
94         ///     <para>\r
95         ///         -or-\r
96         ///     </para>\r
97         ///     <paramref name="cardinality"/> is not one of the <see cref="ImportCardinality"/> \r
98         ///     values.\r
99         /// </exception>\r
100         [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]\r
101         public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, IEnumerable<KeyValuePair<string, Type>> requiredMetadata, \r
102             ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, CreationPolicy requiredCreationPolicy)\r
103             : base(contractName, cardinality, isRecomposable, isPrerequisite)\r
104         {\r
105             Requires.NotNullOrEmpty(contractName, "contractName");\r
106 \r
107             this._requiredTypeIdentity = requiredTypeIdentity;\r
108 \r
109             if (requiredMetadata != null)\r
110             {\r
111                 this._requiredMetadata = requiredMetadata;\r
112             }\r
113 \r
114             this._requiredCreationPolicy = requiredCreationPolicy;\r
115         }\r
116 \r
117         /// <summary>\r
118         ///     The type identity of the export type expected.\r
119         /// </summary>\r
120         /// <value>\r
121         ///     A <see cref="string"/> that is generated by <see cref="AttributedModelServices.GetTypeIdentity(Type)"/>\r
122         ///     on the type that this import expects. If the value is <see langword="null"/> then this import\r
123         ///     doesn't expect a particular type.\r
124         /// </value>\r
125         public virtual string RequiredTypeIdentity\r
126         {\r
127             get { return this._requiredTypeIdentity; }\r
128         }\r
129 \r
130         /// <summary>\r
131         ///     Gets the metadata names of the export required by the import definition.\r
132         /// </summary>\r
133         /// <value>\r
134         ///     An <see cref="IEnumerable{T}"/> of pairs of metadata keys and types of the <see cref="Export"/> required by the \r
135         ///     <see cref="ContractBasedImportDefinition"/>. The default is an empty \r
136         ///     <see cref="IEnumerable{T}"/>.\r
137         /// </value>\r
138         /// <remarks>\r
139         ///     <note type="inheritinfo">\r
140         ///         Overriders of this property should never return <see langword="null"/>\r
141         ///         or return an <see cref="IEnumerable{T}"/> that contains an element that is\r
142         ///         <see langword="null"/>. If the definition does not contain required metadata, \r
143         ///         return an empty <see cref="IEnumerable{T}"/> instead.\r
144         ///     </note>\r
145         /// </remarks>\r
146         [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]\r
147         public virtual IEnumerable<KeyValuePair<string, Type>> RequiredMetadata\r
148         {\r
149             get\r
150             {\r
151                 // NOTE : unlike other arguments, we validate this one as late as possible, because its validation may lead to type loading\r
152                 this.ValidateRequiredMetadata();\r
153 \r
154                 return this._requiredMetadata;\r
155             }\r
156         }\r
157 \r
158         private void ValidateRequiredMetadata()\r
159         {\r
160             if (!this._isRequiredMetadataValidated)\r
161             {\r
162                 foreach (KeyValuePair<string, Type> metadataItem in this._requiredMetadata)\r
163                 {\r
164                     if ((metadataItem.Key == null) || (metadataItem.Value == null))\r
165                     {\r
166                         throw new InvalidOperationException(\r
167                             string.Format(CultureInfo.CurrentCulture, Strings.Argument_NullElement, "requiredMetadata"));\r
168                     }\r
169                 }\r
170                 this._isRequiredMetadataValidated = true;\r
171             }\r
172         }\r
173 \r
174         /// <summary>\r
175         ///     Gets or sets a value indicating that the importer requires a specific \r
176         ///     <see cref="CreationPolicy"/> for the exports used to satisfy this import. T\r
177         /// </summary>\r
178         /// <value>\r
179         ///     <see cref="CreationPolicy.Any"/> - default value, used if the importer doesn't \r
180         ///         require a specific <see cref="CreationPolicy"/>.\r
181         /// \r
182         ///     <see cref="CreationPolicy.Shared"/> - Requires that all exports used should be shared\r
183         ///         by everyone in the container.\r
184         /// \r
185         ///     <see cref="CreationPolicy.NonShared"/> - Requires that all exports used should be \r
186         ///         non-shared in a container and thus everyone gets their own instance.\r
187         /// </value>\r
188         public virtual CreationPolicy RequiredCreationPolicy\r
189         {\r
190             get { return this._requiredCreationPolicy; }\r
191         }\r
192 \r
193         /// <summary>\r
194         ///     Gets an expression that defines conditions that must be matched for the import \r
195         ///     described by the import definition to be satisfied.\r
196         /// </summary>\r
197         /// <returns>\r
198         ///     A <see cref="Expression{TDelegate}"/> containing a <see cref="Func{T, TResult}"/> \r
199         ///     that defines the conditions that must be matched for the \r
200         ///     <see cref="ImportDefinition"/> to be satisfied by an <see cref="Export"/>.\r
201         /// </returns>\r
202         /// <remarks>\r
203         ///     <para>\r
204         ///         This property returns an expression that defines conditions based on the \r
205         ///         <see cref="ImportDefinition.ContractName"/>, <see cref="RequiredTypeIdentity"/>, \r
206         ///         <see cref="RequiredMetadata"/>, and <see cref="RequiredCreationPolicy"/>\r
207         ///         properties. \r
208         ///     </para>\r
209         /// </remarks>\r
210         public override Expression<Func<ExportDefinition, bool>> Constraint\r
211         {   \r
212             get\r
213             {\r
214                 if (this._constraint == null)\r
215                 {\r
216                     this._constraint = ConstraintServices.CreateConstraint(this.ContractName, this.RequiredTypeIdentity, this.RequiredMetadata, this.RequiredCreationPolicy);\r
217                 }\r
218 \r
219                 return this._constraint;\r
220             }\r
221         }\r
222 \r
223         /// <summary>\r
224         ///     Executes an optimized version of the contraint given by the <see cref="Constraint"/> property\r
225         /// </summary>\r
226         /// <param name="exportDefinition">\r
227         ///     A definition for a <see cref="Export"/> used to determine if it satisfies the\r
228         ///     requirements for this <see cref="ImportDefinition"/>.\r
229         /// </param>\r
230         /// <returns>\r
231         ///     <see langword="True"/> if the <see cref="Export"/> satisfies the requirements for\r
232         ///     this <see cref="ImportDefinition"/>, otherwise returns <see langword="False"/>.\r
233         /// </returns>\r
234         /// <remarks>\r
235         ///     <note type="inheritinfo">\r
236         ///         Overrides of this method can provide a more optimized execution of the \r
237         ///         <see cref="Constraint"/> property but the result should remain consistent.\r
238         ///     </note>\r
239         /// </remarks>\r
240         /// <exception cref="ArgumentNullException">\r
241         ///     <paramref name="exportDefinition"/> is <see langword="null"/>.\r
242         /// </exception>\r
243         public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition)\r
244         {\r
245             Requires.NotNull(exportDefinition, "exportDefinition");\r
246 \r
247             if (!StringComparers.ContractName.Equals(this.ContractName, exportDefinition.ContractName))\r
248             {\r
249                 return false;\r
250             }\r
251 \r
252             return MatchRequiredMatadata(exportDefinition);\r
253         }\r
254 \r
255         private bool MatchRequiredMatadata(ExportDefinition definition)\r
256         {\r
257             if (!string.IsNullOrEmpty(this.RequiredTypeIdentity))\r
258             {\r
259                 string exportTypeIdentity = definition.Metadata.GetValue<string>(CompositionConstants.ExportTypeIdentityMetadataName);\r
260 \r
261                 if (!StringComparers.ContractName.Equals(this.RequiredTypeIdentity, exportTypeIdentity))\r
262                 {\r
263                     return false;\r
264                 }\r
265             }\r
266 \r
267             foreach (KeyValuePair<string, Type> metadataItem in this.RequiredMetadata)\r
268             {\r
269                 string metadataKey = metadataItem.Key;\r
270                 Type metadataValueType = metadataItem.Value;\r
271 \r
272                 object metadataValue = null;\r
273                 if (!definition.Metadata.TryGetValue(metadataKey, out metadataValue))\r
274                 {\r
275                     return false;\r
276                 }\r
277 \r
278                 if (metadataValue != null)\r
279                 {\r
280                     // the metadata value is not null, we can rely on IsInstanceOfType to do the right thing\r
281                     if (!metadataValueType.IsInstanceOfType(metadataValue))\r
282                     {\r
283                         return false;\r
284                     }\r
285                 }\r
286                 else\r
287                 {\r
288                     // this is an unfortunate special case - typeof(object).IsInstanceofType(null) == false\r
289                     // basically nulls are not considered valid values for anything\r
290                     // We want them to match anything that is a reference type\r
291                     if (metadataValueType.IsValueType)\r
292                     {\r
293                         // this is a pretty expensive check, but we only invoke it when metadata values are null, which is very rare\r
294                         return false;\r
295                     }\r
296                 }\r
297             }\r
298 \r
299             if (this.RequiredCreationPolicy == CreationPolicy.Any)\r
300             {\r
301                 return true;\r
302             }\r
303 \r
304             CreationPolicy exportPolicy = definition.Metadata.GetValue<CreationPolicy>(CompositionConstants.PartCreationPolicyMetadataName);\r
305             return exportPolicy == CreationPolicy.Any ||\r
306                    exportPolicy == this.RequiredCreationPolicy;\r
307         }\r
308     }\r
309 }\r