【特性】新增refgroup,方便很多字段都引用到同一组引用表的情况。

main
walon 2021-12-05 20:49:00 +08:00
parent 1377eb828d
commit a78c9abd1a
14 changed files with 184 additions and 78 deletions

View File

@ -38,6 +38,8 @@ namespace Luban.Job.Cfg.Defs
private readonly List<string> _defaultGroups = new List<string>(); private readonly List<string> _defaultGroups = new List<string>();
private readonly List<RefGroup> _refGroups = new();
public CfgDefLoader(IAgent agent) : base(agent) public CfgDefLoader(IAgent agent) : base(agent)
{ {
RegisterRootDefineHandler("importexcel", AddImportExcel); RegisterRootDefineHandler("importexcel", AddImportExcel);
@ -46,6 +48,7 @@ namespace Luban.Job.Cfg.Defs
RegisterRootDefineHandler("group", AddGroup); RegisterRootDefineHandler("group", AddGroup);
RegisterModuleDefineHandler("table", AddTable); RegisterModuleDefineHandler("table", AddTable);
RegisterModuleDefineHandler("refgroup", AddRefGroup);
IsBeanFieldMustDefineId = false; IsBeanFieldMustDefineId = false;
@ -59,6 +62,7 @@ namespace Luban.Job.Cfg.Defs
Tables = _cfgTables, Tables = _cfgTables,
Services = _cfgServices, Services = _cfgServices,
Groups = _cfgGroups, Groups = _cfgGroups,
RefGroups = _refGroups,
}; };
BuildCommonDefines(defines); BuildCommonDefines(defines);
return defines; return defines;
@ -835,5 +839,20 @@ namespace Luban.Job.Cfg.Defs
AddBean(defineFile, cb, fullname); 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);
}
} }
} }

View File

