diff --git a/GSS2.Core/Helpers/ControlValueHelper.cs b/GSS2.Core/Helpers/ControlValueHelper.cs index 6b5b761..d9fa64a 100644 --- a/GSS2.Core/Helpers/ControlValueHelper.cs +++ b/GSS2.Core/Helpers/ControlValueHelper.cs @@ -6,22 +6,22 @@ namespace GSS2.Core.Helpers; public static class ControlValueHelper { - private static readonly Dictionary> _valueParsers = new() + private static readonly Dictionary> _parsers = new() { - { typeof(bool ), (obj, _) => ParseValue(obj, bool .TryParse) }, - { typeof(byte ), (obj, _) => ParseValue(obj, byte .TryParse) }, - { typeof(float ), (obj, _) => ParseValue(obj, float .TryParse) }, - { typeof(long ), (obj, _) => ParseValue(obj, long .TryParse) }, - { typeof(ushort ), (obj, _) => ParseValue(obj, ushort.TryParse) }, - { typeof(uint ), (obj, _) => ParseValue(obj, uint .TryParse) }, - { typeof(int ), ParseValueInt }, + { typeof(bool ), (obj, _) => Parse(obj, bool .TryParse) }, + { typeof(byte ), (obj, _) => Parse(obj, byte .TryParse) }, + { typeof(float ), (obj, _) => Parse(obj, float .TryParse) }, + { typeof(long ), (obj, _) => Parse(obj, long .TryParse) }, + { typeof(ushort ), (obj, _) => Parse(obj, ushort.TryParse) }, + { typeof(uint ), (obj, _) => Parse(obj, uint .TryParse) }, + { typeof(int ), ParseInt }, // int - особый случай потому что он может использовать ControlId.Enumerators для прообразования стокового "enum" в значение - { typeof(Rectangle), ParseValueRectangle }, - { typeof(Size ), ParseValueSize }, - { typeof(Point ), ParseValuePoint } + { typeof(Rectangle), ParseRectangle }, + { typeof(Size ), ParseSize }, + { typeof(Point ), ParsePoint } }; private delegate bool ParseValueDelegate(string s, out T value); - private static object? ParseValue(object obj, ParseValueDelegate parser) where T : struct + private static object? Parse(object obj, ParseValueDelegate parser) where T : struct { if (obj is T value) return value; @@ -29,7 +29,7 @@ public static class ControlValueHelper return parsed; return null; } - private static object? ParseValueInt(object obj, ControlId id) + private static object? ParseInt(object obj, ControlId id) { if (obj is int value) return value; @@ -44,7 +44,7 @@ public static class ControlValueHelper return null; } - private static object? ParseValueRectangle(object obj, ControlId id) + private static object? ParseRectangle(object obj, ControlId id) { if (obj is Rectangle value) return value; @@ -65,7 +65,7 @@ public static class ControlValueHelper } return null; } - private static object? ParseValueSize(object obj, ControlId id) + private static object? ParseSize(object obj, ControlId id) { if (obj is Size value) return value; @@ -82,7 +82,7 @@ public static class ControlValueHelper } return null; } - private static object? ParseValuePoint(object obj, ControlId id) + private static object? ParsePoint(object obj, ControlId id) { if (obj is Point value) return value; @@ -99,152 +99,112 @@ public static class ControlValueHelper } return null; } - private static T? ConvertValueStruct(object obj, ControlId id) where T : struct + + private static T ConvertValue(object obj, ControlId id) { - var type = typeof(T); + if (!_parsers.TryGetValue(typeof(T), out var parser)) + throw new Exception(); - if (!_valueParsers.ContainsKey(type)) - return null; - - var parsed = _valueParsers[type](obj, id); - var result = (T?)parsed; - return result; + var parsed = parser(obj, id); + if (parsed is null || parsed is not T typed) + throw new Exception(); + return typed; } - private static T? ConvertValueClass(object obj, ControlId id) where T : class - { - var type = typeof(T); - - if (!_valueParsers.ContainsKey(type)) - return null; - - var parsed = _valueParsers[type](obj, id); - var result = (T?)parsed; - return result; - } - private static readonly Dictionary> _controlValueConverters = new() - { - { ControlTypeEnum.Bool , (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueBool (v.Value); } }, - { ControlTypeEnum.Byte , (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueByte (v.Value); } }, - { ControlTypeEnum.Float , (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueFloat (v.Value); } }, - { ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueInteger32 (v.Value); } }, - { ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueInteger64 (v.Value); } }, - { ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueUnsigned16(v.Value); } }, - { ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertValueStruct(obj, id); return v is null ? null : new ControlValueUnsigned32(v.Value); } }, - { ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertValueClass (obj, id); return v is null ? null : new ControlValueRectangle (v); } }, - { ControlTypeEnum.Size , (obj, id) => { var v = ConvertValueClass (obj, id); return v is null ? null : new ControlValueSize (v); } }, - { ControlTypeEnum.Point , (obj, id) => { var v = ConvertValueClass (obj, id); return v is null ? null : new ControlValuePoint (v); } } - }; - private static readonly Dictionary> _controlValueClapers = new() - { - { ControlTypeEnum.Bool , (value, id, info) => { } }, - { ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByte typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }, - { ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloat typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }, - { ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32 typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }, - { ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64 typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }, - { ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16 typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }, - { ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32 typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } } - }; - public static ControlValue? ConvertToControlValue(object obj, ControlId id, ControlInfo info) - { - if (!_controlValueConverters.TryGetValue(id.Type, out var converter)) - return null; - var controlValue = converter(obj, id); - if (controlValue is null) - return null; - if (!_controlValueClapers.TryGetValue(id.Type, out var clamper)) - return controlValue; - clamper(controlValue, id, info); - return controlValue; - } - - private static readonly Dictionary> _arrayParsers = new() - { - { typeof(bool ), ParseArray }, - { typeof(byte ), ParseArray }, - { typeof(float ), ParseArray }, - { typeof(long ), ParseArray }, - { typeof(ushort ), ParseArray }, - { typeof(uint ), ParseArray }, - { typeof(int ), ParseArray }, - { typeof(Rectangle), ParseArray}, - { typeof(Size ), ParseArray }, - { typeof(Point ), ParseArray } - }; - private static object? ParseArray(object obj, ControlId id) + private static IEnumerable ConvertArray(object obj, ControlId id) { if (obj is not IEnumerable enumerable) - return null; + throw new Exception(); var list = enumerable.Cast().ToList(); - if ((ulong)list.Count != id.Size) - return null; + if ((ulong)list.Count() != id.Size) + throw new Exception(); - var array = new T[list.Count]; + var array = new T[list.Count()]; - for (int i = 0; i < list.Count; i++) - { - var parsed = _valueParsers[typeof(T)](list[i], id); - if (parsed is null) - return null; + for (int i = 0; i < list.Count(); i++) + array[i] = ConvertValue(list.Skip(i).First(), id); - array[i] = (T)parsed; - } - - return array; + return array.AsEnumerable(); } - private static IEnumerable? ConvertArray(object obj, ControlId id) + + private static ControlValue ConvertControlValue(object obj, ControlId id, ControlInfo info) { - var type = typeof(T); + if (!ControlValue.ValueTypes.TryGetValue(id.Type, out var valueType)) + throw new InvalidOperationException(); - if (!_arrayParsers.ContainsKey(type)) - return null; + var convertValue = typeof(ControlValueHelper) + .GetMethod(nameof(ConvertValue), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)? + .MakeGenericMethod([valueType]); + if (convertValue is null) + throw new Exception(); - var parsed = _arrayParsers[type](obj, id); - var result = (T[]?)parsed; - return result; + var value = convertValue.Invoke(null, [obj, id]); + + var controlValueType = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(a => a.GetTypes()) + .FirstOrDefault(t => + !t.IsAbstract && + t.BaseType is { IsGenericType: true } baseType && + baseType.GetGenericTypeDefinition() == typeof(ControlValue<>) && + baseType.GetGenericArguments()[0] == valueType); + + if (controlValueType is null) + throw new Exception(); + + var constructor = controlValueType.GetConstructor([valueType]); + if (constructor is null) + throw new Exception(); + + var controlValue = constructor.Invoke([value]); + if (controlValue is null || controlValue is not ControlValue typed) + throw new Exception(); + + return typed; } - private static readonly Dictionary> _controlArrayConverters = new() + private static ControlValue ConvertControlArray(object obj, ControlId id, ControlInfo info) { - { ControlTypeEnum.Bool , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueBoolSpan (v); } }, - { ControlTypeEnum.Byte , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueByteSpan (v); } }, - { ControlTypeEnum.Float , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueFloatSpan (v); } }, - { ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueInteger32Span (v); } }, - { ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueInteger64Span (v); } }, - { ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueUnsigned16Span(v); } }, - { ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueUnsigned32Span(v); } }, - { ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueRectangleSpan (v); } }, - { ControlTypeEnum.Size , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValueSizeSpan (v); } }, - { ControlTypeEnum.Point , (obj, id) => { var v = ConvertArray(obj, id); return v is null ? null : new ControlValuePointSpan (v); } } - }; - private static readonly Dictionary> _controlArrayClapers = new() + if (!ControlValue.ArrayTypes.TryGetValue(id.Type, out var arrayType)) + throw new Exception(); + if (!ControlValue.ValueTypes.TryGetValue(id.Type, out var valueType)) + throw new InvalidOperationException(); + + var convertArray = typeof(ControlValueHelper) + .GetMethod(nameof(ConvertArray), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)? + .MakeGenericMethod([valueType]); + if (convertArray is null) + throw new Exception(); + + var value = convertArray.Invoke(null, [obj, id]); + + var controlValueType = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(a => a.GetTypes()) + .FirstOrDefault(t => + !t.IsAbstract && + t.BaseType is { IsGenericType: true } baseType && + baseType.GetGenericTypeDefinition() == typeof(ControlValue<>) && + baseType.GetGenericArguments()[0] == arrayType); + + if (controlValueType is null) + throw new Exception(); + + var constructor = controlValueType.GetConstructor([arrayType]); + if (constructor is null) + throw new Exception(); + + var controlValue = constructor.Invoke([value]); + if (controlValue is null || controlValue is not ControlValue typed) + throw new Exception(); + + return typed; + } + + public static ControlValue Convert(object obj, ControlId id, ControlInfo info) { - { ControlTypeEnum.Bool , (value, id, info) => { } }, - { ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByteSpan typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloatSpan typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32Span typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64Span typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16Span typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32Span typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } }, - { ControlTypeEnum.Rectangle , (value, id, info) => { if (value is ControlValueRectangleSpan typed && info.Min is ControlValueRectangle typedMin && info.Max is ControlValueRectangle typedMax) typed.Set(typed.Get().Select(v => new Rectangle(Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ), - Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y ), - Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ), - Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } }, - { ControlTypeEnum.Size , (value, id, info) => { if (value is ControlValueSizeSpan typed && info.Min is ControlValueSize typedMin && info.Max is ControlValueSize typedMax) typed.Set(typed.Get().Select(v => new Size( Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ), - Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } }, - { ControlTypeEnum.Point , (value, id, info) => { if (value is ControlValuePointSpan typed && info.Min is ControlValuePoint typedMin && info.Max is ControlValuePoint typedMax) typed.Set(typed.Get().Select(v => new Point( Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ), - Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y )))); } }, - }; - public static ControlValue? ConvertToControlArray(object obj, ControlId id, ControlInfo info) - { - if (!_controlArrayConverters.ContainsKey(id.Type)) - return null; - var controlValue = _controlArrayConverters[id.Type](obj, id); - if (controlValue is null) - return null; - if (!_controlArrayClapers.TryGetValue(id.Type, out var clamper)) - return controlValue; - clamper(controlValue, id, info); - return controlValue; + if (!id.IsArray) + return ConvertControlValue(obj, id, info); + return ConvertControlArray(obj, id, info); } } \ No newline at end of file