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.

403 lines
19 KiB

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