FormatterHelper.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using System.Linq;
  2. using System.Text;
  3. using System.Text.RegularExpressions;
  4. using UnityEngine;
  5. namespace XCharts.Runtime
  6. {
  7. public static class FormatterHelper
  8. {
  9. public const string PH_NN = "\n";
  10. private static Regex s_Regex = new Regex(@"{([a-h|.|y]\d*)(:\d+(-\d+)?)?(:[c-g|x|p|r]\d*|:0\.#*)?}", RegexOptions.IgnoreCase);
  11. private static Regex s_RegexSub = new Regex(@"(0\.#*)|(\d+-\d+)|(\w+)|(\.)", RegexOptions.IgnoreCase);
  12. private static Regex s_RegexN = new Regex(@"^\d+", RegexOptions.IgnoreCase);
  13. private static Regex s_RegexN_N = new Regex(@"\d+-\d+", RegexOptions.IgnoreCase);
  14. private static Regex s_RegexFn = new Regex(@"[c-g|x|p|r]\d*|0\.#*", RegexOptions.IgnoreCase);
  15. private static Regex s_RegexNewLine = new Regex(@"[\\|/]+n|</br>|<br>|<br/>", RegexOptions.IgnoreCase);
  16. private static Regex s_RegexForAxisLabel = new Regex(@"{value(:[c-g|x|p|r]\d*)?}", RegexOptions.IgnoreCase);
  17. private static Regex s_RegexSubForAxisLabel = new Regex(@"(value)|([c-g|x|p|r]\d*)", RegexOptions.IgnoreCase);
  18. private static Regex s_RegexForSerieLabel = new Regex(@"{[a-h|\.|y]\d*(:[c-g|x|p|r]\d*)?}", RegexOptions.IgnoreCase);
  19. private static Regex s_RegexSubForSerieLabel = new Regex(@"(\.)|([a-h|y]\d*)|([c-g|x|p|r]\d*)", RegexOptions.IgnoreCase);
  20. public static bool NeedFormat(string content)
  21. {
  22. return !string.IsNullOrEmpty(content) && content.IndexOf('{') >= 0;
  23. }
  24. /// <summary>
  25. /// 替换字符串中的通配符,支持的通配符有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}、{h}、{y}。
  26. /// </summary>
  27. /// <param name="content">要替换的字符串</param>
  28. /// <param name="dataIndex">选中的数据项serieData索引</param>
  29. /// <param name="numericFormatter">默认的数字格式化</param>
  30. /// <param name="serie">选中的serie</param>
  31. /// <param name="series">所有serie</param>
  32. /// <param name="theme">用来获取指定index的颜色</param>
  33. /// <param name="category">选中的类目,一般用在折线图和柱状图</param>
  34. /// <returns></returns>
  35. public static bool ReplaceContent(ref string content, int dataIndex, string numericFormatter, Serie serie,
  36. BaseChart chart, string colorName = null)
  37. {
  38. var foundDot = false;
  39. var mc = s_Regex.Matches(content);
  40. if (dataIndex < 0)
  41. {
  42. dataIndex = serie != null ? serie.context.pointerItemDataIndex : 0;
  43. }
  44. foreach (var m in mc)
  45. {
  46. var old = m.ToString();
  47. var args = s_RegexSub.Matches(m.ToString());
  48. var argsCount = args.Count;
  49. if (argsCount <= 0) continue;
  50. int targetIndex = 0;
  51. char p = GetSerieIndex(args[0].ToString(), ref targetIndex);
  52. if (targetIndex >= 0)
  53. {
  54. serie = chart.GetSerie(targetIndex);
  55. if (serie == null) continue;
  56. }
  57. else if (serie != null)
  58. {
  59. targetIndex = serie.index;
  60. }
  61. else
  62. {
  63. serie = chart.GetSerie(0);
  64. targetIndex = 0;
  65. }
  66. if (serie == null) continue;
  67. if (p == '.' || p == 'h' || p == 'H')
  68. {
  69. var bIndex = dataIndex;
  70. if (argsCount >= 2)
  71. {
  72. var args1Str = args[1].ToString();
  73. if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str);
  74. }
  75. var color = string.IsNullOrEmpty(colorName) ?
  76. (Color)chart.GetMarkColor(serie, serie.GetSerieData(bIndex)) :
  77. SeriesHelper.GetNameColor(chart, bIndex, colorName);
  78. if (p == '.')
  79. {
  80. content = content.Replace(old, ChartCached.ColorToDotStr(color));
  81. foundDot = true;
  82. }
  83. else
  84. {
  85. content = content.Replace(old, "#" + ChartCached.ColorToStr(color));
  86. }
  87. }
  88. else if (p == 'a' || p == 'A')
  89. {
  90. if (argsCount == 1)
  91. {
  92. content = content.Replace(old, serie.serieName);
  93. }
  94. }
  95. else if (p == 'b' || p == 'B' || p == 'e' || p == 'E')
  96. {
  97. var bIndex = dataIndex;
  98. if (argsCount >= 2)
  99. {
  100. var args1Str = args[1].ToString();
  101. if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str);
  102. }
  103. var needCategory = p != 'e' && p != 'E' && serie.defaultColorBy != SerieColorBy.Data;
  104. if (needCategory)
  105. {
  106. var category = chart.GetTooltipCategory(serie);
  107. content = content.Replace(old, category);
  108. }
  109. else
  110. {
  111. var serieData = serie.GetSerieData(bIndex);
  112. content = content.Replace(old, serieData.name);
  113. }
  114. }
  115. else if (p == 'g' || p == 'G')
  116. {
  117. content = content.Replace(old, ChartCached.NumberToStr(serie.dataCount, ""));
  118. }
  119. else if (p == 'y' || p == 'Y')
  120. {
  121. if (chart != null)
  122. {
  123. var yAxis = chart.GetChartComponent<YAxis>(0);
  124. if (yAxis != null)
  125. {
  126. var bIndex = dataIndex;
  127. if (argsCount >= 2)
  128. {
  129. var args1Str = args[1].ToString();
  130. if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str);
  131. if (s_RegexFn.IsMatch(args1Str)) numericFormatter = args1Str;
  132. }
  133. if (yAxis.IsCategory())
  134. {
  135. var yCategory = yAxis.GetData(bIndex);
  136. content = content.Replace(old, yCategory);
  137. }
  138. else
  139. {
  140. var value = yAxis.context.pointerValue;
  141. content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter));
  142. }
  143. }
  144. }
  145. }
  146. else if (p == 'c' || p == 'C' || p == 'd' || p == 'D' || p == 'f' || p == 'f')
  147. {
  148. var isPercent = p == 'd' || p == 'D';
  149. var isTotal = p == 'f' || p == 'f';
  150. var bIndex = dataIndex;
  151. var dimensionIndex = -1;
  152. if (argsCount >= 2)
  153. {
  154. var args1Str = args[1].ToString();
  155. if (s_RegexFn.IsMatch(args1Str))
  156. {
  157. numericFormatter = args1Str;
  158. }
  159. else if (s_RegexN_N.IsMatch(args1Str))
  160. {
  161. var temp = args1Str.Split('-');
  162. bIndex = int.Parse(temp[0]);
  163. dimensionIndex = int.Parse(temp[1]);
  164. }
  165. else if (s_RegexN.IsMatch(args1Str))
  166. {
  167. dimensionIndex = int.Parse(args1Str);
  168. }
  169. else
  170. {
  171. Debug.LogError("unmatch:" + args1Str);
  172. continue;
  173. }
  174. }
  175. if (argsCount >= 3)
  176. {
  177. numericFormatter = args[2].ToString();
  178. }
  179. if (dimensionIndex == -1) dimensionIndex = 1;
  180. if (numericFormatter == string.Empty)
  181. {
  182. numericFormatter = SerieHelper.GetNumericFormatter(serie, serie.GetSerieData(bIndex), "");
  183. }
  184. var value = serie.GetData(bIndex, dimensionIndex);
  185. var ignore = serie.IsIgnoreIndex(bIndex);
  186. if (isPercent)
  187. {
  188. var total = serie.GetDataTotal(dimensionIndex, serie.GetSerieData(bIndex));
  189. var percent = total == 0 ? 0 : value / total * 100;
  190. content = content.Replace(old, ChartCached.FloatToStr(percent, numericFormatter));
  191. }
  192. else if (isTotal)
  193. {
  194. var total = serie.GetDataTotal(dimensionIndex, serie.GetSerieData(bIndex));
  195. content = content.Replace(old, ChartCached.FloatToStr(total, numericFormatter));
  196. }
  197. else
  198. {
  199. if (ignore)
  200. content = content.Replace(old, "-");
  201. else
  202. content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter));
  203. }
  204. }
  205. }
  206. content = s_RegexNewLine.Replace(content, PH_NN);
  207. return foundDot;
  208. }
  209. public static void ReplaceSerieLabelContent(ref string content, string numericFormatter, int dataCount, double value, double total,
  210. string serieName, string category, string dataName, Color color, SerieData serieData, BaseChart chart = null)
  211. {
  212. var mc = s_RegexForSerieLabel.Matches(content);
  213. foreach (var m in mc)
  214. {
  215. var old = m.ToString();
  216. var args = s_RegexSubForSerieLabel.Matches(old);
  217. var argsCount = args.Count;
  218. if (argsCount <= 0) continue;
  219. var pstr = args[0].ToString();
  220. var p = pstr.ElementAt(0);
  221. var pIndex = -1;
  222. if (pstr.Length > 1)
  223. {
  224. int.TryParse(pstr.Substring(1, pstr.Length - 1), out pIndex);
  225. }
  226. if (argsCount >= 2)
  227. {
  228. numericFormatter = args[1].ToString();
  229. }
  230. if (p == '.')
  231. {
  232. content = content.Replace(old, ChartCached.ColorToDotStr(color));
  233. }
  234. else if (p == 'a' || p == 'A')
  235. {
  236. content = content.Replace(old, serieName);
  237. }
  238. else if (p == 'b' || p == 'B')
  239. {
  240. content = content.Replace(old, category);
  241. }
  242. else if (p == 'e' || p == 'E')
  243. {
  244. content = content.Replace(old, dataName);
  245. }
  246. else if (p == 'd' || p == 'D')
  247. {
  248. if (serieData != null && serieData.ignore)
  249. content = content.Replace(old, "-");
  250. else
  251. {
  252. var rate = pIndex >= 0 && serieData != null ?
  253. (value == 0 ? 0 : serieData.GetData(pIndex) / value * 100) :
  254. (total == 0 ? 0 : value / total * 100);
  255. content = content.Replace(old, ChartCached.NumberToStr(rate, numericFormatter));
  256. }
  257. }
  258. else if (p == 'c' || p == 'C')
  259. {
  260. if (serieData != null && serieData.ignore)
  261. content = content.Replace(old, "-");
  262. else if (serieData != null && pIndex >= 0)
  263. content = content.Replace(old, ChartCached.NumberToStr(serieData.GetData(pIndex), numericFormatter));
  264. else
  265. content = content.Replace(old, ChartCached.NumberToStr(value, numericFormatter));
  266. }
  267. else if (p == 'f' || p == 'f')
  268. {
  269. content = content.Replace(old, ChartCached.NumberToStr(total, numericFormatter));
  270. }
  271. else if (p == 'g' || p == 'G')
  272. {
  273. content = content.Replace(old, ChartCached.NumberToStr(dataCount, numericFormatter));
  274. }
  275. else if (p == 'h' || p == 'H')
  276. {
  277. content = content.Replace(old, "#" + ChartCached.ColorToStr(color));
  278. }
  279. else if (p == 'y' || p == 'Y')
  280. {
  281. if (chart != null)
  282. {
  283. var yAxis = chart.GetChartComponent<YAxis>(0);
  284. if (yAxis != null)
  285. {
  286. if (yAxis.IsCategory())
  287. {
  288. var yCategory = yAxis.GetData(pIndex >= 0 ? pIndex : (int)value);
  289. content = content.Replace(old, yCategory);
  290. }
  291. else
  292. {
  293. content = content.Replace(old, ChartCached.NumberToStr(value, numericFormatter));
  294. }
  295. }
  296. }
  297. }
  298. }
  299. content = TrimAndReplaceLine(content);
  300. }
  301. private static char GetSerieIndex(string strType, ref int index)
  302. {
  303. index = -1;
  304. if (strType.Length > 1)
  305. {
  306. if (!int.TryParse(strType.Substring(1), out index))
  307. {
  308. index = -1;
  309. }
  310. }
  311. return strType.ElementAt(0);
  312. }
  313. public static string TrimAndReplaceLine(StringBuilder sb)
  314. {
  315. return TrimAndReplaceLine(sb.ToString());
  316. }
  317. public static string TrimAndReplaceLine(string content)
  318. {
  319. return s_RegexNewLine.Replace(content.Trim(), PH_NN);
  320. }
  321. public static void ReplaceAxisLabelContent(ref string content, string numericFormatter, double value)
  322. {
  323. var mc = s_RegexForAxisLabel.Matches(content);
  324. foreach (var m in mc)
  325. {
  326. var old = m.ToString();
  327. var args = s_RegexSubForAxisLabel.Matches(m.ToString());
  328. var argsCount = args.Count;
  329. if (argsCount <= 0) continue;
  330. if (argsCount >= 2)
  331. {
  332. numericFormatter = args[1].ToString();
  333. }
  334. content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter));
  335. }
  336. content = TrimAndReplaceLine(content);
  337. }
  338. public static void ReplaceAxisLabelContent(ref string content, string value)
  339. {
  340. var mc = s_RegexForAxisLabel.Matches(content);
  341. foreach (var m in mc)
  342. {
  343. var old = m.ToString();
  344. var args = s_RegexSubForAxisLabel.Matches(m.ToString());
  345. var argsCount = args.Count;
  346. if (argsCount <= 0) continue;
  347. content = content.Replace(old, value);
  348. }
  349. content = TrimAndReplaceLine(content);
  350. }
  351. }
  352. }