/src/Hocon.Immutable/HoconImmutableLiteral.cs

https://github.com/akkadotnet/HOCON · C# · 530 lines · 471 code · 54 blank · 5 comment · 32 complexity · 59e6008eece0362bd2ccd4ae4716fa8a MD5 · raw file

  1. // -----------------------------------------------------------------------
  2. // <copyright file="HoconImmutableLiteral.cs" company="Akka.NET Project">
  3. // Copyright (C) 2013 - 2020 .NET Foundation <https://github.com/akkadotnet/hocon>
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. using System;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Numerics;
  10. using System.Text.RegularExpressions;
  11. using System.Threading;
  12. namespace Hocon.Immutable
  13. {
  14. public sealed class HoconImmutableLiteral : HoconImmutableElement, IEquatable<HoconImmutableLiteral>
  15. {
  16. public static readonly HoconImmutableLiteral Null = Create(null);
  17. private HoconImmutableLiteral(string value)
  18. {
  19. Value = value;
  20. }
  21. public new string Value { get; }
  22. internal static HoconImmutableLiteral Create(string value)
  23. {
  24. return new HoconImmutableLiteral(value);
  25. }
  26. #region Interface implementations
  27. public override string ToString()
  28. {
  29. return Value;
  30. }
  31. public bool Equals(HoconImmutableLiteral other)
  32. {
  33. if (other == null) return false;
  34. return ReferenceEquals(this, other) || string.Equals(Value, other.Value);
  35. }
  36. public override bool Equals(object obj)
  37. {
  38. return ReferenceEquals(this, obj) || obj is HoconImmutableLiteral other && Equals(other);
  39. }
  40. public override int GetHashCode()
  41. {
  42. return Value != null ? Value.GetHashCode() : 0;
  43. }
  44. #endregion
  45. #region Casting operators
  46. public static implicit operator char(HoconImmutableLiteral lit)
  47. {
  48. return lit.Value?[0] ?? '\0';
  49. }
  50. public static implicit operator char[](HoconImmutableLiteral lit)
  51. {
  52. return lit.Value?.ToCharArray() ?? new char[] { };
  53. }
  54. public static implicit operator string(HoconImmutableLiteral lit)
  55. {
  56. return lit.Value;
  57. }
  58. public static implicit operator bool(HoconImmutableLiteral lit)
  59. {
  60. switch (lit.Value)
  61. {
  62. case "on":
  63. case "true":
  64. case "yes":
  65. return true;
  66. case "off":
  67. case "false":
  68. case "no":
  69. case null:
  70. return false;
  71. default:
  72. throw new HoconException($"Unknown boolean format: {lit.Value}");
  73. }
  74. }
  75. public static implicit operator sbyte(HoconImmutableLiteral lit)
  76. {
  77. var value = lit.Value;
  78. if (value.StartsWith("0x"))
  79. try
  80. {
  81. return Convert.ToSByte(value, 16);
  82. }
  83. catch (Exception e)
  84. {
  85. throw new HoconException($"Could not convert hex value `{value}` to sbyte.", e);
  86. }
  87. if (value.StartsWith("0"))
  88. try
  89. {
  90. return Convert.ToSByte(value, 8);
  91. }
  92. catch (Exception e)
  93. {
  94. throw new HoconException($"Could not convert octal value `{value}` to sbyte.", e);
  95. }
  96. try
  97. {
  98. return sbyte.Parse(value, NumberStyles.Integer);
  99. }
  100. catch (Exception e)
  101. {
  102. throw new HoconException($"Could not convert `{value}` to sbyte.", e);
  103. }
  104. }
  105. public static implicit operator byte(HoconImmutableLiteral lit)
  106. {
  107. var value = lit.Value;
  108. if (value.StartsWith("0x"))
  109. try
  110. {
  111. return Convert.ToByte(value, 16);
  112. }
  113. catch (Exception e)
  114. {
  115. throw new HoconException($"Could not convert hex value `{value}` to byte.", e);
  116. }
  117. if (value.StartsWith("0"))
  118. try
  119. {
  120. return Convert.ToByte(value, 8);
  121. }
  122. catch (Exception e)
  123. {
  124. throw new HoconException($"Could not convert octal value `{value}` to byte.", e);
  125. }
  126. try
  127. {
  128. return byte.Parse(value, NumberStyles.Integer);
  129. }
  130. catch (Exception e)
  131. {
  132. throw new HoconException($"Could not convert `{value}` to byte.", e);
  133. }
  134. }
  135. public static implicit operator short(HoconImmutableLiteral lit)
  136. {
  137. var value = lit.Value;
  138. if (value.StartsWith("0x"))
  139. try
  140. {
  141. return Convert.ToInt16(value, 16);
  142. }
  143. catch (Exception e)
  144. {
  145. throw new HoconException($"Could not convert hex value `{value}` to short.", e);
  146. }
  147. if (value.StartsWith("0"))
  148. try
  149. {
  150. return Convert.ToInt16(value, 8);
  151. }
  152. catch (Exception e)
  153. {
  154. throw new HoconException($"Could not convert octal value `{value}` to short.", e);
  155. }
  156. try
  157. {
  158. return short.Parse(value, NumberStyles.Integer);
  159. }
  160. catch (Exception e)
  161. {
  162. throw new HoconException($"Could not convert `{value}` to short.", e);
  163. }
  164. }
  165. public static implicit operator ushort(HoconImmutableLiteral lit)
  166. {
  167. var value = lit.Value;
  168. if (value.StartsWith("0x"))
  169. try
  170. {
  171. return Convert.ToUInt16(value, 16);
  172. }
  173. catch (Exception e)
  174. {
  175. throw new HoconException($"Could not convert hex value `{value}` to ushort.", e);
  176. }
  177. if (value.StartsWith("0"))
  178. try
  179. {
  180. return Convert.ToUInt16(value, 8);
  181. }
  182. catch (Exception e)
  183. {
  184. throw new HoconException($"Could not convert octal value `{value}` to ushort.", e);
  185. }
  186. try
  187. {
  188. return ushort.Parse(value, NumberStyles.Integer);
  189. }
  190. catch (Exception e)
  191. {
  192. throw new HoconException($"Could not convert `{value}` to ushort.", e);
  193. }
  194. }
  195. public static implicit operator int(HoconImmutableLiteral lit)
  196. {
  197. var value = lit.Value;
  198. if (value.StartsWith("0x"))
  199. try
  200. {
  201. return Convert.ToInt32(value, 16);
  202. }
  203. catch (Exception e)
  204. {
  205. throw new HoconException($"Could not convert hex value `{value}` to int.", e);
  206. }
  207. if (value.StartsWith("0"))
  208. try
  209. {
  210. return Convert.ToInt32(value, 8);
  211. }
  212. catch (Exception e)
  213. {
  214. throw new HoconException($"Could not convert octal value `{value}` to int.", e);
  215. }
  216. try
  217. {
  218. return int.Parse(value, NumberStyles.Integer);
  219. }
  220. catch (Exception e)
  221. {
  222. throw new HoconException($"Could not convert `{value}` to int.", e);
  223. }
  224. }
  225. public static implicit operator uint(HoconImmutableLiteral lit)
  226. {
  227. var value = lit.Value;
  228. if (value.StartsWith("0x"))
  229. try
  230. {
  231. return Convert.ToUInt32(value, 16);
  232. }
  233. catch (Exception e)
  234. {
  235. throw new HoconException($"Could not convert hex value `{value}` to uint.", e);
  236. }
  237. if (value.StartsWith("0"))
  238. try
  239. {
  240. return Convert.ToUInt32(value, 8);
  241. }
  242. catch (Exception e)
  243. {
  244. throw new HoconException($"Could not convert octal value `{value}` to uint.", e);
  245. }
  246. try
  247. {
  248. return uint.Parse(value, NumberStyles.Integer);
  249. }
  250. catch (Exception e)
  251. {
  252. throw new HoconException($"Could not convert `{value}` to uint.", e);
  253. }
  254. }
  255. public static implicit operator long(HoconImmutableLiteral lit)
  256. {
  257. var value = lit.Value;
  258. if (value.StartsWith("0x"))
  259. try
  260. {
  261. return Convert.ToInt64(value, 16);
  262. }
  263. catch (Exception e)
  264. {
  265. throw new HoconException($"Could not convert hex value `{value}` to long.", e);
  266. }
  267. if (value.StartsWith("0"))
  268. try
  269. {
  270. return Convert.ToInt64(value, 8);
  271. }
  272. catch (Exception e)
  273. {
  274. throw new HoconException($"Could not convert octal value `{value}` to long.", e);
  275. }
  276. try
  277. {
  278. return long.Parse(value, NumberStyles.Integer);
  279. }
  280. catch (Exception e)
  281. {
  282. throw new HoconException($"Could not convert `{value}` to long.", e);
  283. }
  284. }
  285. public static implicit operator ulong(HoconImmutableLiteral lit)
  286. {
  287. var value = lit.Value;
  288. if (value.StartsWith("0x"))
  289. try
  290. {
  291. return Convert.ToUInt64(value, 16);
  292. }
  293. catch (Exception e)
  294. {
  295. throw new HoconException($"Could not convert hex value `{value}` to ulong.", e);
  296. }
  297. if (value.StartsWith("0"))
  298. try
  299. {
  300. return Convert.ToUInt64(value, 8);
  301. }
  302. catch (Exception e)
  303. {
  304. throw new HoconException($"Could not convert octal value `{value}` to ulong.", e);
  305. }
  306. try
  307. {
  308. return ulong.Parse(value, NumberStyles.Integer);
  309. }
  310. catch (Exception e)
  311. {
  312. throw new HoconException($"Could not convert `{value}` to ulong.", e);
  313. }
  314. }
  315. public static implicit operator BigInteger(HoconImmutableLiteral lit)
  316. {
  317. var value = lit.Value;
  318. if (value.StartsWith("0x"))
  319. try
  320. {
  321. return BigInteger.Parse(value.Substring(2), NumberStyles.HexNumber);
  322. }
  323. catch (Exception e)
  324. {
  325. throw new HoconException($"Could not convert hex value `{value}` to ulong.", e);
  326. }
  327. if (value.StartsWith("0"))
  328. try
  329. {
  330. return value.Substring(1).Aggregate(new BigInteger(), (b, c) => b * 8 + c - '0');
  331. }
  332. catch (Exception e)
  333. {
  334. throw new HoconException($"Could not convert octal value `{value}` to ulong.", e);
  335. }
  336. try
  337. {
  338. return BigInteger.Parse(value, NumberStyles.Integer);
  339. }
  340. catch (Exception e)
  341. {
  342. throw new HoconException($"Could not convert `{value}` to ulong.", e);
  343. }
  344. }
  345. public static implicit operator float(HoconImmutableLiteral lit)
  346. {
  347. var value = lit.Value;
  348. switch (value)
  349. {
  350. case "+Infinity":
  351. case "Infinity":
  352. return float.PositiveInfinity;
  353. case "-Infinity":
  354. return float.NegativeInfinity;
  355. case "NaN":
  356. return float.NaN;
  357. default:
  358. try
  359. {
  360. return float.Parse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo);
  361. }
  362. catch (Exception e)
  363. {
  364. throw new HoconException($"Could not convert `{value}` to float.", e);
  365. }
  366. }
  367. }
  368. public static implicit operator double(HoconImmutableLiteral lit)
  369. {
  370. var value = lit.Value;
  371. switch (value)
  372. {
  373. case "+Infinity":
  374. case "Infinity":
  375. return double.PositiveInfinity;
  376. case "-Infinity":
  377. return double.NegativeInfinity;
  378. case "NaN":
  379. return double.NaN;
  380. default:
  381. try
  382. {
  383. return double.Parse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo);
  384. }
  385. catch (Exception e)
  386. {
  387. throw new HoconException($"Could not convert `{value}` to double.", e);
  388. }
  389. }
  390. }
  391. public static implicit operator decimal(HoconImmutableLiteral lit)
  392. {
  393. var value = lit.Value;
  394. switch (value)
  395. {
  396. case "+Infinity":
  397. case "Infinity":
  398. case "-Infinity":
  399. case "NaN":
  400. throw new HoconException(
  401. $"Could not convert `{value}` to decimal. Decimal does not support Infinity and NaN");
  402. default:
  403. try
  404. {
  405. return decimal.Parse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo);
  406. }
  407. catch (Exception e)
  408. {
  409. throw new HoconException($"Could not convert `{value}` to decimal.", e);
  410. }
  411. }
  412. }
  413. private static readonly Regex TimeSpanRegex = new Regex(
  414. @"^(?<value>([0-9]+(\.[0-9]+)?))\s*(?<unit>(nanoseconds|nanosecond|nanos|nano|ns|microseconds|microsecond|micros|micro|us|milliseconds|millisecond|millis|milli|ms|seconds|second|s|minutes|minute|m|hours|hour|h|days|day|d))$",
  415. RegexOptions.Compiled);
  416. private static double ParsePositiveValue(string v)
  417. {
  418. var value = double.Parse(v, NumberFormatInfo.InvariantInfo);
  419. if (value < 0)
  420. throw new FormatException("Expected a positive value instead of " + value);
  421. return value;
  422. }
  423. public static implicit operator TimeSpan(HoconImmutableLiteral lit)
  424. {
  425. var res = lit.Value;
  426. if (res.Equals("infinite", StringComparison.OrdinalIgnoreCase)) //Not in Hocon spec
  427. return Timeout.InfiniteTimeSpan;
  428. var match = TimeSpanRegex.Match(res);
  429. if (match.Success)
  430. {
  431. var u = match.Groups["unit"].Value;
  432. var v = ParsePositiveValue(match.Groups["value"].Value);
  433. switch (u)
  434. {
  435. case "nanoseconds":
  436. case "nanosecond":
  437. case "nanos":
  438. case "nano":
  439. case "ns":
  440. return TimeSpan.FromTicks((long) Math.Round(TimeSpan.TicksPerMillisecond * v / 1000000.0));
  441. case "microseconds":
  442. case "microsecond":
  443. case "micros":
  444. case "micro":
  445. return TimeSpan.FromTicks((long) Math.Round(TimeSpan.TicksPerMillisecond * v / 1000.0));
  446. case "milliseconds":
  447. case "millisecond":
  448. case "millis":
  449. case "milli":
  450. case "ms":
  451. return TimeSpan.FromMilliseconds(v);
  452. case "seconds":
  453. case "second":
  454. case "s":
  455. return TimeSpan.FromSeconds(v);
  456. case "minutes":
  457. case "minute":
  458. case "m":
  459. return TimeSpan.FromMinutes(v);
  460. case "hours":
  461. case "hour":
  462. case "h":
  463. return TimeSpan.FromHours(v);
  464. case "days":
  465. case "day":
  466. case "d":
  467. return TimeSpan.FromDays(v);
  468. default:
  469. throw new HoconException($"Unknown TimeSpan unit:{u}");
  470. }
  471. }
  472. return TimeSpan.FromMilliseconds(ParsePositiveValue(res));
  473. }
  474. #endregion
  475. }
  476. }