using System; using System.Collections.Generic; using System.Linq; using System.Text; using Unity.VisualScripting.FullSerializer.Internal; namespace Unity.VisualScripting.FullSerializer { /// /// Serializes and deserializes enums by their current name. /// public class fsEnumConverter : fsConverter { public override bool CanProcess(Type type) { return type.Resolve().IsEnum; } public override bool RequestCycleSupport(Type storageType) { return false; } public override bool RequestInheritanceSupport(Type storageType) { return false; } public override object CreateInstance(fsData data, Type storageType) { // In .NET compact, Enum.ToObject(Type, Object) is defined but the // overloads like Enum.ToObject(Type, int) are not -- so we get // around this by boxing the value. return Enum.ToObject(storageType, (object)0); } public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType) { if (Serializer.Config.SerializeEnumsAsInteger) { serialized = new fsData(Convert.ToInt64(instance)); } else if (fsPortableReflection.GetAttribute(storageType) != null) { var lValue = Convert.ToInt64(instance); var result = new StringBuilder(); var first = true; foreach (var flag in Enum.GetValues(storageType)) { var lFlag = Convert.ToInt64(flag); if (lFlag == 0) { continue; } var isSet = (lValue & lFlag) == lFlag; if (isSet) { if (first == false) { result.Append(","); } first = false; result.Append(flag.ToString()); } } serialized = new fsData(result.ToString()); } else { serialized = new fsData(Enum.GetName(storageType, instance)); } return fsResult.Success; } public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType) { if (data.IsString) { var enumValues = data.AsString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (var i = 0; i < enumValues.Length; ++i) { var enumValue = enumValues[i]; // Verify that the enum name exists; Enum.TryParse is only // available in .NET 4.0 and above :(. var strings = Enum.GetNames(storageType); if (ArrayContains(strings, enumValue) == false) { // Look for a [RenamedFrom("prevName")] attribute on the enum member IEnumerable<(Enum enumMember, string previousName)> valueTuples = Enum.GetValues(storageType).Cast().SelectMany( x => x.GetAttributeOfEnumMember().Select(attr => (x, attr.previousName))); Dictionary enumToRenamedFrom = valueTuples .ToDictionary(x => x.previousName, x => x.enumMember); if (enumToRenamedFrom.TryGetValue(enumValue, out var newName)) enumValues[i] = newName.ToString(); else return fsResult.Fail("Cannot find enum name " + enumValue + " on type " + storageType); } } var underlyingType = Enum.GetUnderlyingType(storageType); if (underlyingType == typeof(ulong)) { ulong instanceValue = 0; for (var i = 0; i < enumValues.Length; ++i) { var enumValue = enumValues[i]; var flagValue = (ulong)Convert.ChangeType(Enum.Parse(storageType, enumValue), typeof(ulong)); instanceValue |= flagValue; } instance = Enum.ToObject(storageType, (object)instanceValue); } else { long instanceValue = 0; for (var i = 0; i < enumValues.Length; ++i) { var enumValue = enumValues[i]; var flagValue = (long)Convert.ChangeType(Enum.Parse(storageType, enumValue), typeof(long)); instanceValue |= flagValue; } instance = Enum.ToObject(storageType, (object)instanceValue); } return fsResult.Success; } else if (data.IsInt64) { var enumValue = (int)data.AsInt64; // In .NET compact, Enum.ToObject(Type, Object) is defined but // the overloads like Enum.ToObject(Type, int) are not -- so we // get around this by boxing the value. instance = Enum.ToObject(storageType, (object)enumValue); return fsResult.Success; } return fsResult.Fail($"EnumConverter encountered an unknown JSON data type for {storageType}: {data.Type}"); } /// /// Returns true if the given value is contained within the specified /// array. /// private static bool ArrayContains(T[] values, T value) { // note: We don't use LINQ because this function will *not* allocate for (var i = 0; i < values.Length; ++i) { if (EqualityComparer.Default.Equals(values[i], value)) { return true; } } return false; } } }