【重构】重构LubanAssistant代码,复用Luban的解析方式

main
walon 2021-10-16 12:55:06 +08:00
parent 953c4db678
commit 3b17094618
24 changed files with 287 additions and 2169 deletions

View File

@ -17,8 +17,8 @@
## 介绍 ## 介绍
目前主流的配置工具功能多为excel文件到json之类格式的转换工具及简单代码生成器只能勉强满足中小类型项目的需求。
在中大型游戏项目中,基本都会有技能、行为树之类的复杂功能。这些功能有非常复杂的数据结构往往使用自定义编辑器制作并以json、xml等文件格式保存。就算常规的excel表也经常出现复杂的数据结构需求。这些简单工具面对此类需求要么无法支持要么就强迫策划和程序使用拆表等奇技淫巧严重影响设计和开发效率 在中大型游戏项目中,就算常规的excel表也经常出现复杂的数据结构需求常规的导表工具面对此类需求要么无法支持要么就强迫策划和程序使用拆表等奇技淫巧严重影响设计和开发效率。另外有复杂GamePlay的游戏基本都会有技能、行为树、关卡之类的复杂功能这些功能有非常复杂的数据结构往往使用自定义编辑器制作并以json、xml等文件格式保存以excel为中心的导表工具无法处理这些数据导致程序往往不得不手动解析它们浪费了很多时间
luban相较于常规的excel导表工具有以下核心优势 luban相较于常规的excel导表工具有以下核心优势
- 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。 - 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。

View File

@ -1,350 +0,0 @@
//using Luban.Common.Utils;
//using Luban.Job.Cfg.Datas;
//using Luban.Job.Cfg.DataSources.Excel;
//using Luban.Job.Cfg.Defs;
//using Luban.Job.Cfg.TypeVisitors;
//using Luban.Job.Common.Types;
//using Luban.Job.Common.TypeVisitors;
//using System;
//using System.Collections.Generic;
//namespace Luban.Job.Cfg.DataCreators
//{
// class ExcelNamedRowDataCreator : ITypeFuncVisitor<TitleRow, bool, bool, DType>
// {
// public static ExcelNamedRowDataCreator Ins { get; } = new ExcelNamedRowDataCreator();
// public DType ReadExcel(TitleRow row, TBean btype)
// {
// return Accept(btype, row, false, false);
// }
// public DType Accept(TBool type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TByte type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TShort type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TFshort type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TInt type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TFint type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TLong type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TFlong type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TFloat type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TDouble type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TEnum type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TString type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TBytes type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TText type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// private static bool IsContainerAndElementNotSepType(TType type)
// {
// switch (type)
// {
// case TArray ta: return ta.ElementType.Apply(IsNotSepTypeVisitor.Ins);
// case TList tl: return tl.ElementType.Apply(IsNotSepTypeVisitor.Ins);
// case TSet ts: return ts.ElementType.Apply(IsNotSepTypeVisitor.Ins);
// case TMap tm: return tm.KeyType.Apply(IsNotSepTypeVisitor.Ins) && tm.ValueType.Apply(IsNotSepTypeVisitor.Ins);
// default: return false;
// }
// throw new NotImplementedException();
// }
// private List<DType> CreateBeanFields(DefBean bean, TitleRow row)
// {
// var list = new List<DType>();
// foreach (DefField f in bean.HierarchyFields)
// {
// string fname = f.Name;
// Title title = row.GetTitle(fname);
// if (title == null)
// {
// throw new Exception($"bean:'{bean.FullName}' 缺失 列:'{fname}',请检查是否写错或者遗漏");
// }
// // 多级标题
// if (title.SubTitles.Count > 0)
// {
// try
// {
// list.Add(f.CType.Apply(this, row.GetSubTitleNamedRow(fname), f.IsMultiRow, f.IsNullable));
// }
// catch (DataCreateException dce)
// {
// dce.Push(bean, f);
// throw;
// }
// catch (Exception e)
// {
// var dce = new DataCreateException(e, $"列:{fname}");
// dce.Push(bean, f);
// throw dce;
// }
// }
// else
// {
// string sep = "";
// if (string.IsNullOrWhiteSpace(sep) && IsContainerAndElementNotSepType(f.CType))
// {
// sep = ";,";
// }
// if (f.IsMultiRow)
// {
// try
// {
// if (f.CType.IsCollection)
// {
// list.Add(f.CType.Apply(MultiRowExcelDataCreator.Ins, row.GetColumnOfMultiRows(f.Name, sep), f.IsNullable, (DefAssembly)bean.AssemblyBase));
// }
// else
// {
// list.Add(f.CType.Apply(ExcelDataCreator.Ins, f, row.GetMultiRowStream(f.Name, sep), (DefAssembly)bean.AssemblyBase));
// }
// }
// catch (DataCreateException dce)
// {
// dce.Push(bean, f);
// throw;
// }
// catch (Exception e)
// {
// var dce = new DataCreateException(e, "");
// dce.Push(bean, f);
// throw dce;
// }
// }
// else
// {
// //ExcelStream stream = row.GetColumn(f.Name, sep, !f.CType.Apply(IsMultiData.Ins));
// ExcelStream stream = row.GetColumn(f.Name);
// try
// {
// list.Add(f.CType.Apply(ExcelDataCreator.Ins, f, stream, (DefAssembly)bean.AssemblyBase));
// }
// catch (DataCreateException dce)
// {
// dce.Push(bean, f);
// throw;
// }
// catch (Exception e)
// {
// var dce = new DataCreateException(e, stream.LastReadDataInfo);
// dce.Push(bean, f);
// throw dce;
// }
// }
// }
// }
// return list;
// }
// public DType Accept(TBean type, TitleRow row, bool multirow, bool nullable)
// {
// var originBean = (DefBean)type.Bean;
// if (originBean.IsAbstractType)
// {
// string subType = row.GetColumn(DefBean.TYPE_NAME_KEY).Read().ToString().Trim();
// if (subType.ToLower() == DefBean.BEAN_NULL_STR)
// {
// if (!type.IsNullable)
// {
// throw new Exception($"type:'{type}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
// }
// return null;
// }
// string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
// DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
// if (implType == null)
// {
// throw new Exception($"type:'{fullType}' 不是 bean 类型");
// }
// return new DBean(originBean, implType, CreateBeanFields(implType, row));
// }
// else
// {
// if (type.IsNullable)
// {
// string subType = row.GetColumn(DefBean.TYPE_NAME_KEY).Read().ToString().Trim();
// if (subType == DefBean.BEAN_NULL_STR)
// {
// return null;
// }
// else if (subType != DefBean.BEAN_NOT_NULL_STR && subType != originBean.Name)
// {
// throw new Exception($"type:'{type.Bean.FullName}' 可空标识:'{subType}' 不合法(只能为{DefBean.BEAN_NOT_NULL_STR}或{DefBean.BEAN_NULL_STR}或{originBean.Name})");
// }
// }
// return new DBean(originBean, originBean, CreateBeanFields(originBean, row));
// }
// }
// private List<DType> ReadList(TBean elementType, TitleRow row, bool multirow)
// {
// var list = new List<DType>();
// // 如果是多行数据以当前title为title,每行读入一个element
// if (multirow)
// {
// //foreach (var sub in row.GenerateSubNameRows(elementType))
// foreach (var sub in row.Elements)
// {
// list.Add(this.Accept(elementType, sub, false, false));
// }
// }
// else
// {
// // 如果不是多行,并且定义了子标题的话。以一个子标题所占的列,读入一个数据
// //foreach (var sub in row.SelfTitle.SubTitleList)
// //{
// // list.Add(this.Accept(elementType, new TitleRow(sub, row.Rows), false, false));
// //}
// throw new NotSupportedException("只有multi_rows=1的list,bean类型才允许有子title");
// }
// return list;
// }
// public DType Accept(TArray type, TitleRow x, bool multirow, bool nullable)
// {
// if (type.ElementType is not TBean bean)
// {
// throw new Exception($"NamedRow 只支持 bean 类型的容器");
// }
// else
// {
// return new DArray(type, ReadList(bean, x, multirow));
// }
// }
// public DType Accept(TList type, TitleRow x, bool multirow, bool nullable)
// {
// if (type.ElementType is not TBean bean)
// {
// throw new Exception($"NamedRow 只支持 bean 类型的容器");
// }
// else
// {
// return new DList(type, ReadList(bean, x, multirow));
// }
// }
// public DType Accept(TSet type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// private bool TryCreateColumnStream(TitleRow x, Title title, out ExcelStream stream)
// {
// var cells = new List<Cell>();
// for (int i = title.FromIndex; i <= title.ToIndex; i++)
// {
// foreach (var row in x.Rows)
// {
// if (row.Count > i)
// {
// var value = row[i].Value;
// if (!(value == null || (value is string s && string.IsNullOrEmpty(s))))
// {
// cells.Add(row[i]);
// }
// }
// }
// }
// if (cells.Count > 0)
// {
// stream = new ExcelStream(cells, 0, cells.Count - 1, "", false);
// return true;
// }
// stream = null;
// return false;
// }
// public DType Accept(TMap type, TitleRow x, bool multirow, bool nullable)
// {
// var map = new Dictionary<DType, DType>();
// foreach (var e in x.Fields)
// {
// var keyData = type.KeyType.Apply(StringDataCreator.Ins, e.Key);
// var valueData = type.ValueType.Apply(ExcelDataCreator.Ins, null, e.Value.AsStream, DefAssembly.LocalAssebmly);
// map.Add(keyData, valueData);
// }
// return new DMap(type, map);
// }
// public DType Accept(TVector2 type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TVector3 type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TVector4 type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// public DType Accept(TDateTime type, TitleRow x, bool multirow, bool nullable)
// {
// throw new NotSupportedException();
// }
// }
//}

