wasSharp – Rev 44

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2016 - 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.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading;

namespace wasSharp.Collections.Specialized
{
    ///////////////////////////////////////////////////////////////////////////
    //    Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3    //
    ///////////////////////////////////////////////////////////////////////////
    /// <summary>
    ///     An implementation of an observable Dictionary.
    /// </summary>
    /// <typeparam name="K">the key type</typeparam>
    /// <typeparam name="V">the value type</typeparam>
    public class ObservableDictionary<K, V> : IDictionary<K, V>, INotifyCollectionChanged
    {
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        private readonly Dictionary<K, V> store = new Dictionary<K, V>();

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

        public V this[K key]
        {
            get
            {
                _lock.EnterReadLock();
                try
                {
                    return store[key];
                }
                finally
                {
                    if (_lock.IsReadLockHeld) _lock.ExitReadLock();
                }
            }

            set
            {
                _lock.EnterWriteLock();
                try
                {
                    store[key] = value;
                }
                finally
                {
                    if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
                }
            }
        }

        public int Count => store.Count;

        public bool IsReadOnly => false;

        public ICollection<K> Keys
        {
            get
            {
                _lock.EnterReadLock();
                try
                {
                    return store.Keys;
                }
                finally
                {
                    if (_lock.IsReadLockHeld) _lock.ExitReadLock();
                }
            }
        }

        public ICollection<V> Values
        {
            get
            {
                _lock.EnterReadLock();
                try
                {
                    return store.Values;
                }
                finally
                {
                    if (_lock.IsReadLockHeld) _lock.ExitReadLock();
                }
            }
        }

        public void Add(KeyValuePair<K, V> item)
        {
            _lock.EnterWriteLock();
            try
            {
                ((IDictionary<K, V>) store).Add(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            IsVirgin = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }

        public void Add(K key, V value)
        {
            _lock.EnterWriteLock();
            try
            {
                store.Add(key, value);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
            IsVirgin = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
                new KeyValuePair<K, V>(key, value)));
        }

        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(KeyValuePair<K, V> item)
        {
            _lock.EnterReadLock();
            try
            {
                return ((IDictionary<K, V>) store).Contains(item);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public bool ContainsKey(K key)
        {
            _lock.EnterReadLock();
            try
            {
                return store.ContainsKey(key);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            _lock.EnterReadLock();
            try
            {
                ((IDictionary<K, V>) store).CopyTo(array, arrayIndex);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

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

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

        public bool Remove(K key)
        {
            KeyValuePair<K, V> item;
            _lock.EnterWriteLock();
            bool removed;
            try
            {
                if (store.ContainsKey(key))
                    item = new KeyValuePair<K, V>(key, store[key]);

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

        public bool TryGetValue(K key, out V value)
        {
            _lock.EnterReadLock();
            try
            {
                return store.TryGetValue(key, out value);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

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

        public event NotifyCollectionChangedEventHandler CollectionChanged;

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