@ -57,6 +57,8 @@ namespace Luban.Job.Cfg.Defs
private readonly List<Service> _cfgServices = new List<Service>(); private readonly List<Service> _cfgServices = new List<Service>();
private readonly Dictionary<string, DefRefGroup> _refGroups = new();
private readonly ConcurrentDictionary<string, TableDataInfo> _recordsByTables = new(); private readonly ConcurrentDictionary<string, TableDataInfo> _recordsByTables = new();
public Dictionary<string, DefTable> CfgTablesByName = new(); public Dictionary<string, DefTable> CfgTablesByName = new();
@ -199,6 +201,20 @@ namespace Luban.Job.Cfg.Defs
return refTypes.Values.ToList(); return refTypes.Values.ToList();
} }
private void AddRefGroup(RefGroup g)
{
if (_refGroups.ContainsKey(g.Name))
{
throw new Exception($"refgroup:{g.Name} 重复");
}
_refGroups.Add(g.Name, new DefRefGroup(g));
}
public DefRefGroup GetRefGroup(string groupName)
{
return _refGroups.TryGetValue(groupName, out var refGroup) ? refGroup : null;
}
public void Load(Defines defines, RemoteAgent agent, GenArgs args) public void Load(Defines defines, RemoteAgent agent, GenArgs args)
{ {
LoadCommon(defines, agent, args); LoadCommon(defines, agent, args);
@ -225,6 +241,11 @@ namespace Luban.Job.Cfg.Defs
this._patches.AddRange(defines.Patches); this._patches.AddRange(defines.Patches);
foreach (var g in defines.RefGroups)
{
AddRefGroup(g);
}
foreach (var e in defines.Enums) foreach (var e in defines.Enums)
{ {
AddType(new DefEnum(e)); AddType(new DefEnum(e));
@ -278,6 +299,7 @@ namespace Luban.Job.Cfg.Defs
throw; throw;
} }
} }
foreach (var type in Types.Values) foreach (var type in Types.Values)
{ {
try try

View File

@ -0,0 +1,22 @@
using Luban.Job.Cfg.RawDefs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.Defs
{
public class DefRefGroup
{
public string Name { get; }
public List<string> Refs { get; }
public DefRefGroup(RefGroup group)
{
this.Name = group.Name;
this.Refs = group.Refs;
}
}
}

View File

@ -38,8 +38,6 @@ namespace Luban.Job.Cfg
[Option("output:exclude_tags", Required = false, HelpText = "export exclude tags. default export all tags")] [Option("output:exclude_tags", Required = false, HelpText = "export exclude tags. default export all tags")]
public string OutputExcludeTags { get; set; } = ""; public string OutputExcludeTags { get; set; } = "";
[Option("template:data:file", Required = false, HelpText = "template name. use with gen_types=data_template")] [Option("template:data:file", Required = false, HelpText = "template name. use with gen_types=data_template")]
public string TemplateDataFile { get; set; } public string TemplateDataFile { get; set; }

View File

@ -5,12 +5,14 @@ namespace Luban.Job.Cfg.RawDefs
{ {
public class Defines : DefinesCommon public class Defines : DefinesCommon
{ {
public List<Patch> Patches { get; set; } = new List<Patch>(); public List<Patch> Patches { get; set; } = new();
public List<Table> Tables { get; set; } = new List<Table>(); public List<Table> Tables { get; set; } = new();
public List<Group> Groups { get; set; } = new List<Group>(); public List<Group> Groups { get; set; } = new();
public List<Service> Services { get; set; } = new List<Service>(); public List<Service> Services { get; set; } = new();
public List<RefGroup> RefGroups { get; set; } = new();
} }
} }

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.RawDefs
{
public class RefGroup
{
public string Name { get; set; }
public List<string> Refs { get; set; }
}
}

View File

@ -24,6 +24,8 @@ namespace Luban.Job.Cfg.Validators
public bool GenRef { get; private set; } public bool GenRef { get; private set; }
private readonly List<(DefTable Table, string Index, bool IgnoreDefault)> _compiledTables = new();
public RefValidator(TType type, string tablesStr) public RefValidator(TType type, string tablesStr)
{ {
Type = type; Type = type;
@ -40,24 +42,23 @@ namespace Luban.Job.Cfg.Validators
var assembly = ctx.Assembly; var assembly = ctx.Assembly;
#if !LUBAN_LITE #if !LUBAN_LITE
foreach (var table in Tables) foreach (var tableInfo in _compiledTables)
{ {
var (actualTable, field, zeroAble) = ParseRefString(table); var (defTable, field, zeroAble) = tableInfo;
if (zeroAble && key.Apply(IsDefaultValue.Ins)) if (zeroAble && key.Apply(IsDefaultValue.Ins))
{ {
return; return;
} }
DefTable ct = assembly.GetCfgTable(actualTable);
switch (ct.Mode) switch (defTable.Mode)
{ {
case ETableMode.ONE: case ETableMode.ONE:
{ {
throw new NotSupportedException($"{actualTable} 是singleton表不支持ref"); throw new NotSupportedException($"{defTable.FullName} 是singleton表不支持ref");
} }
case ETableMode.MAP: case ETableMode.MAP:
{ {
var recordMap = assembly.GetTableDataInfo(ct).FinalRecordMap; var recordMap = assembly.GetTableDataInfo(defTable).FinalRecordMap;
if (recordMap.ContainsKey(key)) if (recordMap.ContainsKey(key))
{ {
return; return;
@ -66,7 +67,7 @@ namespace Luban.Job.Cfg.Validators
} }
case ETableMode.LIST: case ETableMode.LIST:
{ {
var recordMap = assembly.GetTableDataInfo(ct).FinalRecordMapByIndexs[field]; var recordMap = assembly.GetTableDataInfo(defTable).FinalRecordMapByIndexs[field];
if (recordMap.ContainsKey(key)) if (recordMap.ContainsKey(key))
{ {
return; return;
@ -78,11 +79,9 @@ namespace Luban.Job.Cfg.Validators
} }
string source = ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source; string source = ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source;
foreach (var table in Tables) foreach (var table in _compiledTables)
{ {
var (actualTable, field, zeroAble) = ParseRefString(table); assembly.Agent.Error("记录 {0} = {1} (来自文件:{2}) 在引用表:{3} 中不存在", ValidatorContext.CurrentRecordPath, key, source, table.Table.FullName);
DefTable ct = assembly.GetCfgTable(actualTable);
assembly.Agent.Error("记录 {0} = {1} (来自文件:{2}) 在引用表:{3} 中不存在", ValidatorContext.CurrentRecordPath, key, source, table);
} }
#endif #endif
} }
@ -126,80 +125,109 @@ namespace Luban.Job.Cfg.Validators
string fieldName = def.Name; string fieldName = def.Name;
if (Tables.Count == 0) if (Tables.Count == 0)
{ {
throw new Exception($"结构:{ hostTypeName } 字段: { fieldName} ref 不能为空"); throw new Exception($"结构:'{hostTypeName}' 字段: '{fieldName}' ref 不能为空");
} }
var assembly = ((DefField)def).Assembly; var assembly = ((DefField)def).Assembly;
bool first = true;
foreach (var table in Tables) foreach (var table in Tables)
{ {
var (actualTable, indexName, ignoreDefault) = ParseRefString(table); var (actualTable, indexName, ignoreDefault) = ParseRefString(table);
var ct = assembly.GetCfgTable(actualTable); DefTable ct;
if (ct == null) DefRefGroup refGroup;
if ((ct = assembly.GetCfgTable(actualTable)) != null)
{ {
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 不存在"); CompileTable(def, ct, indexName, ignoreDefault);
} }
//if (!ct.NeedExport) else if ((refGroup = assembly.GetRefGroup(actualTable)) != null)
//{
// throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' ref 引用的表:'{actualTable}' 没有导出");
//}
if (ct.IsOneValueTable)
{ {
if (string.IsNullOrEmpty(fieldName)) if (!string.IsNullOrWhiteSpace(indexName))
{ {
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是singleton表索引字段不能为空"); throw new Exception($"refgroup:'{actualTable}' index:'{indexName}' 必须为空");
} }
else foreach (var rawRefTableName in refGroup.Refs)
{ {
if (!ct.ValueTType.Bean.TryGetField(fieldName, out var indexField, out _)) var (actualRefTableName, refIndex, refIgnoreDefault) = ParseRefString(rawRefTableName);
DefTable subTable = assembly.GetCfgTable(actualRefTableName);
if (subTable == null)
{ {
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 未包含索引字段:{fieldName}"); throw new Exception($"结构:'{hostTypeName}' 字段:'{fieldName}' refgroup:'{actualTable}' ref:'{actualRefTableName}' 不存在");
} }
if (!(indexField.CType is TMap tmap)) CompileTable(def, subTable, refIndex, ignoreDefault && refIgnoreDefault);
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 索引字段:{fieldName} type:{indexField.CType.TypeName} 不是map类型");
}
if (tmap.KeyType.TypeName != Type.TypeName)
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} 类型:'{Type.TypeName}' 与被引用的表:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 索引字段:{fieldName} key_type:{tmap.KeyType.TypeName} 不一致");
}
}
}
else if (ct.IsMapTable)
{
if (first && Tables.Count == 1 && ct.NeedExport)
{
// 只引用一个表时才生成ref代码。
// 如果被引用的表没有导出生成ref没有意义还会产生编译错误
GenRef = true;
}
if (!string.IsNullOrEmpty(indexName))
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是map表不能索引子字段");
}
var keyType = ct.KeyTType;
if (keyType.TypeName != Type.TypeName)
{
throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' 类型:'{Type.TypeName}' 与 被引用的map表:'{actualTable}' key类型:'{keyType.TypeName}' 不一致");
} }
} }
else else
{ {
if (string.IsNullOrEmpty(indexName)) throw new Exception($"结构:'{hostTypeName}' 字段:'{fieldName}' ref:'{actualTable}' 不存在");
}
}
if (_compiledTables.Count == 1 && (_compiledTables[0].Table is DefTable t && t.IsMapTable && t.NeedExport))
{
// 只引用一个表时才生成ref代码。
// 如果被引用的表没有导出生成ref没有意义还会产生编译错误
GenRef = true;
}
}
private void CompileTable(DefFieldBase def, DefTable ct, string indexName, bool ignoreDefault)
{
_compiledTables.Add((ct, indexName, ignoreDefault));
string actualTable = ct.FullName;
string hostTypeName = def.HostType.FullName;
string fieldName = def.Name;
//if (!ct.NeedExport)
//{
// throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' ref 引用的表:'{actualTable}' 没有导出");
//}
if (ct.IsOneValueTable)
{
if (string.IsNullOrEmpty(fieldName))
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是singleton表索引字段不能为空");
}
else
{
if (!ct.ValueTType.Bean.TryGetField(fieldName, out var indexField, out _))
{ {
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是list表必须显式指定索引字段"); throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 未包含索引字段:{fieldName}");
} }
var indexField = ct.IndexList.Find(k => k.IndexField.Name == indexName); if (!(indexField.CType is TMap tmap))
if (indexField.Type == null)
{ {
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} 索引字段:{indexName} 不是被引用的list表:{actualTable} 的索引字段,合法值为'{ct.Index}'之一"); throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 索引字段:{fieldName} type:{indexField.CType.TypeName} 不是map类型");
} }
if (indexField.Type.TypeName != Type.TypeName) if (tmap.KeyType.TypeName != Type.TypeName)
{ {
throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' 类型:'{Type.TypeName}' 与 被引用的list表:'{actualTable}' key:{indexName} 类型:'{indexField.Type.TypeName}' 不一致"); throw new Exception($"结构:{hostTypeName} 字段:{fieldName} 类型:'{Type.TypeName}' 与被引用的表:{actualTable} value_type:{ct.ValueTType.Bean.FullName} 索引字段:{fieldName} key_type:{tmap.KeyType.TypeName} 不一致");
} }
} }
first = false; }
else if (ct.IsMapTable)
{
if (!string.IsNullOrEmpty(indexName))
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是map表不能索引子字段");
}
var keyType = ct.KeyTType;
if (keyType.TypeName != Type.TypeName)
{
throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' 类型:'{Type.TypeName}' 与 被引用的map表:'{actualTable}' key类型:'{keyType.TypeName}' 不一致");
}
}
else
{
if (string.IsNullOrEmpty(indexName))
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} ref:{actualTable} 是list表必须显式指定索引字段");
}
var indexField = ct.IndexList.Find(k => k.IndexField.Name == indexName);
if (indexField.Type == null)
{
throw new Exception($"结构:{hostTypeName} 字段:{fieldName} 索引字段:{indexName} 不是被引用的list表:{actualTable} 的索引字段,合法值为'{ct.Index}'之一");
}
if (indexField.Type.TypeName != Type.TypeName)
{
throw new Exception($"type:'{hostTypeName}' field:'{fieldName}' 类型:'{Type.TypeName}' 与 被引用的list表:'{actualTable}' key:{indexName} 类型:'{indexField.Type.TypeName}' 不一致");
}
} }
} }
} }

