838 lines
35 KiB
C#
838 lines
35 KiB
C#
using Luban.Common.Utils;
|
||
using Luban.Job.Cfg.Datas;
|
||
using Luban.Job.Cfg.DataSources.Excel;
|
||
using Luban.Job.Cfg.RawDefs;
|
||
using Luban.Job.Cfg.Utils;
|
||
using Luban.Job.Common.Defs;
|
||
using Luban.Job.Common.RawDefs;
|
||
using Luban.Job.Common.Types;
|
||
using Luban.Server.Common;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Xml.Linq;
|
||
|
||
namespace Luban.Job.Cfg.Defs
|
||
{
|
||
class CfgDefLoader : CommonDefLoader
|
||
{
|
||
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||
|
||
private readonly List<string> _importExcelTableFiles = new();
|
||
private readonly List<string> _importExcelEnumFiles = new();
|
||
private readonly List<string> _importExcelBeanFiles = new();
|
||
|
||
|
||
private readonly List<Branch> _branches = new();
|
||
|
||
private readonly List<Table> _cfgTables = new List<Table>();
|
||
|
||
private readonly List<Service> _cfgServices = new List<Service>();
|
||
|
||
private readonly List<Group> _cfgGroups = new List<Group>();
|
||
|
||
private readonly List<string> _defaultGroups = new List<string>();
|
||
|
||
public CfgDefLoader(RemoteAgent agent) : base(agent)
|
||
{
|
||
RegisterRootDefineHandler("importexcel", AddImportExcel);
|
||
RegisterRootDefineHandler("branch", AddBranch);
|
||
RegisterRootDefineHandler("service", AddService);
|
||
RegisterRootDefineHandler("group", AddGroup);
|
||
|
||
RegisterModuleDefineHandler("table", AddTable);
|
||
|
||
|
||
IsBeanFieldMustDefineId = false;
|
||
}
|
||
|
||
public Defines BuildDefines()
|
||
{
|
||
return new Defines()
|
||
{
|
||
TopModule = TopModule,
|
||
Branches = _branches,
|
||
Consts = this._consts,
|
||
Enums = _enums,
|
||
Beans = _beans,
|
||
Tables = _cfgTables,
|
||
Services = _cfgServices,
|
||
Groups = _cfgGroups,
|
||
};
|
||
}
|
||
|
||
private static readonly List<string> _excelImportRequireAttrs = new List<string> { "name", "type" };
|
||
private void AddImportExcel(XElement e)
|
||
{
|
||
ValidAttrKeys(e, null, _excelImportRequireAttrs);
|
||
var importName = XmlUtil.GetRequiredAttribute(e, "name");
|
||
if (string.IsNullOrWhiteSpace(importName))
|
||
{
|
||
throw new Exception("importexcel 属性name不能为空");
|
||
}
|
||
var type = XmlUtil.GetRequiredAttribute(e, "type");
|
||
if (string.IsNullOrWhiteSpace(type))
|
||
{
|
||
throw new Exception($"importexcel name:'{importName}' type属性不能为空");
|
||
}
|
||
switch (type)
|
||
{
|
||
case "table": this._importExcelTableFiles.Add(importName); break;
|
||
case "enum": this._importExcelEnumFiles.Add(importName); break;
|
||
case "bean": this._importExcelBeanFiles.Add(importName); break;
|
||
default: throw new Exception($"importexcel name:'{importName}' type:'{type}' 不合法. 有效值为 table|enum|bean");
|
||
}
|
||
}
|
||
|
||
private static readonly List<string> _branchRequireAttrs = new List<string> { "name" };
|
||
private void AddBranch(XElement e)
|
||
{
|
||
ValidAttrKeys(e, null, _branchRequireAttrs);
|
||
var branchName = e.Attribute("name").Value;
|
||
if (string.IsNullOrWhiteSpace(branchName))
|
||
{
|
||
throw new Exception("branch 属性name不能为空");
|
||
}
|
||
if (this._branches.Any(b => b.Name == branchName))
|
||
{
|
||
throw new Exception($"branch '{branchName}' 重复");
|
||
}
|
||
_branches.Add(new Branch(branchName));
|
||
}
|
||
|
||
private static readonly List<string> _groupOptionalAttrs = new List<string> { "default" };
|
||
private static readonly List<string> _groupRequireAttrs = new List<string> { "name" };
|
||
|
||
private void AddGroup(XElement e)
|
||
{
|
||
ValidAttrKeys(e, _groupOptionalAttrs, _groupRequireAttrs);
|
||
List<string> groupNames = CreateGroups(e.Attribute("name").Value);
|
||
|
||
foreach (var g in groupNames)
|
||
{
|
||
if (_cfgGroups.Any(cg => cg.Names.Contains(g)))
|
||
{
|
||
throw new Exception($"group名:'{g}' 重复");
|
||
}
|
||
}
|
||
|
||
if (XmlUtil.GetOptionBoolAttribute(e, "default"))
|
||
{
|
||
this._defaultGroups.AddRange(groupNames);
|
||
}
|
||
_cfgGroups.Add(new Group() { Names = groupNames });
|
||
}
|
||
|
||
private void FillValueValidator(CfgField f, string attrValue, string validatorName)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(attrValue))
|
||
{
|
||
var validator = new Validator() { Type = validatorName, Rule = attrValue };
|
||
f.Validators.Add(validator);
|
||
f.ValueValidators.Add(validator);
|
||
}
|
||
}
|
||
|
||
private void FillValidators(string key, string attr, List<Validator> result)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(attr))
|
||
{
|
||
foreach (var validatorStr in attr.Split('#', StringSplitOptions.RemoveEmptyEntries))
|
||
{
|
||
var sepIndex = validatorStr.IndexOf(':');
|
||
if (sepIndex < 0)
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} 类型:'{CurDefine}' key:'{key}' attr:'{attr}' 不是合法的 validator 定义 (key1:value1#key2:value2 ...)");
|
||
}
|
||
result.Add(new Validator() { Type = validatorStr[..sepIndex], Rule = validatorStr[(sepIndex + 1)..] });
|
||
}
|
||
}
|
||
}
|
||
|
||
private readonly List<string> _serviceAttrs = new List<string> { "name", "manager", "group" };
|
||
|
||
private void AddService(XElement e)
|
||
{
|
||
var name = XmlUtil.GetRequiredAttribute(e, "name");
|
||
var manager = XmlUtil.GetRequiredAttribute(e, "manager");
|
||
List<string> groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group"));
|
||
var refs = new List<string>();
|
||
|
||
s_logger.Trace("service name:{name} manager:{manager}", name, manager);
|
||
ValidAttrKeys(e, _serviceAttrs, _serviceAttrs);
|
||
foreach (XElement ele in e.Elements())
|
||
{
|
||
string tagName = ele.Name.LocalName;
|
||
s_logger.Trace("service {service_name} tag: {name} {value}", name, tagName, ele);
|
||
switch (tagName)
|
||
{
|
||
case "ref":
|
||
{
|
||
refs.Add(XmlUtil.GetRequiredAttribute(ele, "name"));
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw new Exception($"service:'{name}' tag:'{tagName}' 非法");
|
||
}
|
||
}
|
||
}
|
||
if (!ValidGroup(groups, out var invalidGroup))
|
||
{
|
||
throw new Exception($"service:'{name}' group:'{invalidGroup}' 不存在");
|
||
}
|
||
_cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs });
|
||
}
|
||
|
||
|
||
private readonly Dictionary<string, Table> _name2CfgTable = new Dictionary<string, Table>();
|
||
|
||
private static List<string> CreateGroups(string s)
|
||
{
|
||
return s.Split(',', ';').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
|
||
}
|
||
|
||
private bool ValidGroup(List<string> groups, out string invalidGroup)
|
||
{
|
||
foreach (var g in groups)
|
||
{
|
||
if (!this._cfgGroups.Any(cg => cg.Names.Contains(g)))
|
||
{
|
||
invalidGroup = g;
|
||
return false;
|
||
}
|
||
}
|
||
invalidGroup = null;
|
||
return true;
|
||
}
|
||
|
||
private ETableMode ConvertMode(string tableName, string modeStr, string indexStr)
|
||
{
|
||
ETableMode mode;
|
||
switch (modeStr)
|
||
{
|
||
case "one":
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(indexStr))
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} table:'{tableName}' mode=one 是单例表,不支持定义index属性");
|
||
}
|
||
mode = ETableMode.ONE;
|
||
break;
|
||
}
|
||
case "map":
|
||
{
|
||
//if ((string.IsNullOrWhiteSpace(indexStr) || indexStr.Split(',').Length != 1))
|
||
//{
|
||
// throw new Exception($"定义文件:{CurImportFile} table:{tableName} 是单键表,必须在index属性里指定1个key");
|
||
//}
|
||
mode = ETableMode.MAP;
|
||
break;
|
||
}
|
||
case "":
|
||
{
|
||
mode = ETableMode.MAP;
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw new ArgumentException($"不支持的 mode:{modeStr}");
|
||
}
|
||
}
|
||
return mode;
|
||
}
|
||
|
||
private readonly List<string> _tableOptionalAttrs = new List<string> { "index", "mode", "group", "branch_input", "comment", "define_from_file" };
|
||
private readonly List<string> _tableRequireAttrs = new List<string> { "name", "value", "input" };
|
||
|
||
private void AddTable(XElement e)
|
||
{
|
||
ValidAttrKeys(e, _tableOptionalAttrs, _tableRequireAttrs);
|
||
string name = XmlUtil.GetRequiredAttribute(e, "name");
|
||
string module = CurNamespace;
|
||
string valueType = XmlUtil.GetRequiredAttribute(e, "value");
|
||
bool defineFromFile = XmlUtil.GetOptionBoolAttribute(e, "define_from_file");
|
||
string index = XmlUtil.GetOptionalAttribute(e, "index");
|
||
string group = XmlUtil.GetOptionalAttribute(e, "group");
|
||
string comment = XmlUtil.GetOptionalAttribute(e, "comment");
|
||
string input = XmlUtil.GetRequiredAttribute(e, "input");
|
||
string branchInput = XmlUtil.GetOptionalAttribute(e, "branch_input");
|
||
string mode = XmlUtil.GetOptionalAttribute(e, "mode");
|
||
AddTable(name, module, valueType, index, mode, group, comment, defineFromFile, input, branchInput);
|
||
}
|
||
|
||
private void AddTable(string name, string module, string valueType, string index, string mode, string group,
|
||
string comment, bool defineFromExcel, string input, string branchInput)
|
||
{
|
||
var p = new Table()
|
||
{
|
||
Name = name,
|
||
Namespace = module,
|
||
ValueType = valueType,
|
||
LoadDefineFromFile = defineFromExcel,
|
||
Index = index,
|
||
Groups = CreateGroups(group),
|
||
Comment = comment,
|
||
Mode = ConvertMode(name, mode, index),
|
||
};
|
||
|
||
if (p.Groups.Count == 0)
|
||
{
|
||
p.Groups = this._defaultGroups;
|
||
}
|
||
else if (!ValidGroup(p.Groups, out var invalidGroup))
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} table:'{p.Name}' group:'{invalidGroup}' 不存在");
|
||
}
|
||
p.InputFiles.AddRange(input.Split(','));
|
||
|
||
if (!string.IsNullOrWhiteSpace(branchInput))
|
||
{
|
||
foreach (var subBranchStr in branchInput.Split('|').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)))
|
||
{
|
||
var nameAndDirs = subBranchStr.Split(':');
|
||
if (nameAndDirs.Length != 2)
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} table:'{p.Name}' branch_input:'{subBranchStr}' 定义不合法");
|
||
}
|
||
var branchDirs = nameAndDirs[1].Split(',', ';').ToList();
|
||
if (!p.BranchInputFiles.TryAdd(nameAndDirs[0], branchDirs))
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} table:'{p.Name}' branch_input:'{subBranchStr}' 子branch:'{nameAndDirs[0]}' 重复");
|
||
}
|
||
}
|
||
}
|
||
|
||
AddTableList(p);
|
||
}
|
||
|
||
private void AddTableList(Table p)
|
||
{
|
||
if (!_name2CfgTable.TryAdd(p.Name, p))
|
||
{
|
||
var exist = _name2CfgTable[p.Name];
|
||
throw new Exception($"定义文件:{CurImportFile} table:'{p.Namespace}.{p.Name}' 与 '{exist.Namespace}.{exist.Name}' 名称不能重复");
|
||
}
|
||
_cfgTables.Add(p);
|
||
}
|
||
|
||
private async Task<CfgBean> LoadTableRecordDefineFromFileAsync(Table table, string dataDir)
|
||
{
|
||
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, table.InputFiles, dataDir);
|
||
var file = inputFileInfos[0];
|
||
|
||
var source = new ExcelDataSource();
|
||
var stream = new MemoryStream(await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5));
|
||
var sheet = source.LoadFirstSheet(file.OriginFile, file.SheetName, stream);
|
||
|
||
var cb = new CfgBean() { Namespace = table.Namespace, Name = table.ValueType, };
|
||
|
||
var rc = sheet.RowColumns;
|
||
var attrRow = sheet.RowColumns[0];
|
||
var titleRow = sheet.RowColumns[1];
|
||
// 有可能没有注释行,此时使用标题行,这个是必须有的
|
||
var descRow = sheet.TitleRows >= 3 ? sheet.RowColumns[2] : titleRow;
|
||
foreach (var f in sheet.RootFields)
|
||
{
|
||
var cf = new CfgField() { Name = f.Name, Id = 0 };
|
||
|
||
|
||
string[] attrs = (attrRow[f.FromIndex].Value?.ToString() ?? "").Trim().Split('&').Select(s => s.Trim()).ToArray();
|
||
|
||
if (attrs.Length == 0 || string.IsNullOrWhiteSpace(attrs[0]))
|
||
{
|
||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' type missing!");
|
||
}
|
||
|
||
// 优先取desc行,如果为空,则取title行
|
||
|
||
cf.Comment = descRow[f.FromIndex].Value?.ToString();
|
||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
||
{
|
||
cf.Comment = titleRow[f.FromIndex].Value?.ToString();
|
||
}
|
||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
||
{
|
||
cf.Comment = "";
|
||
}
|
||
|
||
cf.Type = attrs[0];
|
||
|
||
for (int i = 1; i < attrs.Length; i++)
|
||
{
|
||
var pair = attrs[i].Split('=');
|
||
if (pair.Length != 2)
|
||
{
|
||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' attr:'{attrs[i]}' is invalid!");
|
||
}
|
||
var attrName = pair[0].Trim();
|
||
var attrValue = pair[1].Trim();
|
||
switch (attrName)
|
||
{
|
||
case "index":
|
||
{
|
||
cf.Index = attrValue;
|
||
break;
|
||
}
|
||
case "sep":
|
||
{
|
||
cf.Sep = attrValue;
|
||
break;
|
||
}
|
||
case "ref":
|
||
case "path":
|
||
case "range":
|
||
{
|
||
var validator = new Validator() { Type = attrName, Rule = attrValue };
|
||
cf.Validators.Add(validator);
|
||
cf.ValueValidators.Add(validator);
|
||
break;
|
||
}
|
||
case "multi_lines":
|
||
{
|
||
cf.IsMultiRow = attrValue == "1" || attrValue.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||
break;
|
||
}
|
||
case "group":
|
||
{
|
||
cf.Groups = attrValue.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
|
||
break;
|
||
}
|
||
case "comment":
|
||
{
|
||
cf.Comment = attrValue;
|
||
break;
|
||
}
|
||
case "convert":
|
||
{
|
||
cf.Converter = attrValue;
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' attr:'{attrs[i]}' is invalid!");
|
||
}
|
||
}
|
||
}
|
||
|
||
cb.Fields.Add(cf);
|
||
}
|
||
return cb;
|
||
}
|
||
|
||
private async Task LoadTableRecordDefinesFromFileAsync(string dataDir)
|
||
{
|
||
var loadTasks = new List<Task<CfgBean>>();
|
||
foreach (var table in this._cfgTables.Where(t => t.LoadDefineFromFile))
|
||
{
|
||
loadTasks.Add(Task.Run(async () => await this.LoadTableRecordDefineFromFileAsync(table, dataDir)));
|
||
}
|
||
|
||
foreach (var task in loadTasks)
|
||
{
|
||
this._beans.Add(await task);
|
||
}
|
||
}
|
||
|
||
private async Task LoadTableListFromFileAsync(string dataDir)
|
||
{
|
||
if (this._importExcelTableFiles.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, this._importExcelTableFiles, dataDir);
|
||
|
||
var defTableRecordType = new DefBean(new CfgBean()
|
||
{
|
||
Namespace = "__intern__",
|
||
Name = "__TableRecord__",
|
||
Parent = "",
|
||
Alias = "",
|
||
IsValueType = false,
|
||
Sep = "",
|
||
TypeId = 0,
|
||
IsSerializeCompatible = false,
|
||
Fields = new List<Field>
|
||
{
|
||
new CfgField() { Name = "full_name", Type = "string" },
|
||
new CfgField() { Name = "value_type", Type = "string" },
|
||
new CfgField() { Name = "index", Type = "string" },
|
||
new CfgField() { Name = "mode", Type = "string" },
|
||
new CfgField() { Name = "group", Type = "string" },
|
||
new CfgField() { Name = "comment", Type = "string" },
|
||
new CfgField() { Name = "define_from_excel", Type = "bool" },
|
||
new CfgField() { Name = "input", Type = "string" },
|
||
new CfgField() { Name = "branch_input", Type = "string" },
|
||
}
|
||
})
|
||
{
|
||
AssemblyBase = new DefAssembly("", null, true, Agent),
|
||
};
|
||
defTableRecordType.PreCompile();
|
||
defTableRecordType.Compile();
|
||
defTableRecordType.PostCompile();
|
||
var tableRecordType = new TBean(defTableRecordType, false);
|
||
|
||
foreach (var file in inputFileInfos)
|
||
{
|
||
var source = new ExcelDataSource();
|
||
var bytes = await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5);
|
||
var records = DataLoaderUtil.LoadCfgRecords(tableRecordType, file.OriginFile, null, bytes, true, false);
|
||
foreach (var r in records)
|
||
{
|
||
DBean data = r.Data;
|
||
//s_logger.Info("== read text:{}", r.Data);
|
||
string fullName = (data.GetField("full_name") as DString).Value.Trim();
|
||
if (string.IsNullOrWhiteSpace(fullName))
|
||
{
|
||
throw new Exception($"file:{file.ActualFile} 定义了一个空的table类名");
|
||
}
|
||
string name = TypeUtil.GetName(fullName);
|
||
string module = TypeUtil.GetNamespace(fullName);
|
||
string valueType = (data.GetField("value_type") as DString).Value.Trim();
|
||
string index = (data.GetField("index") as DString).Value.Trim();
|
||
string mode = (data.GetField("mode") as DString).Value.Trim();
|
||
string group = (data.GetField("group") as DString).Value.Trim();
|
||
string comment = (data.GetField("comment") as DString).Value.Trim();
|
||
bool isDefineFromExcel = (data.GetField("define_from_excel") as DBool).Value;
|
||
string inputFile = (data.GetField("input") as DString).Value.Trim();
|
||
string branchInput = (data.GetField("branch_input") as DString).Value.Trim();
|
||
AddTable(name, module, valueType, index, mode, group, comment, isDefineFromExcel, inputFile, branchInput);
|
||
};
|
||
}
|
||
}
|
||
|
||
private async Task LoadEnumListFromFileAsync(string dataDir)
|
||
{
|
||
if (this._importExcelEnumFiles.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, this._importExcelEnumFiles, dataDir);
|
||
|
||
var defTableRecordType = new DefBean(new CfgBean()
|
||
{
|
||
Namespace = "__intern__",
|
||
Name = "__EnumInfo__",
|
||
Parent = "",
|
||
Alias = "",
|
||
IsValueType = false,
|
||
Sep = "",
|
||
TypeId = 0,
|
||
IsSerializeCompatible = false,
|
||
Fields = new List<Field>
|
||
{
|
||
new CfgField() { Name = "full_name", Type = "string" },
|
||
new CfgField() { Name = "item", Type = "string" },
|
||
new CfgField() { Name = "alias", Type = "string" },
|
||
new CfgField() { Name = "value", Type = "string" },
|
||
new CfgField() { Name = "comment", Type = "string" },
|
||
}
|
||
})
|
||
{
|
||
AssemblyBase = new DefAssembly("", null, true, Agent),
|
||
};
|
||
defTableRecordType.PreCompile();
|
||
defTableRecordType.Compile();
|
||
defTableRecordType.PostCompile();
|
||
var tableRecordType = new TBean(defTableRecordType, false);
|
||
|
||
foreach (var file in inputFileInfos)
|
||
{
|
||
var source = new ExcelDataSource();
|
||
var bytes = await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5);
|
||
var records = DataLoaderUtil.LoadCfgRecords(tableRecordType, file.OriginFile, null, bytes, true, false);
|
||
|
||
PEnum curEnum = null;
|
||
foreach (var r in records)
|
||
{
|
||
DBean data = r.Data;
|
||
//s_logger.Info("== read text:{}", r.Data);
|
||
string fullName = (data.GetField("full_name") as DString).Value.Trim();
|
||
if (string.IsNullOrWhiteSpace(fullName))
|
||
{
|
||
throw new Exception($"file:{file.ActualFile} 定义了一个空的enum类名");
|
||
}
|
||
string name = TypeUtil.GetName(fullName);
|
||
string module = TypeUtil.GetNamespace(fullName);
|
||
|
||
if (curEnum == null || curEnum.Name != name || curEnum.Namespace != module)
|
||
{
|
||
curEnum = new PEnum() { Name = name, Namespace = module, IsFlags = false, Comment = "", IsUniqueItemId = true };
|
||
this._enums.Add(curEnum);
|
||
}
|
||
|
||
string item = (data.GetField("item") as DString).Value.Trim();
|
||
if (string.IsNullOrWhiteSpace(item))
|
||
{
|
||
throw new Exception($"file:{file.ActualFile} module:'{module}' name:'{name}' 定义了一个空枚举项");
|
||
}
|
||
string alias = (data.GetField("alias") as DString).Value.Trim();
|
||
string value = (data.GetField("value") as DString).Value.Trim();
|
||
string comment = (data.GetField("comment") as DString).Value.Trim();
|
||
curEnum.Items.Add(new EnumItem() { Name = item, Alias = alias, Value = value, Comment = comment });
|
||
};
|
||
}
|
||
}
|
||
|
||
private async Task LoadBeanListFromFileAsync(string dataDir)
|
||
{
|
||
if (this._importExcelBeanFiles.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, this._importExcelBeanFiles, dataDir);
|
||
|
||
|
||
var ass = new DefAssembly("", null, true, Agent);
|
||
|
||
var defBeanFieldType = new DefBean(new CfgBean()
|
||
{
|
||
Namespace = "__intern__",
|
||
Name = "__FieldInfo__",
|
||
Parent = "",
|
||
Alias = "",
|
||
IsValueType = false,
|
||
Sep = "",
|
||
TypeId = 0,
|
||
IsSerializeCompatible = false,
|
||
Fields = new List<Field>
|
||
{
|
||
new CfgField() { Name = "name", Type = "string" },
|
||
new CfgField() { Name = "type", Type = "string" },
|
||
new CfgField() { Name = "sep", Type = "string" },
|
||
new CfgField() { Name = "is_multi_rows", Type = "bool" },
|
||
new CfgField() { Name = "group", Type = "string" },
|
||
new CfgField() { Name = "reference", Type = "string" },
|
||
new CfgField() { Name = "path", Type = "string" },
|
||
new CfgField() { Name = "comment", Type = "string" },
|
||
}
|
||
})
|
||
{
|
||
AssemblyBase = ass,
|
||
};
|
||
|
||
defBeanFieldType.PreCompile();
|
||
defBeanFieldType.Compile();
|
||
defBeanFieldType.PostCompile();
|
||
|
||
ass.AddType(defBeanFieldType);
|
||
|
||
var defTableRecordType = new DefBean(new CfgBean()
|
||
{
|
||
Namespace = "__intern__",
|
||
Name = "__BeanInfo__",
|
||
Parent = "",
|
||
Alias = "",
|
||
IsValueType = false,
|
||
Sep = "",
|
||
TypeId = 0,
|
||
IsSerializeCompatible = false,
|
||
Fields = new List<Field>
|
||
{
|
||
new CfgField() { Name = "full_name", Type = "string" },
|
||
new CfgField() { Name = "sep", Type = "string" },
|
||
new CfgField() { Name = "comment", Type = "string" },
|
||
new CfgField() { Name = "fields", Type = "list,__FieldInfo__", IsMultiRow = true },
|
||
}
|
||
})
|
||
{
|
||
AssemblyBase = ass,
|
||
};
|
||
ass.AddType(defTableRecordType);
|
||
defTableRecordType.PreCompile();
|
||
defTableRecordType.Compile();
|
||
defTableRecordType.PostCompile();
|
||
ass.MarkMultiRows();
|
||
var tableRecordType = new TBean(defTableRecordType, false);
|
||
|
||
foreach (var file in inputFileInfos)
|
||
{
|
||
var source = new ExcelDataSource();
|
||
var bytes = await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5);
|
||
var records = DataLoaderUtil.LoadCfgRecords(tableRecordType, file.OriginFile, null, bytes, true, false);
|
||
|
||
foreach (var r in records)
|
||
{
|
||
DBean data = r.Data;
|
||
//s_logger.Info("== read text:{}", r.Data);
|
||
string fullName = (data.GetField("full_name") as DString).Value.Trim();
|
||
if (string.IsNullOrWhiteSpace(fullName))
|
||
{
|
||
throw new Exception($"file:{file.ActualFile} 定义了一个空的bean类名");
|
||
}
|
||
string name = TypeUtil.GetName(fullName);
|
||
string module = TypeUtil.GetNamespace(fullName);
|
||
|
||
if (string.IsNullOrWhiteSpace(name))
|
||
{
|
||
throw new Exception($"file:{file.ActualFile} 定义了一个空bean类名");
|
||
}
|
||
|
||
string sep = (data.GetField("sep") as DString).Value.Trim();
|
||
string comment = (data.GetField("comment") as DString).Value.Trim();
|
||
DList fields = data.GetField("fields") as DList;
|
||
var curBean = new CfgBean()
|
||
{
|
||
Name = name,
|
||
Namespace = module,
|
||
Sep = sep,
|
||
Comment = comment,
|
||
Parent = "",
|
||
Fields = fields.Datas.Select(d => (DBean)d).Select(b => this.CreateField(
|
||
(b.GetField("name") as DString).Value.Trim(),
|
||
(b.GetField("type") as DString).Value.Trim(),
|
||
"",
|
||
(b.GetField("sep") as DString).Value.Trim(),
|
||
(b.GetField("is_multi_rows") as DBool).Value,
|
||
(b.GetField("group") as DString).Value,
|
||
"",
|
||
"",
|
||
(b.GetField("comment") as DString).Value.Trim(),
|
||
(b.GetField("reference") as DString).Value.Trim(),
|
||
(b.GetField("path") as DString).Value.Trim(),
|
||
"",
|
||
"",
|
||
"",
|
||
""
|
||
)).ToList(),
|
||
};
|
||
this._beans.Add(curBean);
|
||
};
|
||
}
|
||
}
|
||
|
||
public async Task LoadDefinesFromFileAsync(string dataDir)
|
||
{
|
||
await Task.WhenAll(LoadTableListFromFileAsync(dataDir), LoadEnumListFromFileAsync(dataDir), LoadBeanListFromFileAsync(dataDir));
|
||
await LoadTableRecordDefinesFromFileAsync(dataDir);
|
||
}
|
||
|
||
private static readonly List<string> _fieldOptionalAttrs = new List<string> {
|
||
"index", "sep", "validator", "key_validator", "value_validator",
|
||
"ref", "path", "range", "multi_rows", "group", "res", "convert", "comment" };
|
||
|
||
private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" };
|
||
|
||
protected override Field CreateField(XElement e)
|
||
{
|
||
ValidAttrKeys(e, _fieldOptionalAttrs, _fieldRequireAttrs);
|
||
|
||
return CreateField(XmlUtil.GetRequiredAttribute(e, "name"),
|
||
XmlUtil.GetRequiredAttribute(e, "type"),
|
||
XmlUtil.GetOptionalAttribute(e, "index"),
|
||
XmlUtil.GetOptionalAttribute(e, "sep"),
|
||
XmlUtil.GetOptionBoolAttribute(e, "multi_rows"),
|
||
XmlUtil.GetOptionalAttribute(e, "group"),
|
||
XmlUtil.GetOptionalAttribute(e, "res"),
|
||
XmlUtil.GetOptionalAttribute(e, "convert"),
|
||
XmlUtil.GetOptionalAttribute(e, "comment"),
|
||
XmlUtil.GetOptionalAttribute(e, "ref"),
|
||
XmlUtil.GetOptionalAttribute(e, "path"),
|
||
XmlUtil.GetOptionalAttribute(e, "range"),
|
||
XmlUtil.GetOptionalAttribute(e, "key_validator"),
|
||
XmlUtil.GetOptionalAttribute(e, "value_validator"),
|
||
XmlUtil.GetOptionalAttribute(e, "validator")
|
||
);
|
||
}
|
||
|
||
private Field CreateField(string name, string type, string index, string sep, bool isMultiRow, string group, string resource, string converter,
|
||
string comment, string refs, string path, string range, string keyValidator, string valueValidator, string validator)
|
||
{
|
||
var f = new CfgField()
|
||
{
|
||
Name = name,
|
||
Index = index,
|
||
Sep = sep,
|
||
IsMultiRow = isMultiRow,
|
||
Groups = CreateGroups(group),
|
||
Resource = resource,
|
||
Converter = converter,
|
||
Comment = comment,
|
||
};
|
||
|
||
// 字段与table的默认组不一样。
|
||
// table 默认只属于default=1的组
|
||
// 字段默认属于所有组
|
||
if (f.Groups.Count == 0)
|
||
{
|
||
|
||
}
|
||
else if (!ValidGroup(f.Groups, out var invalidGroup))
|
||
{
|
||
throw new Exception($"定义文件:{CurImportFile} field:'{name}' group:'{invalidGroup}' 不存在");
|
||
}
|
||
f.Type = type;
|
||
|
||
|
||
FillValueValidator(f, refs, "ref");
|
||
FillValueValidator(f, path, "path"); // (ue4|unity|normal|regex);xxx;xxx
|
||
FillValueValidator(f, range, "range");
|
||
|
||
FillValidators("key_validator", keyValidator, f.KeyValidators);
|
||
FillValidators("value_validator", valueValidator, f.ValueValidators);
|
||
FillValidators("validator", validator, f.Validators);
|
||
return f;
|
||
}
|
||
|
||
private static readonly List<string> _beanOptinsAttrs = new List<string> { "value_type", "alias", "sep", "comment" };
|
||
private static readonly List<string> _beanRequireAttrs = new List<string> { "name" };
|
||
|
||
protected override void AddBean(XElement e, string parent)
|
||
{
|
||
ValidAttrKeys(e, _beanOptinsAttrs, _beanRequireAttrs);
|
||
|
||
var b = new CfgBean()
|
||
{
|
||
Name = XmlUtil.GetRequiredAttribute(e, "name"),
|
||
Namespace = CurNamespace,
|
||
Parent = parent.Length > 0 ? parent : "",
|
||
TypeId = 0,
|
||
IsSerializeCompatible = true,
|
||
IsValueType = XmlUtil.GetOptionBoolAttribute(e, "value_type"),
|
||
Alias = XmlUtil.GetOptionalAttribute(e, "alias"),
|
||
Sep = XmlUtil.GetOptionalAttribute(e, "sep"),
|
||
Comment = XmlUtil.GetOptionalAttribute(e, "comment"),
|
||
};
|
||
var childBeans = new List<XElement>();
|
||
|
||
bool defineAnyChildBean = false;
|
||
foreach (XElement fe in e.Elements())
|
||
{
|
||
switch (fe.Name.LocalName)
|
||
{
|
||
case "var":
|
||
{
|
||
if (defineAnyChildBean)
|
||
{
|
||
throw new LoadDefException($"定义文件:{CurImportFile} 类型:{b.FullName} 的多态子bean必须在所有成员字段 <var> 之前定义");
|
||
}
|
||
b.Fields.Add(CreateField(fe)); ;
|
||
break;
|
||
}
|
||
case "bean":
|
||
{
|
||
defineAnyChildBean = true;
|
||
childBeans.Add(fe);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw new LoadDefException($"定义文件:{CurImportFile} 类型:{b.FullName} 不支持 tag:{fe.Name}");
|
||
}
|
||
}
|
||
}
|
||
s_logger.Trace("add bean:{@bean}", b);
|
||
_beans.Add(b);
|
||
|
||
var fullname = b.FullName;
|
||
foreach (var cb in childBeans)
|
||
{
|
||
AddBean(cb, fullname);
|
||
}
|
||
}
|
||
}
|
||
}
|