#nullable enable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Swan.Reflection {
///
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
///
/// The Retrieve method is the most useful one in this class as it
/// calls the retrieval process if the type is not contained
/// in the cache.
///
public class AttributeCache {
private readonly Lazy, IEnumerable>> _data = new Lazy, IEnumerable>>(() => new ConcurrentDictionary, IEnumerable>(), true);
///
/// Initializes a new instance of the class.
///
/// The property cache object.
public AttributeCache(PropertyTypeCache? propertyCache = null) => this.PropertyTypeCache = propertyCache ?? PropertyTypeCache.DefaultCache.Value;
///
/// Gets the default cache.
///
///
/// The default cache.
///
public static Lazy DefaultCache { get; } = new Lazy(() => new AttributeCache());
///
/// A PropertyTypeCache object for caching properties and their attributes.
///
public PropertyTypeCache PropertyTypeCache {
get;
}
///
/// Gets one attribute of a specific type from a member.
///
/// The attribute type.
/// The member.
/// true to inspect the ancestors of element; otherwise, false .
/// An attribute stored for the specified type.
public T RetrieveOne(MemberInfo member, Boolean inherit = false) where T : Attribute {
if(member == null) {
return default!;
}
IEnumerable attr = this.Retrieve(new Tuple(member, typeof(T)), t => member.GetCustomAttributes(typeof(T), inherit));
return ConvertToAttribute(attr);
}
private static T ConvertToAttribute(IEnumerable attr) where T : Attribute => attr?.Any() != true ? (default!) : attr.Count() == 1 ? (T)Convert.ChangeType(attr.First(), typeof(T)) : throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
private IEnumerable Retrieve(Tuple key, Func, IEnumerable> factory) {
if(factory == null) {
throw new ArgumentNullException(nameof(factory));
}
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
}