luban/src/Luban.Job.Cfg/Source/Defs/CfgDefLoader.cs

859 lines
35 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Bright.Collections;
using Luban.Common.Utils;
using Luban.Job.Cfg.Cache;
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.Job.Common.Utils;
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<Patch> _patches = 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>();
private readonly List<RefGroup> _refGroups = new();
public CfgDefLoader(IAgent agent) : base(agent)
{
RegisterRootDefineHandler("importexcel", AddImportExcel);
RegisterRootDefineHandler("patch", AddPatch);
RegisterRootDefineHandler("service", AddService);
RegisterRootDefineHandler("group", AddGroup);
RegisterModuleDefineHandler("table", AddTable);
RegisterModuleDefineHandler("refgroup", AddRefGroup);
IsBeanFieldMustDefineId = false;
}
public Defines BuildDefines()
{
var defines = new Defines()
{
Patches = _patches,
Tables = _cfgTables,
Services = _cfgServices,
Groups = _cfgGroups,
RefGroups = _refGroups,
};
BuildCommonDefines(defines);
return defines;
}
private static readonly List<string> _excelImportRequireAttrs = new List<string> { "name", "type" };
private void AddImportExcel(XElement e)
{
ValidAttrKeys(RootXml, 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> _patchRequireAttrs = new List<string> { "name" };
private void AddPatch(XElement e)
{
ValidAttrKeys(RootXml, e, null, _patchRequireAttrs);
var patchName = e.Attribute("name").Value;
if (string.IsNullOrWhiteSpace(patchName))
{
throw new Exception("patch 属性name不能为空");
}
if (this._patches.Any(b => b.Name == patchName))
{
throw new Exception($"patch '{patchName}' 重复");
}
_patches.Add(new Patch(patchName));
}
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(RootXml, 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 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(RootXml, 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 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 defineFile, string tableName, string modeStr, string indexStr)
{
ETableMode mode;
string[] indexs = indexStr.Split(',');
switch (modeStr)
{
case "1":
case "one":
case "single":
case "singleton":
{
if (!string.IsNullOrWhiteSpace(indexStr))
{
throw new Exception($"定义文件:{defineFile} table:'{tableName}' mode={modeStr} 是单例表不支持定义index属性");
}
mode = ETableMode.ONE;
break;
}
case "map":
{
if ((string.IsNullOrWhiteSpace(indexStr) || indexs.Length != 1))
{
throw new Exception($"定义文件:'{defineFile}' table:'{tableName}' 是单键表index:'{indexStr}'不能包含多个key");
}
mode = ETableMode.MAP;
break;
}
case "list":
{
mode = ETableMode.LIST;
break;
}
case "":
{
if (string.IsNullOrWhiteSpace(indexStr) || indexs.Length == 1)
{
mode = ETableMode.MAP;
}
else
{
mode = ETableMode.LIST;
}
break;
}
default:
{
throw new ArgumentException($"不支持的 mode:{modeStr}");
}
}
return mode;
}
private readonly List<string> _tableOptionalAttrs = new List<string> { "index", "mode", "group", "patch_input", "comment", "define_from_file", "output" };
private readonly List<string> _tableRequireAttrs = new List<string> { "name", "value", "input" };
private void AddTable(string defineFile, XElement e)
{
ValidAttrKeys(defineFile, 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 patchInput = XmlUtil.GetOptionalAttribute(e, "patch_input");
string mode = XmlUtil.GetOptionalAttribute(e, "mode");
string tags = XmlUtil.GetOptionalAttribute(e, "tags");
string output = XmlUtil.GetOptionalAttribute(e, "output");
AddTable(defineFile, name, module, valueType, index, mode, group, comment, defineFromFile, input, patchInput, tags, output);
}
private void AddTable(string defineFile, string name, string module, string valueType, string index, string mode, string group,
string comment, bool defineFromExcel, string input, string patchInput, string tags, string outputFileName)
{
var p = new Table()
{
Name = name,
Namespace = module,
ValueType = valueType,
LoadDefineFromFile = defineFromExcel,
Index = index,
Groups = CreateGroups(group),
Comment = comment,
Mode = ConvertMode(defineFile, name, mode, index),
Tags = tags,
OutputFile = outputFileName,
};
if (p.Groups.Count == 0)
{
p.Groups = this._defaultGroups;
}
else if (!ValidGroup(p.Groups, out var invalidGroup))
{
throw new Exception($"定义文件:{defineFile} table:'{p.Name}' group:'{invalidGroup}' 不存在");
}
p.InputFiles.AddRange(input.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)));
if (!string.IsNullOrWhiteSpace(patchInput))
{
foreach (var subPatchStr in patchInput.Split('|').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)))
{
var nameAndDirs = subPatchStr.Split(':');
if (nameAndDirs.Length != 2)
{
throw new Exception($"定义文件:{defineFile} table:'{p.Name}' patch_input:'{subPatchStr}' 定义不合法");
}
var patchDirs = nameAndDirs[1].Split(',', ';').ToList();
if (!p.PatchInputFiles.TryAdd(nameAndDirs[0], patchDirs))
{
throw new Exception($"定义文件:{defineFile} table:'{p.Name}' patch_input:'{subPatchStr}' 子patch:'{nameAndDirs[0]}' 重复");
}
}
}
_cfgTables.Add(p);
}
private async Task<CfgBean> LoadTableValueTypeDefineFromFileAsync(Table table, string dataDir)
{
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, table.InputFiles, dataDir);
var file = inputFileInfos[0];
RawSheetTableDefInfo tableDefInfo;
if (!ExcelTableValueTypeDefInfoCacheManager.Instance.TryGetTableDefInfo(file.MD5, file.SheetName, out tableDefInfo))
{
var source = new ExcelDataSource();
var stream = new MemoryStream(await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5));
tableDefInfo = source.LoadTableDefInfo(file.OriginFile, file.SheetName, stream);
ExcelTableValueTypeDefInfoCacheManager.Instance.AddTableDefInfoToCache(file.MD5, file.SheetName, tableDefInfo);
}
var ns = TypeUtil.GetNamespace(table.ValueType);
string valueTypeNamespace = string.IsNullOrEmpty(ns) ? table.Namespace : ns;
string valueTypeName = TypeUtil.GetName(table.ValueType);
var cb = new CfgBean() { Namespace = valueTypeNamespace, Name = valueTypeName, Comment = "" };
#if !LUBAN_LITE
foreach (var (name, f) in tableDefInfo.FieldInfos)
{
#else
foreach (var e in tableDefInfo.FieldInfos)
{
var name = e.Key;
var f = e.Value;
#endif
var cf = new CfgField() { Name = name, Id = 0 };
string[] attrs = f.Type.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:'{name}' type missing!");
}
cf.Comment = f.Desc;
cf.Type = attrs[0];
for (int i = 1; i < attrs.Length; i++)
{
#if !LUBAN_LITE
var pair = attrs[i].Split('=', 2);
#else
var pair = attrs[i].Split(new char[] { '=' }, 2);
#endif
if (pair.Length != 2)
{
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' attr:'{attrs[i]}' is invalid!");
}
var attrName = pair[0].Trim();
var attrValue = pair[1].Trim();
switch (attrName)
{
case "index":
case "ref":
case "path":
case "range":
case "sep":
{
cf.Type = cf.Type + "#(" + attrs[i] + ")";
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 "tags":
{
cf.Tags = attrValue;
break;
}
default:
{
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' attr:'{attrs[i]}' is invalid!");
}
}
}
cb.Fields.Add(cf);
}
return cb;
}
private async Task LoadTableValueTypeDefinesFromFileAsync(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.LoadTableValueTypeDefineFromFileAsync(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_file", Type = "bool" },
new CfgField() { Name = "input", Type = "string" },
new CfgField() { Name = "output", Type = "string" },
new CfgField() { Name = "patch_input", Type = "string" },
new CfgField() { Name = "tags", Type = "string" },
}
})
{
AssemblyBase = new DefAssembly("", null, new List<string>(), Agent),
};
defTableRecordType.PreCompile();
defTableRecordType.Compile();
defTableRecordType.PostCompile();
var tableRecordType = TBean.Create(false, defTableRecordType, null);
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);
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();
string name = TypeUtil.GetName(fullName);
if (string.IsNullOrWhiteSpace(fullName) || string.IsNullOrWhiteSpace(name))
{
throw new Exception($"file:{file.ActualFile} 定义了一个空的table类名");
}
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_file") as DBool).Value;
string inputFile = (data.GetField("input") as DString).Value.Trim();
string patchInput = (data.GetField("patch_input") as DString).Value.Trim();
string tags = (data.GetField("tags") as DString).Value.Trim();
string outputFile = (data.GetField("output") as DString).Value.Trim();
AddTable(file.OriginFile, name, module, valueType, index, mode, group, comment, isDefineFromExcel, inputFile, patchInput, tags, outputFile);
};
}
}
private async Task LoadEnumListFromFileAsync(string dataDir)
{
if (this._importExcelEnumFiles.Count == 0)
{
return;
}
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, this._importExcelEnumFiles, dataDir);
var ass = new DefAssembly("", null, new List<string>(), Agent);
var enumItemType = new DefBean(new CfgBean()
{
Namespace = "__intern__",
Name = "__EnumItem__",
Parent = "",
Alias = "",
IsValueType = false,
Sep = "",
TypeId = 0,
IsSerializeCompatible = false,
Fields = new List<Field>
{
new CfgField() { Name = "name", Type = "string" },
new CfgField() { Name = "alias", Type = "string" },
new CfgField() { Name = "value", Type = "string" },
new CfgField() { Name = "comment", Type = "string" },
new CfgField() { Name = "tags", Type = "string" },
}
})
{
AssemblyBase = ass,
};
ass.AddType(enumItemType);
enumItemType.PreCompile();
enumItemType.Compile();
enumItemType.PostCompile();
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 = "comment", Type = "string" },
new CfgField() { Name = "flags", Type = "bool" },
new CfgField() { Name = "tags", Type = "string" },
new CfgField() { Name = "unique", Type = "bool" },
new CfgField() { Name = "items", Type = "list,__EnumItem__" },
}
})
{
AssemblyBase = ass,
};
ass.AddType(defTableRecordType);
defTableRecordType.PreCompile();
defTableRecordType.Compile();
defTableRecordType.PostCompile();
var tableRecordType = TBean.Create(false, defTableRecordType, null);
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);
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();
string name = TypeUtil.GetName(fullName);
if (string.IsNullOrWhiteSpace(fullName) || string.IsNullOrWhiteSpace(name))
{
throw new Exception($"file:{file.ActualFile} 定义了一个空的enum类名");
}
string module = TypeUtil.GetNamespace(fullName);
DList items = (data.GetField("items") as DList);
var curEnum = new PEnum()
{
Name = name,
Namespace = module,
IsFlags = (data.GetField("flags") as DBool).Value,
Tags = (data.GetField("tags") as DString).Value,
Comment = (data.GetField("comment") as DString).Value,
IsUniqueItemId = (data.GetField("unique") as DBool).Value,
Items = items.Datas.Cast<DBean>().Select(d => new EnumItem()
{
Name = (d.GetField("name") as DString).Value,
Alias = (d.GetField("alias") as DString).Value,
Value = (d.GetField("value") as DString).Value,
Comment = (d.GetField("comment") as DString).Value,
Tags = (d.GetField("tags") as DString).Value,
}).ToList(),
};
this._enums.Add(curEnum);
};
}
}
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, new List<string>(), 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 = "group", Type = "string" },
new CfgField() { Name = "comment", Type = "string" },
new CfgField() { Name = "tags", 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 = "tags", Type = "string" },
new CfgField() { Name = "fields", Type = "list,__FieldInfo__" },
}
})
{
AssemblyBase = ass,
};
ass.AddType(defTableRecordType);
defTableRecordType.PreCompile();
defTableRecordType.Compile();
defTableRecordType.PostCompile();
var tableRecordType = TBean.Create(false, defTableRecordType, null);
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);
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();
string name = TypeUtil.GetName(fullName);
if (string.IsNullOrWhiteSpace(fullName) || string.IsNullOrWhiteSpace(name))
{
throw new Exception($"file:{file.ActualFile} 定义了一个空bean类名");
}
string module = TypeUtil.GetNamespace(fullName);
string sep = (data.GetField("sep") as DString).Value.Trim();
string comment = (data.GetField("comment") as DString).Value.Trim();
string tags = (data.GetField("tags") as DString).Value.Trim();
DList fields = data.GetField("fields") as DList;
var curBean = new CfgBean()
{
Name = name,
Namespace = module,
Sep = sep,
Comment = comment,
Tags = tags,
Parent = "",
Fields = fields.Datas.Select(d => (DBean)d).Select(b => this.CreateField(
file.ActualFile,
(b.GetField("name") as DString).Value.Trim(),
(b.GetField("type") as DString).Value.Trim(),
(b.GetField("group") as DString).Value,
(b.GetField("comment") as DString).Value.Trim(),
(b.GetField("tags") as DString).Value.Trim(),
false
)).ToList(),
};
this._beans.Add(curBean);
};
}
}
public async Task LoadDefinesFromFileAsync(string dataDir)
{
await Task.WhenAll(LoadTableListFromFileAsync(dataDir), LoadEnumListFromFileAsync(dataDir), LoadBeanListFromFileAsync(dataDir));
await LoadTableValueTypeDefinesFromFileAsync(dataDir);
}
private static readonly List<string> _fieldOptionalAttrs = new()
{
"ref",
"path",
"group",
"comment",
"tags",
};
private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" };
override protected Field CreateField(string defineFile, XElement e)
{
ValidAttrKeys(defineFile, e, _fieldOptionalAttrs, _fieldRequireAttrs);
string typeStr = XmlUtil.GetRequiredAttribute(e, "type");
string refStr = XmlUtil.GetOptionalAttribute(e, "ref");
if (!string.IsNullOrWhiteSpace(refStr))
{
typeStr = typeStr + "#(ref=" + refStr + ")";
}
string pathStr = XmlUtil.GetOptionalAttribute(e, "path");
if (!string.IsNullOrWhiteSpace(pathStr))
{
typeStr = typeStr + "#(path=" + pathStr + ")";
}
return CreateField(defineFile, XmlUtil.GetRequiredAttribute(e, "name"),
typeStr,
XmlUtil.GetOptionalAttribute(e, "group"),
XmlUtil.GetOptionalAttribute(e, "comment"),
XmlUtil.GetOptionalAttribute(e, "tags"),
false
);
}
private Field CreateField(string defineFile, string name, string type, string group,
string comment, string tags,
bool ignoreNameValidation)
{
var f = new CfgField()
{
Name = name,
Groups = CreateGroups(group),
Comment = comment,
Tags = tags,
IgnoreNameValidation = ignoreNameValidation,
};
// 字段与table的默认组不一样。
// table 默认只属于default=1的组
// 字段默认属于所有组
if (!ValidGroup(f.Groups, out var invalidGroup))
{
throw new Exception($"定义文件:{defineFile} 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(defileFile, "key_validator", keyValidator, f.KeyValidators);
//FillValidators(defileFile, "value_validator", valueValidator, f.ValueValidators);
//FillValidators(defileFile, "validator", validator, f.Validators);
return f;
}
private static readonly List<string> _beanOptinsAttrs = new List<string> { "value_type", "alias", "sep", "comment", "tags", "externaltype" };
private static readonly List<string> _beanRequireAttrs = new List<string> { "name" };
override protected void AddBean(string defineFile, XElement e, string parent)
{
ValidAttrKeys(defineFile, 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"),
Tags = XmlUtil.GetOptionalAttribute(e, "tags"),
ExternalType = XmlUtil.GetOptionalAttribute(e, "externaltype"),
};
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($"定义文件:{defineFile} 类型:{b.FullName} 的多态子bean必须在所有成员字段 <var> 之前定义");
}
b.Fields.Add(CreateField(defineFile, fe)); ;
break;
}
case "bean":
{
defineAnyChildBean = true;
childBeans.Add(fe);
break;
}
default:
{
throw new LoadDefException($"定义文件:{defineFile} 类型:{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(defineFile, cb, fullname);
}
}
private static readonly List<string> _refGroupRequireAttrs = new List<string> { "name", "ref" };
private void AddRefGroup(string defineFile, XElement e)
{
ValidAttrKeys(defineFile, e, null, _refGroupRequireAttrs);
var refGroup = new RefGroup()
{
Name = XmlUtil.GetRequiredAttribute(e, "name"),
Refs = XmlUtil.GetRequiredAttribute(e, "ref").Split(",").Select(s => s.Trim()).ToList(),
};
_refGroups.Add(refGroup);
}
}
}