wasSharp – Rev 44

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace wasSharp.Collections.Specialized
{
    ///////////////////////////////////////////////////////////////////////////
    //    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
    ///////////////////////////////////////////////////////////////////////////
    /// <summary>
    ///     An implementation of an observable HashSet.
    /// </summary>
    /// <typeparam name="T">the object type</typeparam>
    public class ObservableHashSet<T> : ICollection<T>, INotifyCollectionChanged, IEnumerable<T>
    {
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        private readonly HashSet<T> store = new HashSet<T>();

        public ObservableHashSet(HashSet<T> set)
        {
            _lock.EnterWriteLock();
            try
            {
                UnionWith(set);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public ObservableHashSet()
        {
        }

        public ObservableHashSet(T item)
        {
            _lock.EnterWriteLock();
            try
            {
                Add(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public ObservableHashSet(ObservableHashSet<T> other)
        {
            _lock.EnterWriteLock();
            try
            {
                UnionWith(other);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public ObservableHashSet(IEnumerable<T> list)
        {
            _lock.EnterWriteLock();
            try
            {
                UnionWith(list);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public bool IsVirgin { get; private set; } = true;

        public IEnumerator<T> GetEnumerator()
        {
            _lock.EnterReadLock();
            try
            {
                using (var enumerator = store.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                        yield return enumerator.Current;
                }
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            _lock.EnterReadLock();
            try
            {
                using (var enumerator = store.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                        yield return enumerator.Current;
                }
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public void Add(T item)
        {
            _lock.EnterWriteLock();
            try
            {
                store.Add(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            IsVirgin = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }

        public void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                store.Clear();
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            if (!IsVirgin)
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            IsVirgin = false;
        }

        public bool Contains(T item)
        {
            _lock.EnterReadLock();
            try
            {
                return store.Contains(item);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _lock.EnterReadLock();
            try
            {
                store.CopyTo(array, arrayIndex);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public bool Remove(T item)
        {
            _lock.EnterWriteLock();
            bool removed;
            try
            {
                removed = store.Remove(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            IsVirgin = false;
            if (removed)
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
            return removed;
        }

        public int Count => store.Count;

        public bool IsReadOnly => false;

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public void UnionWith(IEnumerable<T> list)
        {
            var added = new List<T>(list.Except(store));
            _lock.EnterWriteLock();
            try
            {
                store.UnionWith(added);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            if (!IsVirgin && added.Any())
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added));
            IsVirgin = false;
        }

        private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
        {
            CollectionChanged?.Invoke(this, args);
        }

        public void ExceptWith(IEnumerable<T> list)
        {
            var removed = new List<T>(list.Intersect(store));
            _lock.EnterWriteLock();
            try
            {
                store.ExceptWith(removed);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            if (!IsVirgin && removed.Any())
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
                    removed));
            IsVirgin = false;
        }

        public void RemoveWhere(Func<T, bool> func)
        {
            var removed = new List<T>(store.Where(func));
            _lock.EnterWriteLock();
            try
            {
                store.ExceptWith(removed);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            if (!IsVirgin && removed.Any())
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
                    removed));
            IsVirgin = false;
        }

        public IEnumerable<T> AsEnumerable()
        {
            _lock.EnterWriteLock();
            try
            {
                return store.AsEnumerable();
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }
    }
}