View File

@ -151,7 +151,7 @@ namespace Luban.Job.Common.Defs
public virtual void PostCompile() public virtual void PostCompile()
{ {
CType.Compile(this); CType.PostCompile(this);
} }
public static void CompileFields<T>(DefTypeBase hostType, List<T> fields, bool verifyId) where T : DefFieldBase public static void CompileFields<T>(DefTypeBase hostType, List<T> fields, bool verifyId) where T : DefFieldBase

View File

@ -28,9 +28,9 @@ namespace Luban.Job.Common.Types
public override bool IsCollection => true; public override bool IsCollection => true;
public override void Compile(DefFieldBase field) public override void PostCompile(DefFieldBase field)
{ {
base.Compile(field); base.PostCompile(field);
foreach (var p in ElementType.Processors) foreach (var p in ElementType.Processors)
{ {

View File

@ -31,9 +31,9 @@ namespace Luban.Job.Common.Types
public override bool IsCollection => true; public override bool IsCollection => true;
public override void Compile(DefFieldBase field) public override void PostCompile(DefFieldBase field)
{ {
base.Compile(field); base.PostCompile(field);
foreach (var p in ElementType.Processors) foreach (var p in ElementType.Processors)
{ {

View File

@ -36,9 +36,9 @@ namespace Luban.Job.Common.Types
public override bool IsCollection => true; public override bool IsCollection => true;
public override void Compile(DefFieldBase field) public override void PostCompile(DefFieldBase field)
{ {
base.Compile(field); base.PostCompile(field);
foreach (var p in KeyType.Processors) foreach (var p in KeyType.Processors)
{ {

View File

@ -31,9 +31,9 @@ namespace Luban.Job.Common.Types
public override bool IsCollection => true; public override bool IsCollection => true;
public override void Compile(DefFieldBase field) public override void PostCompile(DefFieldBase field)
{ {
base.Compile(field); base.PostCompile(field);
foreach (var p in ElementType.Processors) foreach (var p in ElementType.Processors)
{ {

View File

@ -37,7 +37,7 @@ namespace Luban.Job.Common.Types
public abstract bool TryParseFrom(string s); public abstract bool TryParseFrom(string s);
public virtual void Compile(DefFieldBase field) public virtual void PostCompile(DefFieldBase field)
{ {
foreach (var p in Processors) foreach (var p in Processors)
{ {

View File

@ -110,14 +110,14 @@ namespace Luban.Job.Common.Utils
{ {
{ "json", "json" }, { "json", "json" },
{ "lua", "lua" }, { "lua", "lua" },
{ "bin", "bin" }, { "bin", "bytes" },
{ "xml", "xml" }, { "xml", "xml" },
{ "yaml", "yml" }, { "yaml", "yml" },
{ "yml", "yml" }, { "yml", "yml" },
{ "erlang", "erl" }, { "erlang", "erl" },
{ "erl", "erl" }, { "erl", "erl" },
{ "xlsx", "xlsx" }, { "xlsx", "xlsx" },
{ "protobuf", "pb" }, { "protobuf", "bytes" },
}; };
public static string GetOutputFileSuffix(string genType) public static string GetOutputFileSuffix(string genType)