View File

@ -224,19 +224,27 @@ namespace Luban.Job.Cfg.DataCreators
public DType Accept(TText type, Sheet sheet, TitleRow row) public DType Accept(TText type, Sheet sheet, TitleRow row)
{ {
if (row.CellCount != 2) if (string.IsNullOrEmpty(row.SelfTitle.Sep))
{ {
throw new Exception($"text 要求两个字段"); if (row.CellCount != 2)
{
throw new Exception($"text 要求两个字段");
}
int startIndex = row.SelfTitle.FromIndex;
var key = ParseString(row.Row[startIndex].Value);
var text = ParseString(row.Row[startIndex + 1].Value);
if (type.IsNullable && key == null && text == null)
{
return null;
}
DataUtil.ValidateText(key, text);
return new DText(key, text);
} }
int startIndex = row.SelfTitle.FromIndex; else
var key = ParseString(row.Row[startIndex].Value);
var text = ParseString(row.Row[startIndex + 1].Value);
if (type.IsNullable && key == null && text == null)
{ {
return null; var s = row.AsStream("");
return type.Apply(ExcelStreamDataCreator.Ins, s);
} }
DataUtil.ValidateText(key, text);
return new DText(key, text);
} }
private List<DType> CreateBeanFields(DefBean bean, Sheet sheet, TitleRow row) private List<DType> CreateBeanFields(DefBean bean, Sheet sheet, TitleRow row)

View File

@ -36,6 +36,16 @@ namespace Luban.Job.Cfg.DataSources.Excel
} }
} }
public void Load(params RawSheet[] rawSheets)
{
foreach (RawSheet rawSheet in rawSheets)
{
var sheet = new Sheet("__intern__", rawSheet.TableName);
sheet.Load(rawSheet);
_sheets.Add(sheet);
}
}
public RawSheetTableDefInfo LoadTableDefInfo(string rawUrl, string sheetName, Stream stream) public RawSheetTableDefInfo LoadTableDefInfo(string rawUrl, string sheetName, Stream stream)
{ {
return SheetLoadUtil.LoadSheetTableDefInfo(rawUrl, sheetName, stream); return SheetLoadUtil.LoadSheetTableDefInfo(rawUrl, sheetName, stream);

View File

@ -10,8 +10,12 @@ namespace Luban.Job.Cfg.DataSources.Excel
class RawSheet class RawSheet
{ {
public Title Title { get; init; } public Title Title { get; set; }
public List<List<Cell>> Cells { get; init; } public string TableName { get; set; }
public int TitleRowCount { get; set; }
public List<List<Cell>> Cells { get; set; }
} }
} }

View File

@ -8,19 +8,19 @@ namespace Luban.Job.Cfg.DataSources.Excel
{ {
class FieldInfo class FieldInfo
{ {
public string Name { get; init; } public string Name { get; set; }
public Dictionary<string, string> Tags { get; init; } public Dictionary<string, string> Tags { get; set; }
public string Type { get; init; } public string Type { get; set; }
public string BriefDesc { get; init; } public string BriefDesc { get; set; }
public string DetailDesc { get; init; } public string DetailDesc { get; set; }
} }
class RawSheetTableDefInfo class RawSheetTableDefInfo
{ {
public Dictionary<string, FieldInfo> FieldInfos { get; init; } public Dictionary<string, FieldInfo> FieldInfos { get; set; }
} }
} }

View File

@ -67,14 +67,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
bool orientRow; bool orientRow;
int titleRowNum; int titleRowNum;
if (!TryParseMeta(reader, out orientRow, out titleRowNum, out var _)) if (!TryParseMeta(reader, out orientRow, out titleRowNum, out var tableName))
{ {
return null; return null;
} }
var cells = ParseRawSheetContent(reader, orientRow); var cells = ParseRawSheetContent(reader, orientRow);
var title = ParseTitle(cells, reader.MergeCells, orientRow, out _); var title = ParseTitle(cells, reader.MergeCells, orientRow, out _);
cells.RemoveRange(0, Math.Min(titleRowNum, cells.Count)); cells.RemoveRange(0, Math.Min(titleRowNum, cells.Count));
return new RawSheet() { Title = title, Cells = cells }; return new RawSheet() { Title = title, TitleRowCount = titleRowNum, TableName = tableName, Cells = cells };
} }
private static int GetTitleRowNum(CellRange[] mergeCells, bool orientRow) private static int GetTitleRowNum(CellRange[] mergeCells, bool orientRow)
@ -108,8 +108,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, out int titleRowNum) public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, out int titleRowNum)
{ {
var rootTitle = new Title() { var rootTitle = new Title()
Root = true, Name = "__root__", {
Root = true,
Name = "__root__",
Tags = new Dictionary<string, string>(), Tags = new Dictionary<string, string>(),
FromIndex = 0, FromIndex = 0,
ToIndex = cells.Select(r => r.Count).Max() - 1 ToIndex = cells.Select(r => r.Count).Max() - 1
@ -130,10 +132,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
private static bool IsIgnoreTitle(string title) private static bool IsIgnoreTitle(string title)
{ {
#if !LUBAN_LITE
return string.IsNullOrEmpty(title) || title.StartsWith('#'); return string.IsNullOrEmpty(title) || title.StartsWith('#');
#else
return string.IsNullOrEmpty(title) || title.StartsWith("#");
#endif
} }
private static (string Name, Dictionary<string, string> Tags) ParseNameAndMetaAttrs(string nameAndAttrs) public static (string Name, Dictionary<string, string> Tags) ParseNameAndMetaAttrs(string nameAndAttrs)
{ {
var attrs = nameAndAttrs.Split('&'); var attrs = nameAndAttrs.Split('&');
@ -227,24 +233,20 @@ namespace Luban.Job.Cfg.DataSources.Excel
} }
} }
public static bool TryParseMeta(IExcelDataReader reader, out bool orientRow, out int titleRows, out string tableName) public static bool TryParseMeta(List<string> cells, out bool orientRow, out int titleRows, out string tableName)
{ {
orientRow = true; orientRow = true;
titleRows = TITLE_DEFAULT_ROW_NUM; titleRows = TITLE_DEFAULT_ROW_NUM;
tableName = ""; tableName = "";
if (!reader.Read() || reader.FieldCount == 0)
{
return false;
}
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性 // meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
if (reader.GetString(0) != "##") if (cells.Count == 0 || cells[0] != "##")
{ {
return false; return false;
} }
for (int i = 1, n = reader.FieldCount; i < n; i++) foreach (var attr in cells.Skip(1))
{ {
var attr = reader.GetString(i)?.Trim();
if (string.IsNullOrWhiteSpace(attr)) if (string.IsNullOrWhiteSpace(attr))
{ {
continue; continue;
@ -255,8 +257,8 @@ namespace Luban.Job.Cfg.DataSources.Excel
{ {
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}"); throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
} }
string key = ss[0].Trim().ToLower(); string key = ss[0].Trim();
string value = ss[1].Trim().ToLower(); string value = ss[1].Trim();
switch (key) switch (key)
{ {
case "orientation": case "orientation":
@ -291,6 +293,23 @@ namespace Luban.Job.Cfg.DataSources.Excel
return true; return true;
} }
public static bool TryParseMeta(IExcelDataReader reader, out bool orientRow, out int titleRows, out string tableName)
{
if (!reader.Read() || reader.FieldCount == 0)
{
orientRow = true;
titleRows = TITLE_DEFAULT_ROW_NUM;
tableName = "";
return false;
}
var cells = new List<string>();
for (int i = 0, n = reader.FieldCount; i < n; i++)
{
cells.Add(reader.GetString(i)?.Trim());
}
return TryParseMeta(cells, out orientRow, out titleRows, out tableName);
}
private static List<List<Cell>> ParseRawSheetContent(IExcelDataReader reader, bool orientRow, int? maxParseRow = null) private static List<List<Cell>> ParseRawSheetContent(IExcelDataReader reader, bool orientRow, int? maxParseRow = null)
{ {
// TODO 优化性能 // TODO 优化性能

View File

@ -301,8 +301,15 @@ namespace Luban.Job.Cfg.Defs
//var titleRow = sheet.RowColumns[sheet.AttrRowCount]; //var titleRow = sheet.RowColumns[sheet.AttrRowCount];
//// 有可能没有注释行,此时使用标题行,这个是必须有的 //// 有可能没有注释行,此时使用标题行,这个是必须有的
//var descRow = sheet.HeaderRowCount >= sheet.AttrRowCount + 2 ? sheet.RowColumns[sheet.AttrRowCount + 1] : titleRow; //var descRow = sheet.HeaderRowCount >= sheet.AttrRowCount + 2 ? sheet.RowColumns[sheet.AttrRowCount + 1] : titleRow;
#if !LUBAN_LITE
foreach (var (name, f) in tableDefInfo.FieldInfos) 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 }; var cf = new CfgField() { Name = name, Id = 0 };
string[] attrs = f.Type.Trim().Split('&').Select(s => s.Trim()).ToArray(); string[] attrs = f.Type.Trim().Split('&').Select(s => s.Trim()).ToArray();

View File

@ -1,6 +1,8 @@
using Bright.Collections; using Bright.Collections;
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
#if !LUBAN_LITE
using Luban.Job.Cfg.l10n; using Luban.Job.Cfg.l10n;
#endif
using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.RawDefs;
using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Defs; using Luban.Job.Common.Defs;
@ -14,6 +16,8 @@ namespace Luban.Job.Cfg.Defs
{ {
public class TableDataInfo public class TableDataInfo
{ {
public DefTable Table { get; }
public List<Record> MainRecords { get; } public List<Record> MainRecords { get; }
public List<Record> PatchRecords { get; } public List<Record> PatchRecords { get; }
@ -22,8 +26,9 @@ namespace Luban.Job.Cfg.Defs
public Dictionary<DType, Record> FinalRecordMap { get; set; } public Dictionary<DType, Record> FinalRecordMap { get; set; }
public TableDataInfo(List<Record> mainRecords, List<Record> patchRecords) public TableDataInfo(DefTable table, List<Record> mainRecords, List<Record> patchRecords)
{ {
Table = table;
MainRecords = mainRecords; MainRecords = mainRecords;
PatchRecords = patchRecords; PatchRecords = patchRecords;
} }
@ -69,6 +74,7 @@ namespace Luban.Job.Cfg.Defs
public Dictionary<string, DefTable> CfgTables { get; } = new Dictionary<string, DefTable>(); public Dictionary<string, DefTable> CfgTables { get; } = new Dictionary<string, DefTable>();
#if !LUBAN_LITE
public RawTextTable RawTextTable { get; } = new RawTextTable(); public RawTextTable RawTextTable { get; } = new RawTextTable();
public TextTable ExportTextTable { get; private set; } public TextTable ExportTextTable { get; private set; }
@ -83,6 +89,8 @@ namespace Luban.Job.Cfg.Defs
NotConvertTextSet = new NotConvertTextSet(); NotConvertTextSet = new NotConvertTextSet();
} }
#endif
public Patch GetPatch(string name) public Patch GetPatch(string name)
{ {
return _patches.Find(b => b.Name == name); return _patches.Find(b => b.Name == name);
@ -103,7 +111,7 @@ namespace Luban.Job.Cfg.Defs
public void AddDataTable(DefTable table, List<Record> mainRecords, List<Record> patchRecords) public void AddDataTable(DefTable table, List<Record> mainRecords, List<Record> patchRecords)
{ {
_recordsByTables[table.FullName] = new TableDataInfo(mainRecords, patchRecords); _recordsByTables[table.FullName] = new TableDataInfo(table, mainRecords, patchRecords);
} }
public List<Record> GetTableAllDataList(DefTable table) public List<Record> GetTableAllDataList(DefTable table)

View File

@ -42,6 +42,7 @@ namespace Luban.Job.Cfg.Defs
|| (CType is TList tl && tl.ElementType.IsBean) || (CType is TList tl && tl.ElementType.IsBean)
|| (CType is TMap tm && tm.ValueType.IsBean); || (CType is TMap tm && tm.ValueType.IsBean);
#if !LUBAN_LITE
public string CsRefTypeName public string CsRefTypeName
{ {
get get
@ -95,6 +96,7 @@ namespace Luban.Job.Cfg.Defs
return $"{TsRefVarName} : {table.ValueTType.Apply(TypescriptDefineTypeNameVisitor.Ins)}{(IsNullable ? "" : " = undefined!")}"; return $"{TsRefVarName} : {table.ValueTType.Apply(TypescriptDefineTypeNameVisitor.Ins)}{(IsNullable ? "" : " = undefined!")}";
} }
} }
#endif
public string CsRefVarName => $"{CsStyleName}_Ref"; public string CsRefVarName => $"{CsStyleName}_Ref";

View File

@ -1,3 +1,4 @@
using Bright.Collections;
using Luban.Common.Utils; using Luban.Common.Utils;
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.DataVisitors;
@ -86,10 +87,12 @@ namespace Luban.Job.Cfg
{ {
CurrentVisitor = visitor; CurrentVisitor = visitor;
visitor.ValidateTable(t, records); visitor.ValidateTable(t, records);
#if !LUBAN_LITE
if (this.Assembly.NeedL10nTextTranslate) if (this.Assembly.NeedL10nTextTranslate)
{ {
ValidateText(t, records); ValidateText(t, records);
} }
#endif
} }
finally finally
{ {
@ -106,6 +109,7 @@ namespace Luban.Job.Cfg
} }
} }
#if !LUBAN_LITE
private void ValidateText(DefTable table, List<Record> records) private void ValidateText(DefTable table, List<Record> records)
{ {
foreach (var r in records) foreach (var r in records)
@ -115,6 +119,7 @@ namespace Luban.Job.Cfg
} }
CurrentVisitor.CurrentValidateRecord = null; CurrentVisitor.CurrentValidateRecord = null;
} }
#endif
private async Task ValidatePaths() private async Task ValidatePaths()
{ {
@ -165,7 +170,11 @@ namespace Luban.Job.Cfg
{ {
if (ls.All(f => fileNotExistsSet.Contains(f))) if (ls.All(f => fileNotExistsSet.Contains(f)))
{ {
#if !LUBAN_LITE
agent.Error("记录 {0} = {1} (来自文件:{2}) 所引用文件:{3} 不存在", q.DataPath, q.Value, q.Source, string.Join(',', ls)); agent.Error("记录 {0} = {1} (来自文件:{2}) 所引用文件:{3} 不存在", q.DataPath, q.Value, q.Source, string.Join(',', ls));
#else
agent.Error("记录 {0} = {1} (来自文件:{2}) 所引用文件:{3} 不存在", q.DataPath, q.Value, q.Source, string.Join(",", ls));
#endif
} }
break; break;
} }
@ -252,8 +261,10 @@ namespace Luban.Job.Cfg
break; break;
} }
} }
#if !LUBAN_LITE
tableDataInfo.FinalRecords = mainRecords; tableDataInfo.FinalRecords = mainRecords;
tableDataInfo.FinalRecordMap = mainRecordMap; tableDataInfo.FinalRecordMap = mainRecordMap;
#endif
} }
} }
} }

