using System; using System.Collections.Generic; using System.Reflection; using System.Collections.Concurrent; using System.Linq; namespace Unosquare.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 ?? Runtime.PropertyTypeCache; /// /// 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 Boolean Contains(MemberInfo member) => this._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, Boolean inherit = false) where T : Attribute { if(member == null) { throw new ArgumentNullException(nameof(member)); } return this.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, Boolean inherit = false) { if(member == null) { throw new ArgumentNullException(nameof(member)); } if(type == null) { throw new ArgumentNullException(nameof(type)); } return this.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, 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); } /// /// 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(Boolean inherit = false) where TAttribute : Attribute { IEnumerable attr = this.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, Boolean inherit = false) where T : Attribute { if(type == null) { throw new ArgumentNullException(nameof(type)); } return this.PropertyTypeCache.RetrieveAllProperties(type, true) .ToDictionary(x => x, x => this.Retrieve(x, 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, Boolean inherit = false) { if(attributeType == null) { throw new ArgumentNullException(nameof(attributeType)); } return this.PropertyTypeCache.RetrieveAllProperties(true) .ToDictionary(x => x, x => this.Retrieve(x, attributeType, inherit)); } private static T ConvertToAttribute(IEnumerable attr) where T : Attribute { if(attr?.Any() != true) { return default; } if(attr.Count() == 1) { return (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)); } } }