2019-12-08 19:54:52 +01:00
#nullable enable
using System ;
2019-12-04 18:57:18 +01:00
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
2019-12-08 19:54:52 +01:00
namespace Swan.Reflection {
/// <summary>
/// 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.
/// </summary>
public class AttributeCache {
private readonly Lazy < ConcurrentDictionary < Tuple < Object , Type > , IEnumerable < Object > > > _data = new Lazy < ConcurrentDictionary < Tuple < Object , Type > , IEnumerable < Object > > > ( ( ) = > new ConcurrentDictionary < Tuple < Object , Type > , IEnumerable < Object > > ( ) , true ) ;
2019-12-04 18:57:18 +01:00
/// <summary>
2019-12-08 19:54:52 +01:00
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
2019-12-04 18:57:18 +01:00
/// </summary>
2019-12-08 19:54:52 +01:00
/// <param name="propertyCache">The property cache object.</param>
public AttributeCache ( PropertyTypeCache ? propertyCache = null ) = > this . PropertyTypeCache = propertyCache ? ? PropertyTypeCache . DefaultCache . Value ;
/// <summary>
/// Gets the default cache.
/// </summary>
/// <value>
/// The default cache.
/// </value>
public static Lazy < AttributeCache > DefaultCache { get ; } = new Lazy < AttributeCache > ( ( ) = > new AttributeCache ( ) ) ;
/// <summary>
/// A PropertyTypeCache object for caching properties and their attributes.
/// </summary>
public PropertyTypeCache PropertyTypeCache {
get ;
}
/// <summary>
/// Determines whether [contains] [the specified member].
/// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
/// <param name="member">The member.</param>
/// <returns>
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
/// </returns>
public Boolean Contains < T > ( MemberInfo member ) = > this . _data . Value . ContainsKey ( new Tuple < Object , Type > ( member , typeof ( T ) ) ) ;
/// <summary>
/// Gets specific attributes from a member constrained to an attribute.
/// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
/// <param name="member">The member.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An array of the attributes stored for the specified type.</returns>
public IEnumerable < Object > Retrieve < T > ( MemberInfo member , Boolean inherit = false ) where T : Attribute {
if ( member = = null ) {
throw new ArgumentNullException ( nameof ( member ) ) ;
}
return this . Retrieve ( new Tuple < Object , Type > ( member , typeof ( T ) ) , t = > member . GetCustomAttributes < T > ( inherit ) ) ;
}
/// <summary>
/// Gets all attributes of a specific type from a member.
/// </summary>
/// <param name="member">The member.</param>
/// <param name="type">The attribute type.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An array of the attributes stored for the specified type.</returns>
public IEnumerable < Object > 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 < Object , Type > ( member , type ) , t = > member . GetCustomAttributes ( type , inherit ) ) ;
}
/// <summary>
/// Gets one attribute of a specific type from a member.
/// </summary>
/// <typeparam name="T">The attribute type.</typeparam>
/// <param name="member">The member.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An attribute stored for the specified type.</returns>
public T RetrieveOne < T > ( MemberInfo member , Boolean inherit = false ) where T : Attribute {
if ( member = = null ) {
return default ! ;
}
IEnumerable < Object > attr = this . Retrieve ( new Tuple < Object , Type > ( member , typeof ( T ) ) , t = > member . GetCustomAttributes ( typeof ( T ) , inherit ) ) ;
return ConvertToAttribute < T > ( attr ) ;
}
/// <summary>
/// Gets one attribute of a specific type from a generic type.
/// </summary>
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns>An attribute stored for the specified type.</returns>
public TAttribute RetrieveOne < TAttribute , T > ( Boolean inherit = false ) where TAttribute : Attribute {
IEnumerable < Object > attr = this . Retrieve ( new Tuple < Object , Type > ( typeof ( T ) , typeof ( TAttribute ) ) , t = > typeof ( T ) . GetCustomAttributes ( typeof ( TAttribute ) , inherit ) ) ;
return ConvertToAttribute < TAttribute > ( attr ) ;
}
/// <summary>
/// Gets all properties an their attributes of a given type constrained to only attributes.
/// </summary>
/// <typeparam name="T">The type of the attribute to retrieve.</typeparam>
/// <param name="type">The type of the object.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
public Dictionary < PropertyInfo , IEnumerable < Object > > Retrieve < T > ( Type type , Boolean inherit = false ) where T : Attribute = > this . PropertyTypeCache . RetrieveAllProperties ( type , true ) . ToDictionary ( x = > x , x = > this . Retrieve < T > ( x , inherit ) ) ;
/// <summary>
/// Gets all properties and their attributes of a given type.
/// </summary>
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns>
public Dictionary < PropertyInfo , IEnumerable < Object > > RetrieveFromType < T , TAttribute > ( Boolean inherit = false ) = > this . RetrieveFromType < T > ( typeof ( TAttribute ) , inherit ) ;
/// <summary>
/// Gets all properties and their attributes of a given type.
/// </summary>
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
/// <param name="attributeType">Type of the attribute.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns>
public Dictionary < PropertyInfo , IEnumerable < Object > > RetrieveFromType < T > ( Type attributeType , Boolean inherit = false ) {
if ( attributeType = = null ) {
throw new ArgumentNullException ( nameof ( attributeType ) ) ;
}
return this . PropertyTypeCache . RetrieveAllProperties < T > ( true ) . ToDictionary ( x = > x , x = > this . Retrieve ( x , attributeType , inherit ) ) ;
}
private static T ConvertToAttribute < T > ( IEnumerable < Object > 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 < Object > Retrieve ( Tuple < Object , Type > key , Func < Tuple < Object , Type > , IEnumerable < Object > > factory ) {
if ( factory = = null ) {
throw new ArgumentNullException ( nameof ( factory ) ) ;
}
return this . _data . Value . GetOrAdd ( key , k = > factory . Invoke ( k ) . Where ( item = > item ! = null ) ) ;
}
}
2019-12-04 18:57:18 +01:00
}