From 0bcfd68bc171644ea464738129ddeb6d13433f1c Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Sun, 15 Dec 2019 01:07:36 -0600 Subject: [PATCH] Added a fuck load of converters and a default mechanism --- IPA.Loader/Config/Stores/Converters.cs | 294 ++++++++++++++---- .../Config/Stores/CustomObjectConverter.cs | 82 +++++ IPA.Loader/Config/Stores/GeneratedStore.cs | 8 +- IPA.Loader/IPA.Loader.csproj | 1 + 4 files changed, 328 insertions(+), 57 deletions(-) create mode 100644 IPA.Loader/Config/Stores/CustomObjectConverter.cs diff --git a/IPA.Loader/Config/Stores/Converters.cs b/IPA.Loader/Config/Stores/Converters.cs index 8d92f6f0..1d092e6a 100644 --- a/IPA.Loader/Config/Stores/Converters.cs +++ b/IPA.Loader/Config/Stores/Converters.cs @@ -32,82 +32,268 @@ namespace IPA.Config.Stores.Converters => val is FloatingPoint fp ? fp.Value : val is Integer inte ? inte.AsFloat()?.Value : null; + + internal interface IValConv + { + ValueConverter Get(); + } + internal class ValConv : IValConv where T : struct + { + private static readonly IValConv Impl = ValConvImpls.Impl as IValConv ?? new ValConv(); + public ValueConverter Get() => Impl.Get(); + ValueConverter IValConv.Get() + => null; // default to null + } + private class ValConvImpls : IValConv, + IValConv, IValConv, + IValConv, IValConv, + IValConv, IValConv, + IValConv, IValConv, + IValConv, IValConv, + IValConv, IValConv, + IValConv + { + internal static readonly ValConvImpls Impl = new ValConvImpls(); + ValueConverter IValConv.Get() + => new CharConverter(); + ValueConverter IValConv.Get() + => new LongConverter(); + ValueConverter IValConv.Get() + => new ULongConverter(); + ValueConverter IValConv.Get() + => new IntPtrConverter(); + ValueConverter IValConv.Get() + => new UIntPtrConverter(); + ValueConverter IValConv.Get() + => new IntConverter(); + ValueConverter IValConv.Get() + => new UIntConverter(); + ValueConverter IValConv.Get() + => new ShortConverter(); + ValueConverter IValConv.Get() + => new UShortConverter(); + ValueConverter IValConv.Get() + => new ByteConverter(); + ValueConverter IValConv.Get() + => new SByteConverter(); + ValueConverter IValConv.Get() + => new FloatConverter(); + ValueConverter IValConv.Get() + => new DoubleConverter(); + ValueConverter IValConv.Get() + => new DecimalConverter(); + } } /// - /// A for objects normally serialized to config via . + /// Provides generic utilities for converters for certain types. /// - /// the same type parameter that would be passed into - /// - public class CustomObjectConverter : ValueConverter where T : class + /// the type of the that this works on + public static class Converter { - private interface IImpl + private static ValueConverter defaultConverter = null; + /// + /// Gets the default for the current type. + /// + public static ValueConverter Default { - T FromValue(Value value, object parent); - Value ToValue(T obj, object parent); + get + { + if (defaultConverter == null) + defaultConverter = MakeDefault(); + return defaultConverter; + } } - private class Impl : IImpl where U : class, GeneratedStore.IGeneratedStore, T + + private static ValueConverter MakeDefault() { - private static readonly GeneratedStore.GeneratedStoreCreator creator = GeneratedStore.GetCreator(typeof(T)); + var t = typeof(T); - public T FromValue(Value value, object parent) - { // lots of casting here, but it works i promise (parent can be a non-IGeneratedStore, however it won't necessarily behave then) - var obj = creator(parent as GeneratedStore.IGeneratedStore) as U; - obj.Deserialize(value); - return obj; - } + if (t.IsValueType) + { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) + { // this is a Nullable + return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter; + } - public Value ToValue(T obj, object parent) + var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv; + return valConv.Get(); + } + else if (t == typeof(string)) { - if (obj is GeneratedStore.IGeneratedStore store) - return store.Serialize(); - else - return null; // TODO: make this behave sanely instead of just giving null + return new StringConverter() as ValueConverter; + } + else + { + return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter; } } + } - private static readonly IImpl impl = (IImpl)Activator.CreateInstance( - typeof(Impl<>).MakeGenericType(GeneratedStore.GetGeneratedType(typeof(T)))); - + /// + /// A converter for a . + /// + /// the underlying type of the + public class NullableConverter : ValueConverter where T : struct + { + private readonly ValueConverter baseConverter; /// - /// Deserializes into a with the given . + /// Creates a converter with the default converter for the base type. + /// Equivalent to + /// + /// new NullableConverter(Converter<T>.Default) + /// /// - /// the to deserialize - /// the parent object that will own the deserialized value - /// the deserialized value - /// - public static T Deserialize(Value value, object parent) - => impl.FromValue(value, parent); - + /// + /// + public NullableConverter() : this(Converter.Default) { } /// - /// Serializes into a structure, given . + /// Creates a converter with the given underlying . /// - /// the object to serialize - /// the parent object that owns - /// the tree that represents - /// - public static Value Serialize(T obj, object parent) - => impl.ToValue(obj, parent); - + /// the undlerlying to use + public NullableConverter(ValueConverter underlying) + => baseConverter = underlying; /// - /// Deserializes into a with the given . + /// Converts a tree to a value. /// - /// the to deserialize - /// the parent object that will own the deserialized value - /// the deserialized value - /// - public override T FromValue(Value value, object parent) - => impl.FromValue(value, parent); - + /// the tree to convert + /// the object which will own the created object + /// the object represented by + public override T? FromValue(Value value, object parent) + => value == null ? null : new T?(baseConverter.FromValue(value, parent)); /// - /// Serializes into a structure, given . + /// Converts a nullable to a tree. /// - /// the object to serialize - /// the parent object that owns - /// the tree that represents - /// - public override Value ToValue(T obj, object parent) - => impl.ToValue(obj, parent); + /// the value to serialize + /// the object which owns + /// a tree representing . + public override Value ToValue(T? obj, object parent) + => obj == null ? null : baseConverter.ToValue(obj.Value, parent); + } + + internal class StringConverter : ValueConverter + { + public override string FromValue(Value value, object parent) + => (value as Text)?.Value; + + public override Value ToValue(string obj, object parent) + => Value.From(obj); + } + + internal class CharConverter : ValueConverter + { + public override char FromValue(Value value, object parent) + => (value as Text).Value[0]; // can throw nullptr + + public override Value ToValue(char obj, object parent) + => Value.From(char.ToString(obj)); + } + + internal class LongConverter : ValueConverter + { + public override long FromValue(Value value, object parent) + => (value as Integer)?.Value ?? (long)(value as FloatingPoint).Value; + + public override Value ToValue(long obj, object parent) + => Value.From(obj); + } + + internal class ULongConverter : ValueConverter + { + public override ulong FromValue(Value value, object parent) + => (ulong)((value as FloatingPoint)?.Value ?? (value as Integer).Value); + + public override Value ToValue(ulong obj, object parent) + => Value.From(obj); + } + + internal class IntPtrConverter : ValueConverter + { + public override IntPtr FromValue(Value value, object parent) + => (IntPtr)Converter.Default.FromValue(value, parent); + + public override Value ToValue(IntPtr obj, object parent) + => Value.From((long)obj); } + internal class UIntPtrConverter : ValueConverter + { + public override UIntPtr FromValue(Value value, object parent) + => (UIntPtr)Converter.Default.FromValue(value, parent); + + public override Value ToValue(UIntPtr obj, object parent) + => Value.From((decimal)obj); + } + + internal class IntConverter : ValueConverter + { + public override int FromValue(Value value, object parent) + => (int)Converter.Default.FromValue(value, parent); + public override Value ToValue(int obj, object parent) + => Value.From(obj); + } + + internal class UIntConverter : ValueConverter + { + public override uint FromValue(Value value, object parent) + => (uint)Converter.Default.FromValue(value, parent); + public override Value ToValue(uint obj, object parent) + => Value.From(obj); + } + + internal class ShortConverter : ValueConverter + { + public override short FromValue(Value value, object parent) + => (short)Converter.Default.FromValue(value, parent); + public override Value ToValue(short obj, object parent) + => Value.From(obj); + } + + internal class UShortConverter : ValueConverter + { + public override ushort FromValue(Value value, object parent) + => (ushort)Converter.Default.FromValue(value, parent); + public override Value ToValue(ushort obj, object parent) + => Value.From(obj); + } + + internal class ByteConverter : ValueConverter + { + public override byte FromValue(Value value, object parent) + => (byte)Converter.Default.FromValue(value, parent); + public override Value ToValue(byte obj, object parent) + => Value.From(obj); + } + + internal class SByteConverter : ValueConverter + { + public override sbyte FromValue(Value value, object parent) + => (sbyte)Converter.Default.FromValue(value, parent); + public override Value ToValue(sbyte obj, object parent) + => Value.From(obj); + } + + internal class DecimalConverter : ValueConverter + { + public override decimal FromValue(Value value, object parent) + => (value as FloatingPoint)?.Value ?? (value as Integer).Value; + public override Value ToValue(decimal obj, object parent) + => Value.From(obj); + } + + internal class FloatConverter : ValueConverter + { + public override float FromValue(Value value, object parent) + => (float)Converter.Default.FromValue(value, parent); + public override Value ToValue(float obj, object parent) + => Value.From((decimal)obj); + } + + internal class DoubleConverter : ValueConverter + { + public override double FromValue(Value value, object parent) + => (double)Converter.Default.FromValue(value, parent); + public override Value ToValue(double obj, object parent) + => Value.From((decimal)obj); + } } diff --git a/IPA.Loader/Config/Stores/CustomObjectConverter.cs b/IPA.Loader/Config/Stores/CustomObjectConverter.cs new file mode 100644 index 00000000..21402ce9 --- /dev/null +++ b/IPA.Loader/Config/Stores/CustomObjectConverter.cs @@ -0,0 +1,82 @@ +using IPA.Config.Data; +using System; + +namespace IPA.Config.Stores.Converters +{ + /// + /// A for objects normally serialized to config via . + /// + /// the same type parameter that would be passed into + /// + public class CustomObjectConverter : ValueConverter where T : class + { + private interface IImpl + { + T FromValue(Value value, object parent); + Value ToValue(T obj, object parent); + } + private class Impl : IImpl where U : class, GeneratedStore.IGeneratedStore, T + { + private static readonly GeneratedStore.GeneratedStoreCreator creator = GeneratedStore.GetCreator(typeof(T)); + + public T FromValue(Value value, object parent) + { // lots of casting here, but it works i promise (parent can be a non-IGeneratedStore, however it won't necessarily behave then) + var obj = creator(parent as GeneratedStore.IGeneratedStore) as U; + obj.Deserialize(value); + return obj; + } + + public Value ToValue(T obj, object parent) + { + if (obj is GeneratedStore.IGeneratedStore store) + return store.Serialize(); + else + return null; // TODO: make this behave sanely instead of just giving null + } + } + + private static readonly IImpl impl = (IImpl)Activator.CreateInstance( + typeof(Impl<>).MakeGenericType(GeneratedStore.GetGeneratedType(typeof(T)))); + + /// + /// Deserializes into a with the given . + /// + /// the to deserialize + /// the parent object that will own the deserialized value + /// the deserialized value + /// + public static T Deserialize(Value value, object parent) + => impl.FromValue(value, parent); + + /// + /// Serializes into a structure, given . + /// + /// the object to serialize + /// the parent object that owns + /// the tree that represents + /// + public static Value Serialize(T obj, object parent) + => impl.ToValue(obj, parent); + + /// + /// Deserializes into a with the given . + /// + /// the to deserialize + /// the parent object that will own the deserialized value + /// the deserialized value + /// + public override T FromValue(Value value, object parent) + => impl.FromValue(value, parent); + + /// + /// Serializes into a structure, given . + /// + /// the object to serialize + /// the parent object that owns + /// the tree that represents + /// + public override Value ToValue(T obj, object parent) + => impl.ToValue(obj, parent); + } + +} diff --git a/IPA.Loader/Config/Stores/GeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStore.cs index 29c3598c..83903abf 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -276,7 +276,7 @@ namespace IPA.Config.Stores internal delegate IConfigStore GeneratedStoreCreator(IGeneratedStore parent); private static (GeneratedStoreCreator ctor, Type type) MakeCreator(Type type) - { + { // note that this does not and should not use converters by default for everything if (!type.IsClass) throw new ArgumentException("Config type is not a class"); var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor @@ -1071,10 +1071,12 @@ namespace IPA.Config.Stores || valT == typeof(int) || valT == typeof(uint) || valT == typeof(long) - || valT == typeof(ulong)) return typeof(Integer); + || valT == typeof(IntPtr)) return typeof(Integer); if (valT == typeof(float) || valT == typeof(double) - || valT == typeof(decimal)) return typeof(FloatingPoint); + || valT == typeof(decimal) + || valT == typeof(ulong) // ulong gets put into this, because decimal can hold it + || valT == typeof(UIntPtr)) return typeof(FloatingPoint); if (typeof(IEnumerable).IsAssignableFrom(valT)) return typeof(List); // TODO: fill this out the rest of the way diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 0743dc85..5a97c74b 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -96,6 +96,7 @@ +