View File

@ -109,7 +109,11 @@ namespace Luban.Job.Cfg.Validators
string suffix = groups[2].Value.Substring(1); string suffix = groups[2].Value.Substring(1);
if (suffix.EndsWith("_C")) if (suffix.EndsWith("_C"))
{ {
#if !LUBAN_LITE
suffix = suffix[0..^2]; suffix = suffix[0..^2];
#else
suffix = suffix.Substring(0, suffix.Length - 2);
#endif
} }
return path.EndsWith(suffix); return path.EndsWith(suffix);
} }
@ -212,9 +216,17 @@ namespace Luban.Job.Cfg.Validators
string patType = ss[0]; string patType = ss[0];
bool emptyAble = false; bool emptyAble = false;
#if !LUBAN_LITE
if (patType.EndsWith('?')) if (patType.EndsWith('?'))
#else
if (patType.EndsWith("?"))
#endif
{ {
#if !LUBAN_LITE
patType = patType[0..^1]; patType = patType[0..^1];
#else
patType = patType.Substring(0, patType.Length - 1);
#endif
emptyAble = true; emptyAble = true;
} }

View File

@ -82,14 +82,22 @@ namespace Luban.Job.Cfg.Validators
case '(': _includeMinBound = false; break; case '(': _includeMinBound = false; break;
default: ThrowError(); break; default: ThrowError(); break;
} }
#if !LUBAN_LITE
switch (_str[^1]) switch (_str[^1])
#else
switch (_str[_str.Length - 1])
#endif
{ {
case ']': _includeMaxBound = true; break; case ']': _includeMaxBound = true; break;
case ')': _includeMaxBound = false; break; case ')': _includeMaxBound = false; break;
default: ThrowError(); break; default: ThrowError(); break;
} }
#if !LUBAN_LITE
var pars = _str[1..^1].Split(','); var pars = _str[1..^1].Split(',');
#else
var pars = _str.Substring(1, _str.Length - 1).Split(',');
#endif
if (pars.Length != 2) if (pars.Length != 2)
{ {
ThrowError(); ThrowError();

View File

@ -60,11 +60,13 @@ namespace Luban.Job.Cfg.Validators
return; return;
} }
DefTable ct = assembly.GetCfgTable(actualTable); DefTable ct = assembly.GetCfgTable(actualTable);
#if !LUBAN_LITE
var recordMap = assembly.GetTableDataInfo(ct).FinalRecordMap; var recordMap = assembly.GetTableDataInfo(ct).FinalRecordMap;
if (/*recordMap != null &&*/ recordMap.ContainsKey(key)) if (/*recordMap != null &&*/ recordMap.ContainsKey(key))
{ {
return; return;
} }
#endif
} }
foreach (var table in Tables) foreach (var table in Tables)

View File

