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 CorrepsondingAction
{
/// 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, CorrepsondingAction action = CorrepsondingAction.GetSet)
{
TypeRef = typeRef;
Action = action;
}
/// Initializes a new instance of the class.
/// The actions allowed for the type.
public CorrespondingTypeAttribute(CorrepsondingAction action)
{
Action = action;
}
/// Gets the action allowed for the type.
/// The action allowed for the type.
public CorrepsondingAction 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)
{
return GetAttrForObj(value).Any(a => a.Action.IsFlagSet(CorrepsondingAction.Get) && 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)
{
return GetAttrForType(type).Any(a => a.Action.IsFlagSet(CorrepsondingAction.Get) && 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(object value, Type typeRef)
{
return GetAttrForObj(value).Any(a => a.Action.IsFlagSet(CorrepsondingAction.Set) && 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)
{
return GetAttrForType(type).Any(a => a.Action.IsFlagSet(CorrepsondingAction.Set) && a.TypeRef == typeRef);
}
/// Gets the corresponding types for the supplied enumeration value.
/// The enumeration value or class.
/// 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 or class.
/// 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 enum value.
/// An enumeration of all associated CorrespondingTypeAttribute instances.
protected static IEnumerable GetAttrForObj(object value)
{
if (value == 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 == CorrepsondingAction.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 == CorrepsondingAction.Exception)) throw new Exception();
return attr;
}
}
}