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, 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
{
foreach (TEnum v in Enum.GetValues(typeof(TEnum)))
{
if (CanGet(v, typeof(T)))
{
value = v;
return true;
}
}
value = default;
return false;
}
/// 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);
/// 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.HasFlag(action));
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;
}
}
}