@ -98,11 +98,10 @@ namespace LubanAssistant
private void LoadDataToCurrentDoc() private void LoadDataToCurrentDoc()
{ {
Worksheet sheet = Globals.LubanAssistant.Application.ActiveSheet; Worksheet sheet = Globals.LubanAssistant.Application.ActiveSheet;
var metaAttrs = ExcelUtil.ParseMetaAttrs(sheet); var rawSheet = ExcelUtil.ParseRawSheetTitleOnly(sheet);
if (!metaAttrs.TryGetValue("table", out var tableName)) if (string.IsNullOrWhiteSpace(rawSheet.TableName))
{ {
MessageBox.Show($"meta行未指定table名"); MessageBox.Show($"meta行未指定table名");
return; return;
@ -112,9 +111,9 @@ namespace LubanAssistant
{ {
try try
{ {
var tableDataInfo = LastLoadTableData = await DataLoaderUtil.LoadTableDataAsync(RootDefineFile, InputDataDir, tableName); var tableDataInfo = LastLoadTableData = await DataLoaderUtil.LoadTableDataAsync(RootDefineFile, InputDataDir, rawSheet.TableName);
var title = ExcelUtil.ParseTitles(sheet); var title = ExcelUtil.ParseTitles(sheet);
ExcelUtil.FillRecords(sheet, metaAttrs, title, tableDataInfo); ExcelUtil.FillRecords(sheet, rawSheet.TitleRowCount, title, tableDataInfo);
} }
catch (Exception e) catch (Exception e)
{ {
@ -159,12 +158,13 @@ namespace LubanAssistant
} }
} }
private void SaveRecords(Action<Worksheet, Dictionary<string, string>, TableDataInfo, DefTable, TitleInfo> saveTask) private void SaveRecords(Action<Worksheet, int, TableDataInfo, DefTable, Title> saveTask)
{ {
Worksheet sheet = Globals.LubanAssistant.Application.ActiveSheet; Worksheet sheet = Globals.LubanAssistant.Application.ActiveSheet;
var metaAttrs = ExcelUtil.ParseMetaAttrs(sheet); var rawSheet = ExcelUtil.ParseRawSheetTitleOnly(sheet);
if (!metaAttrs.TryGetValue("table", out var tableName)) string tableName = rawSheet.TableName;
if (string.IsNullOrWhiteSpace(tableName))
{ {
MessageBox.Show($"meta行未指定table名"); MessageBox.Show($"meta行未指定table名");
return; return;
@ -181,7 +181,7 @@ namespace LubanAssistant
var tableDef = await DataLoaderUtil.LoadTableDefAsync(RootDefineFile, InputDataDir, tableName); var tableDef = await DataLoaderUtil.LoadTableDefAsync(RootDefineFile, InputDataDir, tableName);
var title = ExcelUtil.ParseTitles(sheet); var title = ExcelUtil.ParseTitles(sheet);
saveTask(sheet, metaAttrs, LastLoadTableData, tableDef, title); saveTask(sheet, rawSheet.TitleRowCount, LastLoadTableData, tableDef, title);
} }
catch (Exception e) catch (Exception e)
{ {
@ -192,13 +192,13 @@ namespace LubanAssistant
private void BtnSaveAllClick(object sender, RibbonControlEventArgs e) private void BtnSaveAllClick(object sender, RibbonControlEventArgs e)
{ {
SaveRecords((Worksheet sheet, Dictionary<string, string> metaAttrs, TableDataInfo tableDataInfo, DefTable defTable, TitleInfo title) => SaveRecords((Worksheet sheet, int titleRowNum, TableDataInfo tableDataInfo, DefTable defTable, Title title) =>
{ {
int usedRowNum = sheet.UsedRange.Rows.Count; int usedRowNum = sheet.UsedRange.Rows.Count;
int firstDataRowNum = int.Parse(metaAttrs["title_rows"]) + 2; int firstDataRowNum = titleRowNum + 2;
if (firstDataRowNum <= usedRowNum) if (firstDataRowNum <= usedRowNum)
{ {
var newRecords = ExcelUtil.LoadRecordsInRange(defTable, sheet, title.RootTitle, (sheet.Range[$"A{firstDataRowNum}:A{usedRowNum}"]).EntireRow); var newRecords = ExcelUtil.LoadRecordsInRange(defTable, sheet, title, (sheet.Range[$"A{firstDataRowNum}:A{usedRowNum}"]).EntireRow);
ExcelUtil.SaveRecords(InputDataDir, defTable, newRecords); ExcelUtil.SaveRecords(InputDataDir, defTable, newRecords);
CleanRemovedRecordFiles(LastLoadTableData, newRecords); CleanRemovedRecordFiles(LastLoadTableData, newRecords);
} }
@ -241,12 +241,12 @@ namespace LubanAssistant
MessageBox.Show("没有选中的行"); MessageBox.Show("没有选中的行");
return; return;
} }
SaveRecords((Worksheet sheet, Dictionary<string, string> metaAttrs, TableDataInfo tableDataInfo, DefTable defTable, TitleInfo title) => SaveRecords((Worksheet sheet, int titleRowNum, TableDataInfo tableDataInfo, DefTable defTable, Title title) =>
{ {
int usedRowNum = sheet.UsedRange.Rows.Count; int usedRowNum = sheet.UsedRange.Rows.Count;
if (title.RowNum + 1 < usedRowNum) if (titleRowNum + 1 < usedRowNum)
{ {
var newRecords = ExcelUtil.LoadRecordsInRange(defTable, sheet, title.RootTitle, selectRange.EntireRow); var newRecords = ExcelUtil.LoadRecordsInRange(defTable, sheet, title, selectRange.EntireRow);
ExcelUtil.SaveRecords(InputDataDir, defTable, newRecords); ExcelUtil.SaveRecords(InputDataDir, defTable, newRecords);
} }
else else

View File

@ -17,51 +17,81 @@ namespace LubanAssistant
{ {
static class ExcelUtil static class ExcelUtil
{ {
public static Dictionary<string, string> ParseMetaAttrs(Worksheet sheet) public static RawSheet ParseRawSheet(Worksheet sheet, Range toSaveRecordRows)
{ {
Range metaRow = sheet.Rows[1]; if (!ParseMetaAttrs(sheet, out var orientRow, out var titleRows, out var tableName))
if (metaRow.Cells[1, 1].Text.ToString() != "##")
{ {
throw new Exception("A1 should be ##"); throw new Exception($"meta行不合法");
} }
var metaAttrs = new Dictionary<string, string>();
for (int i = 2, n = sheet.UsedRange.Columns.Count; i <= n; i++) if (!orientRow)
{ {
Range cell = metaRow.Cells[1, i]; throw new Exception($"目前只支持行表");
string value = cell.Value?.ToString(); }
if (!string.IsNullOrWhiteSpace(value))
Title title = ParseTitles(sheet);
var cells = new List<List<Cell>>();
foreach (Range row in toSaveRecordRows)
{
var rowCell = new List<Cell>();
for (int i = title.FromIndex; i <= title.ToIndex; i++)
{ {
var attrs = value.Split('='); rowCell.Add(new Cell(row.Row - 1, i, (row.Cells[1, i + 1] as Range).Value));
if (attrs.Length != 2)
{
throw new Exception($"invalid meta attr:{value}");
}
metaAttrs.Add(attrs[0], attrs[1]);
} }
cells.Add(rowCell);
} }
return metaAttrs; return new RawSheet() { Title = title, TitleRowCount = titleRows, TableName = tableName, Cells = cells };
} }
public static TitleInfo ParseTitles(Worksheet sheet) public static RawSheet ParseRawSheetTitleOnly(Worksheet sheet)
{ {
if (!ParseMetaAttrs(sheet, out var orientRow, out var titleRows, out var tableName))
{
throw new Exception($"meta行不合法");
}
if (!orientRow)
{
throw new Exception($"目前只支持行表");
}
Title title = ParseTitles(sheet);
var cells = new List<List<Cell>>();
return new RawSheet() { Title = title, TitleRowCount = titleRows, TableName = tableName, Cells = cells };
}
public static bool ParseMetaAttrs(Worksheet sheet, out bool orientRow, out int titleRows, out string tableName)
{
Range metaRow = sheet.Rows[1];
var cells = new List<string>();
for (int i = 1, n = sheet.UsedRange.Columns.Count; i <= n; i++)
{
cells.Add(((Range)metaRow.Cells[1, i]).Value?.ToString());
}
return SheetLoadUtil.TryParseMeta(cells, out orientRow, out titleRows, out tableName);
}
public static Title ParseTitles(Worksheet sheet)
{
int titleRows = 1; int titleRows = 1;
Range c1 = sheet.Cells[2, 1]; Range c1 = sheet.Cells[2, 1];
if (c1.MergeCells) if (c1.MergeCells)
{ {
titleRows = c1.MergeArea.Count; titleRows = c1.MergeArea.Count;
} }
var rootTile = new Title() var rootTile = new Title()
{ {
FromIndex = 2, FromIndex = 0,
ToIndex = sheet.UsedRange.Columns.Count, ToIndex = sheet.UsedRange.Columns.Count - 1,
Name = "__root__", Name = "__root__",
Root = true, Root = true,
Tags = new Dictionary<string, string>(),
}; };
ParseSubTitle(sheet, 2, titleRows + 1, rootTile); ParseSubTitle(sheet, 2, titleRows + 1, rootTile);
return new TitleInfo(rootTile, titleRows); rootTile.Init();
return rootTile;
} }
private static void ParseSubTitle(Worksheet sheet, int rowIndex, int maxRowIndex, Title title) private static void ParseSubTitle(Worksheet sheet, int rowIndex, int maxRowIndex, Title title)
@ -69,51 +99,23 @@ namespace LubanAssistant
Range row = sheet.Rows[rowIndex]; Range row = sheet.Rows[rowIndex];
for (int i = title.FromIndex; i <= title.ToIndex; i++) for (int i = title.FromIndex; i <= title.ToIndex; i++)
{ {
Range subTitleRange = row.Cells[1, i]; Range subTitleRange = row.Cells[1, i + 1];
string subTitleValue = subTitleRange.Value?.ToString(); string subTitleValue = subTitleRange.Value?.ToString();
if (string.IsNullOrWhiteSpace(subTitleValue)) if (string.IsNullOrWhiteSpace(subTitleValue))
{ {
continue; continue;
} }
var attrs = subTitleValue.Split('&'); var (subTitleName, tags) = SheetLoadUtil.ParseNameAndMetaAttrs(subTitleValue);
string subTitleName = attrs[0];
string sep = "";
foreach (var attrPair in attrs.Skip(1))
{
var pairs = attrPair.Split('=');
if (pairs.Length != 2)
{
throw new Exception($"invalid title: {subTitleValue}");
}
switch (pairs[0])
{
case "sep":
{
sep = pairs[1];
break;
}
default:
{
throw new Exception($"invalid title: {subTitleValue}");
}
}
}
if (title.SubTitles.ContainsKey(subTitleName))
{
throw new Exception($"title:{subTitleName} 重复");
}
var newSubTitle = new Title() var newSubTitle = new Title()
{ {
Name = subTitleName, Name = subTitleName,
FromIndex = i, FromIndex = i,
ToIndex = i, Tags = tags,
}; };
if (!string.IsNullOrWhiteSpace(sep))
{
newSubTitle.Sep = sep;
}
if (subTitleRange.MergeCells) if (subTitleRange.MergeCells)
{ {
newSubTitle.ToIndex = i + subTitleRange.MergeArea.Count - 1; newSubTitle.ToIndex = i + subTitleRange.MergeArea.Count - 1;
@ -122,9 +124,8 @@ namespace LubanAssistant
{ {
newSubTitle.ToIndex = i; newSubTitle.ToIndex = i;
} }
title.SubTitles.Add(subTitleName, newSubTitle); title.AddSubTitle(newSubTitle);
} }
title.SubTitleList.AddRange(title.SubTitles.Values);
if (rowIndex < maxRowIndex) if (rowIndex < maxRowIndex)
{ {
foreach (var subTitle in title.SubTitleList) foreach (var subTitle in title.SubTitleList)
@ -134,17 +135,8 @@ namespace LubanAssistant
} }
} }
public static void FillRecords(Worksheet sheet, Dictionary<string, string> metaAttrs, TitleInfo title, TableDataInfo tableDataInfo) public static void FillRecords(Worksheet sheet, int titleRowNum, Title title, TableDataInfo tableDataInfo)
{ {
int titleRowNum = 3;
if (metaAttrs.TryGetValue("title_rows", out var titleRowsStr) && !int.TryParse(titleRowsStr, out titleRowNum))
{
throw new Exception($"meta 属性 title_rows 不合法");
}
if (titleRowNum < title.RowNum)
{
throw new Exception($"meta 属性title_rows不能比字段名行的行数小");
}
int usedRowNum = sheet.UsedRange.Rows.Count; int usedRowNum = sheet.UsedRange.Rows.Count;
if (usedRowNum > titleRowNum + 1) if (usedRowNum > titleRowNum + 1)
{ {
@ -158,35 +150,17 @@ namespace LubanAssistant
{ {
var fillVisitor = new FillSheetVisitor(sheet, nextRowIndex); var fillVisitor = new FillSheetVisitor(sheet, nextRowIndex);
//FillRecord(sheet, ref nextRowIndex, title.RootTitle, rec); //FillRecord(sheet, ref nextRowIndex, title.RootTitle, rec);
nextRowIndex += rec.Data.Apply(fillVisitor, title.RootTitle); nextRowIndex += rec.Data.Apply(fillVisitor, title);
} }
} }
public static List<Record> LoadRecordsInRange(DefTable table, Worksheet sheet, Title title, Range toSaveRecordRows) public static List<Record> LoadRecordsInRange(DefTable table, Worksheet sheet, Title title, Range toSaveRecordRows)
{ {
var recs = new List<Record>(); RawSheet rawSheet = ParseRawSheet(sheet, toSaveRecordRows);
foreach (Range row in toSaveRecordRows) var excelSource = new ExcelDataSource();
{ excelSource.Load(rawSheet);
bool allEmpty = true;
for (int i = title.FromIndex; i <= title.ToIndex; i++) return excelSource.ReadMulti(table.ValueTType);
{
if (!string.IsNullOrEmpty((row.Cells[1, i] as Range).Value?.ToString()))
{
allEmpty = false;
break;
}
}
if (allEmpty)
{
continue;
}
string tags = (row.Cells[1, 1] as Range).Value?.ToString();
recs.Add(new Record(
(DBean)table.ValueTType.Apply(new SheetDataCreator(sheet, row.Row, table.Assembly), title, null),
"",
DataUtil.ParseTags(tags)));
}
return recs;
} }
public static void SaveRecords(string inputDataDir, DefTable table, List<Record> records) public static void SaveRecords(string inputDataDir, DefTable table, List<Record> records)

View File

@ -26,75 +26,77 @@ namespace LubanAssistant
_startRowIndex = startRowIndex; _startRowIndex = startRowIndex;
} }
Range Current(Title title) => _cells[_startRowIndex, title.FromIndex + 1] as Range;
public int Accept(DBool type, Title x) public int Accept(DBool type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DByte type, Title x) public int Accept(DByte type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DShort type, Title x) public int Accept(DShort type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DFshort type, Title x) public int Accept(DFshort type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DInt type, Title x) public int Accept(DInt type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DFint type, Title x) public int Accept(DFint type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DLong type, Title x) public int Accept(DLong type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DFlong type, Title x) public int Accept(DFlong type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DFloat type, Title x) public int Accept(DFloat type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DDouble type, Title x) public int Accept(DDouble type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
public int Accept(DEnum type, Title x) public int Accept(DEnum type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.StrValue; Current(x).Value = type.StrValue;
return 1; return 1;
} }
public int Accept(DString type, Title x) public int Accept(DString type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value; Current(x).Value = type.Value;
return 1; return 1;
} }
@ -109,7 +111,7 @@ namespace LubanAssistant
//{ //{
// throw new Exception($"title:{x.Name}为text类型至少要占两列"); // throw new Exception($"title:{x.Name}为text类型至少要占两列");
//} //}
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
//(_cells[_startRowIndex, x.FromIndex + 1] as Range).Value = type.RawValue; //(_cells[_startRowIndex, x.FromIndex + 1] as Range).Value = type.RawValue;
return 1; return 1;
} }
@ -126,11 +128,11 @@ namespace LubanAssistant
} }
if (type.ImplType != null) if (type.ImplType != null)
{ {
(_cells[_startRowIndex, typeTitle.FromIndex] as Range).Value = type.ImplType.Name; Current(typeTitle).Value = type.ImplType.Name;
} }
else else
{ {
(_cells[_startRowIndex, typeTitle.FromIndex] as Range).Value = DefBean.BEAN_NULL_STR; Current(typeTitle).Value = DefBean.BEAN_NULL_STR;
} }
} }
else else
@ -141,7 +143,7 @@ namespace LubanAssistant
} }
else else
{ {
//(_cells[_startRowIndex, x.FromIndex] as Range).Value = "null"; //Current(x).Value = "null";
throw new Exception($"title:{x.Name} 不支持 值为null的普通bean"); throw new Exception($"title:{x.Name} 不支持 值为null的普通bean");
} }
} }
@ -173,59 +175,59 @@ namespace LubanAssistant
} }
else else
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
} }
return 1; return 1;
} }
public int Accept(DArray type, Title x) public int Accept(DArray type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1; return 1;
} }
public int Accept(DList type, Title x) public int Accept(DList type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1; return 1;
} }
public int Accept(DSet type, Title x) public int Accept(DSet type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1; return 1;
} }
public int Accept(DMap type, Title x) public int Accept(DMap type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep); Current(x).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1; return 1;
} }
public int Accept(DVector2 type, Title x) public int Accept(DVector2 type, Title x)
{ {
var v = type.Value; var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y}"; Current(x).Value = $"{v.X},{v.Y}";
return 1; return 1;
} }
public int Accept(DVector3 type, Title x) public int Accept(DVector3 type, Title x)
{ {
var v = type.Value; var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y},{v.Z}"; Current(x).Value = $"{v.X},{v.Y},{v.Z}";
return 1; return 1;
} }
public int Accept(DVector4 type, Title x) public int Accept(DVector4 type, Title x)
{ {
var v = type.Value; var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y},{v.Z},{v.W}"; Current(x).Value = $"{v.X},{v.Y},{v.Z},{v.W}";
return 1; return 1;
} }
public int Accept(DDateTime type, Title x) public int Accept(DDateTime type, Title x)
{ {
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Time; Current(x).Value = type.Time;
return 1; return 1;
} }
} }

