You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
12 KiB

  1. using IPA.Config.Data;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace IPA.Config.Stores.Converters
  8. {
  9. /// <summary>
  10. /// Provides utility functions for custom converters.
  11. /// </summary>
  12. public static class Converter
  13. {
  14. /// <summary>
  15. /// Gets the integral value of a <see cref="Value"/>, coercing a <see cref="FloatingPoint"/> if necessary,
  16. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  17. /// </summary>
  18. /// <param name="val">the <see cref="Value"/> to get the integral value of</param>
  19. /// <returns>the integral value of <paramref name="val"/>, or <see langword="null"/></returns>
  20. public static long? IntValue(Value val)
  21. => val is Integer inte ? inte.Value :
  22. val is FloatingPoint fp ? fp.AsInteger()?.Value :
  23. null;
  24. /// <summary>
  25. /// Gets the floaing point value of a <see cref="Value"/>, coercing an <see cref="Integer"/> if necessary,
  26. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  27. /// </summary>
  28. /// <param name="val">the <see cref="Value"/> to get the floaing point value of</param>
  29. /// <returns>the floaing point value of <paramref name="val"/>, or <see langword="null"/></returns>
  30. public static decimal? FloatValue(Value val)
  31. => val is FloatingPoint fp ? fp.Value :
  32. val is Integer inte ? inte.AsFloat()?.Value :
  33. null;
  34. internal interface IValConv<T>
  35. {
  36. ValueConverter<T> Get();
  37. }
  38. internal class ValConv<T> : IValConv<T> where T : struct
  39. {
  40. private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
  41. public ValueConverter<T> Get() => Impl.Get();
  42. ValueConverter<T> IValConv<T>.Get()
  43. => null; // default to null
  44. }
  45. private class ValConvImpls : IValConv<char>,
  46. IValConv<IntPtr>, IValConv<UIntPtr>,
  47. IValConv<long>, IValConv<ulong>,
  48. IValConv<int>, IValConv<uint>,
  49. IValConv<short>, IValConv<ushort>,
  50. IValConv<sbyte>, IValConv<byte>,
  51. IValConv<float>, IValConv<double>,
  52. IValConv<decimal>
  53. {
  54. internal static readonly ValConvImpls Impl = new ValConvImpls();
  55. ValueConverter<char> IValConv<char>.Get()
  56. => new CharConverter();
  57. ValueConverter<long> IValConv<long>.Get()
  58. => new LongConverter();
  59. ValueConverter<ulong> IValConv<ulong>.Get()
  60. => new ULongConverter();
  61. ValueConverter<IntPtr> IValConv<IntPtr>.Get()
  62. => new IntPtrConverter();
  63. ValueConverter<UIntPtr> IValConv<UIntPtr>.Get()
  64. => new UIntPtrConverter();
  65. ValueConverter<int> IValConv<int>.Get()
  66. => new IntConverter();
  67. ValueConverter<uint> IValConv<uint>.Get()
  68. => new UIntConverter();
  69. ValueConverter<short> IValConv<short>.Get()
  70. => new ShortConverter();
  71. ValueConverter<ushort> IValConv<ushort>.Get()
  72. => new UShortConverter();
  73. ValueConverter<byte> IValConv<byte>.Get()
  74. => new ByteConverter();
  75. ValueConverter<sbyte> IValConv<sbyte>.Get()
  76. => new SByteConverter();
  77. ValueConverter<float> IValConv<float>.Get()
  78. => new FloatConverter();
  79. ValueConverter<double> IValConv<double>.Get()
  80. => new DoubleConverter();
  81. ValueConverter<decimal> IValConv<decimal>.Get()
  82. => new DecimalConverter();
  83. }
  84. }
  85. /// <summary>
  86. /// Provides generic utilities for converters for certain types.
  87. /// </summary>
  88. /// <typeparam name="T">the type of the <see cref="ValueConverter{T}"/> that this works on</typeparam>
  89. public static class Converter<T>
  90. {
  91. private static ValueConverter<T> defaultConverter = null;
  92. /// <summary>
  93. /// Gets the default <see cref="ValueConverter{T}"/> for the current type.
  94. /// </summary>
  95. public static ValueConverter<T> Default
  96. {
  97. get
  98. {
  99. if (defaultConverter == null)
  100. defaultConverter = MakeDefault();
  101. return defaultConverter;
  102. }
  103. }
  104. private static ValueConverter<T> MakeDefault()
  105. {
  106. var t = typeof(T);
  107. if (t.IsValueType)
  108. { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time
  109. if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
  110. { // this is a Nullable
  111. return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter<T>;
  112. }
  113. var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv<T>;
  114. return valConv.Get();
  115. }
  116. else if (t == typeof(string))
  117. {
  118. return new StringConverter() as ValueConverter<T>;
  119. }
  120. else
  121. {
  122. return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter<T>;
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// A converter for a <see cref="Nullable{T}"/>.
  128. /// </summary>
  129. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  130. public class NullableConverter<T> : ValueConverter<T?> where T : struct
  131. {
  132. private readonly ValueConverter<T> baseConverter;
  133. /// <summary>
  134. /// Creates a converter with the default converter for the base type.
  135. /// Equivalent to
  136. /// <code>
  137. /// new NullableConverter(Converter&lt;T&gt;.Default)
  138. /// </code>
  139. /// </summary>
  140. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  141. /// <seealso cref="Converter{T}.Default"/>
  142. public NullableConverter() : this(Converter<T>.Default) { }
  143. /// <summary>
  144. /// Creates a converter with the given underlying <see cref="ValueConverter{T}"/>.
  145. /// </summary>
  146. /// <param name="underlying">the undlerlying <see cref="ValueConverter{T}"/> to use</param>
  147. public NullableConverter(ValueConverter<T> underlying)
  148. => baseConverter = underlying;
  149. /// <summary>
  150. /// Converts a <see cref="Value"/> tree to a value.
  151. /// </summary>
  152. /// <param name="value">the <see cref="Value"/> tree to convert</param>
  153. /// <param name="parent">the object which will own the created object</param>
  154. /// <returns>the object represented by <paramref name="value"/></returns>
  155. public override T? FromValue(Value value, object parent)
  156. => value == null ? null : new T?(baseConverter.FromValue(value, parent));
  157. /// <summary>
  158. /// Converts a nullable <typeparamref name="T"/> to a <see cref="Value"/> tree.
  159. /// </summary>
  160. /// <param name="obj">the value to serialize</param>
  161. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  162. /// <returns>a <see cref="Value"/> tree representing <paramref name="obj"/>.</returns>
  163. public override Value ToValue(T? obj, object parent)
  164. => obj == null ? null : baseConverter.ToValue(obj.Value, parent);
  165. }
  166. internal class StringConverter : ValueConverter<string>
  167. {
  168. public override string FromValue(Value value, object parent)
  169. => (value as Text)?.Value;
  170. public override Value ToValue(string obj, object parent)
  171. => Value.From(obj);
  172. }
  173. internal class CharConverter : ValueConverter<char>
  174. {
  175. public override char FromValue(Value value, object parent)
  176. => (value as Text).Value[0]; // can throw nullptr
  177. public override Value ToValue(char obj, object parent)
  178. => Value.From(char.ToString(obj));
  179. }
  180. internal class LongConverter : ValueConverter<long>
  181. {
  182. public override long FromValue(Value value, object parent)
  183. => Converter.IntValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  184. public override Value ToValue(long obj, object parent)
  185. => Value.From(obj);
  186. }
  187. internal class ULongConverter : ValueConverter<ulong>
  188. {
  189. public override ulong FromValue(Value value, object parent)
  190. => (ulong)(Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value)));
  191. public override Value ToValue(ulong obj, object parent)
  192. => Value.From(obj);
  193. }
  194. internal class IntPtrConverter : ValueConverter<IntPtr>
  195. {
  196. public override IntPtr FromValue(Value value, object parent)
  197. => (IntPtr)Converter<long>.Default.FromValue(value, parent);
  198. public override Value ToValue(IntPtr obj, object parent)
  199. => Value.From((long)obj);
  200. }
  201. internal class UIntPtrConverter : ValueConverter<UIntPtr>
  202. {
  203. public override UIntPtr FromValue(Value value, object parent)
  204. => (UIntPtr)Converter<ulong>.Default.FromValue(value, parent);
  205. public override Value ToValue(UIntPtr obj, object parent)
  206. => Value.From((decimal)obj);
  207. }
  208. internal class IntConverter : ValueConverter<int>
  209. {
  210. public override int FromValue(Value value, object parent)
  211. => (int)Converter<long>.Default.FromValue(value, parent);
  212. public override Value ToValue(int obj, object parent)
  213. => Value.From(obj);
  214. }
  215. internal class UIntConverter : ValueConverter<uint>
  216. {
  217. public override uint FromValue(Value value, object parent)
  218. => (uint)Converter<long>.Default.FromValue(value, parent);
  219. public override Value ToValue(uint obj, object parent)
  220. => Value.From(obj);
  221. }
  222. internal class ShortConverter : ValueConverter<short>
  223. {
  224. public override short FromValue(Value value, object parent)
  225. => (short)Converter<long>.Default.FromValue(value, parent);
  226. public override Value ToValue(short obj, object parent)
  227. => Value.From(obj);
  228. }
  229. internal class UShortConverter : ValueConverter<ushort>
  230. {
  231. public override ushort FromValue(Value value, object parent)
  232. => (ushort)Converter<long>.Default.FromValue(value, parent);
  233. public override Value ToValue(ushort obj, object parent)
  234. => Value.From(obj);
  235. }
  236. internal class ByteConverter : ValueConverter<byte>
  237. {
  238. public override byte FromValue(Value value, object parent)
  239. => (byte)Converter<long>.Default.FromValue(value, parent);
  240. public override Value ToValue(byte obj, object parent)
  241. => Value.From(obj);
  242. }
  243. internal class SByteConverter : ValueConverter<sbyte>
  244. {
  245. public override sbyte FromValue(Value value, object parent)
  246. => (sbyte)Converter<long>.Default.FromValue(value, parent);
  247. public override Value ToValue(sbyte obj, object parent)
  248. => Value.From(obj);
  249. }
  250. internal class DecimalConverter : ValueConverter<decimal>
  251. {
  252. public override decimal FromValue(Value value, object parent)
  253. => Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  254. public override Value ToValue(decimal obj, object parent)
  255. => Value.From(obj);
  256. }
  257. internal class FloatConverter : ValueConverter<float>
  258. {
  259. public override float FromValue(Value value, object parent)
  260. => (float)Converter<decimal>.Default.FromValue(value, parent);
  261. public override Value ToValue(float obj, object parent)
  262. => Value.From((decimal)obj);
  263. }
  264. internal class DoubleConverter : ValueConverter<double>
  265. {
  266. public override double FromValue(Value value, object parent)
  267. => (double)Converter<decimal>.Default.FromValue(value, parent);
  268. public override Value ToValue(double obj, object parent)
  269. => Value.From((decimal)obj);
  270. }
  271. }