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