View File

@ -229,11 +229,8 @@
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\DataCreateException.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\DataCreateException.cs">
<Link>Source\DataCreators\DataCreateException.cs</Link> <Link>Source\DataCreators\DataCreateException.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\ExcelDataCreator.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\ExcelStreamDataCreator.cs">
<Link>Source\DataCreators\ExcelDataCreator.cs</Link> <Link>Source\DataCreators\ExcelStreamDataCreator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\ExcelNamedRowDataCreator.cs">
<Link>Source\DataCreators\ExcelNamedRowDataCreator.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\JsonDataCreator.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\JsonDataCreator.cs">
<Link>Source\DataCreators\JsonDataCreator.cs</Link> <Link>Source\DataCreators\JsonDataCreator.cs</Link>
@ -244,6 +241,9 @@
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\MultiRowExcelDataCreator.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\MultiRowExcelDataCreator.cs">
<Link>Source\DataCreators\MultiRowExcelDataCreator.cs</Link> <Link>Source\DataCreators\MultiRowExcelDataCreator.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\SheetDataCreator.cs">
<Link>Source\DataCreators\SheetDataCreator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\StringDataCreator.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\StringDataCreator.cs">
<Link>Source\DataCreators\StringDataCreator.cs</Link> <Link>Source\DataCreators\StringDataCreator.cs</Link>
</Compile> </Compile>
@ -271,12 +271,24 @@
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\ExcelStream.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\ExcelStream.cs">
<Link>Source\DataSources\Excel\ExcelStream.cs</Link> <Link>Source\DataSources\Excel\ExcelStream.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\RawSheet.cs">
<Link>Source\DataSources\Excel\RawSheet.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\RawSheetTableDefInfo.cs">
<Link>Source\DataCreators\RawSheetTableDefInfo.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Sheet.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Sheet.cs">
<Link>Source\DataSources\Excel\Sheet.cs</Link> <Link>Source\DataSources\Excel\Sheet.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\SheetLoadUtil.cs">
<Link>Source\DataSources\Excel\SheetLoadUtil.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Title.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Title.cs">
<Link>Source\DataSources\Excel\Title.cs</Link> <Link>Source\DataSources\Excel\Title.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\TitleRow.cs">
<Link>Source\DataSources\Excel\TitleRow.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Json\JsonDataSource.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataSources\Json\JsonDataSource.cs">
<Link>Source\DataSources\Json\JsonDataSource.cs</Link> <Link>Source\DataSources\Json\JsonDataSource.cs</Link>
</Compile> </Compile>
@ -364,18 +376,33 @@
<Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\IDataFuncVisitor.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\IDataFuncVisitor.cs">
<Link>Source\DataVisitors\IDataFuncVisitor.cs</Link> <Link>Source\DataVisitors\IDataFuncVisitor.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\IsDefaultValue.cs">
<Link>Source\DataVisitors\IsDefaultValue.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\ToLiteralVisitorBase.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\ToLiteralVisitorBase.cs">
<Link>Source\DataVisitors\ToLiteralVisitorBase.cs</Link> <Link>Source\DataVisitors\ToLiteralVisitorBase.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\ToStringVisitor.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\ToStringVisitor.cs">
<Link>Source\DataVisitors\ToStringVisitor.cs</Link> <Link>Source\DataVisitors\ToStringVisitor.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataVisitors\ValidatorVisitor.cs">
<Link>Source\DataVisitors\ValidatorVisitor.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\CfgDefLoader.cs">
<Link>Source\Defs\CfgDefLoader.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\CfgDefTypeBase.cs"> <Compile Include="..\Luban.Job.Cfg\Source\Defs\CfgDefTypeBase.cs">
<Link>Source\Defs\CfgDefTypeBase.cs</Link> <Link>Source\Defs\CfgDefTypeBase.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\DefAssembly.cs">
<Link>Source\Defs\DefAssembly.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\DefBean.cs"> <Compile Include="..\Luban.Job.Cfg\Source\Defs\DefBean.cs">
<Link>Source\Defs\DefBean.cs</Link> <Link>Source\Defs\DefBean.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\DefField.cs">
<Link>Source\Defs\DefField.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Defs\DefTable.cs"> <Compile Include="..\Luban.Job.Cfg\Source\Defs\DefTable.cs">
<Link>Source\Defs\DefTable.cs</Link> <Link>Source\Defs\DefTable.cs</Link>
</Compile> </Compile>
@ -418,6 +445,24 @@
<Compile Include="..\Luban.Job.Cfg\Source\Utils\DataUtil.cs"> <Compile Include="..\Luban.Job.Cfg\Source\Utils\DataUtil.cs">
<Link>Source\Utils\DataUtil.cs</Link> <Link>Source\Utils\DataUtil.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\ValidatorContext.cs">
<Link>Source\ValidatorContext.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Validators\IValidator.cs">
<Link>Source\Validators\IValidator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Validators\PathValidator.cs">
<Link>Source\Validators\PathValidator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Validators\RangeValidator.cs">
<Link>Source\Validators\RangeValidator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Validators\RefValidator.cs">
<Link>Source\Validators\RefValidator.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\Validators\ValidatorFactory.cs">
<Link>Source\Validators\ValidatorFactory.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Common\Source\Defs\CommonDefLoader.cs"> <Compile Include="..\Luban.Job.Common\Source\Defs\CommonDefLoader.cs">
<Link>Source\Defs\CommonDefLoader.cs</Link> <Link>Source\Defs\CommonDefLoader.cs</Link>
</Compile> </Compile>
@ -433,6 +478,9 @@
<Compile Include="..\Luban.Job.Common\Source\Defs\DefFieldBase.cs"> <Compile Include="..\Luban.Job.Common\Source\Defs\DefFieldBase.cs">
<Link>Source\Defs\DefFieldBase.cs</Link> <Link>Source\Defs\DefFieldBase.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Common\Source\Defs\DefTypeBase.cs">
<Link>Source\Defs\DefTypeBase.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Common\Source\ELanguage.cs"> <Compile Include="..\Luban.Job.Common\Source\ELanguage.cs">
<Link>Source\ELanguage.cs</Link> <Link>Source\ELanguage.cs</Link>
</Compile> </Compile>
@ -550,7 +598,6 @@
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="SheetDataCreator.cs" />
<Compile Include="Source\DataExporters\RawJsonExportor.cs" /> <Compile Include="Source\DataExporters\RawJsonExportor.cs" />
<Compile Include="ToExcelStringVisitor.cs" /> <Compile Include="ToExcelStringVisitor.cs" />
<EmbeddedResource Include="AssistantTab.resx"> <EmbeddedResource Include="AssistantTab.resx">
@ -577,7 +624,6 @@
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile> </Compile>
<Compile Include="Source\DataSources\Excel\TitleInfo.cs" />
<Compile Include="Source\Utils\AtomicLong.cs" /> <Compile Include="Source\Utils\AtomicLong.cs" />
<Compile Include="Source\Utils\CacheFileUtil.cs" /> <Compile Include="Source\Utils\CacheFileUtil.cs" />
<Compile Include="Source\Collections\CollectionExtension.cs" /> <Compile Include="Source\Collections\CollectionExtension.cs" />
@ -590,10 +636,6 @@
<Compile Include="Source\Common\ValueUtil.cs" /> <Compile Include="Source\Common\ValueUtil.cs" />
<Compile Include="Source\Datas\DText.cs" /> <Compile Include="Source\Datas\DText.cs" />
<Compile Include="Source\Datas\DType.cs" /> <Compile Include="Source\Datas\DType.cs" />
<Compile Include="Source\Defs\CfgDefLoader.cs" />
<Compile Include="Source\Defs\DefAssembly.cs" />
<Compile Include="Source\Defs\DefField.cs" />
<Compile Include="Source\Defs\DefTypeBase.cs" />
<Compile Include="Source\Protos\FileInfo.cs" /> <Compile Include="Source\Protos\FileInfo.cs" />
<Compile Include="Source\Protos\GetImportFileOrDirectory.cs" /> <Compile Include="Source\Protos\GetImportFileOrDirectory.cs" />
<Compile Include="Source\LocalAgent.cs" /> <Compile Include="Source\LocalAgent.cs" />

