Newer
Older
ClosedXML / ClosedXML / Excel / Caching / XLRepositoryBase.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace ClosedXML.Excel.Caching
{
    internal abstract class XLRepositoryBase : IXLRepository
    {
        public abstract void Clear();
    }

    internal abstract class XLRepositoryBase<Tkey, Tvalue> : XLRepositoryBase, IXLRepository<Tkey, Tvalue>
        where Tkey : struct, IEquatable<Tkey>
        where Tvalue : class
    {
        const int CONCURRENCY_LEVEL = 4;
        const int INITIAL_CAPACITY = 1000;

        private readonly ConcurrentDictionary<Tkey, WeakReference> _storage;
        private readonly Func<Tkey, Tvalue> _createNew;
        public XLRepositoryBase(Func<Tkey, Tvalue> createNew) : this(createNew, EqualityComparer<Tkey>.Default)
        {
        }

        public XLRepositoryBase(Func<Tkey, Tvalue> createNew, IEqualityComparer<Tkey> comparer)
        {
            _storage = new ConcurrentDictionary<Tkey, WeakReference>(CONCURRENCY_LEVEL, INITIAL_CAPACITY, comparer);
            _createNew = createNew;
        }

        public bool ContainsKey(Tkey key)
        {
            WeakReference cachedReference;
            if (_storage.TryGetValue(key, out cachedReference))
            {
                var storedValue = cachedReference.Target as Tvalue;
                return (storedValue != null);
            }
            return false;
        }

        public Tvalue Store(Tkey key, Tvalue value)
        {
            if (value == null)
                return null;

            if (!_storage.ContainsKey(key))
            {
                _storage.TryAdd(key, new WeakReference(value));
                return value;
            }
            else
            {
                var cachedReference = _storage[key];
                var storedValue = cachedReference.Target as Tvalue;
                if (storedValue == null)
                {
                    _storage.TryAdd(key, new WeakReference(value));
                    return value;
                }
                return storedValue;
            }
        }

        public Tvalue GetOrCreate(Tkey key)
        {
            WeakReference cachedReference;
            if (_storage.TryGetValue(key, out cachedReference))
            {
                var storedValue = cachedReference.Target as Tvalue;
                if (storedValue != null)
                {
                    return storedValue;
                }
                else
                {
                    WeakReference _;
                    _storage.TryRemove(key, out _);
                }
            }
            
            var value = _createNew(key);
            return Store(key, value);
        }

        public override void Clear()
        {
            _storage.Clear();
        }

        /// <summary>
        /// Enumerate items in repository removing "dead" entries.
        /// </summary>
        public IEnumerator<Tvalue> GetEnumerator()
        {
            return _storage
                .Select(pair =>
                {
                    var val = pair.Value.Target as Tvalue;
                    if (val == null)
                    {
                        WeakReference _;
                        _storage.TryRemove(pair.Key, out _);
                    }
                    return val;
                })
                .Where(val => val != null)
                .GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}