Implement MachineKey.Protect and MachineKey.Unprotect
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / ReflectionModel / ReflectionComposablePart.cs
1 \r
2 // -----------------------------------------------------------------------\r
3 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
4 // -----------------------------------------------------------------------\r
5 using System;\r
6 using System.Collections.Generic;\r
7 using System.ComponentModel.Composition.Hosting;\r
8 using System.ComponentModel.Composition.Primitives;\r
9 using System.Diagnostics.CodeAnalysis;\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 \r
16 namespace System.ComponentModel.Composition.ReflectionModel\r
17 {\r
18     // This \r
19     internal class ReflectionComposablePart : ComposablePart, ICompositionElement\r
20     {\r
21         private readonly ReflectionComposablePartDefinition _definition;\r
22         private readonly Dictionary<ImportDefinition, object> _importValues = new Dictionary<ImportDefinition, object>();\r
23         private readonly Dictionary<ImportDefinition, ImportingItem> _importsCache = new Dictionary<ImportDefinition, ImportingItem>();\r
24         private readonly Dictionary<ExportDefinition, ExportingMember> _exportsCache = new Dictionary<ExportDefinition, ExportingMember>();\r
25         private bool _invokeImportsSatisfied = true;\r
26         private bool _invokingImportsSatisfied = false;\r
27         private bool _initialCompositionComplete = false;\r
28         private volatile object _cachedInstance;\r
29         private object _lock = new object();\r
30 \r
31         public ReflectionComposablePart(ReflectionComposablePartDefinition definition)\r
32         {\r
33             Requires.NotNull(definition, "definition");\r
34 \r
35             this._definition = definition;\r
36         }\r
37 \r
38         public ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart)\r
39         {\r
40             Requires.NotNull(definition, "definition");\r
41             Requires.NotNull(attributedPart, "attributedPart");\r
42 \r
43             this._definition = definition;\r
44 \r
45             if (attributedPart is ValueType)\r
46             {\r
47                 throw new ArgumentException(Strings.ArgumentValueType, "attributedPart");\r
48             }\r
49             this._cachedInstance = attributedPart;\r
50         }\r
51 \r
52         protected virtual void EnsureRunning()\r
53         {\r
54         }\r
55 \r
56         protected virtual void ReleaseInstanceIfNecessary(object instance)\r
57         {\r
58         }\r
59 \r
60         protected object CachedInstance\r
61         {\r
62             get\r
63             {\r
64                 lock (this._lock)\r
65                 {\r
66                     return this._cachedInstance;\r
67                 }\r
68             }\r
69         }\r
70 \r
71         public ReflectionComposablePartDefinition Definition\r
72         {\r
73             get \r
74             {\r
75                 this.EnsureRunning();\r
76                 return this._definition; \r
77             }\r
78         }\r
79 \r
80         public override IDictionary<string, object> Metadata\r
81         {\r
82             get\r
83             {\r
84                 this.EnsureRunning();\r
85                 return this.Definition.Metadata;\r
86             }\r
87         }\r
88 \r
89         public sealed override IEnumerable<ImportDefinition> ImportDefinitions\r
90         {\r
91             get\r
92             {\r
93                 this.EnsureRunning();\r
94                 return this.Definition.ImportDefinitions;\r
95             }\r
96         }\r
97 \r
98         public sealed override IEnumerable<ExportDefinition> ExportDefinitions\r
99         {\r
100             get\r
101             {\r
102                 this.EnsureRunning();\r
103                 return this.Definition.ExportDefinitions;\r
104             }\r
105         }\r
106 \r
107 \r
108         string ICompositionElement.DisplayName\r
109         {\r
110             get { return GetDisplayName(); }\r
111         }\r
112 \r
113         ICompositionElement ICompositionElement.Origin\r
114         {\r
115             get { return Definition; }\r
116         }\r
117 \r
118         // This is the ONLY method which is not executed under the ImportEngine composition lock.\r
119         // We need to protect all state that is accesses\r
120         public override object GetExportedValue(ExportDefinition definition)\r
121         {\r
122             // given the implementation of the ImportEngine, this iwll be called under a lock if the part is still being composed\r
123             // This is only called outside of the lock when the part is fully composed\r
124             // based on that we only protect:\r
125             // _exportsCache - and thus all calls to GetExportingMemberFromDefinition\r
126             // access to _importValues\r
127             // access to _initialCompositionComplete\r
128             // access to _instance\r
129             this.EnsureRunning();\r
130             Requires.NotNull(definition, "definition");\r
131 \r
132             ExportingMember member = null;\r
133             lock (this._lock)\r
134             {\r
135                 member = GetExportingMemberFromDefinition(definition);\r
136                 if (member == null)\r
137                 {\r
138                     throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition");\r
139                 }\r
140                 this.EnsureGettable();\r
141             }\r
142 \r
143             return this.GetExportedValue(member);\r
144         }\r
145 \r
146         public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)\r
147         {\r
148             this.EnsureRunning();\r
149 \r
150             Requires.NotNull(definition, "definition");\r
151             Requires.NotNull(exports, "exports");\r
152 \r
153             ImportingItem item = GetImportingItemFromDefinition(definition);\r
154             if (item == null)\r
155             {\r
156                 throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition");\r
157             }\r
158 \r
159             EnsureSettable(definition);\r
160 \r
161             // Avoid walking over exports many times\r
162             Export[] exportsAsArray = exports.AsArray();\r
163             EnsureCardinality(definition, exportsAsArray);\r
164 \r
165             SetImport(item, exportsAsArray);\r
166         }\r
167 \r
168         public override void Activate()\r
169         {\r
170             this.EnsureRunning();\r
171 \r
172             this.SetNonPrerequisiteImports();\r
173 \r
174             // Whenever we are composed/recomposed notify the instance\r
175             this.NotifyImportSatisfied();\r
176 \r
177             lock (this._lock)\r
178             {\r
179                 this._initialCompositionComplete = true;\r
180             }\r
181         }\r
182 \r
183         public override string ToString()\r
184         {\r
185             return this.GetDisplayName();\r
186         }\r
187 \r
188         private object GetExportedValue(ExportingMember member)\r
189         {\r
190             object instance = null;\r
191             if (member.RequiresInstance)\r
192             {   // Only activate the instance if we actually need to\r
193 \r
194                 instance = this.GetInstanceActivatingIfNeeded();\r
195             }\r
196 \r
197             return member.GetExportedValue(instance, this._lock);\r
198         }\r
199 \r
200         private void SetImport(ImportingItem item, Export[] exports)\r
201         {\r
202             object value = item.CastExportsToImportType(exports);\r
203 \r
204             lock (this._lock)\r
205             {\r
206                 this._invokeImportsSatisfied = true;\r
207                 this._importValues[item.Definition] = value;\r
208             }\r
209         }\r
210 \r
211         private object GetInstanceActivatingIfNeeded()\r
212         {\r
213             if (this._cachedInstance != null)\r
214             {\r
215                 return this._cachedInstance;\r
216             }\r
217             else\r
218             {\r
219                 ConstructorInfo constructor = null;\r
220                 object[] arguments = null;\r
221                 // determine whether activation is required, and collect necessary information for activation\r
222                 // we need to do that under a lock\r
223                 lock (this._lock)\r
224                 {\r
225                     if (!this.RequiresActivation())\r
226                     {\r
227                         return null;\r
228                     }\r
229 \r
230                     constructor = this.Definition.GetConstructor();\r
231                     if (constructor == null)\r
232                     {\r
233                         throw new ComposablePartException(\r
234                             CompositionErrorId.ReflectionModel_PartConstructorMissing,\r
235                             String.Format(CultureInfo.CurrentCulture,\r
236                                 Strings.ReflectionModel_PartConstructorMissing,\r
237                                 this.Definition.GetPartType().FullName),\r
238                             this.Definition.ToElement());\r
239                     }\r
240                     arguments = this.GetConstructorArguments();\r
241                 }\r
242 \r
243                 // create instance outside of the lock\r
244                 object createdInstance = this.CreateInstance(constructor, arguments);\r
245 \r
246                 // set the created instance\r
247                 lock (this._lock)\r
248                 {\r
249                     if (this._cachedInstance == null)\r
250                     {\r
251                         this._cachedInstance = createdInstance;\r
252                         createdInstance = null;\r
253                     }\r
254                 }\r
255 \r
256                 // if the instance has been already set\r
257                 if (createdInstance == null)\r
258                 {\r
259                     this.ReleaseInstanceIfNecessary(createdInstance);\r
260                 }\r
261             }\r
262 \r
263             return this._cachedInstance;\r
264         }\r
265 \r
266         private object[] GetConstructorArguments()\r
267         {\r
268             ReflectionParameterImportDefinition[] parameterImports = this.ImportDefinitions.OfType<ReflectionParameterImportDefinition>().ToArray();\r
269             object[] arguments = new object[parameterImports.Length];\r
270 \r
271             this.UseImportedValues(\r
272                 parameterImports,\r
273                 (import, definition, value) =>\r
274                 {\r
275                     if (definition.Cardinality == ImportCardinality.ZeroOrMore && !import.ImportType.IsAssignableCollectionType)\r
276                     {\r
277                         throw new ComposablePartException(\r
278                             CompositionErrorId.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned,\r
279                             String.Format(CultureInfo.CurrentCulture,\r
280                                 Strings.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned,\r
281                                 this.Definition.GetPartType().FullName,\r
282                                 definition.ImportingLazyParameter.Value.Name),\r
283                             this.Definition.ToElement());\r
284                     }\r
285 \r
286                     arguments[definition.ImportingLazyParameter.Value.Position] = value;\r
287                 },\r
288                 true);\r
289 \r
290             return arguments;\r
291         }\r
292 \r
293         // alwayc called under a lock\r
294         private bool RequiresActivation()\r
295         {\r
296             // If we have any imports then we need activation\r
297             // (static imports are not supported)\r
298             if (this.ImportDefinitions.Any())\r
299             {\r
300                 return true;\r
301             }\r
302 \r
303             // If we have any instance exports, then we also \r
304             // need activation.\r
305             return this.ExportDefinitions.Any(definition =>\r
306             {\r
307                 ExportingMember member = GetExportingMemberFromDefinition(definition);\r
308 \r
309                 return member.RequiresInstance;\r
310             });\r
311         }\r
312 \r
313         // this is called under a lock\r
314         private void EnsureGettable()\r
315         {\r
316             // If we're already composed then we know that \r
317             // all pre-req imports have been satisfied\r
318             if (_initialCompositionComplete)\r
319             {\r
320                 return;\r
321             }\r
322 \r
323             // Make sure all pre-req imports have been set\r
324             foreach (ImportDefinition definition in ImportDefinitions.Where(definition => definition.IsPrerequisite))\r
325             {\r
326                 if (!this._importValues.ContainsKey(definition))\r
327                 {\r
328                     throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,\r
329                                                             Strings.InvalidOperation_GetExportedValueBeforePrereqImportSet,\r
330                                                             definition.ToElement().DisplayName));\r
331                 }\r
332             }\r
333         }\r
334 \r
335         private void EnsureSettable(ImportDefinition definition)\r
336         {\r
337             lock (this._lock)\r
338             {\r
339                 if (this._initialCompositionComplete && !definition.IsRecomposable)\r
340                 {\r
341                     throw new InvalidOperationException(Strings.InvalidOperation_DefinitionCannotBeRecomposed);\r
342                 }\r
343             }\r
344         }\r
345 \r
346         private static void EnsureCardinality(ImportDefinition definition, Export[] exports)\r
347         {\r
348             Requires.NullOrNotNullElements(exports, "exports");\r
349 \r
350             ExportCardinalityCheckResult result = ExportServices.CheckCardinality(definition, exports);\r
351 \r
352             switch (result)\r
353             {\r
354                 case ExportCardinalityCheckResult.NoExports:\r
355                     throw new ArgumentException(Strings.Argument_ExportsEmpty, "exports");\r
356 \r
357                 case ExportCardinalityCheckResult.TooManyExports:\r
358                     throw new ArgumentException(Strings.Argument_ExportsTooMany, "exports");\r
359 \r
360                 default:\r
361                     Assumes.IsTrue(result == ExportCardinalityCheckResult.Match);\r
362                     break;\r
363             }\r
364         }\r
365 \r
366         private object CreateInstance(ConstructorInfo constructor, object[] arguments)\r
367         { \r
368             Exception exception = null;\r
369             object instance = null;\r
370 \r
371             try\r
372             {\r
373                 instance = constructor.SafeInvoke(arguments);\r
374             }\r
375             catch (TypeInitializationException ex) \r
376             { \r
377                 exception = ex; \r
378             }\r
379             catch (TargetInvocationException ex)\r
380             {\r
381                 exception = ex.InnerException;\r
382             }\r
383             \r
384             if (exception != null)\r
385             {\r
386                 throw new ComposablePartException(\r
387                     CompositionErrorId.ReflectionModel_PartConstructorThrewException,\r
388                     String.Format(CultureInfo.CurrentCulture,\r
389                         Strings.ReflectionModel_PartConstructorThrewException,\r
390                         Definition.GetPartType().FullName),\r
391                     Definition.ToElement(),\r
392                     exception);\r
393             }\r
394 \r
395             return instance;\r
396         }\r
397 \r
398         private void SetNonPrerequisiteImports()\r
399         {\r
400             IEnumerable<ImportDefinition> members = this.ImportDefinitions.Where(import => !import.IsPrerequisite);\r
401 \r
402             // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition\r
403             this.UseImportedValues(members, SetExportedValueForImport, false);\r
404         }\r
405 \r
406         private void SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value)\r
407         {\r
408             ImportingMember importMember = (ImportingMember)import;\r
409 \r
410             object instance = this.GetInstanceActivatingIfNeeded();\r
411 \r
412             importMember.SetExportedValue(instance, value);\r
413         }\r
414 \r
415         private void UseImportedValues<TImportDefinition>(IEnumerable<TImportDefinition> definitions, Action<ImportingItem, TImportDefinition, object> useImportValue, bool errorIfMissing)\r
416             where TImportDefinition : ImportDefinition\r
417         {\r
418             var result = CompositionResult.SucceededResult;\r
419 \r
420             foreach (var definition in definitions)\r
421             {\r
422                 ImportingItem import = GetImportingItemFromDefinition(definition);\r
423 \r
424                 object value;\r
425                 if (!TryGetImportValue(definition, out value))\r
426                 {\r
427                     if (!errorIfMissing)\r
428                     {\r
429                         continue;\r
430                     }\r
431 \r
432                     if (definition.Cardinality == ImportCardinality.ExactlyOne)\r
433                     {\r
434                         var error = CompositionError.Create(\r
435                             CompositionErrorId.ImportNotSetOnPart,\r
436                             Strings.ImportNotSetOnPart,\r
437                             this.Definition.GetPartType().FullName,\r
438                             definition.ToString());\r
439                         result = result.MergeError(error);\r
440                         continue;\r
441                     }\r
442                     else\r
443                     {\r
444                         value = import.CastExportsToImportType(new Export[0]);\r
445                     }\r
446                 }\r
447 \r
448                 useImportValue(import, definition, value);\r
449             }\r
450 \r
451             result.ThrowOnErrors();\r
452         }\r
453 \r
454         private bool TryGetImportValue(ImportDefinition definition, out object value)\r
455         {\r
456             lock (this._lock)\r
457             {\r
458                 if (this._importValues.TryGetValue(definition, out value))\r
459                 {\r
460                     this._importValues.Remove(definition);\r
461                     return true;\r
462                 }\r
463             }\r
464 \r
465             value = null;\r
466             return false;\r
467         }\r
468 \r
469         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]\r
470         private void NotifyImportSatisfied()\r
471         {\r
472             if (this._invokeImportsSatisfied && !this._invokingImportsSatisfied)\r
473             {\r
474                 IPartImportsSatisfiedNotification notify = this.GetInstanceActivatingIfNeeded() as IPartImportsSatisfiedNotification;\r
475                 if (notify != null)\r
476                 {\r
477                     try\r
478                     {\r
479                         // Reentrancy on composition notifications is allowed, so set this first to avoid \r
480                         // an infinte loop of notifications.\r
481                         this._invokingImportsSatisfied = true;\r
482 \r
483                         notify.OnImportsSatisfied();\r
484                     }\r
485                     catch (Exception exception)\r
486                     {\r
487                         throw new ComposablePartException(\r
488                             CompositionErrorId.ReflectionModel_PartOnImportsSatisfiedThrewException,\r
489                             String.Format(CultureInfo.CurrentCulture,\r
490                                 Strings.ReflectionModel_PartOnImportsSatisfiedThrewException,\r
491                                 Definition.GetPartType().FullName),\r
492                             Definition.ToElement(),\r
493                             exception);\r
494                     }\r
495                     finally\r
496                     {\r
497                         this._invokingImportsSatisfied = false;\r
498                     }\r
499 \r
500                     this._invokeImportsSatisfied = false;\r
501                 }\r
502             }\r
503         }\r
504 \r
505         // this is always called under a lock\r
506         private ExportingMember GetExportingMemberFromDefinition(ExportDefinition definition)\r
507         {\r
508             ExportingMember result;\r
509             if (!_exportsCache.TryGetValue(definition, out result))\r
510             {\r
511                 result = GetExportingMember(definition);\r
512                 if (result != null)\r
513                 {\r
514                     _exportsCache[definition] = result;\r
515                 }\r
516             }\r
517 \r
518             return result;\r
519         }\r
520 \r
521         private ImportingItem GetImportingItemFromDefinition(ImportDefinition definition)\r
522         {\r
523             ImportingItem result;\r
524             if (!_importsCache.TryGetValue(definition, out result))\r
525             {\r
526                 result = GetImportingItem(definition);\r
527                 if (result != null)\r
528                 {\r
529                     _importsCache[definition] = result;\r
530                 }\r
531             }\r
532 \r
533             return result;\r
534         }\r
535 \r
536         private static ImportingItem GetImportingItem(ImportDefinition definition)\r
537         {\r
538             ReflectionImportDefinition reflectionDefinition = definition as ReflectionImportDefinition;\r
539             if (reflectionDefinition != null)\r
540             {\r
541                 return reflectionDefinition.ToImportingItem();\r
542             }\r
543 \r
544             // Don't recognize it\r
545             return null;\r
546         }\r
547 \r
548         private static ExportingMember GetExportingMember(ExportDefinition definition)\r
549         {\r
550             ReflectionMemberExportDefinition exportDefinition = definition as ReflectionMemberExportDefinition;\r
551             if (exportDefinition != null)\r
552             {\r
553                 return exportDefinition.ToExportingMember();\r
554             }\r
555 \r
556             // Don't recognize it\r
557             return null;\r
558         }\r
559 \r
560         private string GetDisplayName()\r
561         {\r
562             return this.Definition.GetPartType().GetDisplayName();\r
563         }\r
564     }\r
565 }