View File

@ -1,201 +0,0 @@
using Luban.Common.Utils;
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LubanAssistant
{
class SheetDataCreator : ITypeFuncVisitor<Title, DefField, DType>
{
private readonly Worksheet _sheet;
private readonly int _rowIndex;
private readonly DefAssembly _defAss;
public SheetDataCreator(Worksheet sheet, int rowIndex, DefAssembly ass)
{
_sheet = sheet;
_rowIndex = rowIndex;
_defAss = ass;
}
private ExcelStream CreateStream(Title title, bool nameMode = true)
{
return new ExcelStream(new Cell(title.FromIndex, title.ToIndex, _sheet.Cells[_rowIndex, title.FromIndex].Value), title.Sep, nameMode);
}
public DType Accept(TBool type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TByte type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TShort type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TFshort type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TInt type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TFint type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TLong type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TFlong type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TFloat type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TDouble type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TEnum type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TString type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TBytes type, Title x, DefField y)
{
throw new NotSupportedException();
}
public DType Accept(TText type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TBean type, Title title, DefField defField)
{
if (title.HasSubTitle)
{
var originBean = (DefBean)type.Bean;
DefBean implType;
if (originBean.IsAbstractType)
{
if (!title.SubTitles.TryGetValue(DefBean.TYPE_NAME_KEY, out var typeTitle))
{
throw new Exception($"title:{title.Name} 缺失 子title:{DefBean.TYPE_NAME_KEY}");
}
string subType = (_sheet.Cells[_rowIndex, typeTitle.FromIndex] as Range).Value.ToString().Trim();
if (subType.ToLower() == DefBean.BEAN_NULL_STR)
{
if (!type.IsNullable)
{
throw new Exception($"type:'{type}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
}
return null;
}
string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
implType = (DefBean)originBean.GetNotAbstractChildType(subType);
if (implType == null)
{
throw new Exception($"type:'{fullType}' 不是 bean 类型");
}
}
else
{
implType = originBean;
}
var fields = new List<DType>();
foreach (var f in implType.HierarchyFields)
{
if (!title.SubTitles.TryGetValue(f.Name, out var subTitle))
{
throw new Exception($"title:{title.Name} 缺失子title:{f.Name}");
}
fields.Add(f.CType.Apply(this, subTitle, (DefField)f));
}
return new DBean(originBean, implType, fields);
}
else
{
return type.Apply(ExcelDataCreator.Ins, defField, CreateStream(title, false), _defAss);
}
}
public DType Accept(TArray type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TList type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TSet type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TMap type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TVector2 type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TVector3 type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TVector4 type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
public DType Accept(TDateTime type, Title x, DefField y)
{
return type.Apply(ExcelDataCreator.Ins, y, CreateStream(x), _defAss);
}
}
}

View File

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataSources.Excel
{
class TitleInfo
{
public Title RootTitle { get; }
public int RowNum { get; }
public TitleInfo(Title rootTitle, int rowNum)
{
RootTitle = rootTitle;
RowNum = rowNum;
}
}
}

View File

@ -1,893 +0,0 @@
using Bright.Collections;
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.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
{
public 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>();
public CfgDefLoader(IAgent agent) : base(agent)
{
RegisterRootDefineHandler("importexcel", AddImportExcel);
RegisterRootDefineHandler("patch", AddPatch);
RegisterRootDefineHandler("service", AddService);
RegisterRootDefineHandler("group", AddGroup);
RegisterModuleDefineHandler("table", AddTable);
IsBeanFieldMustDefineId = false;
}
public Defines BuildDefines()
{
return new Defines()
{
TopModule = TopModule,
Patches = _patches,
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(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 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 defineFile, string key, string attr, List<Validator> result)
{
if (!string.IsNullOrWhiteSpace(attr))
{
#if !LUBAN_LITE
foreach (var validatorStr in attr.Split('#', StringSplitOptions.RemoveEmptyEntries))
#else
foreach (var validatorStr in attr.Split('#'))
#endif
{
var sepIndex = validatorStr.IndexOf(':');
if (sepIndex <= 0)
{
throw new Exception($"定义文件:{defineFile} key:'{key}' attr:'{attr}' 不是合法的 validator 定义 (key1:value1#key2:value2 ...)");
}
#if !LUBAN_LITE
result.Add(new Validator() { Type = validatorStr[..sepIndex], Rule = validatorStr[(sepIndex + 1)..] });
#else
result.Add(new Validator() { Type = validatorStr.Substring(0, sepIndex), Rule = validatorStr.Substring(sepIndex + 1, validatorStr.Length - sepIndex - 1) });
#endif
}
}
}
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 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 defineFile, string tableName, string modeStr, string indexStr)
{
ETableMode mode;
switch (modeStr)
{
case "one":
{
if (!string.IsNullOrWhiteSpace(indexStr))
{
throw new Exception($"定义文件:{defineFile} 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", "patch_input", "comment", "define_from_file" };
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");
AddTable(defineFile, name, module, valueType, index, mode, group, comment, defineFromFile, input, patchInput, tags);
}
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)
{
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,
};
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];
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];
if (rc.Count < sheet.AttrRowCount + 1)
{
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} 至少包含 属性行和标题行");
}
var titleRow = sheet.RowColumns[sheet.AttrRowCount];
// 有可能没有注释行,此时使用标题行,这个是必须有的
var descRow = sheet.HeaderRowCount >= sheet.AttrRowCount + 2 ? sheet.RowColumns[sheet.AttrRowCount + 1] : 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++)
{
#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:'{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_rows":
{
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;
}
case "default":
{
cf.DefaultValue = attrValue;
break;
}
case "tags":
{
cf.Tags = attrValue;
break;
}
case "orientation":
{
cf.IsRowOrient = DefUtil.ParseOrientation(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 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_excel", Type = "bool" },
new CfgField() { Name = "input", 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);
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_excel") 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();
AddTable(file.OriginFile, name, module, valueType, index, mode, group, comment, isDefineFromExcel, inputFile, patchInput, tags);
};
}
}
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 = "int" },
new CfgField() { Name = "comment", 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);
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);
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();
string name = TypeUtil.GetName(fullName);
if (string.IsNullOrWhiteSpace(fullName) || string.IsNullOrWhiteSpace(name))
{
throw new Exception($"file:{file.ActualFile} 定义了一个空的enum类名");
}
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 DInt).Value.ToString();
string comment = (data.GetField("comment") as DString).Value.Trim();
string tags = (data.GetField("tags") as DString).Value.Trim();
curEnum.Items.Add(new EnumItem() { Name = item, Alias = alias, Value = value, Comment = comment, Tags = tags });
};
}
}
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 = "sep", Type = "string" },
new CfgField() { Name = "is_multi_rows", Type = "bool" },
new CfgField() { Name = "index", Type = "string" },
new CfgField() { Name = "group", Type = "string" },
new CfgField() { Name = "ref", Type = "string", IgnoreNameValidation = true },
new CfgField() { Name = "path", Type = "string" },
new CfgField() { Name = "comment", Type = "string" },
new CfgField() { Name = "tags", Type = "string" },
new CfgField() { Name = "orientation", 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__", IsMultiRow = true },
}
})
{
AssemblyBase = ass,
};
ass.AddType(defTableRecordType);
defTableRecordType.PreCompile();
defTableRecordType.Compile();
defTableRecordType.PostCompile();
ass.MarkMultiRows();
var tableRecordType = TBean.Create(false, defTableRecordType);
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("index") 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("ref") as DString).Value.Trim(),
(b.GetField("path") as DString).Value.Trim(),
"",
"",
"",
"",
(b.GetField("tags") as DString).Value.Trim(),
false,
DefUtil.ParseOrientation((b.GetField("orientation") as DString).Value)
)).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()
{
"index",
"sep",
"validator",
"key_validator",
"value_validator",
"ref",
"path",
"range",
"multi_rows",
"group",
"res",
"convert",
"comment",
"tags",
"default",
"orientation",
};
private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" };
protected override Field CreateField(string defineFile, XElement e)
{
ValidAttrKeys(defineFile, e, _fieldOptionalAttrs, _fieldRequireAttrs);
return CreateField(defineFile, 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"),
XmlUtil.GetOptionalAttribute(e, "tags"),
false,
DefUtil.ParseOrientation(XmlUtil.GetOptionalAttribute(e, "orientation"))
);
}
private Field CreateField(string defileFile, 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, string tags,
bool ignoreNameValidation, bool isRowOrient)
{
var f = new CfgField()
{
Name = name,
Index = index,
Sep = sep,
IsMultiRow = isMultiRow,
Groups = CreateGroups(group),
Resource = resource,
Converter = converter,
Comment = comment,
Tags = tags,
IgnoreNameValidation = ignoreNameValidation,
IsRowOrient = isRowOrient,
};
// 字段与table的默认组不一样。
// table 默认只属于default=1的组
// 字段默认属于所有组
if (f.Groups.Count == 0)
{
}
else if (!ValidGroup(f.Groups, out var invalidGroup))
{
throw new Exception($"定义文件:{defileFile} 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" };
private static readonly List<string> _beanRequireAttrs = new List<string> { "name" };
protected override 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"),
};
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);
}
}
}
}

