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.

404 lines
18 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. using IPA.Config.Data;
  2. using IPA.Config.Stores.Attributes;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using Boolean = IPA.Config.Data.Boolean;
  9. namespace IPA.Config.Stores.Converters
  10. {
  11. /// <summary>
  12. /// Provides utility functions for custom converters.
  13. /// </summary>
  14. public static class Converter
  15. {
  16. /// <summary>
  17. /// Gets the integral value of a <see cref="Value"/>, coercing a <see cref="FloatingPoint"/> if necessary,
  18. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  19. /// </summary>
  20. /// <param name="val">the <see cref="Value"/> to get the integral value of</param>
  21. /// <returns>the integral value of <paramref name="val"/>, or <see langword="null"/></returns>
  22. public static long? IntValue(Value val)
  23. => val is Integer inte ? inte.Value :
  24. val is FloatingPoint fp ? fp.AsInteger()?.Value :
  25. null;
  26. /// <summary>
  27. /// Gets the floaing point value of a <see cref="Value"/>, coercing an <see cref="Integer"/> if necessary,
  28. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  29. /// </summary>
  30. /// <param name="val">the <see cref="Value"/> to get the floaing point value of</param>
  31. /// <returns>the floaing point value of <paramref name="val"/>, or <see langword="null"/></returns>
  32. public static decimal? FloatValue(Value val)
  33. => val is FloatingPoint fp ? fp.Value :
  34. val is Integer inte ? inte.AsFloat()?.Value :
  35. null;
  36. internal interface IValConv<T>
  37. {
  38. ValueConverter<T> Get();
  39. }
  40. internal class ValConv<T> : IValConv<T> where T : struct
  41. {
  42. private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
  43. public ValueConverter<T> Get() => Impl.Get();
  44. ValueConverter<T> IValConv<T>.Get()
  45. => null; // default to null
  46. }
  47. private class ValConvImpls : IValConv<char>,
  48. IValConv<IntPtr>, IValConv<UIntPtr>,
  49. IValConv<long>, IValConv<ulong>,
  50. IValConv<int>, IValConv<uint>,
  51. IValConv<short>, IValConv<ushort>,
  52. IValConv<sbyte>, IValConv<byte>,
  53. IValConv<float>, IValConv<double>,
  54. IValConv<decimal>, IValConv<bool>
  55. {
  56. internal static readonly ValConvImpls Impl = new ValConvImpls();
  57. ValueConverter<char> IValConv<char>.Get() => new CharConverter();
  58. ValueConverter<long> IValConv<long>.Get() => new LongConverter();
  59. ValueConverter<ulong> IValConv<ulong>.Get() => new ULongConverter();
  60. ValueConverter<IntPtr> IValConv<IntPtr>.Get() => new IntPtrConverter();
  61. ValueConverter<UIntPtr> IValConv<UIntPtr>.Get() => new UIntPtrConverter();
  62. ValueConverter<int> IValConv<int>.Get() => new IntConverter();
  63. ValueConverter<uint> IValConv<uint>.Get() => new UIntConverter();
  64. ValueConverter<short> IValConv<short>.Get() => new ShortConverter();
  65. ValueConverter<ushort> IValConv<ushort>.Get() => new UShortConverter();
  66. ValueConverter<byte> IValConv<byte>.Get() => new ByteConverter();
  67. ValueConverter<sbyte> IValConv<sbyte>.Get() => new SByteConverter();
  68. ValueConverter<float> IValConv<float>.Get() => new FloatConverter();
  69. ValueConverter<double> IValConv<double>.Get() => new DoubleConverter();
  70. ValueConverter<decimal> IValConv<decimal>.Get() => new DecimalConverter();
  71. ValueConverter<bool> IValConv<bool>.Get() => new BooleanConverter();
  72. }
  73. }
  74. /// <summary>
  75. /// Provides generic utilities for converters for certain types.
  76. /// </summary>
  77. /// <typeparam name="T">the type of the <see cref="ValueConverter{T}"/> that this works on</typeparam>
  78. public static class Converter<T>
  79. {
  80. private static ValueConverter<T> defaultConverter = null;
  81. /// <summary>
  82. /// Gets the default <see cref="ValueConverter{T}"/> for the current type.
  83. /// </summary>
  84. public static ValueConverter<T> Default
  85. {
  86. get
  87. {
  88. if (defaultConverter == null)
  89. defaultConverter = MakeDefault();
  90. return defaultConverter;
  91. }
  92. }
  93. private static ValueConverter<T> MakeDefault()
  94. {
  95. var t = typeof(T);
  96. if (t.IsValueType)
  97. { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time
  98. if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
  99. { // this is a Nullable
  100. return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter<T>;
  101. }
  102. var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv<T>;
  103. return valConv.Get();
  104. }
  105. else if (t == typeof(string))
  106. {
  107. return new StringConverter() as ValueConverter<T>;
  108. }
  109. else
  110. {
  111. return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter<T>;
  112. }
  113. }
  114. }
  115. /// <summary>
  116. /// A converter for a <see cref="Nullable{T}"/>.
  117. /// </summary>
  118. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  119. public class NullableConverter<T> : ValueConverter<T?> where T : struct
  120. {
  121. private readonly ValueConverter<T> baseConverter;
  122. /// <summary>
  123. /// Creates a converter with the default converter for the base type.
  124. /// Equivalent to
  125. /// <code>
  126. /// new NullableConverter(Converter&lt;T&gt;.Default)
  127. /// </code>
  128. /// </summary>
  129. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  130. /// <seealso cref="Converter{T}.Default"/>
  131. public NullableConverter() : this(Converter<T>.Default) { }
  132. /// <summary>
  133. /// Creates a converter with the given underlying <see cref="ValueConverter{T}"/>.
  134. /// </summary>
  135. /// <param name="underlying">the undlerlying <see cref="ValueConverter{T}"/> to use</param>
  136. public NullableConverter(ValueConverter<T> underlying)
  137. => baseConverter = underlying;
  138. /// <summary>
  139. /// Converts a <see cref="Value"/> tree to a value.
  140. /// </summary>
  141. /// <param name="value">the <see cref="Value"/> tree to convert</param>
  142. /// <param name="parent">the object which will own the created object</param>
  143. /// <returns>the object represented by <paramref name="value"/></returns>
  144. public override T? FromValue(Value value, object parent)
  145. => value == null ? null : new T?(baseConverter.FromValue(value, parent));
  146. /// <summary>
  147. /// Converts a nullable <typeparamref name="T"/> to a <see cref="Value"/> tree.
  148. /// </summary>
  149. /// <param name="obj">the value to serialize</param>
  150. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  151. /// <returns>a <see cref="Value"/> tree representing <paramref name="obj"/>.</returns>
  152. public override Value ToValue(T? obj, object parent)
  153. => obj == null ? null : baseConverter.ToValue(obj.Value, parent);
  154. }
  155. /// <summary>
  156. /// A converter for a <see cref="Nullable{T}"/> that default-constructs a converter of type <typeparamref name="TConverter"/>
  157. /// to use as the underlying converter. Use this in the <see cref="UseConverterAttribute"/>.
  158. /// </summary>
  159. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  160. /// <typeparam name="TConverter">the type to use as an underlying converter</typeparam>
  161. /// <seealso cref="NullableConverter{T}"/>
  162. public sealed class NullableConverter<T, TConverter> : NullableConverter<T>
  163. where T : struct
  164. where TConverter : ValueConverter<T>, new()
  165. {
  166. /// <summary>
  167. /// Creates a converter with a new <typeparamref name="TConverter"/> as the underlying converter.
  168. /// </summary>
  169. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  170. public NullableConverter() : base(new TConverter()) { }
  171. }
  172. /// <summary>
  173. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back.
  174. /// </summary>
  175. /// <typeparam name="T">the enum type</typeparam>
  176. public sealed class EnumConverter<T> : ValueConverter<T>
  177. where T : Enum
  178. {
  179. /// <summary>
  180. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  181. /// </summary>
  182. /// <param name="value">the <see cref="Value"/> to convert</param>
  183. /// <param name="parent">the object which will own the created object</param>
  184. /// <returns>the deserialized enum value</returns>
  185. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  186. public override T FromValue(Value value, object parent)
  187. => value is Text t
  188. ? (T)Enum.Parse(typeof(T), t.Value)
  189. : throw new ArgumentException("Value not a string", nameof(value));
  190. /// <summary>
  191. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  192. /// </summary>
  193. /// <param name="obj">the value to serialize</param>
  194. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  195. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  196. public override Value ToValue(T obj, object parent)
  197. => Value.Text(obj.ToString());
  198. }
  199. /// <summary>
  200. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back,
  201. /// ignoring the case of the serialized value for deseiralization.
  202. /// </summary>
  203. /// <typeparam name="T">the enum type</typeparam>
  204. public sealed class CaseInsensitiveEnumConverter<T> : ValueConverter<T>
  205. where T : Enum
  206. {
  207. /// <summary>
  208. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  209. /// </summary>
  210. /// <param name="value">the <see cref="Value"/> to convert</param>
  211. /// <param name="parent">the object which will own the created object</param>
  212. /// <returns>the deserialized enum value</returns>
  213. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  214. public override T FromValue(Value value, object parent)
  215. => value is Text t
  216. ? (T)Enum.Parse(typeof(T), t.Value, true)
  217. : throw new ArgumentException("Value not a string", nameof(value));
  218. /// <summary>
  219. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  220. /// </summary>
  221. /// <param name="obj">the value to serialize</param>
  222. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  223. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  224. public override Value ToValue(T obj, object parent)
  225. => Value.Text(obj.ToString());
  226. }
  227. /// <summary>
  228. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its underlying value for serialization.
  229. /// </summary>
  230. /// <typeparam name="T">the enum type</typeparam>
  231. public sealed class NumericEnumConverter<T> : ValueConverter<T>
  232. where T : Enum
  233. {
  234. /// <summary>
  235. /// Converts a <see cref="Value"/> that is a numeric node to the corresponding enum value.
  236. /// </summary>
  237. /// <param name="value">the <see cref="Value"/> to convert</param>
  238. /// <param name="parent">the object which will own the created object</param>
  239. /// <returns>the deserialized enum value</returns>
  240. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a numeric node</exception>
  241. public override T FromValue(Value value, object parent)
  242. => (T)Enum.ToObject(typeof(T), Converter.IntValue(value)
  243. ?? throw new ArgumentException("Value not a numeric node", nameof(value)));
  244. /// <summary>
  245. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  246. /// </summary>
  247. /// <param name="obj">the value to serialize</param>
  248. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  249. /// <returns>an <see cref="Integer"/> node representing <paramref name="obj"/></returns>
  250. public override Value ToValue(T obj, object parent)
  251. => Value.Integer(Convert.ToInt64(obj));
  252. }
  253. internal class StringConverter : ValueConverter<string>
  254. {
  255. public override string FromValue(Value value, object parent)
  256. => (value as Text)?.Value;
  257. public override Value ToValue(string obj, object parent)
  258. => Value.From(obj);
  259. }
  260. internal class CharConverter : ValueConverter<char>
  261. {
  262. public override char FromValue(Value value, object parent)
  263. => (value as Text)?.Value[0]
  264. ?? throw new ArgumentException("Value not a text node", nameof(value)); // can throw nullptr
  265. public override Value ToValue(char obj, object parent)
  266. => Value.From(char.ToString(obj));
  267. }
  268. internal class LongConverter : ValueConverter<long>
  269. {
  270. public override long FromValue(Value value, object parent)
  271. => Converter.IntValue(value)
  272. ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  273. public override Value ToValue(long obj, object parent)
  274. => Value.From(obj);
  275. }
  276. internal class ULongConverter : ValueConverter<ulong>
  277. {
  278. public override ulong FromValue(Value value, object parent)
  279. => (ulong)(Converter.FloatValue(value)
  280. ?? throw new ArgumentException("Value not a numeric value", nameof(value)));
  281. public override Value ToValue(ulong obj, object parent)
  282. => Value.From(obj);
  283. }
  284. internal class IntPtrConverter : ValueConverter<IntPtr>
  285. {
  286. public override IntPtr FromValue(Value value, object parent)
  287. => (IntPtr)Converter<long>.Default.FromValue(value, parent);
  288. public override Value ToValue(IntPtr obj, object parent)
  289. => Value.From((long)obj);
  290. }
  291. internal class UIntPtrConverter : ValueConverter<UIntPtr>
  292. {
  293. public override UIntPtr FromValue(Value value, object parent)
  294. => (UIntPtr)Converter<ulong>.Default.FromValue(value, parent);
  295. public override Value ToValue(UIntPtr obj, object parent)
  296. => Value.From((decimal)obj);
  297. }
  298. internal class IntConverter : ValueConverter<int>
  299. {
  300. public override int FromValue(Value value, object parent)
  301. => (int)Converter<long>.Default.FromValue(value, parent);
  302. public override Value ToValue(int obj, object parent)
  303. => Value.From(obj);
  304. }
  305. internal class UIntConverter : ValueConverter<uint>
  306. {
  307. public override uint FromValue(Value value, object parent)
  308. => (uint)Converter<long>.Default.FromValue(value, parent);
  309. public override Value ToValue(uint obj, object parent)
  310. => Value.From(obj);
  311. }
  312. internal class ShortConverter : ValueConverter<short>
  313. {
  314. public override short FromValue(Value value, object parent)
  315. => (short)Converter<long>.Default.FromValue(value, parent);
  316. public override Value ToValue(short obj, object parent)
  317. => Value.From(obj);
  318. }
  319. internal class UShortConverter : ValueConverter<ushort>
  320. {
  321. public override ushort FromValue(Value value, object parent)
  322. => (ushort)Converter<long>.Default.FromValue(value, parent);
  323. public override Value ToValue(ushort obj, object parent)
  324. => Value.From(obj);
  325. }
  326. internal class ByteConverter : ValueConverter<byte>
  327. {
  328. public override byte FromValue(Value value, object parent)
  329. => (byte)Converter<long>.Default.FromValue(value, parent);
  330. public override Value ToValue(byte obj, object parent)
  331. => Value.From(obj);
  332. }
  333. internal class SByteConverter : ValueConverter<sbyte>
  334. {
  335. public override sbyte FromValue(Value value, object parent)
  336. => (sbyte)Converter<long>.Default.FromValue(value, parent);
  337. public override Value ToValue(sbyte obj, object parent)
  338. => Value.From(obj);
  339. }
  340. internal class DecimalConverter : ValueConverter<decimal>
  341. {
  342. public override decimal FromValue(Value value, object parent)
  343. => Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  344. public override Value ToValue(decimal obj, object parent)
  345. => Value.From(obj);
  346. }
  347. internal class FloatConverter : ValueConverter<float>
  348. {
  349. public override float FromValue(Value value, object parent)
  350. => (float)Converter<decimal>.Default.FromValue(value, parent);
  351. public override Value ToValue(float obj, object parent)
  352. => Value.From((decimal)obj);
  353. }
  354. internal class DoubleConverter : ValueConverter<double>
  355. {
  356. public override double FromValue(Value value, object parent)
  357. => (double)Converter<decimal>.Default.FromValue(value, parent);
  358. public override Value ToValue(double obj, object parent)
  359. => Value.From((decimal)obj);
  360. }
  361. internal class BooleanConverter : ValueConverter<bool>
  362. {
  363. public override bool FromValue(Value value, object parent)
  364. => (value as Boolean)?.Value ?? throw new ArgumentException("Value not a Boolean", nameof(value));
  365. public override Value ToValue(bool obj, object parent)
  366. => Value.From(obj);
  367. }
  368. }