From 7ed969d1af74380570e41f9732e00b77f32eea48 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Fri, 11 Jun 2021 19:46:51 +0200 Subject: [PATCH] Moved DynamicEnum to its own library --- MLEM.Data/DynamicEnum.cs | 280 ------------------------- MLEM.Data/Json/DynamicEnumConverter.cs | 19 -- 2 files changed, 299 deletions(-) delete mode 100644 MLEM.Data/DynamicEnum.cs delete mode 100644 MLEM.Data/Json/DynamicEnumConverter.cs diff --git a/MLEM.Data/DynamicEnum.cs b/MLEM.Data/DynamicEnum.cs deleted file mode 100644 index b3e7033..0000000 --- a/MLEM.Data/DynamicEnum.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Reflection; -using MLEM.Data.Json; -using MLEM.Misc; -using Newtonsoft.Json; - -namespace MLEM.Data { - /// - /// A dynamic enum is a class that represents enum-like single-instance value behavior with additional capabilities, including dynamic addition of new arbitrary values. - /// A dynamic enum uses as its underlying type, allowing for an arbitrary number of enum values to be created, even when a -like structure is used that would only allow for up to 64 values in a regular enum. - /// All enum operations including , , and are supported and can be implemented in derived classes using operator overloads. - /// To create a custom dynamic enum, simply create a class that extends . New values can then be added using , or . - /// - /// - /// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used: - /// - /// public static implicit operator BigInteger(MyEnum value) => GetValue(value); - /// public static implicit operator MyEnum(BigInteger value) => GetEnumValue<MyEnum>(value); - /// public static MyEnum operator |(MyEnum left, MyEnum right) => Or(left, right); - /// public static MyEnum operator &(MyEnum left, MyEnum right) => And(left, right); - /// public static MyEnum operator ^(MyEnum left, MyEnum right) => Xor(left, right); - /// public static MyEnum operator ~(MyEnum value) => Neg(value); - /// - /// - [JsonConverter(typeof(DynamicEnumConverter))] - public abstract class DynamicEnum : GenericDataHolder { - - private static readonly Dictionary> Values = new Dictionary>(); - private static readonly Dictionary> FlagCache = new Dictionary>(); - private static readonly Dictionary ParseCache = new Dictionary(); - - private readonly BigInteger value; - private string name; - - /// - /// Creates a new dynamic enum instance. - /// This constructor is protected as it is only invoked via reflection. - /// - /// The name of the enum value - /// The value - protected DynamicEnum(string name, BigInteger value) { - this.value = value; - this.name = name; - } - - /// - /// Returns true if this enum value has the given flag on it. - /// This operation is equivalent to . - /// - /// The flag to query - /// True if the flag is present, false otherwise - public bool HasFlag(DynamicEnum flag) { - return (GetValue(this) & GetValue(flag)) == GetValue(flag); - } - - /// - public override string ToString() { - if (this.name == null) { - var included = new List(); - if (GetValue(this) != 0) { - foreach (var v in GetValues(this.GetType())) { - if (this.HasFlag(v) && GetValue(v) != 0) - included.Add(v); - } - } - this.name = included.Count > 0 ? string.Join(" | ", included) : GetValue(this).ToString(); - } - return this.name; - } - - /// - /// Adds a new enum value to the given enum type - /// - /// The name of the enum value to add - /// The value to add - /// The type to add this value to - /// The newly created enum value - /// Thrown if the name or value passed are already present - public static T Add(string name, BigInteger value) where T : DynamicEnum { - if (!Values.TryGetValue(typeof(T), out var dict)) { - dict = new Dictionary(); - Values.Add(typeof(T), dict); - } - - if (dict.ContainsKey(value)) - throw new ArgumentException($"Duplicate value {value}", nameof(value)); - if (dict.Values.Any(v => v.name == name)) - throw new ArgumentException($"Duplicate name {name}", nameof(name)); - - var ret = Construct(typeof(T), name, value); - dict.Add(value, ret); - return (T) ret; - } - - /// - /// Adds a new enum value to the given enum type . - /// This method differs from in that it automatically determines a value. - /// The value determined will be the next free number in a sequence, which represents the default behavior in an enum if enum values are not explicitly numbered. - /// - /// The name of the enum value to add - /// The type to add this value to - /// The newly created enum value - public static T AddValue(string name) where T : DynamicEnum { - BigInteger value = 0; - if (Values.TryGetValue(typeof(T), out var defined)) { - while (defined.ContainsKey(value)) - value++; - } - return Add(name, value); - } - - /// - /// Adds a new flag enum value to the given enum type . - /// This method differs from in that it automatically determines a value. - /// The value determined will be the next free power of two, allowing enum values to be combined using bitwise operations to create -like behavior. - /// - /// The name of the enum value to add - /// The type to add this value to - /// The newly created enum value - public static T AddFlag(string name) where T : DynamicEnum { - BigInteger value = 0; - if (Values.TryGetValue(typeof(T), out var defined)) { - while (defined.ContainsKey(value)) - value <<= 1; - } - return Add(name, value); - } - - /// - /// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type . - /// A value counts as explicitly defined if it has been added using , or . - /// - /// The type whose values to get - /// The defined values for the given type - public static IEnumerable GetValues() where T : DynamicEnum { - return GetValues(typeof(T)).Cast(); - } - - /// - /// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type . - /// A value counts as explicitly defined if it has been added using , or . - /// - /// The type whose values to get - /// The defined values for the given type - public static IEnumerable GetValues(Type type) { - return Values.TryGetValue(type, out var ret) ? ret.Values : Enumerable.Empty(); - } - - /// - /// Returns the bitwise OR (|) combination of the two dynamic enum values - /// - /// The left value - /// The right value - /// The type of the values - /// The bitwise OR (|) combination - public static T Or(T left, T right) where T : DynamicEnum { - return GetEnumValue(GetValue(left) | GetValue(right)); - } - - /// - /// Returns the bitwise AND (&) combination of the two dynamic enum values - /// - /// The left value - /// The right value - /// The type of the values - /// The bitwise AND (&) combination - public static T And(T left, T right) where T : DynamicEnum { - return GetEnumValue(GetValue(left) & GetValue(right)); - } - - /// - /// Returns the bitwise XOR (^) combination of the two dynamic enum values - /// - /// The left value - /// The right value - /// The type of the values - /// The bitwise XOR (^) combination - public static T Xor(T left, T right) where T : DynamicEnum { - return GetEnumValue(GetValue(left) ^ GetValue(right)); - } - - /// - /// Returns the bitwise NEG (~) combination of the dynamic enum value - /// - /// The value - /// The type of the values - /// The bitwise NEG (~) value - public static T Neg(T value) where T : DynamicEnum { - return GetEnumValue(~GetValue(value)); - } - - /// - /// Returns the representation of the given dynamic enum value - /// - /// The value whose number representation to get - /// The value's number representation - public static BigInteger GetValue(DynamicEnum value) { - return value?.value ?? 0; - } - - /// - /// Returns the defined or combined dynamic enum value for the given representation - /// - /// The value whose dynamic enum value to get - /// The type that the returned dynamic enum should have - /// The defined or combined dynamic enum value - public static T GetEnumValue(BigInteger value) where T : DynamicEnum { - return (T) GetEnumValue(typeof(T), value); - } - - /// - /// Returns the defined or combined dynamic enum value for the given representation - /// - /// The type that the returned dynamic enum should have - /// The value whose dynamic enum value to get - /// The defined or combined dynamic enum value - public static DynamicEnum GetEnumValue(Type type, BigInteger value) { - // get the defined value if it exists - if (Values.TryGetValue(type, out var values) && values.TryGetValue(value, out var defined)) - return defined; - - // otherwise, cache the combined value - if (!FlagCache.TryGetValue(type, out var cache)) { - cache = new Dictionary(); - FlagCache.Add(type, cache); - } - if (!cache.TryGetValue(value, out var combined)) { - combined = Construct(type, null, value); - cache.Add(value, combined); - } - return combined; - } - - /// - /// Parses the given into a dynamic enum value and returns the result. - /// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces. - /// If no enum value can be parsed, null is returned. - /// - /// The string to parse into a dynamic enum value - /// The type of the dynamic enum value to parse - /// The parsed enum value, or null if parsing fails - public static T Parse(string strg) where T : DynamicEnum { - return (T) Parse(typeof(T), strg); - } - - /// - /// Parses the given into a dynamic enum value and returns the result. - /// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces. - /// If no enum value can be parsed, null is returned. /// - /// The type of the dynamic enum value to parse - /// The string to parse into a dynamic enum value - /// The parsed enum value, or null if parsing fails - public static DynamicEnum Parse(Type type, string strg) { - if (!ParseCache.TryGetValue(strg, out var cached)) { - BigInteger? accum = null; - foreach (var val in strg.Split('|')) { - foreach (var defined in GetValues(type)) { - if (defined.name == val.Trim()) { - accum = (accum ?? 0) | GetValue(defined); - break; - } - } - } - if (accum != null) - cached = GetEnumValue(type, accum.Value); - ParseCache.Add(strg, cached); - } - return cached; - } - - private static DynamicEnum Construct(Type type, string name, BigInteger value) { - var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] {typeof(string), typeof(BigInteger)}, null); - return (DynamicEnum) constructor.Invoke(new object[] {name, value}); - } - - } -} \ No newline at end of file diff --git a/MLEM.Data/Json/DynamicEnumConverter.cs b/MLEM.Data/Json/DynamicEnumConverter.cs deleted file mode 100644 index 01f2d21..0000000 --- a/MLEM.Data/Json/DynamicEnumConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace MLEM.Data.Json { - /// - public class DynamicEnumConverter : JsonConverter { - - /// - public override void WriteJson(JsonWriter writer, DynamicEnum value, JsonSerializer serializer) { - writer.WriteValue(value.ToString()); - } - - /// - public override DynamicEnum ReadJson(JsonReader reader, Type objectType, DynamicEnum existingValue, bool hasExistingValue, JsonSerializer serializer) { - return DynamicEnum.Parse(objectType, reader.Value.ToString()); - } - - } -} \ No newline at end of file