View File

@ -1,276 +0,0 @@
using Bright.Collections;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.RawDefs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Defs;
using Luban.Server.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class TableDataInfo
{
public DefTable Table { get; }
public List<Record> MainRecords { get; }
public List<Record> PatchRecords { get; }
//public List<Record> FinalRecords { get; set; }
//public Dictionary<DType, Record> FinalRecordMap { get; set; }
public TableDataInfo(DefTable table, List<Record> mainRecords, List<Record> patchRecords)
{
Table = table;
MainRecords = mainRecords;
PatchRecords = patchRecords;
}
}
public class DefAssembly : DefAssemblyBase
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public new static DefAssembly LocalAssebmly { get => (DefAssembly)DefAssemblyBase.LocalAssebmly; set => DefAssemblyBase.LocalAssebmly = value; }
public Service CfgTargetService { get; private set; }
private readonly string _patchName;
private readonly List<string> _excludeTags;
public Patch TargetPatch { get; private set; }
public TimeZoneInfo TimeZone { get; }
public DefAssembly(string patchName, TimeZoneInfo timezone, List<string> excludeTags, IAgent agent)
{
this._patchName = patchName;
this.TimeZone = timezone;
this._excludeTags = excludeTags;
this.Agent = agent;
}
public bool NeedExport(List<string> groups)
{
if (groups.Count == 0)
{
return true;
}
return groups.Any(g => CfgTargetService.Groups.Contains(g));
}
private readonly List<Patch> _patches = new List<Patch>();
private readonly List<Service> _cfgServices = new List<Service>();
private readonly ConcurrentDictionary<string, TableDataInfo> _recordsByTables = new();
public Dictionary<string, DefTable> CfgTables { get; } = new Dictionary<string, DefTable>();
public Patch GetPatch(string name)
{
return _patches.Find(b => b.Name == name);
}
public void AddCfgTable(DefTable table)
{
if (!CfgTables.TryAdd(table.FullName, table))
{
throw new Exception($"table:'{table.FullName}' duplicated");
}
}
public DefTable GetCfgTable(string name)
{
return CfgTables.TryGetValue(name, out var t) ? t : null;
}
public void AddDataTable(DefTable table, List<Record> mainRecords, List<Record> patchRecords)
{
_recordsByTables[table.FullName] = new TableDataInfo(table, mainRecords, patchRecords);
}
public List<Record> GetTableAllDataList(DefTable table)
{
return _recordsByTables[table.FullName].MainRecords;
}
public TableDataInfo GetTableDataInfo(DefTable table)
{
return _recordsByTables[table.FullName];
}
public List<DefTable> GetExportTables()
{
return Types.Values.Where(t => t is DefTable ct && ct.NeedExport).Select(t => (DefTable)t).ToList();
}
public List<DefTypeBase> GetExportTypes()
{
var refTypes = new Dictionary<string, DefTypeBase>();
var targetService = CfgTargetService;
foreach (var refType in targetService.Refs)
{
if (!this.Types.ContainsKey(refType))
{
throw new Exception($"service:'{targetService.Name}' ref:'{refType}' 类型不存在");
}
if (!refTypes.TryAdd(refType, this.Types[refType]))
{
throw new Exception($"service:'{targetService.Name}' ref:'{refType}' 重复引用");
}
}
foreach (var e in this.Types)
{
if (!refTypes.ContainsKey(e.Key) && (e.Value is DefEnum))
{
refTypes.Add(e.Key, e.Value);
}
}
foreach (var table in GetExportTables())
{
refTypes[table.FullName] = table;
table.ValueTType.Apply(RefTypeVisitor.Ins, refTypes);
}
return refTypes.Values.ToList();
}
public void Load(string outputService, Defines defines)
{
SupportDatetimeType = true;
TopModule = defines.TopModule;
CfgTargetService = defines.Services.Find(s => s.Name == outputService);
if (CfgTargetService == null)
{
throw new ArgumentException($"service:{outputService} not exists");
}
if (!string.IsNullOrWhiteSpace(_patchName))
{
TargetPatch = defines.Patches.Find(b => b.Name == _patchName);
if (TargetPatch == null)
{
throw new Exception($"patch '{_patchName}' not in valid patch set");
}
}
this._patches.AddRange(defines.Patches);
foreach (var e in defines.Enums)
{
AddType(new DefEnum(e));
}
foreach (var b in defines.Beans)
{
AddType(new DefBean((CfgBean)b));
}
foreach (var p in defines.Tables)
{
var table = new DefTable(p);
AddType(table);
AddCfgTable(table);
}
_cfgServices.AddRange(defines.Services);
foreach (var type in Types.Values)
{
type.AssemblyBase = this;
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("precompile type:{0} begin", type.FullName);
type.PreCompile();
s_logger.Trace("precompile type:{0} end", type.FullName);
}
catch (Exception)
{
this.Agent.Error("precompile type:{0} error", type.FullName);
throw;
}
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("compile type:{0} begin", type.FullName);
type.Compile();
s_logger.Trace("compile type:{0} end", type.FullName);
}
catch (Exception)
{
this.Agent.Error("compile type:{0} error", type.FullName);
s_logger.Error("compile type:{0} error", type.FullName);
throw;
}
}
foreach (var type in Types.Values)
{
try
{
s_logger.Trace("post compile type:{0} begin", type.FullName);
type.PostCompile();
s_logger.Trace("post compile type:{0} end", type.FullName);
}
catch (Exception)
{
this.Agent.Error("post compile type:{0} error", type.FullName);
s_logger.Error("post compile type:{0} error", type.FullName);
throw;
}
}
// 丑陋. 怎么写更好?
// 递归 设置DefBean及DefField 的 IsMultiRow
MarkMultiRows();
}
public void MarkMultiRows()
{
var multiRowBeans = new HashSet<DefBean>();
for (bool anyMark = true; anyMark;)
{
anyMark = false;
foreach (var type in this.Types.Values)
{
if (type is DefBean beanType && !beanType.IsMultiRow)
{
bool isMultiRows;
if (beanType.IsNotAbstractType)
{
isMultiRows = beanType.HierarchyFields.Any(f => ((DefField)f).ComputeIsMultiRow());
}
else
{
isMultiRows = beanType.HierarchyNotAbstractChildren.Any(c => ((DefBean)c).IsMultiRow);
}
if (isMultiRows)
{
beanType.IsMultiRow = true;
//s_logger.Info("bean:{bean} is multi row", beanType.FullName);
anyMark = true;
}
}
}
}
}
}
}

