#region Apache License 2.0 /* Nuclex .NET Framework Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #endregion // Apache License 2.0 using System; using System.Collections.Generic; using System.Collections; #if !NO_SPECIALIZED_COLLECTIONS using System.Collections.Specialized; #endif #if !NO_SETS namespace Nuclex.Support.Collections { /// Set which fires events when items are removed or added to it /// Type of items to manage in the set public class ObservableSet : ISet, ICollection, #if !NO_SPECIALIZED_COLLECTIONS INotifyCollectionChanged, #endif IObservableCollection { /// Raised when an item has been added to the collection public event EventHandler> ItemAdded; /// Raised when an item is removed from the collection public event EventHandler> ItemRemoved; /// Raised when an item is replaced in the collection public event EventHandler> ItemReplaced { add { } remove { } } /// Raised when the collection is about to be cleared /// /// This could be covered by calling ItemRemoved for each item currently /// contained in the collection, but it is often simpler and more efficient /// to process the clearing of the entire collection as a special operation. /// public event EventHandler Clearing; /// Raised when the collection has been cleared public event EventHandler Cleared; #if !NO_SPECIALIZED_COLLECTIONS /// Called when the collection has changed public event NotifyCollectionChangedEventHandler CollectionChanged; #endif /// Initializes a new observable set based on a hashed set public ObservableSet() : this(new HashSet()) { } /// /// Initializes a new observable set forwarding operations to the specified set /// /// Set operations will be forwarded to public ObservableSet(ISet set) { this.set = set; } /// Adds an item to the set /// Item that will be added to the set /// /// True if the element was added, false if it was already contained in the set /// public bool Add(TItem item) { bool wasAdded = this.set.Add(item); if(wasAdded) { OnAdded(item); } return wasAdded; } /// Removes all elements that are contained in the collection /// Collection whose elements will be removed from this set public void ExceptWith(IEnumerable other) { if(other == this) { Clear(); return; } foreach(TItem item in other) { if(this.set.Remove(item)) { OnRemoved(item); } } } /// /// Only keeps those elements in this set that are contained in the collection /// /// Other set this set will be filtered by public void IntersectWith(IEnumerable other) { var otherSet = other as ISet; if(otherSet == null) { otherSet = new HashSet(other); } var itemsToRemove = new List(); foreach(TItem item in this.set) { if(!otherSet.Contains(item)) { itemsToRemove.Add(item); } } for(int index = 0; index < itemsToRemove.Count; ++index) { this.set.Remove(itemsToRemove[index]); OnRemoved(itemsToRemove[index]); } } /// /// Determines whether the current set is a proper (strict) subset of a collection /// /// Collection against which the set will be tested /// True if the set is a proper subset of the specified collection public bool IsProperSubsetOf(IEnumerable other) { return this.set.IsProperSubsetOf(other); } /// /// Determines whether the current set is a proper (strict) superset of a collection /// /// Collection against which the set will be tested /// True if the set is a proper superset of the specified collection public bool IsProperSupersetOf(IEnumerable other) { return this.set.IsProperSupersetOf(other); } /// Determines whether the current set is a subset of a collection /// Collection against which the set will be tested /// True if the set is a subset of the specified collection public bool IsSubsetOf(IEnumerable other) { return this.set.IsSubsetOf(other); } /// Determines whether the current set is a superset of a collection /// Collection against which the set will be tested /// True if the set is a superset of the specified collection public bool IsSupersetOf(IEnumerable other) { return this.set.IsSupersetOf(other); } /// /// Determines if the set shares at least one common element with the collection /// /// Collection the set will be tested against /// /// True if the set shares at least one common element with the collection /// public bool Overlaps(IEnumerable other) { return this.set.Overlaps(other); } /// /// Determines whether the set contains the same elements as the specified collection /// /// Collection the set will be tested against /// True if the set contains the same elements as the collection public bool SetEquals(IEnumerable other) { return this.set.SetEquals(other); } /// /// Modifies the current set so that it contains only elements that are present either /// in the current set or in the specified collection, but not both /// /// Collection the set will be excepted with public void SymmetricExceptWith(IEnumerable other) { foreach(TItem item in other) { if(this.set.Remove(item)) { OnRemoved(item); } else { this.Add(item); OnAdded(item); } } } /// /// Modifies the current set so that it contains all elements that are present in both /// the current set and in the specified collection /// /// Collection an union will be built with public void UnionWith(IEnumerable other) { foreach(TItem item in other) { if(this.set.Add(item)) { OnAdded(item); } } } /// Removes all items from the set public void Clear() { OnClearing(); this.set.Clear(); OnCleared(); } /// Determines whether the set contains the specified item /// Item the set will be tested for /// True if the set contains the specified item public bool Contains(TItem item) { return this.set.Contains(item); } /// Copies the contents of the set into an array /// Array the set's contents will be copied to /// /// Index in the array the first copied element will be written to /// public void CopyTo(TItem[] array, int arrayIndex) { this.set.CopyTo(array, arrayIndex); } /// Counts the number of items contained in the set public int Count { get { return this.set.Count; } } /// Determines whether the set is readonly public bool IsReadOnly { get { return this.set.IsReadOnly; } } /// Removes an item from the set /// Item that will be removed from the set /// /// True if the item was contained in the set and is now removed /// public bool Remove(TItem item) { bool wasRemoved = this.set.Remove(item); if(wasRemoved) { OnRemoved(item); } return wasRemoved; } /// Creates an enumerator for the set's contents /// A new enumerator for the sets contents public IEnumerator GetEnumerator() { return this.set.GetEnumerator(); } /// Fires the 'ItemAdded' event /// Item that has been added to the collection protected virtual void OnAdded(TItem item) { if(ItemAdded != null) { ItemAdded(this, new ItemEventArgs(item)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item) ); } #endif } /// Fires the 'ItemRemoved' event /// Item that has been removed from the collection protected virtual void OnRemoved(TItem item) { if(ItemRemoved != null) { ItemRemoved(this, new ItemEventArgs(item)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item) ); } #endif } /// Fires the 'Clearing' event protected virtual void OnClearing() { if(Clearing != null) { Clearing(this, EventArgs.Empty); } } /// Fires the 'Cleared' event protected virtual void OnCleared() { if(Cleared != null) { Cleared(this, EventArgs.Empty); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); } #endif } #region ICollection implementation /// Adds an item to the set /// Item that will be added to the set void ICollection.Add(TItem item) { this.set.Add(item); } #endregion // ICollection implementation #region IEnumerable implementation /// Creates an enumerator for the set's contents /// A new enumerator for the sets contents IEnumerator IEnumerable.GetEnumerator() { return this.set.GetEnumerator(); } #endregion // IEnumerable implementation /// The set being wrapped private ISet set; } } // namespace Nuclex.Support.Collections #endif // !NO_SETS