using IPA.Config.Data; using IPA.Config.Stores.Attributes; 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() => null; // default to null } 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); 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 return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter; } var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv; return valConv.Get(); } else if (t == typeof(string)) { return new StringConverter() as ValueConverter; } else { 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); } }