[mcs] Validate more nameof argument expressions
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / Hosting / ComposablePartCatalogCollection.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Collections.ObjectModel;\r
6 using System.Collections.Generic;\r
7 using System.ComponentModel.Composition.Primitives;\r
8 using System.Linq;\r
9 using System.Text;\r
10 using System.Threading;\r
11 using System.Diagnostics;\r
12 using Microsoft.Internal;\r
13 using Microsoft.Internal.Collections;\r
14 using System.Collections;\r
15 \r
16 namespace System.ComponentModel.Composition.Hosting\r
17 {\r
18     /// <summary>\r
19     ///     This class implements a threadsafe ICollection{T} of ComposablePartCatalog.\r
20     ///     It is exposed as an ICollection(ComposablePartCatalog)\r
21     ///     It is threadsafe, notifications are not marshalled using a SynchronizationContext.\r
22     ///     It is Disposable.\r
23     /// </summary>\r
24     internal class ComposablePartCatalogCollection : ICollection<ComposablePartCatalog>, INotifyComposablePartCatalogChanged, IDisposable\r
25     {\r
26         private readonly Lock _lock = new Lock();\r
27         private Action<ComposablePartCatalogChangeEventArgs> _onChanged;\r
28         private Action<ComposablePartCatalogChangeEventArgs> _onChanging;\r
29         private List<ComposablePartCatalog> _catalogs = new List<ComposablePartCatalog>();\r
30         private volatile bool _isCopyNeeded = false;\r
31         private volatile bool _isDisposed = false;\r
32         private bool _hasChanged = false;\r
33 \r
34         public ComposablePartCatalogCollection(IEnumerable<ComposablePartCatalog> catalogs)\r
35             : this(catalogs, null, null)\r
36         {\r
37         }\r
38 \r
39         public ComposablePartCatalogCollection(\r
40             IEnumerable<ComposablePartCatalog> catalogs,\r
41             Action<ComposablePartCatalogChangeEventArgs> onChanged,\r
42             Action<ComposablePartCatalogChangeEventArgs> onChanging)\r
43         {\r
44             catalogs = catalogs ?? Enumerable.Empty<ComposablePartCatalog>();\r
45             this._catalogs = new List<ComposablePartCatalog>(catalogs);\r
46             this._onChanged = onChanged;\r
47             this._onChanging = onChanging;\r
48 \r
49             SubscribeToCatalogNotifications(catalogs);\r
50         }\r
51 \r
52         public void Add(ComposablePartCatalog item)\r
53         {\r
54             Requires.NotNull(item, "item");\r
55 \r
56             this.ThrowIfDisposed();\r
57 \r
58             var addedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.Parts.ToArray(), false);\r
59 \r
60             using (var atomicComposition = new AtomicComposition())\r
61             {\r
62                 this.RaiseChangingEvent(addedParts, null, atomicComposition);\r
63  \r
64                 using (new WriteLock(this._lock))\r
65                 {\r
66                     if (this._isCopyNeeded)\r
67                     {\r
68                         this._catalogs = new List<ComposablePartCatalog>(this._catalogs);\r
69                         this._isCopyNeeded = false;\r
70                     }\r
71                     this._hasChanged = true;\r
72                     this._catalogs.Add(item);\r
73                 }\r
74                 \r
75                 this.SubscribeToCatalogNotifications(item);\r
76 \r
77                 // Complete after the catalog changes are written\r
78                 atomicComposition.Complete();\r
79             }\r
80 \r
81             this.RaiseChangedEvent(addedParts, null);\r
82         }\r
83 \r
84         /// <summary>\r
85         /// Notify when the contents of the Catalog has changed.\r
86         /// </summary>\r
87         public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;\r
88 \r
89         /// <summary>\r
90         /// Notify when the contents of the Catalog has changing.\r
91         /// </summary>\r
92         public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;\r
93 \r
94         public void Clear()\r
95         {\r
96             this.ThrowIfDisposed();\r
97 \r
98             // No action is required if we are already empty\r
99             ComposablePartCatalog[] catalogs = null;\r
100             using (new ReadLock(this._lock))\r
101             {\r
102                 if (this._catalogs.Count == 0)\r
103                 {\r
104                     return;\r
105                 }\r
106                 catalogs = this._catalogs.ToArray();\r
107             }\r
108 \r
109             //TODO-MT: This is pretty suspect - we can easily eliminate catalogs that aren't listed as being\r
110             // removed.  Then again, the idea of trying to mutate the catalog on two threads at the same time is pretty\r
111             // suspect to begin with.  When would that ever result in a meaningful composition?\r
112 \r
113             // We are doing this outside of the lock, so it's possible that the catalog will continute propagating events from things\r
114             // we are about to unsubscribe from. Given the non-specificity of our event, in the worst case scenario we would simply fire \r
115             // unnecessary events.\r
116 \r
117             var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => catalogs.SelectMany(catalog => catalog.Parts).ToArray(), false);\r
118 \r
119             // Validate the changes before applying them\r
120             using (var atomicComposition = new AtomicComposition())\r
121             {\r
122                 this.RaiseChangingEvent(null, removedParts, atomicComposition);\r
123                 this.UnsubscribeFromCatalogNotifications(catalogs);\r
124 \r
125                 using (new WriteLock(this._lock))\r
126                 {\r
127                     this._catalogs = new List<ComposablePartCatalog>();\r
128 \r
129                     this._isCopyNeeded = false;\r
130                     this._hasChanged = true;\r
131                 }\r
132 \r
133                 // Complete after the catalog changes are written\r
134                 atomicComposition.Complete();\r
135             }\r
136 \r
137             this.RaiseChangedEvent(null, removedParts);\r
138         }\r
139 \r
140         public bool Contains(ComposablePartCatalog item)\r
141         {\r
142             Requires.NotNull(item, "item");\r
143 \r
144             this.ThrowIfDisposed();\r
145 \r
146             using (new ReadLock(this._lock))\r
147             {\r
148                 return this._catalogs.Contains(item);\r
149             }\r
150         }\r
151 \r
152         public void CopyTo(ComposablePartCatalog[] array, int arrayIndex)\r
153         {\r
154             this.ThrowIfDisposed();\r
155 \r
156             using (new ReadLock(this._lock))\r
157             {\r
158                 this._catalogs.CopyTo(array, arrayIndex);\r
159             }\r
160         }\r
161 \r
162         public int Count\r
163         {\r
164             get \r
165             {\r
166                 this.ThrowIfDisposed();\r
167 \r
168                 using (new ReadLock(this._lock))\r
169                 {\r
170                     return this._catalogs.Count;\r
171                 }\r
172             }\r
173         }\r
174 \r
175         public bool IsReadOnly\r
176         {\r
177             get\r
178             {\r
179                 this.ThrowIfDisposed();\r
180 \r
181                 return false;\r
182             }\r
183         }\r
184 \r
185         public bool Remove(ComposablePartCatalog item)\r
186         {\r
187             Requires.NotNull(item, "item");\r
188 \r
189             this.ThrowIfDisposed();\r
190 \r
191             using (new ReadLock(this._lock))\r
192             {\r
193                 if (!this._catalogs.Contains(item))\r
194                 {\r
195                     return false;\r
196                 }\r
197             }\r
198 \r
199             bool isSuccessfulRemoval = false;\r
200 \r
201             var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.Parts.ToArray(), false);\r
202             using (var atomicComposition = new AtomicComposition())\r
203             {\r
204                 this.RaiseChangingEvent(null, removedParts, atomicComposition);\r
205 \r
206                 using (new WriteLock(this._lock))\r
207                 {\r
208                     if (_isCopyNeeded)\r
209                     {\r
210                         this._catalogs = new List<ComposablePartCatalog>(this._catalogs);\r
211                         this._isCopyNeeded = false;\r
212                     }\r
213 \r
214                     isSuccessfulRemoval = this._catalogs.Remove(item);\r
215                     if (isSuccessfulRemoval)\r
216                     {\r
217                         this._hasChanged = true;\r
218                     }\r
219                 }\r
220 \r
221                 this.UnsubscribeFromCatalogNotifications(item);\r
222 \r
223                 // Complete after the catalog changes are written\r
224                 atomicComposition.Complete();\r
225             }\r
226 \r
227             this.RaiseChangedEvent(null, removedParts);\r
228 \r
229             return isSuccessfulRemoval;\r
230         }\r
231 \r
232         internal bool HasChanged\r
233         {\r
234             get\r
235             {\r
236                 this.ThrowIfDisposed();\r
237 \r
238                 using (new ReadLock(this._lock))\r
239                 {\r
240                     return this._hasChanged;\r
241                 }\r
242             }\r
243         }\r
244 \r
245         public IEnumerator<ComposablePartCatalog> GetEnumerator()\r
246         {\r
247             this.ThrowIfDisposed();\r
248 \r
249             using (new ReadLock(this._lock))\r
250             {\r
251                 IEnumerator<ComposablePartCatalog> enumerator = this._catalogs.GetEnumerator();\r
252                 this._isCopyNeeded = true;\r
253                 return enumerator;\r
254             }\r
255         }\r
256 \r
257         IEnumerator IEnumerable.GetEnumerator()\r
258         {\r
259             return this.GetEnumerator();\r
260         }\r
261 \r
262         public void Dispose()\r
263         {\r
264             Dispose(true);\r
265             GC.SuppressFinalize(this);\r
266         }\r
267 \r
268         protected virtual void Dispose(bool disposing)\r
269         {\r
270             if (disposing)\r
271             {\r
272                 if (!this._isDisposed)\r
273                 {\r
274                     bool disposeLock = false;\r
275                     IEnumerable<ComposablePartCatalog> catalogs = null;\r
276                     try\r
277                     {\r
278                         using (new WriteLock(this._lock))\r
279                         {\r
280                             if (!this._isDisposed)\r
281                             {\r
282                                 disposeLock = true;\r
283 \r
284                                 catalogs = this._catalogs;\r
285                                 this._catalogs = null;\r
286 \r
287                                 this._isDisposed = true;\r
288                             }\r
289                         }\r
290                     }\r
291                     finally\r
292                     {\r
293                         if (catalogs != null)\r
294                         {\r
295                             this.UnsubscribeFromCatalogNotifications(catalogs);\r
296                             catalogs.ForEach(catalog => catalog.Dispose());\r
297                         }\r
298 \r
299                         if (disposeLock)\r
300                         {\r
301                             this._lock.Dispose();\r
302                         }\r
303                     }\r
304                 }\r
305             }\r
306         }\r
307 \r
308         private void RaiseChangedEvent(\r
309             Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,\r
310             Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions)\r
311         {\r
312             if (this._onChanged == null || this.Changed == null)\r
313             {\r
314                 return;\r
315             }\r
316 \r
317             var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);\r
318             var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);\r
319 \r
320             this._onChanged.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, null));\r
321         }\r
322 \r
323         public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs e)\r
324         {\r
325             var changedEvent = this.Changed;\r
326             if (changedEvent != null)\r
327             {\r
328                 changedEvent(sender, e);\r
329             }\r
330         }\r
331 \r
332         private void RaiseChangingEvent(\r
333            Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,\r
334            Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions,\r
335            AtomicComposition atomicComposition)\r
336         {\r
337             if (this._onChanging == null || this.Changing == null)\r
338             {\r
339                 return;\r
340             }\r
341             var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);\r
342             var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);\r
343 \r
344             this._onChanging.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, atomicComposition));\r
345         }\r
346 \r
347         public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs e)\r
348         {\r
349             var changingEvent = this.Changing;\r
350             if (changingEvent != null)\r
351             {\r
352                 changingEvent(sender, e);\r
353             }\r
354         }\r
355 \r
356         private void OnContainedCatalogChanged(object sender, ComposablePartCatalogChangeEventArgs e)\r
357         {\r
358             if (this._onChanged == null || this.Changed == null)\r
359             {\r
360                 return;\r
361             }\r
362 \r
363             this._onChanged.Invoke(e);\r
364         }\r
365 \r
366         private void OnContainedCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)\r
367         {\r
368             if (this._onChanging == null || this.Changing == null)\r
369             {\r
370                 return;\r
371             }\r
372 \r
373             this._onChanging.Invoke(e);\r
374         }\r
375 \r
376         private void SubscribeToCatalogNotifications(ComposablePartCatalog catalog)\r
377         {\r
378             INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;\r
379             if (notifyCatalog != null)\r
380             {\r
381                 notifyCatalog.Changed += this.OnContainedCatalogChanged;\r
382                 notifyCatalog.Changing += this.OnContainedCatalogChanging;\r
383             }\r
384         }\r
385 \r
386         private void SubscribeToCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)\r
387         {\r
388             foreach (var catalog in catalogs)\r
389             {\r
390                 SubscribeToCatalogNotifications(catalog);\r
391             }\r
392         }\r
393 \r
394         private void UnsubscribeFromCatalogNotifications(ComposablePartCatalog catalog)\r
395         {\r
396             INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;\r
397             if (notifyCatalog != null)\r
398             {\r
399                 notifyCatalog.Changed -= this.OnContainedCatalogChanged;\r
400                 notifyCatalog.Changing -= this.OnContainedCatalogChanging;\r
401             }\r
402         }\r
403 \r
404         private void UnsubscribeFromCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)\r
405         {\r
406             foreach (var catalog in catalogs)\r
407             {\r
408                 UnsubscribeFromCatalogNotifications(catalog);\r
409             }\r
410         }\r
411 \r
412         private void ThrowIfDisposed()\r
413         {\r
414             if (this._isDisposed)\r
415             {\r
416                 throw ExceptionBuilder.CreateObjectDisposed(this);\r
417             }\r
418         }\r
419     }\r
420 }\r