File: System\ComponentModel\Composition\Hosting\CompositionScopeDefinition.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.Internal;
 
namespace System.ComponentModel.Composition.Hosting
{
    [DebuggerTypeProxy(typeof(CompositionScopeDefinitionDebuggerProxy))]
    public class CompositionScopeDefinition : ComposablePartCatalog, INotifyComposablePartCatalogChanged
    {
        private ComposablePartCatalog? _catalog;
        private IEnumerable<ExportDefinition>? _publicSurface;
        private IEnumerable<CompositionScopeDefinition> _children = Enumerable.Empty<CompositionScopeDefinition>();
        private volatile int _isDisposed;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionScopeDefinition"/> class.
        /// </summary>
        protected CompositionScopeDefinition() { }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionScopeDefinition"/> class.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        /// <param name="children">The children.</param>
        public CompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable<CompositionScopeDefinition> children)
        {
            Requires.NotNull(catalog, nameof(catalog));
            Requires.NullOrNotNullElements(children, nameof(children));
 
            InitializeCompositionScopeDefinition(catalog, children, null);
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionScopeDefinition"/> class.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        /// <param name="children">The children.</param>
        /// <param name="publicSurface">The exports that can be used to create new scopes.</param>
        public CompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable<CompositionScopeDefinition> children, IEnumerable<ExportDefinition> publicSurface)
        {
            Requires.NotNull(catalog, nameof(catalog));
            Requires.NullOrNotNullElements(children, nameof(children));
            Requires.NullOrNotNullElements(publicSurface, nameof(publicSurface));
 
            InitializeCompositionScopeDefinition(catalog, children, publicSurface);
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionScopeDefinition"/> class.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        /// <param name="children">The children.</param>
        /// <param name="publicSurface">The exports that can be used to create new scopes.</param>
        private void InitializeCompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable<CompositionScopeDefinition>? children, IEnumerable<ExportDefinition>? publicSurface)
        {
            _catalog = catalog;
            if (children != null)
            {
                _children = children.ToArray();
            }
            if (publicSurface != null)
            {
                _publicSurface = publicSurface;
            }
 
            if (_catalog is INotifyComposablePartCatalogChanged notifyCatalog)
            {
                notifyCatalog.Changed += OnChangedInternal;
                notifyCatalog.Changing += OnChangingInternal;
            }
        }
 
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                    {
                        if (_catalog is INotifyComposablePartCatalogChanged notifyCatalog)
                        {
                            notifyCatalog.Changed -= OnChangedInternal;
                            notifyCatalog.Changing -= OnChangingInternal;
                        }
                    }
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
 
        /// <summary>
        /// Gets the children.
        /// </summary>
        /// <value>The children.</value>
        public virtual IEnumerable<CompositionScopeDefinition> Children
        {
            get
            {
                ThrowIfDisposed();
 
                return _children;
            }
        }
 
        /// <summary>
        ///     Gets the export definitions that describe the exports surfaced by the CompositionScopedefinition.
        /// </summary>
        /// <value>
        ///     An <see cref="IEnumerable{T}"/> of <see cref="ExportDefinition"/> objects describing
        ///     the exports surfaced by the <see cref="CompositionScopeDefinition"/>.
        /// </value>
        /// <remarks>
        ///     <note type="inheritinfo">
        ///         Overriders of this property must not return <see langword="null"/>.
        ///     </note>
        /// </remarks>
        public virtual IEnumerable<ExportDefinition> PublicSurface
        {
            get
            {
                ThrowIfDisposed();
                if (_publicSurface == null)
                {
                    return this.SelectMany((p) => p.ExportDefinitions);
                }
 
                return _publicSurface;
            }
        }
 
        /// <summary>
        /// Gets an Enumerator for the ComposablePartDefinitions
        /// </summary>
        /// <value>The children.</value>
        public override IEnumerator<ComposablePartDefinition> GetEnumerator()
        {
            Debug.Assert(_catalog != null);
            return _catalog.GetEnumerator();
        }
 
        /// <summary>
        /// Returns the export definitions that match the constraint defined by the specified definition.
        /// </summary>
        /// <param name="definition">The <see cref="ImportDefinition"/> that defines the conditions of the
        /// <see cref="ExportDefinition"/> objects to return.</param>
        /// <returns>
        /// An <see cref="IEnumerable{T}"/> of <see cref="Tuple{T1, T2}"/> containing the
        /// <see cref="ExportDefinition"/> objects and their associated
        /// <see cref="ComposablePartDefinition"/> for objects that match the constraint defined
        /// by <paramref name="definition"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="definition"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// The <see cref="ComposablePartCatalog"/> has been disposed of.
        /// </exception>
        /// <remarks>
        /// <note type="inheritinfo">
        /// Overriders of this property should never return <see langword="null"/>, if no
        /// <see cref="ExportDefinition"/> match the conditions defined by
        /// <paramref name="definition"/>, return an empty <see cref="IEnumerable{T}"/>.
        /// </note>
        /// </remarks>
        public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
        {
            ThrowIfDisposed();
 
            Debug.Assert(_catalog != null);
            return _catalog.GetExports(definition);
        }
 
        internal IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExportsFromPublicSurface(ImportDefinition definition)
        {
            ArgumentNullException.ThrowIfNull(definition);
 
            var exports = new List<Tuple<ComposablePartDefinition, ExportDefinition>>();
 
            foreach (var exportDefinition in PublicSurface)
            {
                if (definition.IsConstraintSatisfiedBy(exportDefinition))
                {
                    foreach (var export in GetExports(definition))
                    {
                        if (export.Item2 == exportDefinition)
                        {
                            exports.Add(export);
                            break;
                        }
                    }
                }
            }
            return exports;
        }
 
        /// <summary>
        /// Notify when the contents of the Catalog has changed.
        /// </summary>
        public event EventHandler<ComposablePartCatalogChangeEventArgs>? Changed;
 
        /// <summary>
        /// Notify when the contents of the Catalog is changing.
        /// </summary>
        public event EventHandler<ComposablePartCatalogChangeEventArgs>? Changing;
 
        /// <summary>
        /// Raises the <see cref="Changed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs"/> instance containing the event data.</param>
        protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e)
        {
            Changed?.Invoke(this, e);
        }
 
        /// <summary>
        /// Raises the <see cref="Changing"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs"/> instance containing the event data.</param>
        protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e)
        {
            Changing?.Invoke(this, e);
        }
 
        private void OnChangedInternal(object? sender, ComposablePartCatalogChangeEventArgs e)
        {
            OnChanged(e);
        }
 
        private void OnChangingInternal(object? sender, ComposablePartCatalogChangeEventArgs e)
        {
            OnChanging(e);
        }
 
        [DebuggerStepThrough]
        private void ThrowIfDisposed()
        {
            if (_isDisposed == 1)
            {
                throw ExceptionBuilder.CreateObjectDisposed(this);
            }
        }
    }
}