|
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations {
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public sealed class EnumDataTypeAttribute : DataTypeAttribute {
public Type EnumType { get; private set; }
public EnumDataTypeAttribute(Type enumType)
: base("Enumeration") {
this.EnumType = enumType;
}
#if !SILVERLIGHT
public
#else
internal
#endif
override bool IsValid(object value) {
if (this.EnumType == null) {
throw new InvalidOperationException(DataAnnotationsResources.EnumDataTypeAttribute_TypeCannotBeNull);
}
if (!this.EnumType.IsEnum) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.EnumDataTypeAttribute_TypeNeedsToBeAnEnum, this.EnumType.FullName));
}
if (value == null) {
return true;
}
string stringValue = value as string;
if (stringValue != null && String.IsNullOrEmpty(stringValue)) {
return true;
}
Type valueType = value.GetType();
if (valueType.IsEnum && this.EnumType != valueType) {
// don't match a different enum that might map to the same underlying integer
//
return false;
}
if (!valueType.IsValueType && valueType != typeof(string)) {
// non-value types cannot be converted
return false;
}
if (valueType == typeof(bool) ||
valueType == typeof(float) ||
valueType == typeof(double) ||
valueType == typeof(decimal) ||
valueType == typeof(char)) {
// non-integral types cannot be converted
return false;
}
object convertedValue;
if (valueType.IsEnum) {
Debug.Assert(valueType == value.GetType(), "The valueType should equal the Type of the value");
convertedValue = value;
} else {
try {
if (stringValue != null) {
convertedValue = Enum.Parse(this.EnumType, stringValue, false);
} else {
convertedValue = Enum.ToObject(this.EnumType, value);
}
} catch (ArgumentException) {
//
return false;
}
}
if (IsEnumTypeInFlagsMode(this.EnumType)) {
//
string underlying = GetUnderlyingTypeValueString(this.EnumType, convertedValue);
string converted = convertedValue.ToString();
return !underlying.Equals(converted);
} else {
return Enum.IsDefined(this.EnumType, convertedValue);
}
}
private static bool IsEnumTypeInFlagsMode(Type enumType) {
return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0;
}
private static string GetUnderlyingTypeValueString(Type enumType, object enumValue) {
return Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture).ToString();
}
}
}
|