From edafdca9980c38c3dd9304889dedf07a40cfadbe Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Wed, 1 Apr 2020 23:35:43 -0500 Subject: [PATCH] Added support for a zero-argument UseConverterAttribute that uses the default converter for the type --- IPA.Loader/Config/Stores/Attributes.cs | 15 +- IPA.Loader/Config/Stores/Converters.cs | 462 +++++++++--------- .../GeneratedStoreImpl/ObjectStructure.cs | 4 + 3 files changed, 253 insertions(+), 228 deletions(-) diff --git a/IPA.Loader/Config/Stores/Attributes.cs b/IPA.Loader/Config/Stores/Attributes.cs index 593d1b52..574dfd80 100644 --- a/IPA.Loader/Config/Stores/Attributes.cs +++ b/IPA.Loader/Config/Stores/Attributes.cs @@ -1,4 +1,5 @@ -using System; +using IPA.Config.Stores.Converters; +using System; using System.ComponentModel; using System.Linq; @@ -33,6 +34,11 @@ namespace IPA.Config.Stores.Attributes [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class UseConverterAttribute : Attribute { + /// + /// Gets whether or not to use the default converter for the member type instead of the specified type. + /// + public bool UseDefaultConverterForType { get; } + /// /// Gets the type of the converter to use. /// @@ -49,12 +55,19 @@ namespace IPA.Config.Stores.Attributes /// public bool IsGenericConverter => ConverterTargetType != null; + /// + /// Creates a new specifying to use the default converter type for the target member. + /// + public UseConverterAttribute() + => UseDefaultConverterForType = true; + /// /// Creates a new with a given . /// /// the type to assign to public UseConverterAttribute(Type converterType) { + UseDefaultConverterForType = false; ConverterType = converterType; var baseT = ConverterType.BaseType; diff --git a/IPA.Loader/Config/Stores/Converters.cs b/IPA.Loader/Config/Stores/Converters.cs index 6c425288..ba8b6c10 100644 --- a/IPA.Loader/Config/Stores/Converters.cs +++ b/IPA.Loader/Config/Stores/Converters.cs @@ -13,7 +13,7 @@ namespace IPA.Config.Stores.Converters /// /// Provides utility functions for custom converters. /// - public static class Converter + public static class Converter { /// /// Gets the integral value of a , coercing a if necessary, @@ -36,16 +36,74 @@ namespace IPA.Config.Stores.Converters val is Integer inte ? inte.AsFloat()?.Value : null; + internal static Type GetDefaultConverterType(Type t) + { + if (t.IsEnum) + { + return typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t); + } + if (t.IsGenericType) + { + var generic = t.GetGenericTypeDefinition(); + var args = t.GetGenericArguments(); + if (generic == typeof(List<>)) + return (typeof(ListConverter<>).MakeGenericType(args)); + else if (generic == typeof(IList<>)) + return (typeof(IListConverter<>).MakeGenericType(args)); + else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string)) + return (typeof(DictionaryConverter<>).MakeGenericType(args[1])); + else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string)) + return (typeof(IDictionaryConverter<>).MakeGenericType(args[1])); +#if NET4 + else if (generic == typeof(ISet<>)) + return (typeof(ISetConverter<>).MakeGenericType(args)); + else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string)) + return (typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1])); +#endif + } + var iCollBase = t.GetInterfaces() + .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); + if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null) + { // if it implements ICollection and has a default constructor + var valueType = iCollBase.GetGenericArguments().First(); + return (typeof(CollectionConverter<,>).MakeGenericType(valueType, t)); + } + if (t == typeof(string)) + { + //Logger.log.Debug($"gives StringConverter"); + return typeof(StringConverter); + } + 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 + //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>"); + return (typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))); + } + + //Logger.log.Debug($"gives converter for value type {t}"); + var valConv = Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t)) as IValConv; + return valConv.Get(); + } + + //Logger.log.Debug($"gives CustomObjectConverter<{t}>"); + return (typeof(CustomObjectConverter<>).MakeGenericType(t)); + } + + internal interface IValConv + { + Type Get(); + } internal interface IValConv { - ValueConverter Get(); + Type Get(); } - internal class ValConv : IValConv where T : struct + internal class ValConv : IValConv, IValConv where T : struct { private static readonly IValConv Impl = ValConvImpls.Impl as IValConv ?? new ValConv(); - public ValueConverter Get() => Impl.Get(); - ValueConverter IValConv.Get() - => new CustomValueTypeConverter(); + public Type Get() => Impl.Get(); + Type IValConv.Get() + => typeof(CustomValueTypeConverter); } private class ValConvImpls : IValConv, IValConv, IValConv, @@ -57,21 +115,21 @@ namespace IPA.Config.Stores.Converters 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(); - ValueConverter IValConv.Get() => new BooleanConverter(); + Type IValConv.Get() => typeof(CharConverter); + Type IValConv.Get() => typeof(LongConverter); + Type IValConv.Get() => typeof(ULongConverter); + Type IValConv.Get() => typeof(IntPtrConverter); + Type IValConv.Get() => typeof(UIntPtrConverter); + Type IValConv.Get() => typeof(IntConverter); + Type IValConv.Get() => typeof(UIntConverter); + Type IValConv.Get() => typeof(ShortConverter); + Type IValConv.Get() => typeof(UShortConverter); + Type IValConv.Get() => typeof(ByteConverter); + Type IValConv.Get() => typeof(SByteConverter); + Type IValConv.Get() => typeof(FloatConverter); + Type IValConv.Get() => typeof(DoubleConverter); + Type IValConv.Get() => typeof(DecimalConverter); + Type IValConv.Get() => typeof(BooleanConverter); } } @@ -88,7 +146,7 @@ namespace IPA.Config.Stores.Converters public static ValueConverter Default => defaultConverter ??= MakeDefault(); - internal static ValueConverter MakeDefault(bool allowValuesAndCustoms = true) + internal static ValueConverter MakeDefault() { var t = typeof(T); //Logger.log.Debug($"Converter<{t}>.MakeDefault()"); @@ -96,57 +154,7 @@ namespace IPA.Config.Stores.Converters static ValueConverter MakeInstOf(Type ty) => Activator.CreateInstance(ty) as ValueConverter; - if (t.IsEnum) - { - return MakeInstOf(typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t)); - } - if (t == typeof(string)) - { - //Logger.log.Debug($"gives StringConverter"); - return new StringConverter() as ValueConverter; - } - if (t.IsGenericType) - { - var generic = t.GetGenericTypeDefinition(); - var args = t.GetGenericArguments(); - if (generic == typeof(List<>)) - return MakeInstOf(typeof(ListConverter<>).MakeGenericType(args)); - else if (generic == typeof(IList<>)) - return MakeInstOf(typeof(IListConverter<>).MakeGenericType(args)); - else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(DictionaryConverter<>).MakeGenericType(args[1])); - else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(IDictionaryConverter<>).MakeGenericType(args[1])); -#if NET4 - else if (generic == typeof(ISet<>)) - return MakeInstOf(typeof(ISetConverter<>).MakeGenericType(args)); - else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1])); -#endif - } - var iCollBase = t.GetInterfaces() - .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); - if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null) - { // if it implements ICollection and has a default constructor - var valueType = iCollBase.GetGenericArguments().First(); - return MakeInstOf(typeof(CollectionConverter<,>).MakeGenericType(valueType, t)); - } - if (!allowValuesAndCustoms) return null; - 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 - //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>"); - return MakeInstOf(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))); - } - - //Logger.log.Debug($"gives converter for value type {t}"); - var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv; - return valConv.Get(); - } - - //Logger.log.Debug($"gives CustomObjectConverter<{t}>"); - return MakeInstOf(typeof(CustomObjectConverter<>).MakeGenericType(t)); + return MakeInstOf(Converter.GetDefaultConverterType(t)); } } @@ -198,8 +206,8 @@ namespace IPA.Config.Stores.Converters /// the underlying type of the /// the type to use as an underlying converter /// - public sealed class NullableConverter : NullableConverter - where T : struct + public sealed class NullableConverter : NullableConverter + where T : struct where TConverter : ValueConverter, new() { /// @@ -283,7 +291,7 @@ namespace IPA.Config.Stores.Converters /// the deserialized enum value /// if is not a numeric node public override T FromValue(Value value, object parent) - => (T)Enum.ToObject(typeof(T), Converter.IntValue(value) + => (T)Enum.ToObject(typeof(T), Converter.IntValue(value) ?? throw new ArgumentException("Value not a numeric node", nameof(value))); /// @@ -296,184 +304,184 @@ namespace IPA.Config.Stores.Converters => Value.Integer(Convert.ToInt64(obj)); } - /// - /// A converter for instances of . - /// + /// + /// A converter for instances of . + /// /// the value type of the dictionary - public class IDictionaryConverter : ValueConverter> + public class IDictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. - /// - protected ValueConverter BaseConverter { get; } - - /// - /// Constructs an using the default converter for the value type. - /// - public IDictionaryConverter() : this(Converter.Default) { } - /// - /// Constructs an using the specified converter for the value. - /// - /// the converter for the value - public IDictionaryConverter(ValueConverter converter) - => BaseConverter = converter; - - /// - /// Converts a to an that is represented by it. - /// - /// the to convert - /// the parent that will own the resulting object - /// the deserialized dictionary - public override IDictionary FromValue(Value value, object parent) - => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) - ?.ToDictionary(p => p.Key, p => p.val) - ?? throw new ArgumentException("Value not a map", nameof(value)); - - /// - /// Serializes an into a containing its values. - /// - /// the dictionary to serialize - /// the object that owns the dictionary - /// the dictionary serialized as a - public override Value ToValue(IDictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + /// + protected ValueConverter BaseConverter { get; } + + /// + /// Constructs an using the default converter for the value type. + /// + public IDictionaryConverter() : this(Converter.Default) { } + /// + /// Constructs an using the specified converter for the value. + /// + /// the converter for the value + public IDictionaryConverter(ValueConverter converter) + => BaseConverter = converter; + + /// + /// Converts a to an that is represented by it. + /// + /// the to convert + /// the parent that will own the resulting object + /// the deserialized dictionary + public override IDictionary FromValue(Value value, object parent) + => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) + ?.ToDictionary(p => p.Key, p => p.val) + ?? throw new ArgumentException("Value not a map", nameof(value)); + + /// + /// Serializes an into a containing its values. + /// + /// the dictionary to serialize + /// the object that owns the dictionary + /// the dictionary serialized as a + public override Value ToValue(IDictionary obj, object parent) + => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } - /// - /// A converter for instances of , specifying a value converter as a type parameter. - /// - /// the value type of the dictionary + /// + /// A converter for instances of , specifying a value converter as a type parameter. + /// + /// the value type of the dictionary /// the converter type for values public sealed class IDictionaryConverter : IDictionaryConverter - where TConverter : ValueConverter, new() - { - /// - /// Constructs a new with a new instance of - /// as the value converter. - /// + where TConverter : ValueConverter, new() + { + /// + /// Constructs a new with a new instance of + /// as the value converter. + /// public IDictionaryConverter() : base(new TConverter()) { } - } - - - /// - /// A converter for instances of . - /// + } + + + /// + /// A converter for instances of . + /// /// the value type of the dictionary - public class DictionaryConverter : ValueConverter> + public class DictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. - /// - protected ValueConverter BaseConverter { get; } - - /// - /// Constructs an using the default converter for the value type. - /// - public DictionaryConverter() : this(Converter.Default) { } - /// - /// Constructs an using the specified converter for the value. - /// - /// the converter for the value - public DictionaryConverter(ValueConverter converter) - => BaseConverter = converter; - - /// - /// Converts a to a that is represented by it. - /// - /// the to convert - /// the parent that will own the resulting object - /// the deserialized dictionary - public override Dictionary FromValue(Value value, object parent) - => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) - ?.ToDictionary(p => p.Key, p => p.val) - ?? throw new ArgumentException("Value not a map", nameof(value)); - - /// - /// Serializes a into a containing its values. - /// - /// the dictionary to serialize - /// the object that owns the dictionary - /// the dictionary serialized as a - public override Value ToValue(Dictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + /// + protected ValueConverter BaseConverter { get; } + + /// + /// Constructs an using the default converter for the value type. + /// + public DictionaryConverter() : this(Converter.Default) { } + /// + /// Constructs an using the specified converter for the value. + /// + /// the converter for the value + public DictionaryConverter(ValueConverter converter) + => BaseConverter = converter; + + /// + /// Converts a to a that is represented by it. + /// + /// the to convert + /// the parent that will own the resulting object + /// the deserialized dictionary + public override Dictionary FromValue(Value value, object parent) + => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) + ?.ToDictionary(p => p.Key, p => p.val) + ?? throw new ArgumentException("Value not a map", nameof(value)); + + /// + /// Serializes a into a containing its values. + /// + /// the dictionary to serialize + /// the object that owns the dictionary + /// the dictionary serialized as a + public override Value ToValue(Dictionary obj, object parent) + => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } - /// - /// A converter for instances of , specifying a value converter as a type parameter. - /// - /// the value type of the dictionary + /// + /// A converter for instances of , specifying a value converter as a type parameter. + /// + /// the value type of the dictionary /// the converter type for values public sealed class DictionaryConverter : DictionaryConverter - where TConverter : ValueConverter, new() - { - /// - /// Constructs a new with a new instance of - /// as the value converter. - /// + where TConverter : ValueConverter, new() + { + /// + /// Constructs a new with a new instance of + /// as the value converter. + /// public DictionaryConverter() : base(new TConverter()) { } - } - + } + #if NET4 - /// - /// A converter for instances of . - /// + /// + /// A converter for instances of . + /// /// the value type of the dictionary - public class IReadOnlyDictionaryConverter : ValueConverter> + public class IReadOnlyDictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. - /// - protected ValueConverter BaseConverter { get; } - - /// - /// Constructs an using the default converter for the value type. - /// - public IReadOnlyDictionaryConverter() : this(Converter.Default) { } - /// - /// Constructs an using the specified converter for the value. - /// - /// the converter for the value - public IReadOnlyDictionaryConverter(ValueConverter converter) - => BaseConverter = converter; - - /// - /// Converts a to an that is represented by it. - /// - /// the to convert - /// the parent that will own the resulting object - /// the deserialized dictionary - public override IReadOnlyDictionary FromValue(Value value, object parent) - => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) - ?.ToDictionary(p => p.Key, p => p.val) - ?? throw new ArgumentException("Value not a map", nameof(value)); - - /// - /// Serializes an into a containing its values. - /// - /// the dictionary to serialize - /// the object that owns the dictionary - /// the dictionary serialized as a - public override Value ToValue(IReadOnlyDictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + /// + protected ValueConverter BaseConverter { get; } + + /// + /// Constructs an using the default converter for the value type. + /// + public IReadOnlyDictionaryConverter() : this(Converter.Default) { } + /// + /// Constructs an using the specified converter for the value. + /// + /// the converter for the value + public IReadOnlyDictionaryConverter(ValueConverter converter) + => BaseConverter = converter; + + /// + /// Converts a to an that is represented by it. + /// + /// the to convert + /// the parent that will own the resulting object + /// the deserialized dictionary + public override IReadOnlyDictionary FromValue(Value value, object parent) + => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) + ?.ToDictionary(p => p.Key, p => p.val) + ?? throw new ArgumentException("Value not a map", nameof(value)); + + /// + /// Serializes an into a containing its values. + /// + /// the dictionary to serialize + /// the object that owns the dictionary + /// the dictionary serialized as a + public override Value ToValue(IReadOnlyDictionary obj, object parent) + => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } - /// - /// A converter for instances of , specifying a value converter as a type parameter. - /// - /// the value type of the dictionary + /// + /// A converter for instances of , specifying a value converter as a type parameter. + /// + /// the value type of the dictionary /// the converter type for values public sealed class IReadOnlyDictionaryConverter : IReadOnlyDictionaryConverter - where TConverter : ValueConverter, new() - { - /// - /// Constructs a new with a new instance of - /// as the value converter. - /// + where TConverter : ValueConverter, new() + { + /// + /// Constructs a new with a new instance of + /// as the value converter. + /// public IReadOnlyDictionaryConverter() : base(new TConverter()) { } - } + } #endif - + internal class StringConverter : ValueConverter { public override string FromValue(Value value, object parent) @@ -486,7 +494,7 @@ namespace IPA.Config.Stores.Converters internal class CharConverter : ValueConverter { public override char FromValue(Value value, object parent) - => (value as Text)?.Value[0] + => (value as Text)?.Value[0] ?? throw new ArgumentException("Value not a text node", nameof(value)); // can throw nullptr public override Value ToValue(char obj, object parent) @@ -496,7 +504,7 @@ namespace IPA.Config.Stores.Converters internal class LongConverter : ValueConverter { public override long FromValue(Value value, object parent) - => Converter.IntValue(value) + => Converter.IntValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value)); public override Value ToValue(long obj, object parent) @@ -506,7 +514,7 @@ namespace IPA.Config.Stores.Converters internal class ULongConverter : ValueConverter { public override ulong FromValue(Value value, object parent) - => (ulong)(Converter.FloatValue(value) + => (ulong)(Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value))); public override Value ToValue(ulong obj, object parent) diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs index 9467ee7c..963dd712 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs @@ -1,4 +1,5 @@ using IPA.Config.Stores.Attributes; +using IPA.Config.Stores.Converters; using IPA.Logging; using IPA.Utilities; using System; @@ -69,6 +70,9 @@ namespace IPA.Config.Stores var converterAttr = attrs.Select(o => o as UseConverterAttribute).NonNull().FirstOrDefault(); if (converterAttr != null) { + if (converterAttr.UseDefaultConverterForType) + converterAttr = new UseConverterAttribute(Converter.GetDefaultConverterType(member.Type)); + member.Converter = converterAttr.ConverterType; member.IsGenericConverter = converterAttr.IsGenericConverter;