123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- using System.Data;
- using System.Text.RegularExpressions;
- namespace etoy
- {
- sealed class CommonTableCsvParser
- {
- // excel 上的规则
- // 默认0列为主键(long/int) 0-3行按顺序表示:字段名、数据类型、数据标签(Tag)、字段描述, 正式数据从 4行开始
- // * 0 1 2 3 4 .... 备注
- // 0 #字段名 no name xxxx yyyy (‘#’开头忽略)
- // 1 #类型 int string repeated int repeated CustomType
- // 2 #Tags Tag1 Tag2 Tag1;Tag2;Tag3 Tag4 (';'分号隔开)
- // 3 #字段描述 主键 名字 XXXXXXX YYYYYY
- // 4 #正式数据
- const char TAG_SEPARATOR = ';';
- const char TAG_SEPARATOR_VALUE = ':';
- const string FIELD_REPEATED = "[]";
- public Table Parse(DataTable dt, string path)
- {
- //string tableDescription = dt.Rows[0][0].ToString().ToDescription();
- TableTag tag = GetTableTag(dt, out string tableName, path);
- Dictionary<int, FieldInfo> fieldInfos = GetFieldInfos(dt, tableName);
- var table = new Table
- {
- Path = path,
- Name = tableName,
- Description = tableName,
- TableTag = tag,
- FieldInfos = fieldInfos.Values.ToArray()
- };
- var rowOffset = 4;
- if (TryGetTableEnum(dt, fieldInfos, tableName, out var @enum))
- {
- table.tableEnum = @enum;
- rowOffset = 5;
- }
- table.Rows = GetRows(dt, fieldInfos, rowOffset);
- return table;
- }
- Dictionary<int, FieldInfo> GetFieldInfos(DataTable dt, string tableName)
- {
- int columnCount = dt.Columns.Count;
- int rowOffset = 0;
- Dictionary<int, FieldInfo> fieldInfos = new Dictionary<int, FieldInfo>();
- for (int i = 0; i < columnCount; i++)
- {
- string fieldName = dt.Rows[rowOffset][i].ToString().ToFieldName();
- if (string.IsNullOrEmpty(fieldName))
- break;
- if (fieldName.StartsWith("#"))
- continue;
- //const string REPEATED_STR = "repeated";
- string fieldType = dt.Rows[rowOffset + 1][i].ToString().Trim();
- bool isRepeaded = fieldType.Contains(FIELD_REPEATED);
- if (isRepeaded) fieldType = fieldType.Substring(0, fieldType.IndexOf(FIELD_REPEATED)).Trim();
- string tags = dt.Rows[rowOffset + 2][i].ToString().ToTags();
- string description = dt.Rows[rowOffset + 3][i].ToString().ToDescription();
- FieldInfo field = new FieldInfo();
- field.Column = i;
- field.FieldName = fieldName;
- field.FieldType = fieldType;
- field.IsRepeated = isRepeaded;
- field.Description = description;
- field.Tags = tags;
- field.TagInfos = GetFieldTagInfos(rowOffset + 2, i, tags, tableName);
- fieldInfos[i] = field;
- }
- return fieldInfos;
- }
- FieldTagInfo[] GetFieldTagInfos(int excelRow, int excelCol, string tags, string tableName)
- {
- if (string.IsNullOrEmpty(tags))
- return new FieldTagInfo[0];
- string[] fieldTags = tags.Split(TAG_SEPARATOR);
- if (fieldTags.Length > 0)
- {
- List<FieldTagInfo> result = new List<FieldTagInfo>();
- for (int i = 0, length = fieldTags.Length; i < length; i++)
- {
- string[] kv = fieldTags[i].Split(TAG_SEPARATOR_VALUE);
- if (kv.Length == 1)
- {
- bool valid = Enum.TryParse<FieldTag>(kv[0], true, out var fieldTag);
- if (!valid)
- { // tag字段只写了 'c'或's'或'sc'
- if (kv[0].Contains('c', StringComparison.OrdinalIgnoreCase) || kv[0].Contains('s', StringComparison.OrdinalIgnoreCase)){
- result.Add(new FieldTagInfo()
- {
- Key = FieldTag.Output,
- Value = kv[0]
- });
- }
- else
- throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
- }
- else
- {
- FieldTagInfo info = new FieldTagInfo();
- info.Key = fieldTag;
- info.Value = null;
- result.Add(info);
- }
- }
- else if (kv.Length == 2)
- {
- bool valid = Enum.TryParse<FieldTag>(kv[0], true, out var fieldTag);
- if (!valid)
- throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
- FieldTagInfo info = new FieldTagInfo();
- info.Key = fieldTag;
- info.Value = kv[1];
- result.Add(info);
- }
- else
- throw new Exception($"配置表({tableName}) 不支持该Tag格式: [{tags}], 应满足以下格式: [Tag1Key:Tag1Value;Tag2Key:Tag2Value] (行: {excelRow}, 列: {excelCol})");
- }
- return result.ToArray();
- }
- return new FieldTagInfo[0];
- }
- Regex _enumRegex = new Regex(@"enum{name:(\w+)\s*?;\s*?key:(\w+)\s*?;\s*?value:(\w+)\s*?;\s*?summary:(\w+)\s*?;\s*?}");
- bool TryGetTableEnum(DataTable dt, Dictionary<int, FieldInfo> fieldInfos, string tableName, out TableEnum tableEnum)
- {
- // 第5行第一列
- var cellVal = dt.Rows[4][0].ToString().ToCellValue();
- var match = _enumRegex.Match(cellVal);
- if (match.Success)
- {
- var name = match.Groups[1].Value;
- var key = match.Groups[2].Value;
- var value = match.Groups[3].Value;
- var summary = match.Groups[4].Value;
- var fieldNames = new HashSet<string>();
- foreach(var fieldInfo in fieldInfos.Values)
- {
- if (fieldInfo.IsRepeated)
- continue;
- fieldNames.Add(fieldInfo.FieldName);
- }
- if (!fieldNames.Contains(key))
- {
- throw new Exception($"配置表({tableName}) 枚举类型字段名错误(行:5,列:1): {cellVal}");
- }
- if (!fieldNames.Contains(value))
- {
- throw new Exception($"配置表({tableName}) 枚举值字段名错误(行:5,列:1): {cellVal}");
- }
- if (!fieldNames.Contains(summary))
- {
- throw new Exception($"配置表({tableName}) 枚举说明字段名错误(行:5,列:1): {cellVal}");
- }
- tableEnum = new TableEnum()
- {
- name = name,
- key = key,
- value = value,
- summary = summary,
- };
- return true;
- }
- tableEnum = null;
- return false;
- }
- List<Row> GetRows(DataTable dt, Dictionary<int, FieldInfo> fieldInfos, int rowOffset = 4)
- {
- int rowCount = dt.Rows.Count;
- int columnCount = dt.Columns.Count;
- List<Row> rows = new List<Row>();
- for (int i = rowOffset; i < rowCount; i++)
- {
- int emptyCount = 0;
- List<Cell> cells = new List<Cell>();
- for (int j = 0; j < columnCount; j++)
- {
- if (fieldInfos.TryGetValue(j, out var fieldInfo))
- {
- var value = dt.Rows[i][j].ToString().ToCellValue();
- if (string.IsNullOrEmpty(value))
- emptyCount++;
- // last set value
- cells.Add(new Cell
- {
- Row = i,
- Column = j,
- Value = value,
- FieldInfo = fieldInfo
- });
- }
- }
- //-------------------------------------------
- // 过滤掉【全部为空】的情况 (#:filter field)
- //-------------------------------------------
- // a b c d #e #f
- // * * * * * *
- // * * * * * *
- // [ ] * *
- // [ filter ] * *
- // [ ] * *
- //-------------------------------------------
- if (emptyCount < cells.Count)
- {
- cells.Sort((a, b) => a.Column.CompareTo(b.Column));
- Row row = new Row();
- row.Cells = cells;
- rows.Add(row);
- }
- }
- return rows;
- }
- TableTag GetTableTag(DataTable dt, out string newName, string path)
- {
- string tableName = string.Empty;
- //if (dt.Columns.Count > 2)
- //{
- // _ = dt.Rows[0][1].ToString();//表名
- // tableName = dt.Rows[0][2].ToString().ToTableName();
- //}
- tableName = string.IsNullOrEmpty(tableName) ? Path.GetFileNameWithoutExtension(path).ToTableName() : tableName;
- const string C = "c_";
- const string S = "s_";
- const string E = "e_";
- if (tableName.StartsWith(C))
- {
- newName = tableName.Substring(C.Length);
- return TableTag.Client;
- }
- if (tableName.StartsWith(S))
- {
- newName = tableName.Substring(S.Length);
- return TableTag.Server;
- }
- if (tableName.StartsWith(E))
- {
- newName = tableName.Substring(E.Length);
- return TableTag.Editor;
- }
- newName = tableName;
- return TableTag.All;
- }
- }
- }
|