using IPA.Config.Data;
using IPA.Config.Stores.Attributes;
using IPA.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Boolean = IPA.Config.Data.Boolean;
namespace IPA.Config.Stores.Converters
{
///
/// Provides utility functions for custom converters.
///
public static class Converter
{
///
/// Gets the integral value of a , coercing a if necessary,
/// or if is not an or .
///
/// the to get the integral value of
/// the integral value of , or
public static long? IntValue(Value val)
=> val is Integer inte ? inte.Value :
val is FloatingPoint fp ? fp.AsInteger()?.Value :
null;
///
/// Gets the floaing point value of a , coercing an if necessary,
/// or if is not an or .
///
/// the to get the floaing point value of
/// the floaing point value of , or
public static decimal? FloatValue(Value val)
=> 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()
=> new CustomValueTypeConverter();
}
private class ValConvImpls : IValConv,
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();
ValueConverter IValConv.Get() => new BooleanConverter();
}
}
///
/// Provides generic utilities for converters for certain types.
///
/// the type of the that this works on
public static class Converter
{
private static ValueConverter defaultConverter = null;
///
/// Gets the default for the current type.
///
public static ValueConverter Default
=> defaultConverter ??= MakeDefault();
private static ValueConverter MakeDefault()
{
var t = typeof(T);
//Logger.log.Debug($"Converter<{t}>.MakeDefault()");
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 Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter;
}
//Logger.log.Debug($"gives converter for value type {t}");
var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv;
return valConv.Get();
}
else if (t == typeof(string))
{
//Logger.log.Debug($"gives StringConverter");
return new StringConverter() as ValueConverter;
}
else
{
//Logger.log.Debug($"gives CustomObjectConverter<{t}>");
return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter;
}
}
}
///
/// A converter for a .
///
/// the underlying type of the
public class NullableConverter : ValueConverter where T : struct
{
private readonly ValueConverter baseConverter;
///
/// Creates a converter with the default converter for the base type.
/// Equivalent to
///
/// new NullableConverter(Converter<T>.Default)
///
///
///
///
public NullableConverter() : this(Converter.Default) { }
///
/// Creates a converter with the given underlying .
///
/// the undlerlying to use
public NullableConverter(ValueConverter underlying)
=> baseConverter = underlying;
///
/// Converts a tree to a value.
///
/// 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));
///
/// Converts a nullable to a tree.
///
/// 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);
}
///
/// A converter for a that default-constructs a converter of type
/// to use as the underlying converter. Use this in the .
///
/// the underlying type of the
/// the type to use as an underlying converter
///
public sealed class NullableConverter : NullableConverter
where T : struct
where TConverter : ValueConverter, new()
{
///
/// Creates a converter with a new as the underlying converter.
///
///
public NullableConverter() : base(new TConverter()) { }
}
///
/// A converter for an enum of type , that converts the enum to its string representation and back.
///
/// the enum type
public sealed class EnumConverter : ValueConverter
where T : Enum
{
///
/// Converts a that is a node to the corresponding enum value.
///
/// the to convert
/// the object which will own the created object
/// the deserialized enum value
/// if is not a node
public override T FromValue(Value value, object parent)
=> value is Text t
? (T)Enum.Parse(typeof(T), t.Value)
: throw new ArgumentException("Value not a string", nameof(value));
///
/// Converts an enum of type to a node corresponding to its value.
///
/// the value to serialize
/// the object which owns
/// a node representing
public override Value ToValue(T obj, object parent)
=> Value.Text(obj.ToString());
}
///
/// A converter for an enum of type , that converts the enum to its string representation and back,
/// ignoring the case of the serialized value for deseiralization.
///
/// the enum type
public sealed class CaseInsensitiveEnumConverter : ValueConverter
where T : Enum
{
///
/// Converts a that is a node to the corresponding enum value.
///
/// the to convert
/// the object which will own the created object
/// the deserialized enum value
/// if is not a node
public override T FromValue(Value value, object parent)
=> value is Text t
? (T)Enum.Parse(typeof(T), t.Value, true)
: throw new ArgumentException("Value not a string", nameof(value));
///
/// Converts an enum of type to a node corresponding to its value.
///
/// the value to serialize
/// the object which owns
/// a node representing
public override Value ToValue(T obj, object parent)
=> Value.Text(obj.ToString());
}
///
/// A converter for an enum of type , that converts the enum to its underlying value for serialization.
///
/// the enum type
public sealed class NumericEnumConverter : ValueConverter
where T : Enum
{
///
/// Converts a that is a numeric node to the corresponding enum value.
///
/// the to convert
/// the object which will own the created object
/// 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)
?? throw new ArgumentException("Value not a numeric node", nameof(value)));
///
/// Converts an enum of type to a node corresponding to its value.
///
/// the value to serialize
/// the object which owns
/// an node representing
public override Value ToValue(T obj, object parent)
=> Value.Integer(Convert.ToInt64(obj));
}
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]
?? throw new ArgumentException("Value not a text node", nameof(value)); // 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)
=> Converter.IntValue(value)
?? throw new ArgumentException("Value not a numeric value", nameof(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)(Converter.FloatValue(value)
?? throw new ArgumentException("Value not a numeric value", nameof(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)
=> Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(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);
}
internal class BooleanConverter : ValueConverter
{
public override bool FromValue(Value value, object parent)
=> (value as Boolean)?.Value ?? throw new ArgumentException("Value not a Boolean", nameof(value));
public override Value ToValue(bool obj, object parent)
=> Value.From(obj);
}
}