View File

@ -1,181 +0,0 @@
using Luban.Common.Utils;
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.RawDefs;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Luban.Job.Cfg.Defs
{
public class DefField : DefFieldBase
{
public DefAssembly Assembly => (DefAssembly)HostType.AssemblyBase;
public bool RawIsMultiRow { get; }
public bool IsMultiRow { get; private set; }
public bool ComputeIsMultiRow()
{
if (IsMultiRow)
{
return true;
}
switch (CType)
{
case TBean b: { return IsMultiRow = ((DefBean)b.Bean).IsMultiRow; }
case TList b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
case TArray b: { return IsMultiRow = b.ElementType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
case TMap b: { return IsMultiRow = b.ValueType is TBean b2 && ((DefBean)b2.Bean).IsMultiRow; }
default: return false;
}
}
public string Index { get; }
public List<string> Groups { get; }
public DefField IndexField { get; private set; }
public string Sep { get; set; }
// 如果没有指定sep
// 如果是bean,且指定了sep则使用此值
// 如果是vectorN,使用 ,
public string ActualSep => string.IsNullOrWhiteSpace(Sep) ? (CType is TBean bean ? ((DefBean)bean.Bean).Sep : "") : Sep;
public bool NeedExport => Assembly.NeedExport(this.Groups);
public TEnum Remapper { get; private set; }
public CfgField RawDefine { get; }
public string GetTextKeyName(string name) => name + TText.L10N_FIELD_SUFFIX;
public bool GenTextKey => this.CType is TText;
public string DefaultValue { get; }
public DType DefalutDtypeValue { get; private set; }
public bool IsRowOrient { get; }
public DefField(DefTypeBase host, CfgField f, int idOffset) : base(host, f, idOffset)
{
Index = f.Index;
Sep = f.Sep;
this.IsMultiRow = this.RawIsMultiRow = f.IsMultiRow;
this.Groups = f.Groups;
this.RawDefine = f;
this.DefaultValue = f.DefaultValue;
this.IsRowOrient = f.IsRowOrient;
}
public override void Compile()
{
base.Compile();
if (!string.IsNullOrWhiteSpace(this.DefaultValue))
{
this.DefalutDtypeValue = CType.Apply(StringDataCreator.Ins, this.DefaultValue);
}
switch (CType)
{
case TArray t:
{
if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0)
{
throw new Exception($"container element type:'{e.Bean.FullName}' can't be empty bean");
}
if (t.ElementType is TText)
{
throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text");
}
break;
}
case TList t:
{
if (t.ElementType is TBean e && !e.IsDynamic && e.Bean.HierarchyFields.Count == 0)
{
throw new Exception($"container element type:'{e.Bean.FullName}' can't be empty bean");
}
if (t.ElementType is TText)
{
throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text");
}
break;
}
case TSet t:
{
if (t.ElementType is TText)
{
throw new Exception($"bean:{HostType.FullName} field:{Name} container element type can't text");
}
break;
}
case TMap t:
{
if (t.KeyType is TText)
{
throw new Exception($"bean:{HostType.FullName} field:{Name} container key type can't text");
}
if (t.ValueType is TText)
{
throw new Exception($"bean:{HostType.FullName} field:{Name} container value type can't text");
}
break;
}
}
if (IsMultiRow && !CType.IsCollection && !CType.IsBean)
{
throw new Exception($"只有容器类型才支持 multi_line 属性");
}
if (string.IsNullOrEmpty(Sep) && CType is TBean bean)
{
Sep = bean.GetBeanAs<DefBean>().Sep;
}
if (!string.IsNullOrEmpty(Index))
{
if ((CType is TArray tarray) && (tarray.ElementType is TBean b))
{
if ((IndexField = b.GetBeanAs<DefBean>().GetField(Index)) == null)
{
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. index not exist");
}
}
else if ((CType is TList tlist) && (tlist.ElementType is TBean tb))
{
if ((IndexField = tb.GetBeanAs<DefBean>().GetField(Index)) == null)
{
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. index not exist");
}
}
else
{
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' index:'{Index}'. only array:bean or list:bean support index");
}
}
if (!string.IsNullOrEmpty(this.RawDefine.Converter))
{
this.Remapper = AssemblyBase.GetDefTType(HostType.Namespace, this.RawDefine.Converter, this.IsNullable) as TEnum;
if (this.Remapper == null)
{
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' converter:'{this.RawDefine.Converter}' not exists");
}
}
}
}
}

View File

@ -1,69 +0,0 @@
using Luban.Common.Utils;
using Luban.Server.Common;
using System.Collections.Generic;
namespace Luban.Job.Common.Defs
{
public abstract class DefTypeBase
{
public DefAssemblyBase AssemblyBase { get; set; }
public int Id { get; protected set; }
public string TopModule => AssemblyBase.TopModule;
public IAgent Agent => AssemblyBase.Agent;
public string Name { get; set; }
public string Namespace { get; set; }
public string FullName => TypeUtil.MakeFullName(Namespace, Name);
public string NamespaceWithTopModule => TypeUtil.MakeNamespace(AssemblyBase.TopModule, Namespace);
public string FullNameWithTopModule => TypeUtil.MakeFullName(AssemblyBase.TopModule, FullName);
public string JavaFullName => TypeUtil.MakeFullName(Namespace, Name);
public string GoFullName => TypeUtil.MakeGoFullName(Namespace, Name);
public string GoPkgName => TypeUtil.MakeGoPkgName(Namespace);
public string CppNamespaceBegin => TypeUtil.MakeCppNamespaceBegin(Namespace);
public string CppNamespaceEnd => TypeUtil.MakeCppNamespaceEnd(Namespace);
public string CppFullNameWithTopModule => TypeUtil.MakeCppFullName(AssemblyBase.TopModule, FullName);
public string TypescriptNamespaceBegin => TypeUtil.MakeTypescriptNamespaceBegin(Namespace);
public string TypescriptNamespaceEnd => TypeUtil.MakeTypescriptNamespaceEnd(Namespace);
public string CppFullName => TypeUtil.MakeCppFullName(Namespace, Name);
public string PyFullName => TypeUtil.MakePyFullName(Namespace, Name);
public string RustFullName => TypeUtil.MakeRustFullName(Namespace, Name);
public string Comment { get; protected set; }
public Dictionary<string, string> Tags { get; protected set; }
public bool HasTag(string attrName)
{
return Tags != null && Tags.ContainsKey(attrName);
}
public string GetTag(string attrName)
{
return Tags != null && Tags.TryGetValue(attrName, out var value) ? value : null;
}
public virtual void PreCompile() { }
public abstract void Compile();
public virtual void PostCompile() { }
}
}