/mcs/class/System.ComponentModel.Composition.4.5/src/ComponentModel/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs
C# | 415 lines | 320 code | 69 blank | 26 comment | 41 complexity | 58e209cc86499c081d979be8e7a063ad MD5 | raw file
- // -----------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // -----------------------------------------------------------------------
- using System;
- using System.Collections.ObjectModel;
- using System.Collections.Generic;
- using System.ComponentModel.Composition.Primitives;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Diagnostics;
- using Microsoft.Internal;
- using Microsoft.Internal.Collections;
- using System.Collections;
- namespace System.ComponentModel.Composition.Hosting
- {
- /// <summary>
- /// This class implements a threadsafe ICollection{T} of ComposablePartCatalog.
- /// It is exposed as an ICollection(ComposablePartCatalog)
- /// It is threadsafe, notifications are not marshalled using a SynchronizationContext.
- /// It is Disposable.
- /// </summary>
- internal class ComposablePartCatalogCollection : ICollection<ComposablePartCatalog>, INotifyComposablePartCatalogChanged, IDisposable
- {
- private readonly Lock _lock = new Lock();
- private Action<ComposablePartCatalogChangeEventArgs> _onChanged;
- private Action<ComposablePartCatalogChangeEventArgs> _onChanging;
- private List<ComposablePartCatalog> _catalogs = new List<ComposablePartCatalog>();
- private volatile bool _isCopyNeeded = false;
- private volatile bool _isDisposed = false;
- private bool _hasChanged = false;
- public ComposablePartCatalogCollection(
- IEnumerable<ComposablePartCatalog> catalogs,
- Action<ComposablePartCatalogChangeEventArgs> onChanged,
- Action<ComposablePartCatalogChangeEventArgs> onChanging)
- {
- catalogs = catalogs ?? Enumerable.Empty<ComposablePartCatalog>();
- this._catalogs = new List<ComposablePartCatalog>(catalogs);
- this._onChanged = onChanged;
- this._onChanging = onChanging;
- SubscribeToCatalogNotifications(catalogs);
- }
- public void Add(ComposablePartCatalog item)
- {
- Requires.NotNull(item, "item");
- this.ThrowIfDisposed();
- var addedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly);
- using (var atomicComposition = new AtomicComposition())
- {
- this.RaiseChangingEvent(addedParts, null, atomicComposition);
-
- using (new WriteLock(this._lock))
- {
- if (this._isCopyNeeded)
- {
- this._catalogs = new List<ComposablePartCatalog>(this._catalogs);
- this._isCopyNeeded = false;
- }
- this._hasChanged = true;
- this._catalogs.Add(item);
- }
-
- this.SubscribeToCatalogNotifications(item);
- // Complete after the catalog changes are written
- atomicComposition.Complete();
- }
- this.RaiseChangedEvent(addedParts, null);
- }
- /// <summary>
- /// Notify when the contents of the Catalog has changed.
- /// </summary>
- public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;
- /// <summary>
- /// Notify when the contents of the Catalog has changing.
- /// </summary>
- public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;
- public void Clear()
- {
- this.ThrowIfDisposed();
- // No action is required if we are already empty
- ComposablePartCatalog[] catalogs = null;
- using (new ReadLock(this._lock))
- {
- if (this._catalogs.Count == 0)
- {
- return;
- }
- catalogs = this._catalogs.ToArray();
- }
- //TODO-MT: This is pretty suspect - we can easily eliminate catalogs that aren't listed as being
- // removed. Then again, the idea of trying to mutate the catalog on two threads at the same time is pretty
- // suspect to begin with. When would that ever result in a meaningful composition?
- // We are doing this outside of the lock, so it's possible that the catalog will continute propagating events from things
- // we are about to unsubscribe from. Given the non-specificity of our event, in the worst case scenario we would simply fire
- // unnecessary events.
- var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => catalogs.SelectMany(catalog => catalog).ToArray(), LazyThreadSafetyMode.PublicationOnly);
- // Validate the changes before applying them
- using (var atomicComposition = new AtomicComposition())
- {
- this.RaiseChangingEvent(null, removedParts, atomicComposition);
- this.UnsubscribeFromCatalogNotifications(catalogs);
- using (new WriteLock(this._lock))
- {
- this._catalogs = new List<ComposablePartCatalog>();
- this._isCopyNeeded = false;
- this._hasChanged = true;
- }
- // Complete after the catalog changes are written
- atomicComposition.Complete();
- }
- this.RaiseChangedEvent(null, removedParts);
- }
- public bool Contains(ComposablePartCatalog item)
- {
- Requires.NotNull(item, "item");
- this.ThrowIfDisposed();
- using (new ReadLock(this._lock))
- {
- return this._catalogs.Contains(item);
- }
- }
- public void CopyTo(ComposablePartCatalog[] array, int arrayIndex)
- {
- this.ThrowIfDisposed();
- using (new ReadLock(this._lock))
- {
- this._catalogs.CopyTo(array, arrayIndex);
- }
- }
- public int Count
- {
- get
- {
- this.ThrowIfDisposed();
- using (new ReadLock(this._lock))
- {
- return this._catalogs.Count;
- }
- }
- }
- public bool IsReadOnly
- {
- get
- {
- this.ThrowIfDisposed();
- return false;
- }
- }
- public bool Remove(ComposablePartCatalog item)
- {
- Requires.NotNull(item, "item");
- this.ThrowIfDisposed();
- using (new ReadLock(this._lock))
- {
- if (!this._catalogs.Contains(item))
- {
- return false;
- }
- }
- bool isSuccessfulRemoval = false;
- var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly);
- using (var atomicComposition = new AtomicComposition())
- {
- this.RaiseChangingEvent(null, removedParts, atomicComposition);
- using (new WriteLock(this._lock))
- {
- if (_isCopyNeeded)
- {
- this._catalogs = new List<ComposablePartCatalog>(this._catalogs);
- this._isCopyNeeded = false;
- }
- isSuccessfulRemoval = this._catalogs.Remove(item);
- if (isSuccessfulRemoval)
- {
- this._hasChanged = true;
- }
- }
- this.UnsubscribeFromCatalogNotifications(item);
- // Complete after the catalog changes are written
- atomicComposition.Complete();
- }
- this.RaiseChangedEvent(null, removedParts);
- return isSuccessfulRemoval;
- }
- internal bool HasChanged
- {
- get
- {
- this.ThrowIfDisposed();
- using (new ReadLock(this._lock))
- {
- return this._hasChanged;
- }
- }
- }
- public IEnumerator<ComposablePartCatalog> GetEnumerator()
- {
- this.ThrowIfDisposed();
- using (new WriteLock(this._lock))
- {
- IEnumerator<ComposablePartCatalog> enumerator = this._catalogs.GetEnumerator();
- this._isCopyNeeded = true;
- return enumerator;
- }
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.GetEnumerator();
- }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (!this._isDisposed)
- {
- bool disposeLock = false;
- IEnumerable<ComposablePartCatalog> catalogs = null;
- try
- {
- using (new WriteLock(this._lock))
- {
- if (!this._isDisposed)
- {
- disposeLock = true;
- catalogs = this._catalogs;
- this._catalogs = null;
- this._isDisposed = true;
- }
- }
- }
- finally
- {
- if (catalogs != null)
- {
- this.UnsubscribeFromCatalogNotifications(catalogs);
- catalogs.ForEach(catalog => catalog.Dispose());
- }
- if (disposeLock)
- {
- this._lock.Dispose();
- }
- }
- }
- }
- }
- private void RaiseChangedEvent(
- Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,
- Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions)
- {
- if (this._onChanged == null || this.Changed == null)
- {
- return;
- }
- var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);
- var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);
- this._onChanged.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, null));
- }
- public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs e)
- {
- var changedEvent = this.Changed;
- if (changedEvent != null)
- {
- changedEvent(sender, e);
- }
- }
- private void RaiseChangingEvent(
- Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,
- Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions,
- AtomicComposition atomicComposition)
- {
- if (this._onChanging == null || this.Changing == null)
- {
- return;
- }
- var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);
- var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);
- this._onChanging.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, atomicComposition));
- }
- public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs e)
- {
- var changingEvent = this.Changing;
- if (changingEvent != null)
- {
- changingEvent(sender, e);
- }
- }
- private void OnContainedCatalogChanged(object sender, ComposablePartCatalogChangeEventArgs e)
- {
- if (this._onChanged == null || this.Changed == null)
- {
- return;
- }
- this._onChanged.Invoke(e);
- }
- private void OnContainedCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)
- {
- if (this._onChanging == null || this.Changing == null)
- {
- return;
- }
- this._onChanging.Invoke(e);
- }
- private void SubscribeToCatalogNotifications(ComposablePartCatalog catalog)
- {
- INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;
- if (notifyCatalog != null)
- {
- notifyCatalog.Changed += this.OnContainedCatalogChanged;
- notifyCatalog.Changing += this.OnContainedCatalogChanging;
- }
- }
- private void SubscribeToCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)
- {
- foreach (var catalog in catalogs)
- {
- SubscribeToCatalogNotifications(catalog);
- }
- }
- private void UnsubscribeFromCatalogNotifications(ComposablePartCatalog catalog)
- {
- INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;
- if (notifyCatalog != null)
- {
- notifyCatalog.Changed -= this.OnContainedCatalogChanged;
- notifyCatalog.Changing -= this.OnContainedCatalogChanging;
- }
- }
- private void UnsubscribeFromCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)
- {
- foreach (var catalog in catalogs)
- {
- UnsubscribeFromCatalogNotifications(catalog);
- }
- }
- private void ThrowIfDisposed()
- {
- if (this._isDisposed)
- {
- throw ExceptionBuilder.CreateObjectDisposed(this);
- }
- }
- }
- }