diff --git a/IPA.Loader/Config/Stores/Converters.cs b/IPA.Loader/Config/Stores/Converters.cs index e56d3308..853fd29f 100644 --- a/IPA.Loader/Config/Stores/Converters.cs +++ b/IPA.Loader/Config/Stores/Converters.cs @@ -34,5 +34,47 @@ namespace IPA.Config.Stores.Converters null; } + 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 + 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)))); + + public static T Deserialize(Value value, object parent) + => impl.FromValue(value, parent); + + public static Value Serialize(T obj, object parent) + => impl.ToValue(obj, parent); + + public override T FromValue(Value value, object parent) + => impl.FromValue(value, parent); + + 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 bcc6426c..29c3598c 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -177,8 +177,7 @@ namespace IPA.Config.Stores } } - private static Dictionary> generatedCreators = new Dictionary>(); - private static Dictionary> memberMaps = new Dictionary>(); + private static readonly Dictionary generatedCreators = new Dictionary(); public static T Create() where T : class => (T)Create(typeof(T)); @@ -190,14 +189,29 @@ namespace IPA.Config.Stores internal static T Create(IGeneratedStore parent) where T : class => (T)Create(typeof(T), parent); private static IConfigStore Create(Type type, IGeneratedStore parent) + => GetCreator(type)(parent); + + internal static GeneratedStoreCreator GetCreator(Type t) + { + if (generatedCreators.TryGetValue(t, out var gen)) + return gen.ctor; + else + { + gen = MakeCreator(t); + generatedCreators.Add(t, gen); + return gen.ctor; + } + } + + internal static Type GetGeneratedType(Type t) { - if (generatedCreators.TryGetValue(type, out var creator)) - return creator(parent); + if (generatedCreators.TryGetValue(t, out var gen)) + return gen.type; else { - creator = MakeCreator(type); - generatedCreators.Add(type, creator); - return creator(parent); + gen = MakeCreator(t); + generatedCreators.Add(t, gen); + return gen.type; } } @@ -259,8 +273,12 @@ namespace IPA.Config.Stores public ConstructorInfo Nullable_Construct => Type.GetConstructor(new[] { NullableWrappedType }); } - private static Func MakeCreator(Type type) + internal delegate IConfigStore GeneratedStoreCreator(IGeneratedStore parent); + + private static (GeneratedStoreCreator ctor, Type type) MakeCreator(Type type) { + if (!type.IsClass) throw new ArgumentException("Config type is not a class"); + var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor if (baseCtor == null) throw new ArgumentException("Config type does not have a public parameterless constructor"); @@ -758,18 +776,11 @@ namespace IPA.Config.Stores var genType = typeBuilder.CreateType(); var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent"); - var creatorDel = Expression.Lambda>( + var creatorDel = Expression.Lambda( Expression.New(ctor, parentParam), parentParam ).Compile(); - { // register a member map - var dict = new Dictionary(); - foreach (var member in structure) - dict.Add(member.Name, member.Type); - memberMaps.Add(type, dict); - } - - return creatorDel; + return (creatorDel, genType); } private delegate LocalBuilder GetLocal(Type type, int idx = 0); diff --git a/IPA.Loader/Config/Stores/ValueConverter.cs b/IPA.Loader/Config/Stores/ValueConverter.cs index aec5dde6..c0f8e0db 100644 --- a/IPA.Loader/Config/Stores/ValueConverter.cs +++ b/IPA.Loader/Config/Stores/ValueConverter.cs @@ -1,9 +1,5 @@ using IPA.Config.Data; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace IPA.Config.Stores { @@ -12,10 +8,10 @@ namespace IPA.Config.Stores /// . /// /// - /// The object returned from , if fed into , - /// should return equivalent structures. Similarly, if the result of - /// is fed into , the resulting object should be equivalent to the one passed to - /// . + /// The object returned from , if fed into , + /// should return equivalent structures. Similarly, if the result of + /// is fed into , the resulting object should be equivalent to the one passed to + /// . /// public interface IValueConverter { @@ -23,14 +19,16 @@ namespace IPA.Config.Stores /// Converts the given object to a . /// /// the object to convert + /// the owning object of /// a representation of as a structure - Value ToValue(object obj); + Value ToValue(object obj, object parent); /// /// Converts the given to the object type handled by this converter. /// /// the to deserialize + /// the object that will own the result /// the deserialized object - object FromValue(Value value); + object FromValue(Value value, object parent); /// /// Gets the type that this handles. /// @@ -48,19 +46,21 @@ namespace IPA.Config.Stores /// Converts the given object to a . /// /// the object to convert + /// the owning object of /// a representation of as a structure - /// - public abstract Value ToValue(T obj); + /// + public abstract Value ToValue(T obj, object parent); /// /// Converts the given to the object type handled by this converter. /// /// the to deserialize + /// the object that will own the result /// the deserialized object - /// - public abstract T FromValue(Value value); + /// + public abstract T FromValue(Value value, object parent); - Value IValueConverter.ToValue(object obj) => ToValue((T)obj); - object IValueConverter.FromValue(Value value) => FromValue(value); + Value IValueConverter.ToValue(object obj, object parent) => ToValue((T)obj, parent); + object IValueConverter.FromValue(Value value, object parent) => FromValue(value, parent); Type IValueConverter.Type => typeof(T); } }