NumToWordsBase.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Based on RSDN RusCurrency class
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. namespace FastReport.Functions
  6. {
  7. internal abstract class NumToWordsBase
  8. {
  9. #region Private Methods
  10. private string Str(decimal value, WordInfo senior, WordInfo junior)
  11. {
  12. bool minus = false;
  13. if (value < 0)
  14. {
  15. value = -value;
  16. minus = true;
  17. }
  18. long n = (long)value;
  19. int remainder = (int)((value - n + 0.005m) * 100);
  20. if (remainder >= 100)
  21. {
  22. n++;
  23. remainder = 0;
  24. }
  25. StringBuilder r = new StringBuilder();
  26. Str(n, senior, r);
  27. if (minus)
  28. r.Insert(0, GetMinus() + " ");
  29. if (junior != null)
  30. {
  31. r.Append(GetDecimalSeparator() + remainder.ToString("00 "));
  32. r.Append(Case(remainder, junior));
  33. }
  34. r[0] = char.ToUpper(r[0]);
  35. return r.ToString();
  36. }
  37. #endregion
  38. #region Protected Methods
  39. protected abstract string GetFixedWords(bool male, long value);
  40. protected abstract string GetTen(bool male, long value);
  41. protected abstract string GetHund(bool male, long value);
  42. protected abstract WordInfo GetThousands();
  43. protected abstract WordInfo GetMillions();
  44. protected abstract WordInfo GetMilliards();
  45. protected abstract WordInfo GetTrillions();
  46. protected abstract CurrencyInfo GetCurrency(string currencyName);
  47. protected abstract string GetZero();
  48. protected abstract string GetMinus();
  49. protected virtual void Str(long value, WordInfo senior, StringBuilder result)
  50. {
  51. if (value == 0)
  52. result.Append(GetZero() + " " + senior.many + " ");
  53. else
  54. {
  55. if (value % 1000 != 0)
  56. result.Append(Str1000(value, senior, 1));
  57. else
  58. result.Append(" " + senior.many + " ");
  59. value /= 1000;
  60. result.Insert(0, Str1000(value, GetThousands(), 2));
  61. value /= 1000;
  62. result.Insert(0, Str1000(value, GetMillions(), 3));
  63. value /= 1000;
  64. result.Insert(0, Str1000(value, GetMilliards(), 4));
  65. value /= 1000;
  66. result.Insert(0, Str1000(value, GetTrillions(), 5));
  67. result.Replace(" ", " ");
  68. }
  69. }
  70. protected virtual string Str1000(long value, WordInfo info, int counter)
  71. {
  72. long val = value % 1000;
  73. if (val == 0)
  74. return "";
  75. StringBuilder r = new StringBuilder();
  76. // add hundred
  77. string hund = GetHund(info.male, val);
  78. if (hund != "")
  79. r.Append(hund);
  80. // decide whether to use the 100-10 separator or not
  81. string sep100_10 = Get100_10Separator();
  82. if (value < 1000 && hund == "")
  83. sep100_10 = "";
  84. val = val % 100;
  85. if (val < GetFixedWordsCount())
  86. {
  87. // val is less than fixed words count (usually 20), get fixed words
  88. string frac20 = GetFixedWords(info.male, val);
  89. if (frac20 != "")
  90. r.Append(sep100_10 + frac20);
  91. }
  92. else
  93. {
  94. // val is greater than fixed words count (usually 20), get tens separately
  95. string ten = GetTen(info.male, val / 10);
  96. string frac10 = GetFixedWords(info.male, val % 10);
  97. // decide whether to use 10-1 separator or not
  98. if (ten != "" && frac10 != "")
  99. r.Append(sep100_10 + ten + Get10_1Separator() + frac10);
  100. else if (ten != "")
  101. r.Append(sep100_10 + ten);
  102. else if (frac10 != "")
  103. r.Append(sep100_10 + frac10);
  104. }
  105. // add currency/group word
  106. r.Append(" ");
  107. r.Append(Case(value, info));
  108. // make the result starting with letter and ending with space
  109. if (r.Length != 0)
  110. r.Append(" ");
  111. return r.ToString().TrimStart(' ');
  112. }
  113. protected virtual int GetFixedWordsCount()
  114. {
  115. return 20;
  116. }
  117. protected virtual string GetDecimalSeparator()
  118. {
  119. return "";
  120. }
  121. protected virtual string Get10_1Separator()
  122. {
  123. return " ";
  124. }
  125. protected virtual string Get100_10Separator()
  126. {
  127. return " ";
  128. }
  129. protected virtual string Case(long value, WordInfo info)
  130. {
  131. if (value % 100 == 1)
  132. return info.one;
  133. return info.many;
  134. }
  135. #endregion
  136. #region Public Methods
  137. public string ConvertCurrency(decimal value, string currencyName)
  138. {
  139. try
  140. {
  141. CurrencyInfo currency = GetCurrency(currencyName);
  142. return Str(value, currency.senior, currency.junior);
  143. }
  144. catch (KeyNotFoundException e)
  145. {
  146. throw new Exception("Currency \"" + currencyName + "\" is not defined in the \"" + GetType().Name +
  147. "\" converter.");
  148. }
  149. catch (NotImplementedException e)
  150. {
  151. throw new Exception("Method " + e.TargetSite.ToString() + " wasn't implemented");
  152. }
  153. catch (Exception e)
  154. {
  155. throw new Exception("There is an exception - "+e.ToString());
  156. }
  157. }
  158. public string ConvertNumber(decimal value, bool male, string one, string two, string many)
  159. {
  160. return Str(value, new WordInfo(male, one, two, many), null);
  161. }
  162. public string ConvertNumber(decimal value, bool male,
  163. string seniorOne, string seniorTwo, string seniorMany,
  164. string juniorOne, string juniorTwo, string juniorMany)
  165. {
  166. return Str(value,
  167. new WordInfo(male, seniorOne, seniorTwo, seniorMany),
  168. new WordInfo(male, juniorOne, juniorTwo, juniorMany));
  169. }
  170. #endregion
  171. }
  172. internal class WordInfo
  173. {
  174. public bool male;
  175. public string one;
  176. public string two;
  177. public string many;
  178. public WordInfo(bool male, string one, string two, string many)
  179. {
  180. this.male = male;
  181. this.one = one;
  182. this.two = two;
  183. this.many = many;
  184. }
  185. public WordInfo(string one, string many)
  186. : this(true, one, many, many)
  187. {
  188. }
  189. public WordInfo(string all)
  190. : this(true, all, all, all)
  191. {
  192. }
  193. }
  194. internal class CurrencyInfo
  195. {
  196. public WordInfo senior;
  197. public WordInfo junior;
  198. public CurrencyInfo(WordInfo senior, WordInfo junior)
  199. {
  200. this.senior = senior;
  201. this.junior = junior;
  202. }
  203. }
  204. }