CommonTableCsvParser.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using System.Data;
  2. using System.Text.RegularExpressions;
  3. namespace etoy
  4. {
  5. sealed class CommonTableCsvParser
  6. {
  7. // excel 上的规则
  8. // 默认0列为主键(long/int) 0-3行按顺序表示:字段名、数据类型、数据标签(Tag)、字段描述, 正式数据从 4行开始
  9. // * 0 1 2 3 4 .... 备注
  10. // 0 #字段名 no name xxxx yyyy (‘#’开头忽略)
  11. // 1 #类型 int string repeated int repeated CustomType
  12. // 2 #Tags Tag1 Tag2 Tag1;Tag2;Tag3 Tag4 (';'分号隔开)
  13. // 3 #字段描述 主键 名字 XXXXXXX YYYYYY
  14. // 4 #正式数据
  15. const char TAG_SEPARATOR = ';';
  16. const char TAG_SEPARATOR_VALUE = ':';
  17. const string FIELD_REPEATED = "[]";
  18. public Table Parse(DataTable dt, string path)
  19. {
  20. //string tableDescription = dt.Rows[0][0].ToString().ToDescription();
  21. TableTag tag = GetTableTag(dt, out string tableName, path);
  22. Dictionary<int, FieldInfo> fieldInfos = GetFieldInfos(dt, tableName);
  23. var table = new Table
  24. {
  25. Path = path,
  26. Name = tableName,
  27. Description = tableName,
  28. TableTag = tag,
  29. FieldInfos = fieldInfos.Values.ToArray()
  30. };
  31. var rowOffset = 4;
  32. if (TryGetTableEnum(dt, fieldInfos, tableName, out var @enum))
  33. {
  34. table.tableEnum = @enum;
  35. rowOffset = 5;
  36. }
  37. table.Rows = GetRows(dt, fieldInfos, rowOffset);
  38. return table;
  39. }
  40. Dictionary<int, FieldInfo> GetFieldInfos(DataTable dt, string tableName)
  41. {
  42. int columnCount = dt.Columns.Count;
  43. int rowOffset = 0;
  44. Dictionary<int, FieldInfo> fieldInfos = new Dictionary<int, FieldInfo>();
  45. for (int i = 0; i < columnCount; i++)
  46. {
  47. string fieldName = dt.Rows[rowOffset][i].ToString().ToFieldName();
  48. if (string.IsNullOrEmpty(fieldName))
  49. break;
  50. if (fieldName.StartsWith("#"))
  51. continue;
  52. //const string REPEATED_STR = "repeated";
  53. string fieldType = dt.Rows[rowOffset + 1][i].ToString().Trim();
  54. bool isRepeaded = fieldType.Contains(FIELD_REPEATED);
  55. if (isRepeaded) fieldType = fieldType.Substring(0, fieldType.IndexOf(FIELD_REPEATED)).Trim();
  56. string tags = dt.Rows[rowOffset + 2][i].ToString().ToTags();
  57. string description = dt.Rows[rowOffset + 3][i].ToString().ToDescription();
  58. FieldInfo field = new FieldInfo();
  59. field.Column = i;
  60. field.FieldName = fieldName;
  61. field.FieldType = fieldType;
  62. field.IsRepeated = isRepeaded;
  63. field.Description = description;
  64. field.Tags = tags;
  65. field.TagInfos = GetFieldTagInfos(rowOffset + 2, i, tags, tableName);
  66. fieldInfos[i] = field;
  67. }
  68. return fieldInfos;
  69. }
  70. FieldTagInfo[] GetFieldTagInfos(int excelRow, int excelCol, string tags, string tableName)
  71. {
  72. if (string.IsNullOrEmpty(tags))
  73. return new FieldTagInfo[0];
  74. string[] fieldTags = tags.Split(TAG_SEPARATOR);
  75. if (fieldTags.Length > 0)
  76. {
  77. List<FieldTagInfo> result = new List<FieldTagInfo>();
  78. for (int i = 0, length = fieldTags.Length; i < length; i++)
  79. {
  80. string[] kv = fieldTags[i].Split(TAG_SEPARATOR_VALUE);
  81. if (kv.Length == 1)
  82. {
  83. bool valid = Enum.TryParse<FieldTag>(kv[0], true, out var fieldTag);
  84. if (!valid)
  85. { // tag字段只写了 'c'或's'或'sc'
  86. if (kv[0].Contains('c', StringComparison.OrdinalIgnoreCase) || kv[0].Contains('s', StringComparison.OrdinalIgnoreCase)){
  87. result.Add(new FieldTagInfo()
  88. {
  89. Key = FieldTag.Output,
  90. Value = kv[0]
  91. });
  92. }
  93. else
  94. throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
  95. }
  96. else
  97. {
  98. FieldTagInfo info = new FieldTagInfo();
  99. info.Key = fieldTag;
  100. info.Value = null;
  101. result.Add(info);
  102. }
  103. }
  104. else if (kv.Length == 2)
  105. {
  106. bool valid = Enum.TryParse<FieldTag>(kv[0], true, out var fieldTag);
  107. if (!valid)
  108. throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
  109. FieldTagInfo info = new FieldTagInfo();
  110. info.Key = fieldTag;
  111. info.Value = kv[1];
  112. result.Add(info);
  113. }
  114. else
  115. throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
  116. }
  117. return result.ToArray();
  118. }
  119. return new FieldTagInfo[0];
  120. }
  121. Regex _enumRegex = new Regex(@"enum{name:(\w+)\s*?;\s*?key:(\w+)\s*?;\s*?value:(\w+)\s*?;\s*?summary:(\w+)\s*?;\s*?}");
  122. bool TryGetTableEnum(DataTable dt, Dictionary<int, FieldInfo> fieldInfos, string tableName, out TableEnum tableEnum)
  123. {
  124. // 第5行第一列
  125. var cellVal = dt.Rows[4][0].ToString().ToCellValue();
  126. var match = _enumRegex.Match(cellVal);
  127. if (match.Success)
  128. {
  129. var name = match.Groups[1].Value;
  130. var key = match.Groups[2].Value;
  131. var value = match.Groups[3].Value;
  132. var summary = match.Groups[4].Value;
  133. var fieldNames = new HashSet<string>();
  134. foreach(var fieldInfo in fieldInfos.Values)
  135. {
  136. if (fieldInfo.IsRepeated)
  137. continue;
  138. fieldNames.Add(fieldInfo.FieldName);
  139. }
  140. if (!fieldNames.Contains(key))
  141. {
  142. throw new Exception($"配置表({tableName}) 枚举类型字段名错误(行:5,列:1): {cellVal}");
  143. }
  144. if (!fieldNames.Contains(value))
  145. {
  146. throw new Exception($"配置表({tableName}) 枚举值字段名错误(行:5,列:1): {cellVal}");
  147. }
  148. if (!fieldNames.Contains(summary))
  149. {
  150. throw new Exception($"配置表({tableName}) 枚举说明字段名错误(行:5,列:1): {cellVal}");
  151. }
  152. tableEnum = new TableEnum()
  153. {
  154. name = name,
  155. key = key,
  156. value = value,
  157. summary = summary,
  158. };
  159. return true;
  160. }
  161. tableEnum = null;
  162. return false;
  163. }
  164. List<Row> GetRows(DataTable dt, Dictionary<int, FieldInfo> fieldInfos, int rowOffset = 4)
  165. {
  166. int rowCount = dt.Rows.Count;
  167. int columnCount = dt.Columns.Count;
  168. List<Row> rows = new List<Row>();
  169. for (int i = rowOffset; i < rowCount; i++)
  170. {
  171. int emptyCount = 0;
  172. List<Cell> cells = new List<Cell>();
  173. for (int j = 0; j < columnCount; j++)
  174. {
  175. if (fieldInfos.TryGetValue(j, out var fieldInfo))
  176. {
  177. var value = dt.Rows[i][j].ToString().ToCellValue();
  178. if (string.IsNullOrEmpty(value))
  179. emptyCount++;
  180. // last set value
  181. cells.Add(new Cell
  182. {
  183. Row = i,
  184. Column = j,
  185. Value = value,
  186. FieldInfo = fieldInfo
  187. });
  188. }
  189. }
  190. //-------------------------------------------
  191. // 过滤掉【全部为空】的情况 (#:filter field)
  192. //-------------------------------------------
  193. // a b c d #e #f
  194. // * * * * * *
  195. // * * * * * *
  196. // [ ] * *
  197. // [ filter ] * *
  198. // [ ] * *
  199. //-------------------------------------------
  200. if (emptyCount < cells.Count)
  201. {
  202. cells.Sort((a, b) => a.Column.CompareTo(b.Column));
  203. Row row = new Row();
  204. row.Cells = cells;
  205. rows.Add(row);
  206. }
  207. }
  208. return rows;
  209. }
  210. TableTag GetTableTag(DataTable dt, out string newName, string path)
  211. {
  212. string tableName = string.Empty;
  213. //if (dt.Columns.Count > 2)
  214. //{
  215. // _ = dt.Rows[0][1].ToString();//表名
  216. // tableName = dt.Rows[0][2].ToString().ToTableName();
  217. //}
  218. tableName = string.IsNullOrEmpty(tableName) ? Path.GetFileNameWithoutExtension(path).ToTableName() : tableName;
  219. const string C = "c_";
  220. const string S = "s_";
  221. const string E = "e_";
  222. if (tableName.StartsWith(C))
  223. {
  224. newName = tableName.Substring(C.Length);
  225. return TableTag.Client;
  226. }
  227. if (tableName.StartsWith(S))
  228. {
  229. newName = tableName.Substring(S.Length);
  230. return TableTag.Server;
  231. }
  232. if (tableName.StartsWith(E))
  233. {
  234. newName = tableName.Substring(E.Length);
  235. return TableTag.Editor;
  236. }
  237. newName = tableName;
  238. return TableTag.All;
  239. }
  240. }
  241. }