Browse Source

Added support for a zero-argument UseConverterAttribute that uses the default converter for the type

pull/46/head
Anairkoen Schno 4 years ago
parent
commit
e1d6686f13
3 changed files with 253 additions and 228 deletions
  1. +14
    -1
      IPA.Loader/Config/Stores/Attributes.cs
  2. +235
    -227
      IPA.Loader/Config/Stores/Converters.cs
  3. +4
    -0
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs

+ 14
- 1
IPA.Loader/Config/Stores/Attributes.cs View File

@ -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
{
/// <summary>
/// Gets whether or not to use the default converter for the member type instead of the specified type.
/// </summary>
public bool UseDefaultConverterForType { get; }
/// <summary>
/// Gets the type of the converter to use.
/// </summary>
@ -49,12 +55,19 @@ namespace IPA.Config.Stores.Attributes
/// </summary>
public bool IsGenericConverter => ConverterTargetType != null;
/// <summary>
/// Creates a new <see cref="UseConverterAttribute"/> specifying to use the default converter type for the target member.
/// </summary>
public UseConverterAttribute()
=> UseDefaultConverterForType = true;
/// <summary>
/// Creates a new <see cref="UseConverterAttribute"/> with a given <see cref="ConverterType"/>.
/// </summary>
/// <param name="converterType">the type to assign to <see cref="ConverterType"/></param>
public UseConverterAttribute(Type converterType)
{
UseDefaultConverterForType = false;
ConverterType = converterType;
var baseT = ConverterType.BaseType;


+ 235
- 227
IPA.Loader/Config/Stores/Converters.cs View File

@ -13,7 +13,7 @@ namespace IPA.Config.Stores.Converters
/// <summary>
/// Provides utility functions for custom converters.
/// </summary>
public static class Converter
public static class Converter
{
/// <summary>
/// Gets the integral value of a <see cref="Value"/>, coercing a <see cref="FloatingPoint"/> 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<T>
{
ValueConverter<T> Get();
Type Get();
}
internal class ValConv<T> : IValConv<T> where T : struct
internal class ValConv<T> : IValConv, IValConv<T> where T : struct
{
private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
public ValueConverter<T> Get() => Impl.Get();
ValueConverter<T> IValConv<T>.Get()
=> new CustomValueTypeConverter<T>();
public Type Get() => Impl.Get();
Type IValConv<T>.Get()
=> typeof(CustomValueTypeConverter<T>);
}
private class ValConvImpls : IValConv<char>,
IValConv<IntPtr>, IValConv<UIntPtr>,
@ -57,21 +115,21 @@ namespace IPA.Config.Stores.Converters
IValConv<decimal>, IValConv<bool>
{
internal static readonly ValConvImpls Impl = new ValConvImpls();
ValueConverter<char> IValConv<char>.Get() => new CharConverter();
ValueConverter<long> IValConv<long>.Get() => new LongConverter();
ValueConverter<ulong> IValConv<ulong>.Get() => new ULongConverter();
ValueConverter<IntPtr> IValConv<IntPtr>.Get() => new IntPtrConverter();
ValueConverter<UIntPtr> IValConv<UIntPtr>.Get() => new UIntPtrConverter();
ValueConverter<int> IValConv<int>.Get() => new IntConverter();
ValueConverter<uint> IValConv<uint>.Get() => new UIntConverter();
ValueConverter<short> IValConv<short>.Get() => new ShortConverter();
ValueConverter<ushort> IValConv<ushort>.Get() => new UShortConverter();
ValueConverter<byte> IValConv<byte>.Get() => new ByteConverter();
ValueConverter<sbyte> IValConv<sbyte>.Get() => new SByteConverter();
ValueConverter<float> IValConv<float>.Get() => new FloatConverter();
ValueConverter<double> IValConv<double>.Get() => new DoubleConverter();
ValueConverter<decimal> IValConv<decimal>.Get() => new DecimalConverter();
ValueConverter<bool> IValConv<bool>.Get() => new BooleanConverter();
Type IValConv<char>.Get() => typeof(CharConverter);
Type IValConv<long>.Get() => typeof(LongConverter);
Type IValConv<ulong>.Get() => typeof(ULongConverter);
Type IValConv<IntPtr>.Get() => typeof(IntPtrConverter);
Type IValConv<UIntPtr>.Get() => typeof(UIntPtrConverter);
Type IValConv<int>.Get() => typeof(IntConverter);
Type IValConv<uint>.Get() => typeof(UIntConverter);
Type IValConv<short>.Get() => typeof(ShortConverter);
Type IValConv<ushort>.Get() => typeof(UShortConverter);
Type IValConv<byte>.Get() => typeof(ByteConverter);
Type IValConv<sbyte>.Get() => typeof(SByteConverter);
Type IValConv<float>.Get() => typeof(FloatConverter);
Type IValConv<double>.Get() => typeof(DoubleConverter);
Type IValConv<decimal>.Get() => typeof(DecimalConverter);
Type IValConv<bool>.Get() => typeof(BooleanConverter);
}
}
@ -88,7 +146,7 @@ namespace IPA.Config.Stores.Converters
public static ValueConverter<T> Default
=> defaultConverter ??= MakeDefault();
internal static ValueConverter<T> MakeDefault(bool allowValuesAndCustoms = true)
internal static ValueConverter<T> MakeDefault()
{
var t = typeof(T);
//Logger.log.Debug($"Converter<{t}>.MakeDefault()");
@ -96,57 +154,7 @@ namespace IPA.Config.Stores.Converters
static ValueConverter<T> MakeInstOf(Type ty)
=> Activator.CreateInstance(ty) as ValueConverter<T>;
if (t.IsEnum)
{
return MakeInstOf(typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t));
}
if (t == typeof(string))
{
//Logger.log.Debug($"gives StringConverter");
return new StringConverter() as ValueConverter<T>;
}
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<T>;
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
/// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
/// <typeparam name="TConverter">the type to use as an underlying converter</typeparam>
/// <seealso cref="NullableConverter{T}"/>
public sealed class NullableConverter<T, TConverter> : NullableConverter<T>
where T : struct
public sealed class NullableConverter<T, TConverter> : NullableConverter<T>
where T : struct
where TConverter : ValueConverter<T>, new()
{
/// <summary>
@ -283,7 +291,7 @@ namespace IPA.Config.Stores.Converters
/// <returns>the deserialized enum value</returns>
/// <exception cref="ArgumentException">if <paramref name="value"/> is not a numeric node</exception>
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)));
/// <summary>
@ -296,184 +304,184 @@ namespace IPA.Config.Stores.Converters
=> Value.Integer(Convert.ToInt64(obj));
}
/// <summary>
/// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>.
/// </summary>
/// <summary>
/// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
public class IDictionaryConverter<TValue> : ValueConverter<IDictionary<string, TValue>>
public class IDictionaryConverter<TValue> : ValueConverter<IDictionary<string, TValue>>
{
/// <summary>
/// Gets the converter for the dictionary's value type.
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public IDictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public IDictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override IDictionary<string, TValue> 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));
/// <summary>
/// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(IDictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public IDictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public IDictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override IDictionary<string, TValue> 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));
/// <summary>
/// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(IDictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
}
/// <summary>
/// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <summary>
/// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <typeparam name="TConverter">the converter type for values</typeparam>
public sealed class IDictionaryConverter<TValue, TConverter> : IDictionaryConverter<TValue>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
public IDictionaryConverter() : base(new TConverter()) { }
}
/// <summary>
/// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
}
/// <summary>
/// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
public class DictionaryConverter<TValue> : ValueConverter<Dictionary<string, TValue>>
public class DictionaryConverter<TValue> : ValueConverter<Dictionary<string, TValue>>
{
/// <summary>
/// Gets the converter for the dictionary's value type.
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public DictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public DictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to a <see cref="Dictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override Dictionary<string, TValue> 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));
/// <summary>
/// Serializes a <see cref="Dictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(Dictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public DictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public DictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to a <see cref="Dictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override Dictionary<string, TValue> 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));
/// <summary>
/// Serializes a <see cref="Dictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(Dictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
}
/// <summary>
/// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <summary>
/// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <typeparam name="TConverter">the converter type for values</typeparam>
public sealed class DictionaryConverter<TValue, TConverter> : DictionaryConverter<TValue>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
public DictionaryConverter() : base(new TConverter()) { }
}
}
#if NET4
/// <summary>
/// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>.
/// </summary>
/// <summary>
/// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
public class IReadOnlyDictionaryConverter<TValue> : ValueConverter<IReadOnlyDictionary<string, TValue>>
public class IReadOnlyDictionaryConverter<TValue> : ValueConverter<IReadOnlyDictionary<string, TValue>>
{
/// <summary>
/// Gets the converter for the dictionary's value type.
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public IReadOnlyDictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public IReadOnlyDictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override IReadOnlyDictionary<string, TValue> 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));
/// <summary>
/// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(IReadOnlyDictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
/// </summary>
protected ValueConverter<TValue> BaseConverter { get; }
/// <summary>
/// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the default converter for the value type.
/// </summary>
public IReadOnlyDictionaryConverter() : this(Converter<TValue>.Default) { }
/// <summary>
/// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the specified converter for the value.
/// </summary>
/// <param name="converter">the converter for the value</param>
public IReadOnlyDictionaryConverter(ValueConverter<TValue> converter)
=> BaseConverter = converter;
/// <summary>
/// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
/// </summary>
/// <param name="value">the <see cref="Map"/> to convert</param>
/// <param name="parent">the parent that will own the resulting object</param>
/// <returns>the deserialized dictionary</returns>
public override IReadOnlyDictionary<string, TValue> 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));
/// <summary>
/// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
/// </summary>
/// <param name="obj">the dictionary to serialize</param>
/// <param name="parent">the object that owns the dictionary</param>
/// <returns>the dictionary serialized as a <see cref="Map"/></returns>
public override Value ToValue(IReadOnlyDictionary<string, TValue> obj, object parent)
=> Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
}
/// <summary>
/// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <summary>
/// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
/// </summary>
/// <typeparam name="TValue">the value type of the dictionary</typeparam>
/// <typeparam name="TConverter">the converter type for values</typeparam>
public sealed class IReadOnlyDictionaryConverter<TValue, TConverter> : IReadOnlyDictionaryConverter<TValue>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IReadOnlyDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
where TConverter : ValueConverter<TValue>, new()
{
/// <summary>
/// Constructs a new <see cref="IReadOnlyDictionaryConverter{TValue, TConverter}"/> with a new instance of
/// <typeparamref name="TConverter"/> as the value converter.
/// </summary>
public IReadOnlyDictionaryConverter() : base(new TConverter()) { }
}
}
#endif
internal class StringConverter : ValueConverter<string>
{
public override string FromValue(Value value, object parent)
@ -486,7 +494,7 @@ namespace IPA.Config.Stores.Converters
internal class CharConverter : ValueConverter<char>
{
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<long>
{
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<ulong>
{
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)


+ 4
- 0
IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs View File

@ -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;


Loading…
Cancel
Save