PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.ComponentModel.Composition.4.5/src/ComponentModel/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs

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