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.

723 lines
33 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
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 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 UnityEngine;
  10. using Boolean = IPA.Config.Data.Boolean;
  11. namespace IPA.Config.Stores.Converters
  12. {
  13. /// <summary>
  14. /// Provides utility functions for custom converters.
  15. /// </summary>
  16. public static class Converter
  17. {
  18. /// <summary>
  19. /// Gets the integral value of a <see cref="Value"/>, coercing a <see cref="FloatingPoint"/> if necessary,
  20. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  21. /// </summary>
  22. /// <param name="val">the <see cref="Value"/> to get the integral value of</param>
  23. /// <returns>the integral value of <paramref name="val"/>, or <see langword="null"/></returns>
  24. public static long? IntValue(Value val)
  25. => val is Integer inte ? inte.Value :
  26. val is FloatingPoint fp ? fp.AsInteger()?.Value :
  27. null;
  28. /// <summary>
  29. /// Gets the floaing point value of a <see cref="Value"/>, coercing an <see cref="Integer"/> if necessary,
  30. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  31. /// </summary>
  32. /// <param name="val">the <see cref="Value"/> to get the floaing point value of</param>
  33. /// <returns>the floaing point value of <paramref name="val"/>, or <see langword="null"/></returns>
  34. public static decimal? FloatValue(Value val)
  35. => val is FloatingPoint fp ? fp.Value :
  36. val is Integer inte ? inte.AsFloat()?.Value :
  37. null;
  38. internal static Type GetDefaultConverterType(Type t)
  39. {
  40. if (t.IsEnum)
  41. {
  42. return typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t);
  43. }
  44. if (t.IsGenericType)
  45. {
  46. var generic = t.GetGenericTypeDefinition();
  47. var args = t.GetGenericArguments();
  48. if (generic == typeof(List<>))
  49. return (typeof(ListConverter<>).MakeGenericType(args));
  50. else if (generic == typeof(IList<>))
  51. return (typeof(IListConverter<>).MakeGenericType(args));
  52. else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string))
  53. return (typeof(DictionaryConverter<>).MakeGenericType(args[1]));
  54. else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string))
  55. return (typeof(IDictionaryConverter<>).MakeGenericType(args[1]));
  56. #if NET4
  57. else if (generic == typeof(ISet<>))
  58. return (typeof(ISetConverter<>).MakeGenericType(args));
  59. else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string))
  60. return (typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1]));
  61. #endif
  62. }
  63. var iCollBase = t.GetInterfaces()
  64. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>));
  65. if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null)
  66. { // if it implements ICollection and has a default constructor
  67. var valueType = iCollBase.GetGenericArguments().First();
  68. return (typeof(CollectionConverter<,>).MakeGenericType(valueType, t));
  69. }
  70. if (t == typeof(string))
  71. {
  72. //Logger.log.Debug($"gives StringConverter");
  73. return typeof(StringConverter);
  74. }
  75. if (t.IsValueType)
  76. { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time
  77. if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
  78. { // this is a Nullable
  79. //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>");
  80. return (typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t)));
  81. }
  82. //Logger.log.Debug($"gives converter for value type {t}");
  83. var valConv = Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t)) as IValConv;
  84. return valConv.Get();
  85. }
  86. //Logger.log.Debug($"gives CustomObjectConverter<{t}>");
  87. return (typeof(CustomObjectConverter<>).MakeGenericType(t));
  88. }
  89. internal interface IValConv
  90. {
  91. Type Get();
  92. }
  93. internal interface IValConv<T>
  94. {
  95. Type Get();
  96. }
  97. internal class ValConv<T> : IValConv, IValConv<T> where T : struct
  98. {
  99. private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
  100. public Type Get() => Impl.Get();
  101. Type IValConv<T>.Get()
  102. => typeof(CustomValueTypeConverter<T>);
  103. }
  104. private class ValConvImpls : IValConv<char>,
  105. IValConv<IntPtr>, IValConv<UIntPtr>,
  106. IValConv<long>, IValConv<ulong>,
  107. IValConv<int>, IValConv<uint>,
  108. IValConv<short>, IValConv<ushort>,
  109. IValConv<sbyte>, IValConv<byte>,
  110. IValConv<float>, IValConv<double>,
  111. IValConv<decimal>, IValConv<bool>,
  112. IValConv<DateTime>, IValConv<DateTimeOffset>,
  113. IValConv<TimeSpan>
  114. {
  115. internal static readonly ValConvImpls Impl = new ValConvImpls();
  116. Type IValConv<char>.Get() => typeof(CharConverter);
  117. Type IValConv<long>.Get() => typeof(LongConverter);
  118. Type IValConv<ulong>.Get() => typeof(ULongConverter);
  119. Type IValConv<IntPtr>.Get() => typeof(IntPtrConverter);
  120. Type IValConv<UIntPtr>.Get() => typeof(UIntPtrConverter);
  121. Type IValConv<int>.Get() => typeof(IntConverter);
  122. Type IValConv<uint>.Get() => typeof(UIntConverter);
  123. Type IValConv<short>.Get() => typeof(ShortConverter);
  124. Type IValConv<ushort>.Get() => typeof(UShortConverter);
  125. Type IValConv<byte>.Get() => typeof(ByteConverter);
  126. Type IValConv<sbyte>.Get() => typeof(SByteConverter);
  127. Type IValConv<float>.Get() => typeof(FloatConverter);
  128. Type IValConv<double>.Get() => typeof(DoubleConverter);
  129. Type IValConv<decimal>.Get() => typeof(DecimalConverter);
  130. Type IValConv<bool>.Get() => typeof(BooleanConverter);
  131. Type IValConv<DateTime>.Get() => typeof(DateTimeConverter);
  132. Type IValConv<DateTimeOffset>.Get() => typeof(DateTimeOffsetConverter);
  133. Type IValConv<TimeSpan>.Get() => typeof(TimeSpanConverter);
  134. }
  135. }
  136. /// <summary>
  137. /// Provides generic utilities for converters for certain types.
  138. /// </summary>
  139. /// <typeparam name="T">the type of the <see cref="ValueConverter{T}"/> that this works on</typeparam>
  140. public static class Converter<T>
  141. {
  142. private static ValueConverter<T> defaultConverter = null;
  143. /// <summary>
  144. /// Gets the default <see cref="ValueConverter{T}"/> for the current type.
  145. /// </summary>
  146. public static ValueConverter<T> Default
  147. => defaultConverter ??= MakeDefault();
  148. internal static ValueConverter<T> MakeDefault()
  149. {
  150. var t = typeof(T);
  151. //Logger.log.Debug($"Converter<{t}>.MakeDefault()");
  152. static ValueConverter<T> MakeInstOf(Type ty)
  153. => Activator.CreateInstance(ty) as ValueConverter<T>;
  154. return MakeInstOf(Converter.GetDefaultConverterType(t));
  155. }
  156. }
  157. /// <summary>
  158. /// A converter for a <see cref="Nullable{T}"/>.
  159. /// </summary>
  160. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  161. public class NullableConverter<T> : ValueConverter<T?> where T : struct
  162. {
  163. private readonly ValueConverter<T> baseConverter;
  164. /// <summary>
  165. /// Creates a converter with the default converter for the base type.
  166. /// Equivalent to
  167. /// <code>
  168. /// new NullableConverter(Converter&lt;T&gt;.Default)
  169. /// </code>
  170. /// </summary>
  171. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  172. /// <seealso cref="Converter{T}.Default"/>
  173. public NullableConverter() : this(Converter<T>.Default) { }
  174. /// <summary>
  175. /// Creates a converter with the given underlying <see cref="ValueConverter{T}"/>.
  176. /// </summary>
  177. /// <param name="underlying">the undlerlying <see cref="ValueConverter{T}"/> to use</param>
  178. public NullableConverter(ValueConverter<T> underlying)
  179. => baseConverter = underlying;
  180. /// <summary>
  181. /// Converts a <see cref="Value"/> tree to a value.
  182. /// </summary>
  183. /// <param name="value">the <see cref="Value"/> tree to convert</param>
  184. /// <param name="parent">the object which will own the created object</param>
  185. /// <returns>the object represented by <paramref name="value"/></returns>
  186. public override T? FromValue(Value value, object parent)
  187. => value == null ? null : new T?(baseConverter.FromValue(value, parent));
  188. /// <summary>
  189. /// Converts a nullable <typeparamref name="T"/> to a <see cref="Value"/> tree.
  190. /// </summary>
  191. /// <param name="obj">the value to serialize</param>
  192. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  193. /// <returns>a <see cref="Value"/> tree representing <paramref name="obj"/>.</returns>
  194. public override Value ToValue(T? obj, object parent)
  195. => obj == null ? null : baseConverter.ToValue(obj.Value, parent);
  196. }
  197. /// <summary>
  198. /// A converter for a <see cref="Nullable{T}"/> that default-constructs a converter of type <typeparamref name="TConverter"/>
  199. /// to use as the underlying converter. Use this in the <see cref="UseConverterAttribute"/>.
  200. /// </summary>
  201. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  202. /// <typeparam name="TConverter">the type to use as an underlying converter</typeparam>
  203. /// <seealso cref="NullableConverter{T}"/>
  204. public sealed class NullableConverter<T, TConverter> : NullableConverter<T>
  205. where T : struct
  206. where TConverter : ValueConverter<T>, new()
  207. {
  208. /// <summary>
  209. /// Creates a converter with a new <typeparamref name="TConverter"/> as the underlying converter.
  210. /// </summary>
  211. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  212. public NullableConverter() : base(new TConverter()) { }
  213. }
  214. /// <summary>
  215. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back.
  216. /// </summary>
  217. /// <typeparam name="T">the enum type</typeparam>
  218. public sealed class EnumConverter<T> : ValueConverter<T>
  219. where T : Enum
  220. {
  221. /// <summary>
  222. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  223. /// </summary>
  224. /// <param name="value">the <see cref="Value"/> to convert</param>
  225. /// <param name="parent">the object which will own the created object</param>
  226. /// <returns>the deserialized enum value</returns>
  227. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  228. public override T FromValue(Value value, object parent)
  229. => value is Text t
  230. ? (T)Enum.Parse(typeof(T), t.Value)
  231. : throw new ArgumentException("Value not a string", nameof(value));
  232. /// <summary>
  233. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  234. /// </summary>
  235. /// <param name="obj">the value to serialize</param>
  236. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  237. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  238. public override Value ToValue(T obj, object parent)
  239. => Value.Text(obj.ToString());
  240. }
  241. /// <summary>
  242. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back,
  243. /// ignoring the case of the serialized value for deseiralization.
  244. /// </summary>
  245. /// <typeparam name="T">the enum type</typeparam>
  246. public sealed class CaseInsensitiveEnumConverter<T> : ValueConverter<T>
  247. where T : Enum
  248. {
  249. /// <summary>
  250. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  251. /// </summary>
  252. /// <param name="value">the <see cref="Value"/> to convert</param>
  253. /// <param name="parent">the object which will own the created object</param>
  254. /// <returns>the deserialized enum value</returns>
  255. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  256. public override T FromValue(Value value, object parent)
  257. => value is Text t
  258. ? (T)Enum.Parse(typeof(T), t.Value, true)
  259. : throw new ArgumentException("Value not a string", nameof(value));
  260. /// <summary>
  261. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  262. /// </summary>
  263. /// <param name="obj">the value to serialize</param>
  264. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  265. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  266. public override Value ToValue(T obj, object parent)
  267. => Value.Text(obj.ToString());
  268. }
  269. /// <summary>
  270. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its underlying value for serialization.
  271. /// </summary>
  272. /// <typeparam name="T">the enum type</typeparam>
  273. public sealed class NumericEnumConverter<T> : ValueConverter<T>
  274. where T : Enum
  275. {
  276. /// <summary>
  277. /// Converts a <see cref="Value"/> that is a numeric node to the corresponding enum value.
  278. /// </summary>
  279. /// <param name="value">the <see cref="Value"/> to convert</param>
  280. /// <param name="parent">the object which will own the created object</param>
  281. /// <returns>the deserialized enum value</returns>
  282. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a numeric node</exception>
  283. public override T FromValue(Value value, object parent)
  284. => (T)Enum.ToObject(typeof(T), Converter.IntValue(value)
  285. ?? throw new ArgumentException("Value not a numeric node", nameof(value)));
  286. /// <summary>
  287. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  288. /// </summary>
  289. /// <param name="obj">the value to serialize</param>
  290. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  291. /// <returns>an <see cref="Integer"/> node representing <paramref name="obj"/></returns>
  292. public override Value ToValue(T obj, object parent)
  293. => Value.Integer(Convert.ToInt64(obj));
  294. }
  295. /// <summary>
  296. /// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>.
  297. /// </summary>
  298. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  299. public class IDictionaryConverter<TValue> : ValueConverter<IDictionary<string, TValue>>
  300. {
  301. /// <summary>
  302. /// Gets the converter for the dictionary's value type.
  303. /// </summary>
  304. protected ValueConverter<TValue> BaseConverter { get; }
  305. /// <summary>
  306. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
  307. /// </summary>
  308. public IDictionaryConverter() : this(Converter<TValue>.Default) { }
  309. /// <summary>
  310. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
  311. /// </summary>
  312. /// <param name="converter">the converter for the value</param>
  313. public IDictionaryConverter(ValueConverter<TValue> converter)
  314. => BaseConverter = converter;
  315. /// <summary>
  316. /// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
  317. /// </summary>
  318. /// <param name="value">the <see cref="Map"/> to convert</param>
  319. /// <param name="parent">the parent that will own the resulting object</param>
  320. /// <returns>the deserialized dictionary</returns>
  321. public override IDictionary<string, TValue> FromValue(Value value, object parent)
  322. => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  323. ?.ToDictionary(p => p.Key, p => p.val)
  324. ?? throw new ArgumentException("Value not a map", nameof(value));
  325. /// <summary>
  326. /// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  327. /// </summary>
  328. /// <param name="obj">the dictionary to serialize</param>
  329. /// <param name="parent">the object that owns the dictionary</param>
  330. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  331. public override Value ToValue(IDictionary<string, TValue> obj, object parent)
  332. => Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  333. }
  334. /// <summary>
  335. /// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  336. /// </summary>
  337. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  338. /// <typeparam name="TConverter">the converter type for values</typeparam>
  339. public sealed class IDictionaryConverter<TValue, TConverter> : IDictionaryConverter<TValue>
  340. where TConverter : ValueConverter<TValue>, new()
  341. {
  342. /// <summary>
  343. /// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
  344. /// <typeparamref name="TConverter"/> as the value converter.
  345. /// </summary>
  346. public IDictionaryConverter() : base(new TConverter()) { }
  347. }
  348. /// <summary>
  349. /// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>.
  350. /// </summary>
  351. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  352. public class DictionaryConverter<TValue> : ValueConverter<Dictionary<string, TValue>>
  353. {
  354. /// <summary>
  355. /// Gets the converter for the dictionary's value type.
  356. /// </summary>
  357. protected ValueConverter<TValue> BaseConverter { get; }
  358. /// <summary>
  359. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
  360. /// </summary>
  361. public DictionaryConverter() : this(Converter<TValue>.Default) { }
  362. /// <summary>
  363. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
  364. /// </summary>
  365. /// <param name="converter">the converter for the value</param>
  366. public DictionaryConverter(ValueConverter<TValue> converter)
  367. => BaseConverter = converter;
  368. /// <summary>
  369. /// Converts a <see cref="Map"/> to a <see cref="Dictionary{TKey, TValue}"/> that is represented by it.
  370. /// </summary>
  371. /// <param name="value">the <see cref="Map"/> to convert</param>
  372. /// <param name="parent">the parent that will own the resulting object</param>
  373. /// <returns>the deserialized dictionary</returns>
  374. public override Dictionary<string, TValue> FromValue(Value value, object parent)
  375. => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  376. ?.ToDictionary(p => p.Key, p => p.val)
  377. ?? throw new ArgumentException("Value not a map", nameof(value));
  378. /// <summary>
  379. /// Serializes a <see cref="Dictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  380. /// </summary>
  381. /// <param name="obj">the dictionary to serialize</param>
  382. /// <param name="parent">the object that owns the dictionary</param>
  383. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  384. public override Value ToValue(Dictionary<string, TValue> obj, object parent)
  385. => Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  386. }
  387. /// <summary>
  388. /// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  389. /// </summary>
  390. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  391. /// <typeparam name="TConverter">the converter type for values</typeparam>
  392. public sealed class DictionaryConverter<TValue, TConverter> : DictionaryConverter<TValue>
  393. where TConverter : ValueConverter<TValue>, new()
  394. {
  395. /// <summary>
  396. /// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
  397. /// <typeparamref name="TConverter"/> as the value converter.
  398. /// </summary>
  399. public DictionaryConverter() : base(new TConverter()) { }
  400. }
  401. #if NET4
  402. /// <summary>
  403. /// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>.
  404. /// </summary>
  405. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  406. public class IReadOnlyDictionaryConverter<TValue> : ValueConverter<IReadOnlyDictionary<string, TValue>>
  407. {
  408. /// <summary>
  409. /// Gets the converter for the dictionary's value type.
  410. /// </summary>
  411. protected ValueConverter<TValue> BaseConverter { get; }
  412. /// <summary>
  413. /// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the default converter for the value type.
  414. /// </summary>
  415. public IReadOnlyDictionaryConverter() : this(Converter<TValue>.Default) { }
  416. /// <summary>
  417. /// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the specified converter for the value.
  418. /// </summary>
  419. /// <param name="converter">the converter for the value</param>
  420. public IReadOnlyDictionaryConverter(ValueConverter<TValue> converter)
  421. => BaseConverter = converter;
  422. /// <summary>
  423. /// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
  424. /// </summary>
  425. /// <param name="value">the <see cref="Map"/> to convert</param>
  426. /// <param name="parent">the parent that will own the resulting object</param>
  427. /// <returns>the deserialized dictionary</returns>
  428. public override IReadOnlyDictionary<string, TValue> FromValue(Value value, object parent)
  429. => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  430. ?.ToDictionary(p => p.Key, p => p.val)
  431. ?? throw new ArgumentException("Value not a map", nameof(value));
  432. /// <summary>
  433. /// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  434. /// </summary>
  435. /// <param name="obj">the dictionary to serialize</param>
  436. /// <param name="parent">the object that owns the dictionary</param>
  437. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  438. public override Value ToValue(IReadOnlyDictionary<string, TValue> obj, object parent)
  439. => Value.From(obj.Select(p => new KeyValuePair<string, Value>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  440. }
  441. /// <summary>
  442. /// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  443. /// </summary>
  444. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  445. /// <typeparam name="TConverter">the converter type for values</typeparam>
  446. public sealed class IReadOnlyDictionaryConverter<TValue, TConverter> : IReadOnlyDictionaryConverter<TValue>
  447. where TConverter : ValueConverter<TValue>, new()
  448. {
  449. /// <summary>
  450. /// Constructs a new <see cref="IReadOnlyDictionaryConverter{TValue, TConverter}"/> with a new instance of
  451. /// <typeparamref name="TConverter"/> as the value converter.
  452. /// </summary>
  453. public IReadOnlyDictionaryConverter() : base(new TConverter()) { }
  454. }
  455. #endif
  456. /// <summary>
  457. /// A converter for <see cref="Color"/> objects.
  458. /// </summary>
  459. public sealed class HexColorConverter : ValueConverter<Color>
  460. {
  461. /// <summary>
  462. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding <see cref="Color" /> object.
  463. /// </summary>
  464. /// <param name="value">the <see cref="Value"/> to convert</param>
  465. /// <param name="parent">the object which will own the created object</param>
  466. /// <returns>the deserialized Color object</returns>
  467. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node or couldn't be parsed into a Color object</exception>
  468. public override Color FromValue(Value value, object parent)
  469. {
  470. if (value is Text t)
  471. {
  472. if (ColorUtility.TryParseHtmlString(t.Value, out Color color))
  473. {
  474. return color;
  475. }
  476. throw new ArgumentException("Value cannot be parsed into a Color.", nameof(value));
  477. }
  478. throw new ArgumentException("Value not a string", nameof(value));
  479. }
  480. /// <summary>
  481. /// Converts color of type <see cref="Color"/> to a <see cref="Value"/> node.
  482. /// </summary>
  483. /// <param name="obj">the object to serialize</param>
  484. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  485. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  486. public override Value ToValue(Color obj, object parent) => Value.Text($"#{ColorUtility.ToHtmlStringRGB(obj)}");
  487. }
  488. internal class StringConverter : ValueConverter<string>
  489. {
  490. public override string FromValue(Value value, object parent)
  491. => (value as Text)?.Value;
  492. public override Value ToValue(string obj, object parent)
  493. => Value.From(obj);
  494. }
  495. internal class CharConverter : ValueConverter<char>
  496. {
  497. public override char FromValue(Value value, object parent)
  498. => (value as Text)?.Value[0]
  499. ?? throw new ArgumentException("Value not a text node", nameof(value)); // can throw nullptr
  500. public override Value ToValue(char obj, object parent)
  501. => Value.From(char.ToString(obj));
  502. }
  503. internal class LongConverter : ValueConverter<long>
  504. {
  505. public override long FromValue(Value value, object parent)
  506. => Converter.IntValue(value)
  507. ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  508. public override Value ToValue(long obj, object parent)
  509. => Value.From(obj);
  510. }
  511. internal class ULongConverter : ValueConverter<ulong>
  512. {
  513. public override ulong FromValue(Value value, object parent)
  514. => (ulong)(Converter.FloatValue(value)
  515. ?? throw new ArgumentException("Value not a numeric value", nameof(value)));
  516. public override Value ToValue(ulong obj, object parent)
  517. => Value.From(obj);
  518. }
  519. internal class IntPtrConverter : ValueConverter<IntPtr>
  520. {
  521. public override IntPtr FromValue(Value value, object parent)
  522. => (IntPtr)Converter<long>.Default.FromValue(value, parent);
  523. public override Value ToValue(IntPtr obj, object parent)
  524. => Value.From((long)obj);
  525. }
  526. internal class UIntPtrConverter : ValueConverter<UIntPtr>
  527. {
  528. public override UIntPtr FromValue(Value value, object parent)
  529. => (UIntPtr)Converter<ulong>.Default.FromValue(value, parent);
  530. public override Value ToValue(UIntPtr obj, object parent)
  531. => Value.From((decimal)obj);
  532. }
  533. internal class IntConverter : ValueConverter<int>
  534. {
  535. public override int FromValue(Value value, object parent)
  536. => (int)Converter<long>.Default.FromValue(value, parent);
  537. public override Value ToValue(int obj, object parent)
  538. => Value.From(obj);
  539. }
  540. internal class UIntConverter : ValueConverter<uint>
  541. {
  542. public override uint FromValue(Value value, object parent)
  543. => (uint)Converter<long>.Default.FromValue(value, parent);
  544. public override Value ToValue(uint obj, object parent)
  545. => Value.From(obj);
  546. }
  547. internal class ShortConverter : ValueConverter<short>
  548. {
  549. public override short FromValue(Value value, object parent)
  550. => (short)Converter<long>.Default.FromValue(value, parent);
  551. public override Value ToValue(short obj, object parent)
  552. => Value.From(obj);
  553. }
  554. internal class UShortConverter : ValueConverter<ushort>
  555. {
  556. public override ushort FromValue(Value value, object parent)
  557. => (ushort)Converter<long>.Default.FromValue(value, parent);
  558. public override Value ToValue(ushort obj, object parent)
  559. => Value.From(obj);
  560. }
  561. internal class ByteConverter : ValueConverter<byte>
  562. {
  563. public override byte FromValue(Value value, object parent)
  564. => (byte)Converter<long>.Default.FromValue(value, parent);
  565. public override Value ToValue(byte obj, object parent)
  566. => Value.From(obj);
  567. }
  568. internal class SByteConverter : ValueConverter<sbyte>
  569. {
  570. public override sbyte FromValue(Value value, object parent)
  571. => (sbyte)Converter<long>.Default.FromValue(value, parent);
  572. public override Value ToValue(sbyte obj, object parent)
  573. => Value.From(obj);
  574. }
  575. internal class DecimalConverter : ValueConverter<decimal>
  576. {
  577. public override decimal FromValue(Value value, object parent)
  578. => Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  579. public override Value ToValue(decimal obj, object parent)
  580. => Value.From(obj);
  581. }
  582. internal class FloatConverter : ValueConverter<float>
  583. {
  584. public override float FromValue(Value value, object parent)
  585. => (float)Converter<decimal>.Default.FromValue(value, parent);
  586. public override Value ToValue(float obj, object parent)
  587. => Value.From((decimal)obj);
  588. }
  589. internal class DoubleConverter : ValueConverter<double>
  590. {
  591. public override double FromValue(Value value, object parent)
  592. => (double)Converter<decimal>.Default.FromValue(value, parent);
  593. public override Value ToValue(double obj, object parent)
  594. => Value.From((decimal)obj);
  595. }
  596. internal class BooleanConverter : ValueConverter<bool>
  597. {
  598. public override bool FromValue(Value value, object parent)
  599. => (value as Boolean)?.Value ?? throw new ArgumentException("Value not a Boolean", nameof(value));
  600. public override Value ToValue(bool obj, object parent)
  601. => Value.From(obj);
  602. }
  603. internal class DateTimeConverter : ValueConverter<DateTime>
  604. {
  605. public override DateTime FromValue(Value value, object parent)
  606. {
  607. if (!(value is Text text))
  608. {
  609. throw new ArgumentException("Value is not of type Text", nameof(value));
  610. }
  611. if (DateTime.TryParse(text.Value, out var dateTime))
  612. {
  613. return dateTime;
  614. }
  615. throw new ArgumentException($"Parsing failed, {text.Value}");
  616. }
  617. public override Value ToValue(DateTime obj, object parent) => Value.Text(obj.ToString("O"));
  618. }
  619. internal class DateTimeOffsetConverter : ValueConverter<DateTimeOffset>
  620. {
  621. public override DateTimeOffset FromValue(Value value, object parent)
  622. {
  623. if (!(value is Text text))
  624. {
  625. throw new ArgumentException("Value is not of type Text", nameof(value));
  626. }
  627. if (DateTimeOffset.TryParse(text.Value, out var dateTime))
  628. {
  629. return dateTime;
  630. }
  631. throw new ArgumentException($"Parsing failed, {text.Value}");
  632. }
  633. public override Value ToValue(DateTimeOffset obj, object parent) => Value.Text(obj.ToString("O"));
  634. }
  635. internal class TimeSpanConverter : ValueConverter<TimeSpan>
  636. {
  637. public override TimeSpan FromValue(Value value, object parent)
  638. {
  639. if (!(value is Text text))
  640. {
  641. throw new ArgumentException("Value is not of type Text", nameof(value));
  642. }
  643. if (TimeSpan.TryParse(text.Value, out var dateTime))
  644. {
  645. return dateTime;
  646. }
  647. throw new ArgumentException($"Parsing failed, {text.Value}");
  648. }
  649. public override Value ToValue(TimeSpan obj, object parent) => Value.Text(obj.ToString());
  650. }
  651. }