#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; } /// /// 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 => 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. /// 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(Boolean inherit = false) => this.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, 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 => 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)); } } }