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;