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)); } } }