luban/src/Luban.Job.Cfg/Source/Validators/RefValidator.cs

235 lines
9.5 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 Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.RawDefs;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Validators
{
[Validator("ref")]
public class RefValidator : IValidator
{
public List<string> Tables { get; }
public string FirstTable => GetActualTableName(Tables[0]);
public TType Type { get; }
public bool GenRef { get; private set; }
private readonly List<(DefTable Table, string Index, bool IgnoreDefault)> _compiledTables = new();
public RefValidator(TType type, string tablesStr)
{
Type = type;
this.Tables = DefUtil.TrimBracePairs(tablesStr).Split(',').Select(s => s.Trim()).ToList();
}
public void Validate(ValidatorContext ctx, TType type, DType key)
{
// 对于可空字段,跳过检查
if (type.IsNullable && key == null)
{
return;
}
var assembly = ctx.Assembly;
foreach (var tableInfo in _compiledTables)
{
var (defTable, field, zeroAble) = tableInfo;
if (zeroAble && key.Apply(IsDefaultValue.Ins))
{
return;
}
switch (defTable.Mode)
{
case ETableMode.ONE:
{
throw new NotSupportedException($"{defTable.FullName} 是singleton表不支持ref");
}
case ETableMode.MAP:
{
var recordMap = assembly.GetTableDataInfo(defTable).FinalRecordMap;
if (recordMap.ContainsKey(key))
{
return;
}
break;
}
case ETableMode.LIST:
{
var recordMap = assembly.GetTableDataInfo(defTable).FinalRecordMapByIndexs[field];
if (recordMap.ContainsKey(key))
{
return;
}
break;
}
default: throw new NotSupportedException();
}
}
string source = ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source;
foreach (var table in _compiledTables)
{
assembly.Agent.Error("记录 {0} = {1} (来自文件:{2}) 在引用表:{3} 中不存在", ValidatorContext.CurrentRecordPath, key, source, table.Table.FullName);
}
}
private static string GetActualTableName(string table)
{
var (actualTable, _, _) = ParseRefString(table);
return actualTable;
}
private static (string TableName, string FieldName, bool IgnoreDefault) ParseRefString(string refStr)
{
bool ignoreDefault = false;
if (refStr.EndsWith("?"))
{
refStr = refStr.Substring(0, refStr.Length - 1);
ignoreDefault = true;
}
string tableName;
string fieldName;
int sepIndex = refStr.IndexOf('@');
if (sepIndex >= 0)
{
tableName = refStr.Substring(sepIndex + 1);
fieldName = refStr.Substring(0, sepIndex);
}
else
{
tableName = refStr;
fieldName = "";
}
return (tableName, fieldName, ignoreDefault);
}
public void Compile(DefFieldBase def)
{
string hostTypeName = def.HostType.FullName;
string fieldName = def.Name;
if (Tables.Count == 0)
{
throw new Exception($"结构:'{hostTypeName}' 字段: '{fieldName}' ref 不能为空");
}
var assembly = ((DefField)def).Assembly;
bool anyRefGroup = false;
foreach (var table in Tables)
{
var (actualTable, indexName, ignoreDefault) = ParseRefString(table);
DefTable ct;
DefRefGroup refGroup;
if ((ct = assembly.GetCfgTable(actualTable)) != null)
{
CompileTable(def, ct, indexName, ignoreDefault);
}
else if ((refGroup = assembly.GetRefGroup(actualTable)) != null)
{
if (!string.IsNullOrWhiteSpace(indexName))
{
throw new Exception($"refgroup:'{actualTable}' index:'{indexName}' 必须为空");
}
foreach (var rawRefTableName in refGroup.Refs)
{
var (actualRefTableName, refIndex, refIgnoreDefault) = ParseRefString(rawRefTableName);
DefTable subTable = assembly.GetCfgTable(actualRefTableName);
if (subTable == null)
{
throw new Exception($"结构:'{hostTypeName}' 字段:'{fieldName}' refgroup:'{actualTable}' ref:'{actualRefTableName}' 不存在");
}
CompileTable(def, subTable, refIndex, ignoreDefault && refIgnoreDefault);
}
anyRefGroup = true;
}
else
{
throw new Exception($"结构:'{hostTypeName}' 字段:'{fieldName}' ref:'{actualTable}' 不存在");
}
}
if (!anyRefGroup && _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} value_type:{ct.ValueTType.Bean.FullName} 未包含索引字段:{fieldName}");
}
if (!(indexField.CType is TMap tmap))
{
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 (!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}' 不一致");
}
}
}
}
}