// ----------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.ComponentModel.Composition.Factories; namespace System.ComponentModel.Composition { public class FilteringCollection : AdaptingCollection { public FilteringCollection(Func, bool> filter) : base(e => e.Where(filter)) { } } public class OrderingCollection : AdaptingCollection { public OrderingCollection(Func, object> keySelector) : this(keySelector, false) { } public OrderingCollection(Func, object> keySelector, bool descending) : base(e => descending ? e.OrderByDescending(keySelector) : e.OrderBy(keySelector)) { } } public class AdaptingCollection : AdaptingCollection> { public AdaptingCollection(Func>>, IEnumerable>>> adaptor) : base(adaptor) { } } public class AdaptingCollection : ICollection>, INotifyCollectionChanged { private readonly List> _allItems = new List>(); private readonly Func>, IEnumerable>> _adaptor = null; private List> _adaptedItems = null; public AdaptingCollection() : this(null) { } public AdaptingCollection(Func>, IEnumerable>> adaptor) { this._adaptor = adaptor; } public event NotifyCollectionChangedEventHandler CollectionChanged; public void ReapplyAdaptor() { if (this._adaptedItems != null) { this._adaptedItems = null; this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } protected virtual IEnumerable> Adapt(IEnumerable> collection) { if (this._adaptor != null) { return this._adaptor.Invoke(collection); } return collection; } protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged; if (collectionChanged != null) { collectionChanged.Invoke(this, e); } } private List> AdaptedItems { get { if (this._adaptedItems == null) { this._adaptedItems = Adapt(this._allItems).ToList(); } return this._adaptedItems; } } #region ICollection Implementation // Accessors work directly against adapted collection public bool Contains(Lazy item) { return this.AdaptedItems.Contains(item); } public void CopyTo(Lazy[] array, int arrayIndex) { this.AdaptedItems.CopyTo(array, arrayIndex); } public int Count { get { return this.AdaptedItems.Count; } } public bool IsReadOnly { get { return false; } } public IEnumerator> GetEnumerator() { return this.AdaptedItems.GetEnumerator(); } Collections.IEnumerator Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } // Mutation methods work against complete collection // and then force a reset of the adapted collection public void Add(Lazy item) { this._allItems.Add(item); ReapplyAdaptor(); } public void Clear() { this._allItems.Clear(); ReapplyAdaptor(); } public bool Remove(Lazy item) { bool removed = this._allItems.Remove(item); ReapplyAdaptor(); return removed; } #endregion } [TestClass] public class AdaptingCollectionTests { public interface IContract { } public interface INetworkAwareMetadata { [DefaultValue(false)] bool RequiresOnline { get; } } [Export(typeof(IContract))] [ExportMetadata("RequiresOnline", true)] public class NetworkExport : IContract { } [Export(typeof(IContract))] public class NonNetworkExport : IContract { } public class FilterExports { public FilterExports() { this.OnlineOnly = new AdaptingCollection(e => e.Where(p => p.Metadata.RequiresOnline)); this.OnlineOnly2 = new FilteringCollection(p => p.Metadata.RequiresOnline); } [ImportMany] public AdaptingCollection OnlineOnly { get; set; } [ImportMany] public FilteringCollection OnlineOnly2 { get; set; } } [TestMethod] public void TestFilteringImports() { var container = ContainerFactory.CreateWithAttributedCatalog(typeof(NetworkExport), typeof(NonNetworkExport)); var filterExports = new FilterExports(); container.ComposeParts(filterExports); Assert.AreEqual(1, filterExports.OnlineOnly.Count); Assert.AreEqual(1, filterExports.OnlineOnly2.Count); } public interface IOrderMetadata { [DefaultValue(Int32.MaxValue)] int Order { get; } } [Export(typeof(IContract))] [ExportMetadata("Order", 2)] public class BExport : IContract { } [Export(typeof(IContract))] [ExportMetadata("Order", 1)] public class AExport : IContract { } [Export(typeof(IContract))] public class CExport : IContract { } public class OrderExportsByMetadata { public OrderExportsByMetadata() { this.OrderedItems = new AdaptingCollection(e => e.OrderBy(p => p.Metadata.Order)); this.OrderedItems2 = new OrderingCollection(p => p.Metadata.Order); } [ImportMany] public AdaptingCollection OrderedItems { get; set; } [ImportMany] public OrderingCollection OrderedItems2 { get; set; } } [TestMethod] public void TestOrderingImportsByMetadata() { var container = ContainerFactory.CreateWithAttributedCatalog(typeof(BExport), typeof(AExport), typeof(CExport)); var orderExports = new OrderExportsByMetadata(); container.ComposeParts(orderExports); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(0).Value, typeof(AExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(1).Value, typeof(BExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(2).Value, typeof(CExport)); Assert.IsInstanceOfType(orderExports.OrderedItems2.ElementAt(0).Value, typeof(AExport)); Assert.IsInstanceOfType(orderExports.OrderedItems2.ElementAt(1).Value, typeof(BExport)); Assert.IsInstanceOfType(orderExports.OrderedItems2.ElementAt(2).Value, typeof(CExport)); } public class OrderExportsByName { public OrderExportsByName(bool descending) { if (descending) { this.OrderedItems = new AdaptingCollection(e => e.OrderByDescending(p => p.Value.GetType().FullName)); } else { this.OrderedItems = new AdaptingCollection(e => e.OrderBy(p => p.Value.GetType().FullName)); } } [ImportMany] public AdaptingCollection OrderedItems { get; set; } } [TestMethod] public void TestOrderingImportsByTypeName() { var container = ContainerFactory.CreateWithAttributedCatalog(typeof(BExport), typeof(AExport), typeof(CExport)); var orderExports = new OrderExportsByName(false); container.ComposeParts(orderExports); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(0).Value, typeof(AExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(1).Value, typeof(BExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(2).Value, typeof(CExport)); orderExports = new OrderExportsByName(true); container.ComposeParts(orderExports); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(0).Value, typeof(CExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(1).Value, typeof(BExport)); Assert.IsInstanceOfType(orderExports.OrderedItems.ElementAt(2).Value, typeof(AExport)); } public interface IDynamicFilteredMetadata { bool Dynamic { get; } } [Export(typeof(IContract))] [ExportMetadata("Dynamic", true)] public class Dynamic1 : IContract { } [Export(typeof(IContract))] [ExportMetadata("Dynamic", true)] public class Dynamic2 : IContract { } [Export(typeof(IContract))] [ExportMetadata("Dynamic", false)] public class NonDynamic1 : IContract { } public class DynamicFilteredCollection : AdaptingCollection where M : IDynamicFilteredMetadata { public DynamicFilteredCollection() { } private bool _includeDynamic = false; public bool IncludeDynamic { get { return this._includeDynamic; } set { if (this._includeDynamic != value) { this.ReapplyAdaptor(); } this._includeDynamic = value; } } protected override IEnumerable> Adapt(IEnumerable> collection) { return collection.Where(p => !p.Metadata.Dynamic || IncludeDynamic); } } public class DynamicExports { [ImportMany] public DynamicFilteredCollection DynamicCollection { get; set; } } [TestMethod] public void TestDyamicallyFilteringImports() { var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Dynamic1), typeof(Dynamic2), typeof(NonDynamic1)); var dynamicExports = new DynamicExports(); container.ComposeParts(dynamicExports); Assert.AreEqual(1, dynamicExports.DynamicCollection.Count); dynamicExports.DynamicCollection.IncludeDynamic = true; Assert.AreEqual(3, dynamicExports.DynamicCollection.Count); } public class DynamicExportsNoSubType { public DynamicExportsNoSubType() { this.DynamicCollection = new AdaptingCollection(e => e.Where(p => !p.Metadata.Dynamic || this.IncludeDynamic)); } private bool _includeDynamic = false; public bool IncludeDynamic { get { return this._includeDynamic; } set { if (this._includeDynamic != value) { this.DynamicCollection.ReapplyAdaptor(); } this._includeDynamic = value; } } [ImportMany] public AdaptingCollection DynamicCollection { get; set; } } [TestMethod] public void TestDyamicallyFilteringNoSubTypeImports() { var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Dynamic1), typeof(Dynamic2), typeof(NonDynamic1)); var dynamicExports = new DynamicExportsNoSubType(); container.ComposeParts(dynamicExports); Assert.AreEqual(1, dynamicExports.DynamicCollection.Count); dynamicExports.IncludeDynamic = true; Assert.AreEqual(3, dynamicExports.DynamicCollection.Count); } } }