using System; using System.Collections.Generic; using System.Linq; using Vanara.Extensions; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global namespace Vanara.InteropServices { /// Actions that can be taken with a corresponding type. [Flags] public enum CorrespondingAction { /// No actions may be taken. None = 0, /// The type can be retrieved. Get = 1, /// The type can be set. Set = 2, /// The type can be retrieved and set. GetSet = 3, /// Throw a if this enumeration value is used. Exception = 4 } /// /// Attribute for enum values that provides information about corresponding types and related actions. Useful for Get/Set methods that /// use an enumeration value to determine the type to get or set. /// /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] public class CorrespondingTypeAttribute : Attribute { /// Initializes a new instance of the class. /// The type that corresponds to this enumeration value. /// The actions allowed for the type. public CorrespondingTypeAttribute(Type typeRef, CorrespondingAction action = CorrespondingAction.GetSet) { TypeRef = typeRef; Action = action; } /// Initializes a new instance of the class. /// The actions allowed for the type. public CorrespondingTypeAttribute(CorrespondingAction action) => Action = action; /// Gets the action allowed for the type. /// The action allowed for the type. public CorrespondingAction Action { get; } /// Gets the type that corresponds to this enumeration value. /// The type that corresponds to this enumeration value. public Type TypeRef { get; } /// Determines whether this instance can get the type for the specified enum value or class. /// The enumeration value or class instance. /// The type supplied by the user to validate. /// true if this instance can get the specified type; otherwise, false. public static bool CanGet(object value, Type typeRef) => GetAttrForObj(value).Any(a => a.Action.IsFlagSet(CorrespondingAction.Get) && a.TypeRef == typeRef); /// Determines whether this instance can get the type for the specified enum type. /// The enumeration type. /// The type supplied by the user to validate. /// true if this instance can get the specified type; otherwise, false. public static bool CanGet(TEnum value, Type typeRef) where TEnum : System.Enum => GetAttrForEnum(value, CorrespondingAction.Get).Any(a => a.TypeRef == typeRef); /// Determines whether this type can get the specified reference type. /// The class type. /// The type supplied by the user to validate. /// true if this type can get the specified reference type; otherwise, false. public static bool CanGet(Type type, Type typeRef) => GetAttrForType(type).Any(a => a.Action.IsFlagSet(CorrespondingAction.Get) && a.TypeRef == typeRef); /// Determines whether an enum value exists that supports a corresponding type of . /// The corresponding type to look for. /// The type of the enum. /// The value of type that has the corresponding type . /// if this instance can get the specified value; otherwise, . public static bool CanGet(out TEnum value) where TEnum : struct, System.Enum => CanGet(typeof(T), out value); /// Determines whether an enum value exists that supports a corresponding type of . /// The type of the enum. /// The corresponding type to look for. /// The value of type that has the corresponding type . /// if this instance can get the specified value; otherwise, . public static bool CanGet(Type typeRef, out TEnum value) where TEnum : struct, System.Enum { foreach (TEnum v in Enum.GetValues(typeof(TEnum))) { if (CanGet(v, typeRef)) { value = v; return true; } } value = default; return false; } /// Determines whether an enum value exists that supports a corresponding type of . /// The corresponding type to look for. /// The type of the enum. /// If not , the enumeration value to check. /// The value of type that has the corresponding type . /// if this instance can get the specified value; otherwise, . public static bool CanGet(TEnum? input, out TEnum value) where TEnum : struct, System.Enum => input.HasValue ? CanGet(value = input.Value, typeof(T)) : CanGet(out value); /// Determines whether this instance can set the type for the specified enum value or class. /// The enumeration value or class instance. /// The type supplied by the user to validate. /// true if this instance can set the specified type; otherwise, false. public static bool CanSet(object value, Type typeRef) => GetAttrForObj(value).Any(a => a.Action.IsFlagSet(CorrespondingAction.Set) && a.TypeRef == typeRef); /// Determines whether this instance can set the type for the specified enum value or class. /// The enumeration value or class instance. /// The type supplied by the user to validate. /// true if this instance can set the specified type; otherwise, false. public static bool CanSet(TEnum value, Type typeRef) where TEnum : System.Enum => GetAttrForEnum(value, CorrespondingAction.Set).Any(a => a.TypeRef == typeRef); /// Determines whether this type can set the specified reference type. /// The class type. /// The type supplied by the user to validate. /// true if this type can set the specified reference type; otherwise, false. public static bool CanSet(Type type, Type typeRef) => GetAttrForType(type).Any(a => a.Action.IsFlagSet(CorrespondingAction.Set) && a.TypeRef == typeRef); /// Determines whether an enum value exists that supports a corresponding type of . /// The corresponding type to look for. /// The type of the enum. /// The value of type that has the corresponding type . /// if this instance can get the specified value; otherwise, . public static bool CanSet(out TEnum value) where TEnum : struct, System.Enum => CanSet(typeof(T), out value); /// Determines whether an enum value exists that supports a corresponding type of . /// The type of the enum. /// The type supplied by the user to validate. /// The value of type that has the corresponding type . /// if this instance can get the specified value; otherwise, . public static bool CanSet(Type typeRef, out TEnum value) where TEnum : struct, System.Enum { foreach (TEnum v in Enum.GetValues(typeof(TEnum))) { if (CanSet(v, typeRef)) { value = v; return true; } } value = default; return false; } /// Gets the corresponding types for the supplied enumeration value. /// The class or structure instance. /// The types defined by the attribute. public static IEnumerable GetCorrespondingTypes(object enumValue) => GetAttrForObj(enumValue).Select(a => a.TypeRef); /// Gets the corresponding types for the supplied enumeration value. /// The enumeration value. /// The types defined by the attribute. public static IEnumerable GetCorrespondingTypes(TEnum enumValue) where TEnum : System.Enum => GetAttrForEnum(enumValue).Select(a => a.TypeRef); /// Gets the corresponding types for the supplied enumeration value. /// The enumeration value. /// The action to filter for. /// The types defined by the attribute. public static IEnumerable GetCorrespondingTypes(TEnum enumValue, CorrespondingAction action) where TEnum : System.Enum => GetAttrForEnum(enumValue, action).Select(a => a.TypeRef); /// Gets the corresponding types for the supplied enumeration value. /// The class type. /// The types defined by the attribute. public static IEnumerable GetCorrespondingTypes(Type type) => GetAttrForType(type).Select(a => a.TypeRef); /// Gets the CorrespondingTypeAttribute instances associated with an enum value. /// The type of the enum. /// The enum value. /// The action to filter for. /// An enumeration of all associated CorrespondingTypeAttribute instances. protected static IEnumerable GetAttrForEnum(TEnum value, CorrespondingAction action = (CorrespondingAction)0xf) where TEnum : System.Enum { var valueType = value.GetType(); var attr = valueType.GetField(value.ToString()).GetCustomAttributes(false, a => (a.Action & action) > 0); if (attr.Any(a => a.Action == CorrespondingAction.Exception)) throw new Exception(); //if (!attr.Any()) return new CorrespondingTypeAttribute[0]; return attr; } /// Gets the CorrespondingTypeAttribute instances associated with an enum value or class instance. /// The enum value or class instance. /// An enumeration of all associated CorrespondingTypeAttribute instances. protected static IEnumerable GetAttrForObj(object value) { if (value is null) throw new ArgumentNullException(nameof(value)); var valueType = value.GetType(); if (!valueType.IsEnum && !valueType.IsClass) throw new ArgumentException("Value must be an enumeration or class value.", nameof(value)); var attr = valueType.IsEnum ? valueType.GetField(value.ToString()).GetCustomAttributes() : valueType.GetCustomAttributes(); if (!attr.Any()) return new CorrespondingTypeAttribute[0]; if (attr.Any(a => a.Action == CorrespondingAction.Exception)) throw new Exception(); return attr; } /// Gets the CorrespondingTypeAttribute instances associated with a type. /// The type. /// An enumeration of all associated CorrespondingTypeAttribute instances. protected static IEnumerable GetAttrForType(Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); var attr = type.GetCustomAttributes(); if (!attr.Any()) return new CorrespondingTypeAttribute[0]; if (attr.Any(a => a.Action == CorrespondingAction.Exception)) throw new Exception(); return attr; } } }