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.

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