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)
{
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; }
///
/// Determines whether [contains] [the specified member].
///
/// The type of the attribute to be retrieved.
/// The member.
///
/// true if [contains] [the specified member]; otherwise, false .
///
public bool Contains(MemberInfo member) => _data.Value.ContainsKey(new Tuple(member, typeof(T)));
///
/// Gets specific attributes from a member constrained to an attribute.
///
/// The type of the attribute to be retrieved.
/// The member.
/// true to inspect the ancestors of element; otherwise, false .
/// An array of the attributes stored for the specified type.
public IEnumerable Retrieve(MemberInfo member, bool inherit = false)
where T : Attribute
{
if (member == null)
throw new ArgumentNullException(nameof(member));
return Retrieve(new Tuple(member, typeof(T)), t => member.GetCustomAttributes(inherit));
}
///
/// Gets all attributes of a specific type from a member.
///
/// The member.
/// The attribute type.
/// true to inspect the ancestors of element; otherwise, false .
/// An array of the attributes stored for the specified type.
public IEnumerable Retrieve(MemberInfo member, Type type, bool inherit = false)
{
if (member == null)
throw new ArgumentNullException(nameof(member));
if (type == null)
throw new ArgumentNullException(nameof(type));
return Retrieve(
new Tuple(member, type),
t => member.GetCustomAttributes(type, inherit));
}
///
/// 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, bool inherit = false)
where T : Attribute
{
if (member == null)
return default;
var attr = Retrieve(
new Tuple(member, typeof(T)),
t => member.GetCustomAttributes(typeof(T), inherit));
return ConvertToAttribute(attr);
}
///
/// Gets one attribute of a specific type from a generic type.
///
/// The type of the attribute.
/// The type to retrieve the attribute.
/// if set to true [inherit].
/// An attribute stored for the specified type.
public TAttribute RetrieveOne(bool inherit = false)
where TAttribute : Attribute
{
var attr = Retrieve(
new Tuple(typeof(T), typeof(TAttribute)),
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
return ConvertToAttribute(attr);
}
///
/// Gets all properties an their attributes of a given type constrained to only attributes.
///
/// The type of the attribute to retrieve.
/// The type of the object.
/// true to inspect the ancestors of element; otherwise, false .
/// A dictionary of the properties and their attributes stored for the specified type.
public Dictionary> Retrieve(Type type, bool inherit = false)
where T : Attribute =>
PropertyTypeCache.RetrieveAllProperties(type, true)
.ToDictionary(x => x, x => Retrieve(x, inherit));
///
/// Gets all properties and their attributes of a given type.
///
/// The object type used to extract the properties from.
/// The type of the attribute.
/// true to inspect the ancestors of element; otherwise, false .
///
/// A dictionary of the properties and their attributes stored for the specified type.
///
public Dictionary> RetrieveFromType(bool inherit = false)
=> RetrieveFromType(typeof(TAttribute), inherit);
///
/// Gets all properties and their attributes of a given type.
///
/// The object type used to extract the properties from.
/// Type of the attribute.
/// true to inspect the ancestors of element; otherwise, false .
///
/// A dictionary of the properties and their attributes stored for the specified type.
///
public Dictionary> RetrieveFromType(Type attributeType, bool inherit = false)
{
if (attributeType == null)
throw new ArgumentNullException(nameof(attributeType));
return PropertyTypeCache.RetrieveAllProperties(true)
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
}
private static T ConvertToAttribute(IEnumerable attr)
where T : Attribute
{
if (attr?.Any() != true)
return default;
return 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 _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
}