【重构】重构excel解析代码及将field上的属性移到type
parent
4308db1a83
commit
4613169811
|
|
@ -1,613 +0,0 @@
|
||||||
using Bright.Collections;
|
|
||||||
using Luban.Common.Utils;
|
|
||||||
using Luban.Job.Cfg.Datas;
|
|
||||||
using Luban.Job.Cfg.DataSources.Excel;
|
|
||||||
using Luban.Job.Cfg.Defs;
|
|
||||||
using Luban.Job.Cfg.Utils;
|
|
||||||
using Luban.Job.Common.Types;
|
|
||||||
using Luban.Job.Common.TypeVisitors;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.DataCreators
|
|
||||||
{
|
|
||||||
enum EReadPolicy
|
|
||||||
{
|
|
||||||
SKIP_NULL_CELL = 0x1,
|
|
||||||
SKIP_BLANK_CELL = 0x2,
|
|
||||||
NULL_AS_NULL = 0x4,
|
|
||||||
NULL_STR_AS_NULL = 0x8,
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvalidExcelDataException : Exception
|
|
||||||
{
|
|
||||||
public InvalidExcelDataException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public InvalidExcelDataException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public InvalidExcelDataException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InvalidExcelDataException(SerializationInfo info, StreamingContext context) : base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExcelDataCreator : ITypeFuncVisitor<DefField, ExcelStream, DefAssembly, DType>
|
|
||||||
{
|
|
||||||
public static ExcelDataCreator Ins { get; } = new ExcelDataCreator();
|
|
||||||
|
|
||||||
private bool CheckNull(bool nullable, object o)
|
|
||||||
{
|
|
||||||
return nullable && (o == null || (o is string s && s == "null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckIsDefault(bool namedMode, object value)
|
|
||||||
{
|
|
||||||
if (namedMode)
|
|
||||||
{
|
|
||||||
if (value == null || (value is string s && string.IsNullOrEmpty(s)))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CreateBool(object x)
|
|
||||||
{
|
|
||||||
if (x is bool b)
|
|
||||||
{
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
var s = x.ToString().ToLower().Trim();
|
|
||||||
switch (s)
|
|
||||||
{
|
|
||||||
case "true":
|
|
||||||
case "是": return true;
|
|
||||||
case "false":
|
|
||||||
case "否": return false;
|
|
||||||
default: throw new InvalidExcelDataException($"{s} 不是 bool 类型的值 (true 或 false)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TBool type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DBool.ValueOf(false);
|
|
||||||
}
|
|
||||||
return DBool.ValueOf(CreateBool(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TByte type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DByte.Default;
|
|
||||||
}
|
|
||||||
if (!byte.TryParse(d.ToString(), out byte v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 byte 类型值");
|
|
||||||
}
|
|
||||||
return new DByte(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TShort type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DShort.Default;
|
|
||||||
}
|
|
||||||
if (!short.TryParse(d.ToString(), out short v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 short 类型值");
|
|
||||||
}
|
|
||||||
return new DShort(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFshort type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DFshort.Default;
|
|
||||||
}
|
|
||||||
if (!short.TryParse(d.ToString(), out short v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 short 类型值");
|
|
||||||
}
|
|
||||||
return new DFshort(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TInt type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DInt.ValueOf(0);
|
|
||||||
}
|
|
||||||
var ds = d.ToString();
|
|
||||||
if (field?.Remapper is TEnum te)
|
|
||||||
{
|
|
||||||
if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
|
||||||
{
|
|
||||||
return DInt.ValueOf(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!int.TryParse(ds, out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 int 类型值");
|
|
||||||
}
|
|
||||||
return DInt.ValueOf(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFint type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DFint.Default;
|
|
||||||
}
|
|
||||||
var ds = d.ToString();
|
|
||||||
if (field?.Remapper is TEnum te)
|
|
||||||
{
|
|
||||||
if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
|
||||||
{
|
|
||||||
return new DFint(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!int.TryParse(ds, out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 int 类型值");
|
|
||||||
}
|
|
||||||
return new DFint(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TLong type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DLong.Default;
|
|
||||||
}
|
|
||||||
var ds = d.ToString();
|
|
||||||
if (field?.Remapper is TEnum te)
|
|
||||||
{
|
|
||||||
if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
|
||||||
{
|
|
||||||
return DLong.ValueOf(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!long.TryParse(ds, out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 long 类型值");
|
|
||||||
}
|
|
||||||
return DLong.ValueOf(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFlong type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DFlong.Default;
|
|
||||||
}
|
|
||||||
var ds = d.ToString();
|
|
||||||
if (field?.Remapper is TEnum te)
|
|
||||||
{
|
|
||||||
if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
|
||||||
{
|
|
||||||
return new DFlong(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!long.TryParse(ds, out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 long 类型值");
|
|
||||||
}
|
|
||||||
return new DFlong(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFloat type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DFloat.ValueOf(0);
|
|
||||||
}
|
|
||||||
if (!float.TryParse(d.ToString(), out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 float 类型值");
|
|
||||||
}
|
|
||||||
return DFloat.ValueOf(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TDouble type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DDouble.Default;
|
|
||||||
}
|
|
||||||
if (!double.TryParse(d.ToString(), out var v))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"{d} 不是 double 类型值");
|
|
||||||
}
|
|
||||||
return new DDouble(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TEnum type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d) && field?.DefalutDtypeValue != null)
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue;
|
|
||||||
}
|
|
||||||
return new DEnum(type, d.ToString().Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TString type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("excel string类型在标题头对应模式下必须正好占据一个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckIsDefault(x.NamedMode, d) && field?.DefalutDtypeValue != null)
|
|
||||||
{
|
|
||||||
return field.DefalutDtypeValue;
|
|
||||||
}
|
|
||||||
var s = ParseString(d);
|
|
||||||
if (s == null)
|
|
||||||
{
|
|
||||||
if (type.IsNullable)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("字段不是nullable类型,不能为null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return DString.ValueOf(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TBytes type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ParseString(object d)
|
|
||||||
{
|
|
||||||
if (d == null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
else if (d is string s)
|
|
||||||
{
|
|
||||||
return DataUtil.UnEscapeString(s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return d.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TText type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 2)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("excel text 类型在标题头对应模式下必须正好占据2个单元格");
|
|
||||||
}
|
|
||||||
string key = ParseString(x.Read(x.NamedMode));
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
if (type.IsNullable)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("该字段不是nullable类型,不能为null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string text = ParseString(x.Read(x.NamedMode));
|
|
||||||
DataUtil.ValidateText(key, text);
|
|
||||||
return new DText(key, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<DType> CreateBeanFields(DefBean bean, ExcelStream stream, DefAssembly ass)
|
|
||||||
{
|
|
||||||
var list = new List<DType>();
|
|
||||||
foreach (DefField f in bean.HierarchyFields)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string sep = f.ActualSep;
|
|
||||||
if (string.IsNullOrWhiteSpace(sep))
|
|
||||||
{
|
|
||||||
list.Add(f.CType.Apply(this, f, stream, ass));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
list.Add(f.CType.Apply(this, f, new ExcelStream(stream.ReadCell(), sep, false), ass));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
var originBean = (DefBean)type.Bean;
|
|
||||||
|
|
||||||
if (originBean.IsAbstractType)
|
|
||||||
{
|
|
||||||
string subType = x.Read().ToString();
|
|
||||||
if (subType.ToLower().Trim() == DefBean.BEAN_NULL_STR)
|
|
||||||
{
|
|
||||||
if (!type.IsNullable)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"type:{type.Bean.FullName}不是可空类型. 不能为空");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
|
|
||||||
DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
|
|
||||||
if (implType == null)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"type:{fullType} 不是bean类型");
|
|
||||||
}
|
|
||||||
return new DBean(originBean, implType, CreateBeanFields(implType, x, ass));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (type.IsNullable)
|
|
||||||
{
|
|
||||||
string subType = x.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, x, ass));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 容器类统统不支持 type.IsNullable
|
|
||||||
// 因为貌似没意义?
|
|
||||||
public List<DType> ReadList(TType type, DefField field, ExcelStream stream, DefAssembly ass)
|
|
||||||
{
|
|
||||||
stream.NamedMode = false;
|
|
||||||
string sep = type is TBean bean ? ((DefBean)bean.Bean).Sep : null;
|
|
||||||
var datas = new List<DType>();
|
|
||||||
while (!stream.TryReadEOF())
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(sep))
|
|
||||||
{
|
|
||||||
datas.Add(type.Apply(this, field, stream, ass));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
datas.Add(type.Apply(this, field, new ExcelStream(stream.ReadCell(), sep, false), ass)); ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return datas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TArray type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
return new DArray(type, ReadList(type.ElementType, field, x, ass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TList type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
return new DList(type, ReadList(type.ElementType, field, x, ass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TSet type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
return new DSet(type, ReadList(type.ElementType, field, x, ass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TMap type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
x.NamedMode = false;
|
|
||||||
string sep = type.ValueType is TBean bean ? ((DefBean)bean.Bean).Sep : null;
|
|
||||||
|
|
||||||
var datas = new Dictionary<DType, DType>();
|
|
||||||
while (!x.TryReadEOF())
|
|
||||||
{
|
|
||||||
var key = type.KeyType.Apply(this, null, x, ass);
|
|
||||||
var value = string.IsNullOrWhiteSpace(sep) ? type.ValueType.Apply(this, null, x, ass) : type.ValueType.Apply(this, null, new ExcelStream(x.ReadCell(), sep, false), ass);
|
|
||||||
if (!datas.TryAdd(key, value))
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException($"map 的 key:{key} 重复");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new DMap(type, datas);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TVector2 type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DVector2.Default;
|
|
||||||
}
|
|
||||||
return DataUtil.CreateVector(type, d.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TVector3 type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DVector3.Default;
|
|
||||||
}
|
|
||||||
return DataUtil.CreateVector(type, d.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TVector4 type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
if (x.NamedMode && x.IncludeNullAndEmptySize != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidExcelDataException("在标题头对应模式下必须正好占据1个单元格");
|
|
||||||
}
|
|
||||||
var d = x.Read(x.NamedMode);
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d))
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue ?? DVector4.Default;
|
|
||||||
}
|
|
||||||
return DataUtil.CreateVector(type, d.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TDateTime type, DefField field, ExcelStream x, DefAssembly ass)
|
|
||||||
{
|
|
||||||
var d = x.Read();
|
|
||||||
if (CheckNull(type.IsNullable, d))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (CheckIsDefault(x.NamedMode, d) && field?.DefalutDtypeValue != null)
|
|
||||||
{
|
|
||||||
return field?.DefalutDtypeValue;
|
|
||||||
}
|
|
||||||
if (d is System.DateTime datetime)
|
|
||||||
{
|
|
||||||
return new DDateTime(datetime);
|
|
||||||
}
|
|
||||||
return DataUtil.CreateDateTime(d.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,352 +1,350 @@
|
||||||
using Luban.Common.Utils;
|
//using Luban.Common.Utils;
|
||||||
using Luban.Job.Cfg.Datas;
|
//using Luban.Job.Cfg.Datas;
|
||||||
using Luban.Job.Cfg.DataSources.Excel;
|
//using Luban.Job.Cfg.DataSources.Excel;
|
||||||
using Luban.Job.Cfg.Defs;
|
//using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.TypeVisitors;
|
//using Luban.Job.Cfg.TypeVisitors;
|
||||||
using Luban.Job.Common.Types;
|
//using Luban.Job.Common.Types;
|
||||||
using Luban.Job.Common.TypeVisitors;
|
//using Luban.Job.Common.TypeVisitors;
|
||||||
using System;
|
//using System;
|
||||||
using System.Collections.Generic;
|
//using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.DataCreators
|
//namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
|
||||||
class ExcelNamedRowDataCreator : ITypeFuncVisitor<Sheet.NamedRow, bool, bool, DType>
|
|
||||||
{
|
|
||||||
public static ExcelNamedRowDataCreator Ins { get; } = new ExcelNamedRowDataCreator();
|
|
||||||
|
|
||||||
|
|
||||||
public DType ReadExcel(Sheet.NamedRow row, TBean btype)
|
|
||||||
{
|
|
||||||
return Accept(btype, row, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TBool type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TByte type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TShort type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFshort type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TInt type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFint type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TLong type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFlong type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TFloat type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TDouble type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TEnum type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TString type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TBytes type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TText type, Sheet.NamedRow 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, Sheet.NamedRow 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 = f.ActualSep;
|
|
||||||
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.IsRowOrient), f.IsNullable, (DefAssembly)bean.AssemblyBase));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
list.Add(f.CType.Apply(ExcelDataCreator.Ins, f, row.GetMultiRowStream(f.Name, sep, f.IsRowOrient), (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));
|
|
||||||
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, Sheet.NamedRow row, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
var originBean = (DefBean)type.Bean;
|
|
||||||
if (originBean.IsAbstractType)
|
|
||||||
{
|
|
||||||
string subType = row.GetColumn(DefBean.TYPE_NAME_KEY, null, true).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, null, true).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, Sheet.NamedRow row, bool multirow)
|
|
||||||
{
|
|
||||||
var list = new List<DType>();
|
|
||||||
// 如果是多行数据,以当前title为title,每行读入一个element
|
|
||||||
if (multirow)
|
|
||||||
{
|
|
||||||
//foreach (var sub in row.GenerateSubNameRows(elementType))
|
|
||||||
foreach (var sub in Sheet.NamedRow.CreateMultiRowNamedRow(row.Rows, row.SelfTitle, elementType))
|
|
||||||
{
|
|
||||||
list.Add(this.Accept(elementType, sub, false, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果不是多行,并且定义了子标题的话。以一个子标题所占的列,读入一个数据
|
|
||||||
|
|
||||||
//foreach (var sub in row.SelfTitle.SubTitleList)
|
|
||||||
//{
|
//{
|
||||||
// list.Add(this.Accept(elementType, new Sheet.NamedRow(sub, row.Rows), false, false));
|
// 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);
|
||||||
// }
|
// }
|
||||||
throw new NotSupportedException("只有multi_rows=1的list,bean类型才允许有子title");
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TArray type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// public DType Accept(TBool type, TitleRow x, bool multirow, bool nullable)
|
||||||
{
|
// {
|
||||||
if (type.ElementType is not TBean bean)
|
// throw new NotSupportedException();
|
||||||
{
|
// }
|
||||||
throw new Exception($"NamedRow 只支持 bean 类型的容器");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new DArray(type, ReadList(bean, x, multirow));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TList type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// public DType Accept(TByte type, TitleRow x, bool multirow, bool nullable)
|
||||||
{
|
// {
|
||||||
if (type.ElementType is not TBean bean)
|
// throw new NotSupportedException();
|
||||||
{
|
// }
|
||||||
throw new Exception($"NamedRow 只支持 bean 类型的容器");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new DList(type, ReadList(bean, x, multirow));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TSet type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// public DType Accept(TShort type, TitleRow x, bool multirow, bool nullable)
|
||||||
{
|
// {
|
||||||
throw new NotSupportedException();
|
// 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;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
private bool TryCreateColumnStream(Sheet.NamedRow x, Title title, out ExcelStream stream)
|
// public DType Accept(TBean type, TitleRow row, bool multirow, bool nullable)
|
||||||
{
|
// {
|
||||||
var cells = new List<Cell>();
|
// var originBean = (DefBean)type.Bean;
|
||||||
for (int i = title.FromIndex; i <= title.ToIndex; i++)
|
// if (originBean.IsAbstractType)
|
||||||
{
|
// {
|
||||||
foreach (var row in x.Rows)
|
// string subType = row.GetColumn(DefBean.TYPE_NAME_KEY).Read().ToString().Trim();
|
||||||
{
|
// if (subType.ToLower() == DefBean.BEAN_NULL_STR)
|
||||||
if (row.Count > i)
|
// {
|
||||||
{
|
// if (!type.IsNullable)
|
||||||
var value = row[i].Value;
|
// {
|
||||||
if (!(value == null || (value is string s && string.IsNullOrEmpty(s))))
|
// throw new Exception($"type:'{type}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
|
||||||
{
|
// }
|
||||||
cells.Add(row[i]);
|
// return null;
|
||||||
}
|
// }
|
||||||
}
|
// string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
|
||||||
}
|
// DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
|
||||||
}
|
// if (implType == null)
|
||||||
if (cells.Count > 0)
|
// {
|
||||||
{
|
// throw new Exception($"type:'{fullType}' 不是 bean 类型");
|
||||||
stream = new ExcelStream(cells, 0, cells.Count - 1, "", false);
|
// }
|
||||||
return true;
|
// return new DBean(originBean, implType, CreateBeanFields(implType, row));
|
||||||
}
|
// }
|
||||||
stream = null;
|
// else
|
||||||
return false;
|
// {
|
||||||
}
|
// 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})");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
public DType Accept(TMap type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// return new DBean(originBean, originBean, CreateBeanFields(originBean, row));
|
||||||
{
|
// }
|
||||||
var map = new Dictionary<DType, DType>();
|
// }
|
||||||
foreach (var e in x.Titles)
|
|
||||||
{
|
|
||||||
if (TryCreateColumnStream(x, e.Value, out var stream))
|
|
||||||
{
|
|
||||||
var keyData = type.KeyType.Apply(StringDataCreator.Ins, e.Key);
|
|
||||||
var valueData = type.ValueType.Apply(ExcelDataCreator.Ins, null, stream, DefAssembly.LocalAssebmly);
|
|
||||||
map.Add(keyData, valueData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new DMap(type, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TVector2 type, Sheet.NamedRow x, bool multirow, bool nullable)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DType Accept(TVector3 type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// private List<DType> ReadList(TBean elementType, TitleRow row, bool multirow)
|
||||||
{
|
// {
|
||||||
throw new NotSupportedException();
|
// 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
|
||||||
|
// {
|
||||||
|
// // 如果不是多行,并且定义了子标题的话。以一个子标题所占的列,读入一个数据
|
||||||
|
|
||||||
public DType Accept(TVector4 type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// //foreach (var sub in row.SelfTitle.SubTitleList)
|
||||||
{
|
// //{
|
||||||
throw new NotSupportedException();
|
// // 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(TDateTime type, Sheet.NamedRow x, bool multirow, bool nullable)
|
// public DType Accept(TArray type, TitleRow x, bool multirow, bool nullable)
|
||||||
{
|
// {
|
||||||
throw new NotSupportedException();
|
// 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();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,467 @@
|
||||||
|
using Bright.Collections;
|
||||||
|
using Luban.Common.Utils;
|
||||||
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources.Excel;
|
||||||
|
using Luban.Job.Cfg.Defs;
|
||||||
|
using Luban.Job.Cfg.Utils;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using Luban.Job.Common.TypeVisitors;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataCreators
|
||||||
|
{
|
||||||
|
class InvalidExcelDataException : Exception
|
||||||
|
{
|
||||||
|
public InvalidExcelDataException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExcelDataException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExcelDataException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InvalidExcelDataException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExcelStreamDataCreator : ITypeFuncVisitor<ExcelStream, DType>
|
||||||
|
{
|
||||||
|
public static ExcelStreamDataCreator Ins { get; } = new ExcelStreamDataCreator();
|
||||||
|
|
||||||
|
private bool CheckNull(bool nullable, object o)
|
||||||
|
{
|
||||||
|
return nullable && (o == null || (o is string s && s == "null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CreateBool(object x)
|
||||||
|
{
|
||||||
|
if (x is bool b)
|
||||||
|
{
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
var s = x.ToString().ToLower().Trim();
|
||||||
|
switch (s)
|
||||||
|
{
|
||||||
|
case "true":
|
||||||
|
case "是": return true;
|
||||||
|
case "false":
|
||||||
|
case "否": return false;
|
||||||
|
default: throw new InvalidExcelDataException($"{s} 不是 bool 类型的值 (true 或 false)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TBool type, ExcelStream x)
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DBool.ValueOf(CreateBool(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TByte type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!byte.TryParse(d.ToString(), out byte v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 byte 类型值");
|
||||||
|
}
|
||||||
|
return DByte.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TShort type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!short.TryParse(d.ToString(), out short v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 short 类型值");
|
||||||
|
}
|
||||||
|
return DShort.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFshort type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!short.TryParse(d.ToString(), out short v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 short 类型值");
|
||||||
|
}
|
||||||
|
return DFshort.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TInt type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var ds = d.ToString();
|
||||||
|
//if (field?.Remapper is TEnum te)
|
||||||
|
//{
|
||||||
|
// if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
||||||
|
// {
|
||||||
|
// return DInt.ValueOf(c);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if (!int.TryParse(ds, out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 int 类型值");
|
||||||
|
}
|
||||||
|
return DInt.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFint type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var ds = d.ToString();
|
||||||
|
//if (field?.Remapper is TEnum te)
|
||||||
|
//{
|
||||||
|
// if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
||||||
|
// {
|
||||||
|
// return new DFint(c);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if (!int.TryParse(ds, out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 int 类型值");
|
||||||
|
}
|
||||||
|
return DFint.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TLong type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var ds = d.ToString();
|
||||||
|
//if (field?.Remapper is TEnum te)
|
||||||
|
//{
|
||||||
|
// if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
||||||
|
// {
|
||||||
|
// return DLong.ValueOf(c);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if (!long.TryParse(ds, out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 long 类型值");
|
||||||
|
}
|
||||||
|
return DLong.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFlong type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var ds = d.ToString();
|
||||||
|
//if (field?.Remapper is TEnum te)
|
||||||
|
//{
|
||||||
|
// if (te.DefineEnum.TryValueByNameOrAlias(ds, out var c))
|
||||||
|
// {
|
||||||
|
// return new DFlong(c);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if (!long.TryParse(ds, out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 long 类型值");
|
||||||
|
}
|
||||||
|
return DFlong.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFloat type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!float.TryParse(d.ToString(), out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 float 类型值");
|
||||||
|
}
|
||||||
|
return DFloat.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TDouble type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!double.TryParse(d.ToString(), out var v))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"{d} 不是 double 类型值");
|
||||||
|
}
|
||||||
|
return DDouble.ValueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TEnum type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DEnum(type, d.ToString().Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TString type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
var s = ParseString(d);
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
if (type.IsNullable)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException("字段不是nullable类型,不能为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DString.ValueOf(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TBytes type, ExcelStream x)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseString(object d)
|
||||||
|
{
|
||||||
|
if (d == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
else if (d is string s)
|
||||||
|
{
|
||||||
|
return DataUtil.UnEscapeString(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return d.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TText type, ExcelStream x)
|
||||||
|
{
|
||||||
|
string key = ParseString(x.Read());
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
if (type.IsNullable)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException("该字段不是nullable类型,不能为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string text = ParseString(x.Read());
|
||||||
|
DataUtil.ValidateText(key, text);
|
||||||
|
return new DText(key, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DType> CreateBeanFields(DefBean bean, ExcelStream stream)
|
||||||
|
{
|
||||||
|
var list = new List<DType>();
|
||||||
|
foreach (DefField f in bean.HierarchyFields)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string sep = f.Tags.TryGetValue("tag", out var s) ? s : null;
|
||||||
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
|
{
|
||||||
|
list.Add(f.CType.Apply(this, stream));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add(f.CType.Apply(this, new ExcelStream(stream.ReadCell(), sep)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, ExcelStream x)
|
||||||
|
{
|
||||||
|
var originBean = (DefBean)type.Bean;
|
||||||
|
|
||||||
|
if (originBean.IsAbstractType)
|
||||||
|
{
|
||||||
|
string subType = x.Read().ToString();
|
||||||
|
if (subType.ToLower().Trim() == DefBean.BEAN_NULL_STR)
|
||||||
|
{
|
||||||
|
if (!type.IsNullable)
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"type:{type.Bean.FullName}不是可空类型. 不能为空");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
string fullType = TypeUtil.MakeFullName(originBean.Namespace, subType);
|
||||||
|
DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
|
||||||
|
if (implType == null)
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"type:{fullType} 不是bean类型");
|
||||||
|
}
|
||||||
|
return new DBean(originBean, implType, CreateBeanFields(implType, x));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type.IsNullable)
|
||||||
|
{
|
||||||
|
string subType = x.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, x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 容器类统统不支持 type.IsNullable
|
||||||
|
// 因为貌似没意义?
|
||||||
|
public List<DType> ReadList(TType type, ExcelStream stream)
|
||||||
|
{
|
||||||
|
string sep = type is TBean bean ? ((DefBean)bean.Bean).Sep : null;
|
||||||
|
var datas = new List<DType>();
|
||||||
|
while (!stream.TryReadEOF())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
|
{
|
||||||
|
datas.Add(type.Apply(this, stream));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
datas.Add(type.Apply(this, new ExcelStream(stream.ReadCell(), sep))); ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TArray type, ExcelStream x)
|
||||||
|
{
|
||||||
|
return new DArray(type, ReadList(type.ElementType, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TList type, ExcelStream x)
|
||||||
|
{
|
||||||
|
return new DList(type, ReadList(type.ElementType, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TSet type, ExcelStream x)
|
||||||
|
{
|
||||||
|
return new DSet(type, ReadList(type.ElementType, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TMap type, ExcelStream x)
|
||||||
|
{
|
||||||
|
string sep = type.ValueType is TBean bean ? ((DefBean)bean.Bean).Sep : null;
|
||||||
|
|
||||||
|
var datas = new Dictionary<DType, DType>();
|
||||||
|
while (!x.TryReadEOF())
|
||||||
|
{
|
||||||
|
var key = type.KeyType.Apply(this, x);
|
||||||
|
var value = string.IsNullOrWhiteSpace(sep) ? type.ValueType.Apply(this, x) : type.ValueType.Apply(this, new ExcelStream(x.ReadCell(), sep));
|
||||||
|
if (!datas.TryAdd(key, value))
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException($"map 的 key:{key} 重复");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DMap(type, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector2 type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector3 type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector4 type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TDateTime type, ExcelStream x)
|
||||||
|
{
|
||||||
|
var d = x.Read();
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (d is System.DateTime datetime)
|
||||||
|
{
|
||||||
|
return new DDateTime(datetime);
|
||||||
|
}
|
||||||
|
return DataUtil.CreateDateTime(d.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,17 +22,17 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TByte type, JsonElement x, DefAssembly ass)
|
public DType Accept(TByte type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DByte(x.GetByte());
|
return DByte.ValueOf(x.GetByte());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TShort type, JsonElement x, DefAssembly ass)
|
public DType Accept(TShort type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DShort(x.GetInt16());
|
return DShort.ValueOf(x.GetInt16());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFshort type, JsonElement x, DefAssembly ass)
|
public DType Accept(TFshort type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFshort(x.GetInt16());
|
return DFshort.ValueOf(x.GetInt16());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TInt type, JsonElement x, DefAssembly ass)
|
public DType Accept(TInt type, JsonElement x, DefAssembly ass)
|
||||||
|
|
@ -42,7 +42,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFint type, JsonElement x, DefAssembly ass)
|
public DType Accept(TFint type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFint(x.GetInt32());
|
return DFint.ValueOf(x.GetInt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TLong type, JsonElement x, DefAssembly ass)
|
public DType Accept(TLong type, JsonElement x, DefAssembly ass)
|
||||||
|
|
@ -52,7 +52,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFlong type, JsonElement x, DefAssembly ass)
|
public DType Accept(TFlong type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFlong(x.GetInt64());
|
return DFlong.ValueOf(x.GetInt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFloat type, JsonElement x, DefAssembly ass)
|
public DType Accept(TFloat type, JsonElement x, DefAssembly ass)
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TDouble type, JsonElement x, DefAssembly ass)
|
public DType Accept(TDouble type, JsonElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DDouble(x.GetDouble());
|
return DDouble.ValueOf(x.GetDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TEnum type, JsonElement x, DefAssembly ass)
|
public DType Accept(TEnum type, JsonElement x, DefAssembly ass)
|
||||||
|
|
|
||||||
|
|
@ -23,17 +23,17 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TByte type, object x, DefAssembly ass)
|
public DType Accept(TByte type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DByte((byte)(int)x);
|
return DByte.ValueOf((byte)(int)x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TShort type, object x, DefAssembly ass)
|
public DType Accept(TShort type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DShort((short)(int)x);
|
return DShort.ValueOf((short)(int)x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFshort type, object x, DefAssembly ass)
|
public DType Accept(TFshort type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFshort((short)(int)x);
|
return DFshort.ValueOf((short)(int)x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TInt type, object x, DefAssembly ass)
|
public DType Accept(TInt type, object x, DefAssembly ass)
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFint type, object x, DefAssembly ass)
|
public DType Accept(TFint type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFint((int)x);
|
return DFint.ValueOf((int)x);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long ToLong(object x)
|
private long ToLong(object x)
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFlong type, object x, DefAssembly ass)
|
public DType Accept(TFlong type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFlong(ToLong(x));
|
return DFlong.ValueOf(ToLong(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFloat type, object x, DefAssembly ass)
|
public DType Accept(TFloat type, object x, DefAssembly ass)
|
||||||
|
|
@ -99,7 +99,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TDouble type, object x, DefAssembly ass)
|
public DType Accept(TDouble type, object x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DDouble(ToDouble(x));
|
return DDouble.ValueOf(ToDouble(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TEnum type, object x, DefAssembly ass)
|
public DType Accept(TEnum type, object x, DefAssembly ass)
|
||||||
|
|
|
||||||
|
|
@ -1,163 +1,163 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
//using Luban.Job.Cfg.Datas;
|
||||||
using Luban.Job.Cfg.DataSources.Excel;
|
//using Luban.Job.Cfg.DataSources.Excel;
|
||||||
using Luban.Job.Cfg.Defs;
|
//using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Common.Types;
|
//using Luban.Job.Common.Types;
|
||||||
using Luban.Job.Common.TypeVisitors;
|
//using Luban.Job.Common.TypeVisitors;
|
||||||
using System;
|
//using System;
|
||||||
using System.Collections.Generic;
|
//using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.DataCreators
|
//namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
//{
|
||||||
class MultiRowExcelDataCreator : ITypeFuncVisitor<IEnumerable<ExcelStream>, bool, DefAssembly, DType>
|
// class MultiRowExcelDataCreator : ITypeFuncVisitor<IEnumerable<ExcelStream>, bool, DefAssembly, DType>
|
||||||
{
|
// {
|
||||||
public static MultiRowExcelDataCreator Ins { get; } = new MultiRowExcelDataCreator();
|
// public static MultiRowExcelDataCreator Ins { get; } = new MultiRowExcelDataCreator();
|
||||||
|
|
||||||
public DType Accept(TBool type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TBool type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TByte type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TByte type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TShort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TShort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TFshort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TFshort type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TInt type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TInt type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TFint type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TFint type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TLong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TLong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TFlong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TFlong type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TFloat type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TFloat type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TDouble type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TDouble type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TEnum type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TEnum type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TString type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TString type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TBytes type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TBytes type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TText type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TText type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TBean type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TBean type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
private List<DType> ReadMultiRow(TType type, IEnumerable<ExcelStream> rows, DefAssembly ass)
|
// private List<DType> ReadMultiRow(TType type, IEnumerable<ExcelStream> rows, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
var list = new List<DType>();
|
// var list = new List<DType>();
|
||||||
foreach (var stream in rows)
|
// foreach (var stream in rows)
|
||||||
{
|
// {
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
list.Add(type.Apply(ExcelDataCreator.Ins, null, stream, ass));
|
// list.Add(type.Apply(ExcelStreamDataCreator.Ins, null, stream, ass));
|
||||||
}
|
// }
|
||||||
catch (Exception e)
|
// catch (Exception e)
|
||||||
{
|
// {
|
||||||
var dce = new DataCreateException(e, stream.LastReadDataInfo);
|
// var dce = new DataCreateException(e, stream.LastReadDataInfo);
|
||||||
throw dce;
|
// throw dce;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return list;
|
// return list;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TArray type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TArray type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
return new DArray(type, ReadMultiRow(type.ElementType, x, ass));
|
// return new DArray(type, ReadMultiRow(type.ElementType, x, ass));
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TList type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TList type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
return new DList(type, ReadMultiRow(type.ElementType, x, ass));
|
// return new DList(type, ReadMultiRow(type.ElementType, x, ass));
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TSet type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TSet type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
return new DSet(type, ReadMultiRow(type.ElementType, x, ass));
|
// return new DSet(type, ReadMultiRow(type.ElementType, x, ass));
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TMap type, IEnumerable<ExcelStream> rows, bool y, DefAssembly ass)
|
// public DType Accept(TMap type, IEnumerable<ExcelStream> rows, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
var map = new Dictionary<DType, DType>();
|
// var map = new Dictionary<DType, DType>();
|
||||||
foreach (var stream in rows)
|
// foreach (var stream in rows)
|
||||||
{
|
// {
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
DType key = type.KeyType.Apply(ExcelDataCreator.Ins, null, stream, ass);
|
// DType key = type.KeyType.Apply(ExcelStreamDataCreator.Ins, null, stream, ass);
|
||||||
DType value = type.ValueType.Apply(ExcelDataCreator.Ins, null, stream, ass);
|
// DType value = type.ValueType.Apply(ExcelStreamDataCreator.Ins, null, stream, ass);
|
||||||
map.Add(key, value);
|
// map.Add(key, value);
|
||||||
}
|
// }
|
||||||
catch (Exception e)
|
// catch (Exception e)
|
||||||
{
|
// {
|
||||||
var dce = new DataCreateException(e, stream.LastReadDataInfo);
|
// var dce = new DataCreateException(e, stream.LastReadDataInfo);
|
||||||
throw dce;
|
// throw dce;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return new DMap(type, map);
|
// return new DMap(type, map);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TVector2 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TVector2 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TVector3 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TVector3 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TVector4 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TVector4 type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DType Accept(TDateTime type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
// public DType Accept(TDateTime type, IEnumerable<ExcelStream> x, bool y, DefAssembly ass)
|
||||||
{
|
// {
|
||||||
throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,570 @@
|
||||||
|
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.Cfg.Utils;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using Luban.Job.Common.TypeVisitors;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataCreators
|
||||||
|
{
|
||||||
|
class SheetDataCreator : ITypeFuncVisitor<Sheet, TitleRow, DType>
|
||||||
|
{
|
||||||
|
public static SheetDataCreator Ins { get; } = new();
|
||||||
|
|
||||||
|
private bool CheckNull(bool nullable, object o)
|
||||||
|
{
|
||||||
|
return nullable && (o == null || (o is string s && s == "null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckDefault(object o)
|
||||||
|
{
|
||||||
|
return o == null || (o is string s && s.Length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TBool type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DBool.ValueOf(false);
|
||||||
|
}
|
||||||
|
if (x is bool v)
|
||||||
|
{
|
||||||
|
return DBool.ValueOf(v);
|
||||||
|
}
|
||||||
|
return DBool.ValueOf(bool.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TByte type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DByte.Default;
|
||||||
|
}
|
||||||
|
return DByte.ValueOf(byte.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TShort type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DShort.Default;
|
||||||
|
}
|
||||||
|
return DShort.ValueOf(short.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFshort type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DFshort.Default;
|
||||||
|
}
|
||||||
|
return DFshort.ValueOf(short.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TInt type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DInt.Default;
|
||||||
|
}
|
||||||
|
return DInt.ValueOf(int.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFint type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DFint.Default;
|
||||||
|
}
|
||||||
|
return DFint.ValueOf(int.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TLong type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DLong.Default;
|
||||||
|
}
|
||||||
|
return DLong.ValueOf(long.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFlong type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DFlong.Default;
|
||||||
|
}
|
||||||
|
return DFlong.ValueOf(long.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TFloat type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DFloat.Default;
|
||||||
|
}
|
||||||
|
return DFloat.ValueOf(float.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TDouble type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (CheckDefault(x))
|
||||||
|
{
|
||||||
|
return DDouble.Default;
|
||||||
|
}
|
||||||
|
return DDouble.ValueOf(double.Parse(x.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TEnum type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
object x = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, x))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DEnum(type, x.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string ParseString(object d)
|
||||||
|
{
|
||||||
|
if (d == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
else if (d is string s)
|
||||||
|
{
|
||||||
|
return DataUtil.UnEscapeString(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return d.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TString type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var s = ParseString(row.Current);
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
if (type.IsNullable)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidExcelDataException("字段不是nullable类型,不能为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DString.ValueOf(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TBytes type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TText type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string key;
|
||||||
|
string text;
|
||||||
|
var sep = GetSep(type);
|
||||||
|
if (!string.IsNullOrWhiteSpace(sep))
|
||||||
|
{
|
||||||
|
var keyText = row.Current.ToString().Split(sep);
|
||||||
|
if (keyText.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception($"'{row.Current}' 不是合法text值");
|
||||||
|
}
|
||||||
|
key = ParseString(keyText[0]);
|
||||||
|
text = ParseString(keyText[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (row.Row.Count != 2)
|
||||||
|
{
|
||||||
|
throw new Exception($"text 要求两个字段");
|
||||||
|
}
|
||||||
|
key = ParseString(row.Row[0].Value);
|
||||||
|
text = ParseString(row.Row[1].Value);
|
||||||
|
}
|
||||||
|
DataUtil.ValidateText(key, text);
|
||||||
|
return new DText(key, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DType> CreateBeanFields(DefBean bean, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var list = new List<DType>();
|
||||||
|
foreach (DefField f in bean.HierarchyFields)
|
||||||
|
{
|
||||||
|
string fname = f.Name;
|
||||||
|
TitleRow field = row.GetSubTitleNamedRow(fname);
|
||||||
|
if (field == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"bean:'{bean.FullName}' 缺失 列:'{fname}',请检查是否写错或者遗漏");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
list.Add(f.CType.Apply(this, sheet, field));
|
||||||
|
}
|
||||||
|
catch (DataCreateException dce)
|
||||||
|
{
|
||||||
|
dce.Push(bean, f);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
var dce = new DataCreateException(e, $"列:{fname}");
|
||||||
|
dce.Push(bean, f);
|
||||||
|
throw dce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TBean type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string sep = GetSep(type);
|
||||||
|
|
||||||
|
if (row.Row != null)
|
||||||
|
{
|
||||||
|
var s = row.AsStream(sep);
|
||||||
|
return type.Apply(ExcelStreamDataCreator.Ins, s);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (row.Rows != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
//var s = row.AsMultiRowStream(sep);
|
||||||
|
//return new DArray(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Fields != null)
|
||||||
|
{
|
||||||
|
var originBean = (DefBean)type.Bean;
|
||||||
|
if (originBean.IsAbstractType)
|
||||||
|
{
|
||||||
|
string subType = row.GetSubTitleNamedRow(DefBean.TYPE_NAME_KEY).Current.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);
|
||||||
|
DefBean implType = (DefBean)originBean.GetNotAbstractChildType(subType);
|
||||||
|
if (implType == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"type:'{fullType}' 不是 bean 类型");
|
||||||
|
}
|
||||||
|
return new DBean(originBean, implType, CreateBeanFields(implType, sheet, row));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type.IsNullable)
|
||||||
|
{
|
||||||
|
string subType = row.GetSubTitleNamedRow(DefBean.TYPE_NAME_KEY).Current.Value.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, sheet, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (row.Elements != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSep(TType type)
|
||||||
|
{
|
||||||
|
if (type.Tags.TryGetValue("sep", out var s) && !string.IsNullOrWhiteSpace(s))
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TArray ta: return ta.ElementType.Apply(IsNotSepTypeVisitor.Ins) ? "," : "";
|
||||||
|
case TList ta: return ta.ElementType.Apply(IsNotSepTypeVisitor.Ins) ? "," : "";
|
||||||
|
case TSet ta: return ta.ElementType.Apply(IsNotSepTypeVisitor.Ins) ? "," : "";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DType> ReadList(TType type, ExcelStream stream)
|
||||||
|
{
|
||||||
|
var datas = new List<DType>();
|
||||||
|
while (!stream.TryReadEOF())
|
||||||
|
{
|
||||||
|
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, stream));
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DType> ReadList(TType type, IEnumerable<ExcelStream> streams)
|
||||||
|
{
|
||||||
|
var datas = new List<DType>();
|
||||||
|
foreach (var stream in streams)
|
||||||
|
{
|
||||||
|
while (!stream.TryReadEOF())
|
||||||
|
{
|
||||||
|
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, stream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return datas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TArray type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string sep = GetSep(type);
|
||||||
|
|
||||||
|
if (row.Row != null)
|
||||||
|
{
|
||||||
|
var s = row.AsStream(sep);
|
||||||
|
return new DArray(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Rows != null)
|
||||||
|
{
|
||||||
|
var s = row.AsMultiRowStream(sep);
|
||||||
|
return new DArray(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Fields != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else if (row.Elements != null)
|
||||||
|
{
|
||||||
|
return new DArray(type, row.Elements.Select(e => type.ElementType.Apply(this, sheet, e)).ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TList type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string sep = GetSep(type);
|
||||||
|
|
||||||
|
if (row.Row != null)
|
||||||
|
{
|
||||||
|
var s = row.AsStream(sep);
|
||||||
|
return new DList(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Rows != null)
|
||||||
|
{
|
||||||
|
var s = row.AsMultiRowStream(sep);
|
||||||
|
return new DList(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Fields != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else if (row.Elements != null)
|
||||||
|
{
|
||||||
|
return new DList(type, row.Elements.Select(e => type.ElementType.Apply(this, sheet, e)).ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TSet type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string sep = GetSep(type);
|
||||||
|
|
||||||
|
if (row.Row != null)
|
||||||
|
{
|
||||||
|
var s = row.AsStream(sep);
|
||||||
|
return new DSet(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Rows != null)
|
||||||
|
{
|
||||||
|
var s = row.AsMultiRowStream(sep);
|
||||||
|
return new DSet(type, ReadList(type.ElementType, s));
|
||||||
|
}
|
||||||
|
else if (row.Fields != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else if (row.Elements != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TMap type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
string sep = GetSep(type);
|
||||||
|
|
||||||
|
if (row.Row != null)
|
||||||
|
{
|
||||||
|
var s = row.AsStream(sep);
|
||||||
|
var datas = new Dictionary<DType, DType>();
|
||||||
|
|
||||||
|
while (!s.TryReadEOF())
|
||||||
|
{
|
||||||
|
var key = type.KeyType.Apply(ExcelStreamDataCreator.Ins, s);
|
||||||
|
var value = type.ValueType.Apply(ExcelStreamDataCreator.Ins, s);
|
||||||
|
datas.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DMap(type, datas);
|
||||||
|
}
|
||||||
|
else if (row.Rows != null)
|
||||||
|
{
|
||||||
|
var datas = new Dictionary<DType, DType>();
|
||||||
|
foreach (ExcelStream s in row.AsMultiRowStream(sep))
|
||||||
|
{
|
||||||
|
while (!s.TryReadEOF())
|
||||||
|
{
|
||||||
|
var key = type.KeyType.Apply(ExcelStreamDataCreator.Ins, s);
|
||||||
|
var value = type.ValueType.Apply(ExcelStreamDataCreator.Ins, s);
|
||||||
|
datas.Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DMap(type, datas);
|
||||||
|
}
|
||||||
|
else if (row.Fields != null)
|
||||||
|
{
|
||||||
|
var datas = new Dictionary<DType, DType>();
|
||||||
|
foreach (var e in row.Fields)
|
||||||
|
{
|
||||||
|
var keyData = type.KeyType.Apply(StringDataCreator.Ins, e.Key);
|
||||||
|
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, e.Value.AsStream(sep));
|
||||||
|
datas.Add(keyData, valueData);
|
||||||
|
}
|
||||||
|
return new DMap(type, datas);
|
||||||
|
}
|
||||||
|
else if (row.Elements != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector2 type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var d = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector3 type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var d = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TVector4 type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var d = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DataUtil.CreateVector(type, d.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DType Accept(TDateTime type, Sheet sheet, TitleRow row)
|
||||||
|
{
|
||||||
|
var d = row.Current.Value;
|
||||||
|
if (CheckNull(type.IsNullable, d))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (d is System.DateTime datetime)
|
||||||
|
{
|
||||||
|
return new DDateTime(datetime);
|
||||||
|
}
|
||||||
|
return DataUtil.CreateDateTime(d.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (byte.TryParse(x, out var b))
|
if (byte.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DByte(b);
|
return DByte.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (short.TryParse(x, out var b))
|
if (short.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DShort(b);
|
return DShort.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (short.TryParse(x, out var b))
|
if (short.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DFshort(b);
|
return DFshort.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -73,7 +73,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (int.TryParse(x, out var b))
|
if (int.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DFint(b);
|
return DFint.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (long.TryParse(x, out var b))
|
if (long.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DFlong(b);
|
return DFlong.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -121,7 +121,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
{
|
{
|
||||||
if (double.TryParse(x, out var b))
|
if (double.TryParse(x, out var b))
|
||||||
{
|
{
|
||||||
return new DDouble(b);
|
return DDouble.ValueOf(b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,17 +23,17 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TByte type, XElement x, DefAssembly ass)
|
public DType Accept(TByte type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DByte(byte.Parse(x.Value.Trim()));
|
return DByte.ValueOf(byte.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TShort type, XElement x, DefAssembly ass)
|
public DType Accept(TShort type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DShort(short.Parse(x.Value.Trim()));
|
return DShort.ValueOf(short.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFshort type, XElement x, DefAssembly ass)
|
public DType Accept(TFshort type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFshort(short.Parse(x.Value.Trim()));
|
return DFshort.ValueOf(short.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TInt type, XElement x, DefAssembly ass)
|
public DType Accept(TInt type, XElement x, DefAssembly ass)
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFint type, XElement x, DefAssembly ass)
|
public DType Accept(TFint type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFint(int.Parse(x.Value.Trim()));
|
return DFint.ValueOf(int.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TLong type, XElement x, DefAssembly ass)
|
public DType Accept(TLong type, XElement x, DefAssembly ass)
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFlong type, XElement x, DefAssembly ass)
|
public DType Accept(TFlong type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DFlong(long.Parse(x.Value.Trim()));
|
return DFlong.ValueOf(long.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFloat type, XElement x, DefAssembly ass)
|
public DType Accept(TFloat type, XElement x, DefAssembly ass)
|
||||||
|
|
@ -63,7 +63,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TDouble type, XElement x, DefAssembly ass)
|
public DType Accept(TDouble type, XElement x, DefAssembly ass)
|
||||||
{
|
{
|
||||||
return new DDouble(double.Parse(x.Value.Trim()));
|
return DDouble.ValueOf(double.Parse(x.Value.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TEnum type, XElement x, DefAssembly ass)
|
public DType Accept(TEnum type, XElement x, DefAssembly ass)
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,17 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TByte type, YamlNode x, DefAssembly y)
|
public DType Accept(TByte type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DByte(byte.Parse(GetLowerTextValue(x)));
|
return DByte.ValueOf(byte.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TShort type, YamlNode x, DefAssembly y)
|
public DType Accept(TShort type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DShort(short.Parse(GetLowerTextValue(x)));
|
return DShort.ValueOf(short.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFshort type, YamlNode x, DefAssembly y)
|
public DType Accept(TFshort type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DFshort(short.Parse(GetLowerTextValue(x)));
|
return DFshort.ValueOf(short.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TInt type, YamlNode x, DefAssembly y)
|
public DType Accept(TInt type, YamlNode x, DefAssembly y)
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFint type, YamlNode x, DefAssembly y)
|
public DType Accept(TFint type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DFint(int.Parse(GetLowerTextValue(x)));
|
return DFint.ValueOf(int.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TLong type, YamlNode x, DefAssembly y)
|
public DType Accept(TLong type, YamlNode x, DefAssembly y)
|
||||||
|
|
@ -63,7 +63,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TFlong type, YamlNode x, DefAssembly y)
|
public DType Accept(TFlong type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DFlong(long.Parse(GetLowerTextValue(x)));
|
return DFlong.ValueOf(long.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TFloat type, YamlNode x, DefAssembly y)
|
public DType Accept(TFloat type, YamlNode x, DefAssembly y)
|
||||||
|
|
@ -73,7 +73,7 @@ namespace Luban.Job.Cfg.DataCreators
|
||||||
|
|
||||||
public DType Accept(TDouble type, YamlNode x, DefAssembly y)
|
public DType Accept(TDouble type, YamlNode x, DefAssembly y)
|
||||||
{
|
{
|
||||||
return new DDouble(double.Parse(GetLowerTextValue(x)));
|
return DDouble.ValueOf(double.Parse(GetLowerTextValue(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DType Accept(TEnum type, YamlNode x, DefAssembly y)
|
public DType Accept(TEnum type, YamlNode x, DefAssembly y)
|
||||||
|
|
|
||||||
|
|
@ -16,96 +16,27 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
private readonly List<Sheet> _sheets = new List<Sheet>();
|
private readonly List<Sheet> _sheets = new List<Sheet>();
|
||||||
|
|
||||||
|
|
||||||
private System.Text.Encoding DetectCsvEncoding(Stream fs)
|
|
||||||
{
|
|
||||||
Ude.CharsetDetector cdet = new Ude.CharsetDetector();
|
|
||||||
cdet.Feed(fs);
|
|
||||||
cdet.DataEnd();
|
|
||||||
fs.Seek(0, SeekOrigin.Begin);
|
|
||||||
if (cdet.Charset != null)
|
|
||||||
{
|
|
||||||
s_logger.Debug("Charset: {}, confidence: {}", cdet.Charset, cdet.Confidence);
|
|
||||||
return System.Text.Encoding.GetEncoding(cdet.Charset) ?? System.Text.Encoding.Default;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return System.Text.Encoding.Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Load(string rawUrl, string sheetName, Stream stream)
|
public override void Load(string rawUrl, string sheetName, Stream stream)
|
||||||
{
|
{
|
||||||
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
||||||
RawUrl = rawUrl;
|
RawUrl = rawUrl;
|
||||||
string ext = Path.GetExtension(rawUrl);
|
|
||||||
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration() { FallbackEncoding = DetectCsvEncoding(stream) }))
|
|
||||||
|
foreach (RawSheet rawSheet in SheetLoadUtil.LoadRawSheets(rawUrl, sheetName, stream))
|
||||||
{
|
{
|
||||||
do
|
var sheet = new Sheet(rawUrl, sheetName);
|
||||||
{
|
sheet.Load(rawSheet);
|
||||||
if (sheetName == null || reader.Name == sheetName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var sheet = ReadSheet(rawUrl, reader);
|
|
||||||
if (sheet != null)
|
|
||||||
{
|
|
||||||
_sheets.Add(sheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
} while (reader.NextResult());
|
|
||||||
}
|
|
||||||
if (_sheets.Count == 0)
|
if (_sheets.Count == 0)
|
||||||
{
|
{
|
||||||
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
|
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sheet LoadFirstSheet(string rawUrl, string sheetName, Stream stream)
|
public RawSheetTableDefInfo LoadTableDefInfo(string rawUrl, string sheetName, Stream stream)
|
||||||
{
|
{
|
||||||
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
return null;
|
||||||
RawUrl = rawUrl;
|
|
||||||
string ext = Path.GetExtension(rawUrl);
|
|
||||||
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream))
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (sheetName == null || reader.Name == sheetName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var sheet = ReadSheetHeader(rawUrl, reader);
|
|
||||||
if (sheet != null)
|
|
||||||
{
|
|
||||||
return sheet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} while (reader.NextResult());
|
|
||||||
}
|
|
||||||
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Sheet ReadSheet(string url, IExcelDataReader reader)
|
|
||||||
{
|
|
||||||
var sheet = new Sheet(url, reader.Name ?? "");
|
|
||||||
return sheet.Load(reader, false) ? sheet : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Sheet ReadSheetHeader(string url, IExcelDataReader reader)
|
|
||||||
{
|
|
||||||
var sheet = new Sheet(url, reader.Name ?? "");
|
|
||||||
return sheet.Load(reader, true) ? sheet : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override List<Record> ReadMulti(TBean type)
|
public override List<Record> ReadMulti(TBean type)
|
||||||
|
|
@ -115,7 +46,11 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
datas.AddRange(sheet.ReadMulti(type));
|
foreach (TitleRow row in sheet.GetRows())
|
||||||
|
{
|
||||||
|
var data = (DBean)type.Apply(SheetDataCreator.Ins, sheet, row);
|
||||||
|
datas.Add(new Record(data, sheet.RawUrl, row.Tags));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (DataCreateException dce)
|
catch (DataCreateException dce)
|
||||||
{
|
{
|
||||||
|
|
@ -132,13 +67,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
|
||||||
public override Record ReadOne(TBean type)
|
public override Record ReadOne(TBean type)
|
||||||
{
|
{
|
||||||
var datas = ReadMulti(type);
|
//var datas = ReadMulti(type);
|
||||||
switch (datas.Count)
|
//switch (datas.Count)
|
||||||
{
|
//{
|
||||||
case 1: return datas[0];
|
// case 1: return datas[0];
|
||||||
case 0: throw new Exception($"单例表不能为空,必须包含且只包含1个记录");
|
// case 0: throw new Exception($"单例表不能为空,必须包含且只包含1个记录");
|
||||||
default: throw new Exception($"单例表必须恰好包含1个记录. 但当前记录数为:{datas.Count}");
|
// default: throw new Exception($"单例表必须恰好包含1个记录. 但当前记录数为:{datas.Count}");
|
||||||
}
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,8 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
private readonly int _toIndex;
|
private readonly int _toIndex;
|
||||||
private int _curIndex;
|
private int _curIndex;
|
||||||
|
|
||||||
|
public ExcelStream(List<Cell> datas, int fromIndex, int toIndex, string sep)
|
||||||
/// <summary>
|
|
||||||
/// NamedMode下 string可以用空白表达空字符串,而不必用null或""
|
|
||||||
/// </summary>
|
|
||||||
public bool NamedMode { get; set; }
|
|
||||||
|
|
||||||
public ExcelStream(List<Cell> datas, int fromIndex, int toIndex, string sep, bool namedMode)
|
|
||||||
{
|
{
|
||||||
NamedMode = namedMode;
|
|
||||||
if (string.IsNullOrWhiteSpace(sep))
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
{
|
{
|
||||||
this._datas = datas;
|
this._datas = datas;
|
||||||
|
|
@ -54,9 +47,8 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExcelStream(Cell cell, string sep, bool namedMode)
|
public ExcelStream(Cell cell, string sep)
|
||||||
{
|
{
|
||||||
NamedMode = namedMode;
|
|
||||||
if (string.IsNullOrWhiteSpace(sep))
|
if (string.IsNullOrWhiteSpace(sep))
|
||||||
{
|
{
|
||||||
this._datas = new List<Cell> { cell };
|
this._datas = new List<Cell> { cell };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
class RawSheet
|
||||||
|
{
|
||||||
|
public Title Title { get; init; }
|
||||||
|
|
||||||
|
public List<List<Cell>> Cells { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
class FieldInfo
|
||||||
|
{
|
||||||
|
public string Type { get; init; }
|
||||||
|
|
||||||
|
public string BriefDesc { get; init; }
|
||||||
|
|
||||||
|
public string DetailDesc { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawSheetTableDefInfo
|
||||||
|
{
|
||||||
|
public Dictionary<string, FieldInfo> FieldInfos { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ using Bright.Collections;
|
||||||
using ExcelDataReader;
|
using ExcelDataReader;
|
||||||
using Luban.Job.Cfg.DataCreators;
|
using Luban.Job.Cfg.DataCreators;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
using Luban.Job.Cfg.Defs;
|
|
||||||
using Luban.Job.Cfg.Utils;
|
using Luban.Job.Cfg.Utils;
|
||||||
using Luban.Job.Common.Types;
|
using Luban.Job.Common.Types;
|
||||||
using Luban.Job.Common.Utils;
|
using Luban.Job.Common.Utils;
|
||||||
|
|
@ -12,224 +11,14 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.DataSources.Excel
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
|
|
||||||
class Sheet
|
class Sheet
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
private const int TITLE_MIN_ROW_NUM = 2;
|
|
||||||
private const int TITLE_MAX_ROW_NUM = 10;
|
|
||||||
private const int TITLE_DEFAULT_ROW_NUM = 3;
|
|
||||||
|
|
||||||
private bool IsOrientRow { get; set; } = true; // 以行为数据读取方向
|
|
||||||
|
|
||||||
public int HeaderRowCount { get; private set; } = TITLE_DEFAULT_ROW_NUM; // 默认有三行是标题行. 第一行是字段名,第二行是中文描述,第三行是注释
|
|
||||||
|
|
||||||
public int AttrRowCount { get; private set; }
|
|
||||||
|
|
||||||
public string RawUrl { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
private List<List<Cell>> _rowColumns;
|
public string RawUrl { get; }
|
||||||
|
|
||||||
private Title _rootTitle;
|
|
||||||
|
|
||||||
public List<Title> RootFields => _rootTitle.SubTitleList;
|
|
||||||
|
|
||||||
public List<List<Cell>> RowColumns => _rowColumns;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class NamedRow
|
|
||||||
{
|
|
||||||
public static IEnumerable<NamedRow> CreateMultiRowNamedRow(List<List<Cell>> rows, Title title, TBean bean)
|
|
||||||
{
|
|
||||||
if (!((DefBean)bean.Bean).IsMultiRow)
|
|
||||||
{
|
|
||||||
foreach (var row in rows)
|
|
||||||
{
|
|
||||||
if (Sheet.IsBlankRow(row, title.FromIndex, title.ToIndex))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
yield return new NamedRow(title, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
List<DefField> notMultiRowFields = bean.Bean.HierarchyFields.Select(f => (DefField)f).Where(f => !f.IsMultiRow && f.IsRowOrient).ToList();
|
|
||||||
List<List<Cell>> recordRows = null;
|
|
||||||
foreach (var row in rows)
|
|
||||||
{
|
|
||||||
// 忽略全空的行
|
|
||||||
if (Sheet.IsBlankRow(row, title.FromIndex, title.ToIndex))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 如果非多行数据全空,或者跟记录第一行完全相同说明该行属于多行数据
|
|
||||||
if (notMultiRowFields.All(f =>
|
|
||||||
{
|
|
||||||
var fieldTitle = title.SubTitles[f.Name];
|
|
||||||
return Sheet.IsBlankRow(row, fieldTitle.FromIndex, fieldTitle.ToIndex);
|
|
||||||
}) || (title.Root && recordRows != null && notMultiRowFields.All(f =>
|
|
||||||
{
|
|
||||||
var fieldTitle = title.SubTitles[f.Name];
|
|
||||||
return Sheet.IsSameRow(row, recordRows[0], fieldTitle.FromIndex, fieldTitle.ToIndex);
|
|
||||||
})))
|
|
||||||
{
|
|
||||||
if (recordRows == null)
|
|
||||||
{
|
|
||||||
recordRows = new List<List<Cell>>();
|
|
||||||
}
|
|
||||||
recordRows.Add(row);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (recordRows != null)
|
|
||||||
{
|
|
||||||
yield return new NamedRow(title, recordRows);
|
|
||||||
}
|
|
||||||
recordRows = new List<List<Cell>>();
|
|
||||||
recordRows.Add(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (recordRows != null)
|
|
||||||
{
|
|
||||||
yield return new NamedRow(title, recordRows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Title SelfTitle { get; }
|
|
||||||
|
|
||||||
public List<List<Cell>> Rows { get; }
|
|
||||||
|
|
||||||
public Dictionary<string, Title> Titles => SelfTitle.SubTitles;
|
|
||||||
|
|
||||||
public List<Title> TitleList => SelfTitle.SubTitleList;
|
|
||||||
|
|
||||||
public NamedRow(Title selfTitle, List<Cell> row)
|
|
||||||
{
|
|
||||||
SelfTitle = selfTitle;
|
|
||||||
Rows = new List<List<Cell>>() { row };
|
|
||||||
}
|
|
||||||
|
|
||||||
public NamedRow(Title selfTitle, List<List<Cell>> rows)
|
|
||||||
{
|
|
||||||
SelfTitle = selfTitle;
|
|
||||||
Rows = rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int RowCount => Rows.Count;
|
|
||||||
|
|
||||||
private void CheckEmptySinceSecondRow(string name, int fromIndex, int toIndex)
|
|
||||||
{
|
|
||||||
for (int i = 1; i < Rows.Count; i++)
|
|
||||||
{
|
|
||||||
var row = Rows[i];
|
|
||||||
if (!IsBlankRow(row, fromIndex, toIndex))
|
|
||||||
{
|
|
||||||
throw new Exception($"字段:{name} 不是多行字段,只能第一行填值. {Bright.Common.StringUtil.CollectionToString(row)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Title GetTitle(string name)
|
|
||||||
{
|
|
||||||
return Titles.TryGetValue(name, out var title) ? title : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExcelStream GetColumn(string name, string sep, bool namedMode)
|
|
||||||
{
|
|
||||||
if (Titles.TryGetValue(name, out var title))
|
|
||||||
{
|
|
||||||
// 只有顶级root支持才允许非multi_rows字段与第一行相同时,判定为同个记录
|
|
||||||
if (!this.SelfTitle.Root)
|
|
||||||
{
|
|
||||||
CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
|
|
||||||
}
|
|
||||||
var es = new ExcelStream(Rows[0], title.FromIndex, title.ToIndex, sep, namedMode);
|
|
||||||
return es;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public NamedRow GetSubTitleNamedRow(string name)
|
|
||||||
{
|
|
||||||
Title title = Titles[name];
|
|
||||||
return new NamedRow(title, this.Rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<NamedRow> GenerateSubNameRows(TBean bean)
|
|
||||||
{
|
|
||||||
foreach (var row in Rows)
|
|
||||||
{
|
|
||||||
if (SelfTitle != null ? IsBlankRow(row, SelfTitle.FromIndex, SelfTitle.ToIndex) : IsBlankRow(row))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
yield return new NamedRow(SelfTitle, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<ExcelStream> GetColumnOfMultiRows(string name, string sep, bool isRowOrient)
|
|
||||||
{
|
|
||||||
if (Titles.TryGetValue(name, out var title))
|
|
||||||
{
|
|
||||||
if (isRowOrient)
|
|
||||||
{
|
|
||||||
foreach (var row in Rows)
|
|
||||||
{
|
|
||||||
if (IsBlankRow(row, title.FromIndex, title.ToIndex))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
yield return new ExcelStream(row, title.FromIndex, title.ToIndex, sep, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = title.FromIndex; i <= title.ToIndex; i++)
|
|
||||||
{
|
|
||||||
if (!IsBlankColumn(Rows, i))
|
|
||||||
{
|
|
||||||
var cells = Rows.Where(r => r.Count > i).Select(r => r[i]).Where(v => !(v.Value == null || (v.Value is string s && string.IsNullOrEmpty(s)))).ToList();
|
|
||||||
yield return new ExcelStream(cells, 0, cells.Count - 1, sep, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ExcelStream GetMultiRowStream(string name, string sep, bool isRowOrient)
|
|
||||||
{
|
|
||||||
if (Titles.TryGetValue(name, out var title))
|
|
||||||
{
|
|
||||||
if (isRowOrient)
|
|
||||||
{
|
|
||||||
var totalCells = Rows.SelectMany(r => r.GetRange(title.FromIndex, title.ToIndex - title.FromIndex + 1))
|
|
||||||
.Where(c => c.Value != null && !(c.Value is string s && string.IsNullOrWhiteSpace(s))).ToList();
|
|
||||||
return new ExcelStream(totalCells, 0, totalCells.Count - 1, sep, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotSupportedException($"bean类型多行数据不支持纵向填写");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Sheet(string rawUrl, string name)
|
public Sheet(string rawUrl, string name)
|
||||||
{
|
{
|
||||||
|
|
@ -237,401 +26,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Load(IExcelDataReader reader, bool headerOnly)
|
public void Load(RawSheet rawSheet)
|
||||||
{
|
{
|
||||||
//s_logger.Info("read sheet:{sheet}", reader.Name);
|
|
||||||
if (!ParseMeta(reader))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadRemainRows(reader, headerOnly);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ParseMeta(IExcelDataReader reader)
|
|
||||||
{
|
|
||||||
if (!reader.Read() || reader.FieldCount == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
|
|
||||||
if (reader.GetString(0) != "##")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1, n = reader.FieldCount; i < n; i++)
|
|
||||||
{
|
|
||||||
var attr = reader.GetString(i);
|
|
||||||
if (string.IsNullOrWhiteSpace(attr))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss = attr.Split(':', '=');
|
|
||||||
if (ss.Length != 2)
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
|
|
||||||
}
|
|
||||||
string key = ss[0].ToLower();
|
|
||||||
string value = ss[1].ToLower();
|
|
||||||
switch (key)
|
|
||||||
{
|
|
||||||
case "orientation":
|
|
||||||
{
|
|
||||||
IsOrientRow = DefUtil.ParseOrientation(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "title_rows":
|
|
||||||
{
|
|
||||||
if (!int.TryParse(value, out var v))
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 meta 定义 title_rows:{value} 属性值只能为整数[{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}]");
|
|
||||||
}
|
|
||||||
if (v < TITLE_MIN_ROW_NUM || v > TITLE_MAX_ROW_NUM)
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 title_rows 应该在 [{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}] 范围内,默认是{TITLE_DEFAULT_ROW_NUM}");
|
|
||||||
}
|
|
||||||
HeaderRowCount = v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "table":
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: orientation=r|row|c|column,title_rows=<number>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetRowTag(List<Cell> row)
|
|
||||||
{
|
|
||||||
if (row.Count == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (row[0].Value == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return row[0].Value.ToString().Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitSubTitles(Title parentTitle, List<List<Cell>> rows, CellRange[] mergeCells, int maxDepth, int depth, int fromColumn, int toColumn)
|
|
||||||
{
|
|
||||||
List<Cell> row = rows[depth];
|
|
||||||
|
|
||||||
//if (row.Count > fromColumn)
|
|
||||||
//{
|
|
||||||
// row = row.GetRange(fromColumn, Math.Min(row.Count, toColumn + 1) - fromColumn);
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var mergeCell in mergeCells)
|
|
||||||
{
|
|
||||||
if (mergeCell.FromRow == depth + 1 && mergeCell.FromColumn >= fromColumn && mergeCell.ToColumn <= toColumn)
|
|
||||||
{
|
|
||||||
string subTitleName = row[mergeCell.FromColumn].Value?.ToString()?.Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(subTitleName))
|
|
||||||
{
|
|
||||||
var newTitle = new Title() { Name = subTitleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
|
|
||||||
if (depth + 1 < maxDepth)
|
|
||||||
{
|
|
||||||
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, mergeCell.FromColumn, mergeCell.ToColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
parentTitle.AddSubTitle(newTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = fromColumn; i <= toColumn; i++)
|
|
||||||
{
|
|
||||||
if (i >= row.Count)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = row[i].Value?.ToString()?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentTitle.SubTitles.TryGetValue(name, out var oldTitle))
|
|
||||||
{
|
|
||||||
if (oldTitle.FromIndex != i)
|
|
||||||
{
|
|
||||||
throw new Exception($"sub title 列:{name} 重复");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var newTitle = new Title() { Name = name, FromIndex = i, ToIndex = i };
|
|
||||||
if (depth + 1 < maxDepth)
|
|
||||||
{
|
|
||||||
InitSubTitles(newTitle, rows, mergeCells, maxDepth, depth + 1, i, i);
|
|
||||||
}
|
|
||||||
parentTitle.AddSubTitle(newTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const string ROOT_TITLE_NAME = "__<root>__";
|
|
||||||
|
|
||||||
private void LoadRemainRows(IExcelDataReader reader, bool headerOnly)
|
|
||||||
{
|
|
||||||
// TODO 优化性能
|
|
||||||
// 几个思路
|
|
||||||
// 1. 没有 title 的列不加载
|
|
||||||
// 2. 空行优先跳过
|
|
||||||
// 3. 跳过null或者empty的单元格
|
|
||||||
var rows = new List<List<Cell>>();
|
|
||||||
int rowIndex = 0;
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
|
||||||
++rowIndex; // 第1行是 meta ,标题及数据行从第2行开始
|
|
||||||
// 重点优化横表的headerOnly模式, 此模式下只读前几行标题行,不读数据行
|
|
||||||
if (headerOnly && this.IsOrientRow && rowIndex > this.HeaderRowCount)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var row = new List<Cell>();
|
|
||||||
for (int i = 0, n = reader.FieldCount; i < n; i++)
|
|
||||||
{
|
|
||||||
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
|
|
||||||
}
|
|
||||||
rows.Add(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsOrientRow)
|
|
||||||
{
|
|
||||||
this._rowColumns = rows;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 转置这个行列
|
|
||||||
int maxColumn = rows.Select(r => r.Count).Max();
|
|
||||||
this._rowColumns = new List<List<Cell>>();
|
|
||||||
for (int i = 0; i < maxColumn; i++)
|
|
||||||
{
|
|
||||||
var row = new List<Cell>();
|
|
||||||
for (int j = 0; j < rows.Count; j++)
|
|
||||||
{
|
|
||||||
row.Add(i < rows[j].Count ? rows[j][i] : new Cell(j + 1, i, null));
|
|
||||||
}
|
|
||||||
this._rowColumns.Add(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._rowColumns.Count < 1)
|
|
||||||
{
|
|
||||||
throw new Exception($"没有定义字段名行");
|
|
||||||
}
|
|
||||||
|
|
||||||
_rootTitle = new Title() { Root = true, Name = ROOT_TITLE_NAME, FromIndex = 1, ToIndex = rows.Select(r => r.Count).Max() - 1 };
|
|
||||||
|
|
||||||
int fieldRowCount = 1;
|
|
||||||
int attrRowCount = 1;
|
|
||||||
if (reader.MergeCells != null)
|
|
||||||
{
|
|
||||||
if (IsOrientRow)
|
|
||||||
{
|
|
||||||
foreach (var mergeCell in reader.MergeCells)
|
|
||||||
{
|
|
||||||
if (mergeCell.FromRow == 1 && mergeCell.FromColumn == 0 && mergeCell.ToColumn == 0)
|
|
||||||
{
|
|
||||||
fieldRowCount = mergeCell.ToRow - mergeCell.FromRow + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var mergeCell in reader.MergeCells)
|
|
||||||
{
|
|
||||||
if (mergeCell.FromRow == 1 + fieldRowCount && mergeCell.FromColumn == 0 && mergeCell.ToColumn == 0)
|
|
||||||
{
|
|
||||||
attrRowCount = mergeCell.ToRow - mergeCell.FromRow + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var mergeCell in reader.MergeCells)
|
|
||||||
{
|
|
||||||
if (IsOrientRow)
|
|
||||||
{
|
|
||||||
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
|
|
||||||
if (mergeCell.FromRow == 1)
|
|
||||||
{
|
|
||||||
// 标题 行
|
|
||||||
fieldRowCount = Math.Max(fieldRowCount, mergeCell.ToRow - mergeCell.FromRow + 1);
|
|
||||||
var titleName = _rowColumns[0][mergeCell.FromColumn].Value?.ToString()?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newTitle = new Title() { Name = titleName, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
|
|
||||||
if (fieldRowCount > 1)
|
|
||||||
{
|
|
||||||
InitSubTitles(newTitle, rows, reader.MergeCells, fieldRowCount, 1, mergeCell.FromColumn, mergeCell.ToColumn);
|
|
||||||
}
|
|
||||||
_rootTitle.AddSubTitle(newTitle);
|
|
||||||
//s_logger.Info("=== sheet:{sheet} title:{title}", Name, newTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mergeCell.FromColumn <= 0 && mergeCell.ToColumn >= 0)
|
|
||||||
{
|
|
||||||
// 标题 行
|
|
||||||
var titleName = _rowColumns[0][mergeCell.FromRow - 1].Value?.ToString()?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_rootTitle.AddSubTitle(new Title() { Name = titleName, FromIndex = mergeCell.FromRow - 1, ToIndex = mergeCell.ToRow - 1 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.AttrRowCount = attrRowCount;
|
|
||||||
|
|
||||||
//TODO 其实有bug. 未处理只占一列的 多级标题头
|
|
||||||
|
|
||||||
// 上面的代码处理完Merge列,接下来处理非Merge的列
|
|
||||||
var titleRow = _rowColumns[0];
|
|
||||||
for (int i = 0; i < titleRow.Count; i++)
|
|
||||||
{
|
|
||||||
var name = titleRow[i].Value?.ToString()?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_rootTitle.SubTitles.TryGetValue(name, out var oldTitle))
|
|
||||||
{
|
|
||||||
if (oldTitle.FromIndex != i)
|
|
||||||
{
|
|
||||||
throw new Exception($"列:{name} 重复");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_rootTitle.AddSubTitle(new Title() { Name = name, FromIndex = i, ToIndex = i });
|
|
||||||
}
|
|
||||||
if (_rootTitle.SubTitleList.Count == 0)
|
|
||||||
{
|
|
||||||
throw new Exception($"没有定义任何有效 列");
|
|
||||||
}
|
|
||||||
_rootTitle.SortSubTitles();
|
|
||||||
|
|
||||||
if (headerOnly)
|
|
||||||
{
|
|
||||||
// 删除字段名行,保留属性行开始的行
|
|
||||||
this._rowColumns.RemoveRange(0, Math.Min(fieldRowCount, this._rowColumns.Count));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 删除所有标题行,包含字段名行、属性行、标题、描述等等非有效数据行
|
|
||||||
this._rowColumns.RemoveRange(0, Math.Min(HeaderRowCount, this._rowColumns.Count));
|
|
||||||
// 删除忽略的记录行
|
|
||||||
this._rowColumns.RemoveAll(row => DataUtil.IsIgnoreTag(GetRowTag(row)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsBlankRow(List<Cell> row)
|
public IEnumerable<TitleRow> GetRows()
|
||||||
{
|
{
|
||||||
// 第一列被策划用于表示是否注释掉此行
|
yield return null;
|
||||||
// 忽略此列是否空白
|
|
||||||
return row.GetRange(1, row.Count - 1).All(c => c.Value == null || (c.Value is string s && string.IsNullOrWhiteSpace(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsBlankRow(List<Cell> row, int fromIndex, int toIndex)
|
|
||||||
{
|
|
||||||
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row.Count - 1); i <= n; i++)
|
|
||||||
{
|
|
||||||
var v = row[i].Value;
|
|
||||||
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSameRow(List<Cell> row1, List<Cell> row2, int fromIndex, int toIndex)
|
|
||||||
{
|
|
||||||
if (row2.Count < toIndex - 1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row1.Count - 1); i <= n; i++)
|
|
||||||
{
|
|
||||||
var v1 = row1[i].Value;
|
|
||||||
var v2 = row2[i].Value;
|
|
||||||
if (v1 != v2)
|
|
||||||
{
|
|
||||||
if (v1 == null)
|
|
||||||
{
|
|
||||||
if (!(v2 is string s && string.IsNullOrWhiteSpace(s)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (v2 == null)
|
|
||||||
{
|
|
||||||
if (!(v1 is string s && string.IsNullOrWhiteSpace(s)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return v1.ToString() == v2.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsBlankColumn(List<List<Cell>> rows, int column)
|
|
||||||
{
|
|
||||||
foreach (List<Cell> row in rows)
|
|
||||||
{
|
|
||||||
if (column >= row.Count)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var v = row[column].Value;
|
|
||||||
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Record> ReadMulti(TBean type)
|
|
||||||
{
|
|
||||||
foreach (var recordNamedRow in NamedRow.CreateMultiRowNamedRow(this._rowColumns, this._rootTitle, type))
|
|
||||||
{
|
|
||||||
var tags = DataUtil.ParseTags(GetRowTag(recordNamedRow.Rows[0]));
|
|
||||||
var data = (DBean)ExcelNamedRowDataCreator.Ins.ReadExcel(recordNamedRow, type);
|
|
||||||
yield return new Record(data, RawUrl, tags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,466 @@
|
||||||
|
using ExcelDataReader;
|
||||||
|
using Luban.Job.Common.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
static class SheetLoadUtil
|
||||||
|
{
|
||||||
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private const int TITLE_MIN_ROW_NUM = 2;
|
||||||
|
private const int TITLE_MAX_ROW_NUM = 10;
|
||||||
|
private const int TITLE_DEFAULT_ROW_NUM = 3;
|
||||||
|
|
||||||
|
//private bool IsOrientRow { get; set; } = true; // 以行为数据读取方向
|
||||||
|
|
||||||
|
//public int HeaderRowCount { get; private set; } = TITLE_DEFAULT_ROW_NUM; // 默认有三行是标题行. 第一行是字段名,第二行是中文描述,第三行是注释
|
||||||
|
|
||||||
|
//public int AttrRowCount { get; private set; }
|
||||||
|
|
||||||
|
//public string RawUrl { get; }
|
||||||
|
|
||||||
|
//public string Name { get; }
|
||||||
|
|
||||||
|
//private List<List<Cell>> _rowColumns;
|
||||||
|
|
||||||
|
//private Title _rootTitle;
|
||||||
|
|
||||||
|
//public List<Title> RootFields => _rootTitle.SubTitleList;
|
||||||
|
|
||||||
|
//public List<List<Cell>> RowColumns => _rowColumns;
|
||||||
|
|
||||||
|
private static System.Text.Encoding DetectCsvEncoding(Stream fs)
|
||||||
|
{
|
||||||
|
Ude.CharsetDetector cdet = new Ude.CharsetDetector();
|
||||||
|
cdet.Feed(fs);
|
||||||
|
cdet.DataEnd();
|
||||||
|
fs.Seek(0, SeekOrigin.Begin);
|
||||||
|
if (cdet.Charset != null)
|
||||||
|
{
|
||||||
|
s_logger.Debug("Charset: {}, confidence: {}", cdet.Charset, cdet.Confidence);
|
||||||
|
return System.Text.Encoding.GetEncoding(cdet.Charset) ?? System.Text.Encoding.Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return System.Text.Encoding.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<RawSheet> LoadRawSheets(string rawUrl, string sheetName, Stream stream)
|
||||||
|
{
|
||||||
|
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
||||||
|
string ext = Path.GetExtension(rawUrl);
|
||||||
|
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration() { FallbackEncoding = DetectCsvEncoding(stream) }))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (sheetName == null || reader.Name == sheetName)
|
||||||
|
{
|
||||||
|
RawSheet sheet;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sheet = ParseRawSheet(reader);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败.", e);
|
||||||
|
}
|
||||||
|
if (sheet != null)
|
||||||
|
{
|
||||||
|
yield return sheet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (reader.NextResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RawSheet ParseRawSheet(IExcelDataReader reader)
|
||||||
|
{
|
||||||
|
bool orientRow;
|
||||||
|
int titleRowNum;
|
||||||
|
|
||||||
|
if (!TryParseMeta(reader, out orientRow, out titleRowNum, out var _))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var cells = ParseRawSheetContent(reader, orientRow);
|
||||||
|
var title = ParseTitle(cells, reader.MergeCells, orientRow);
|
||||||
|
cells.RemoveRange(0, Math.Min(titleRowNum, cells.Count));
|
||||||
|
return new RawSheet() { Title = title, Cells = cells };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetTitleRowNum(CellRange[] mergeCells, bool orientRow)
|
||||||
|
{
|
||||||
|
if (mergeCells == null)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (orientRow)
|
||||||
|
{
|
||||||
|
foreach (var mergeCell in mergeCells)
|
||||||
|
{
|
||||||
|
if (mergeCell.FromRow == 1 && mergeCell.FromColumn == 0)
|
||||||
|
{
|
||||||
|
return mergeCell.ToRow - mergeCell.FromRow + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var mergeCell in mergeCells)
|
||||||
|
{
|
||||||
|
if (mergeCell.FromColumn == 1 && mergeCell.FromRow == 0)
|
||||||
|
{
|
||||||
|
return mergeCell.ToColumn - mergeCell.FromColumn + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow)
|
||||||
|
{
|
||||||
|
var rootTitle = new Title() { Root = true, Name = "__root__", FromIndex = 0, ToIndex = cells.Select(r => r.Count).Max() - 1 };
|
||||||
|
|
||||||
|
int titleRowNum = GetTitleRowNum(mergeCells, orientRow);
|
||||||
|
|
||||||
|
ParseSubTitles(rootTitle, cells, mergeCells, orientRow, 1, titleRowNum);
|
||||||
|
|
||||||
|
rootTitle.SortSubTitles();
|
||||||
|
|
||||||
|
if (rootTitle.SubTitleList.Count == 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"没有定义任何有效 列");
|
||||||
|
}
|
||||||
|
return rootTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsIgnoreTitle(string title)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(title) || title.StartsWith('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string Name, string Sep) ParseNameAndMetaAttrs(string nameAndAttrs)
|
||||||
|
{
|
||||||
|
var attrs = nameAndAttrs.Split('&');
|
||||||
|
|
||||||
|
string titleName = attrs[0];
|
||||||
|
string sep = "";
|
||||||
|
foreach (var attrPair in attrs.Skip(1))
|
||||||
|
{
|
||||||
|
var pairs = attrPair.Split('=');
|
||||||
|
if (pairs.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception($"invalid title: {nameAndAttrs}");
|
||||||
|
}
|
||||||
|
switch (pairs[0])
|
||||||
|
{
|
||||||
|
case "sep":
|
||||||
|
{
|
||||||
|
sep = pairs[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new Exception($"invalid title: {nameAndAttrs}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (titleName, sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseSubTitles(Title title, List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, int curDepth, int maxDepth)
|
||||||
|
{
|
||||||
|
|
||||||
|
var titleRow = cells[curDepth - 1];
|
||||||
|
foreach (var mergeCell in mergeCells)
|
||||||
|
{
|
||||||
|
Title subTitle = null;
|
||||||
|
if (orientRow)
|
||||||
|
{
|
||||||
|
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
|
||||||
|
if (mergeCell.FromRow == curDepth)
|
||||||
|
{
|
||||||
|
var nameAndAttrs = titleRow[mergeCell.FromColumn].Value?.ToString()?.Trim();
|
||||||
|
if (IsIgnoreTitle(nameAndAttrs))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var (titleName, sep) = ParseNameAndMetaAttrs(nameAndAttrs);
|
||||||
|
subTitle = new Title() { Name = titleName, Sep = sep, FromIndex = mergeCell.FromColumn, ToIndex = mergeCell.ToColumn };
|
||||||
|
//s_logger.Info("=== sheet:{sheet} title:{title}", Name, newTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mergeCell.FromColumn == curDepth - 1)
|
||||||
|
{
|
||||||
|
// 标题 行
|
||||||
|
var nameAndAttrs = titleRow[mergeCell.FromRow - 1].Value?.ToString()?.Trim();
|
||||||
|
if (IsIgnoreTitle(nameAndAttrs))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var (titleName, sep) = ParseNameAndMetaAttrs(nameAndAttrs);
|
||||||
|
subTitle = new Title() { Name = titleName, Sep = sep, FromIndex = mergeCell.FromRow - 1, ToIndex = mergeCell.ToRow - 1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subTitle == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curDepth < maxDepth)
|
||||||
|
{
|
||||||
|
ParseSubTitles(subTitle, cells, mergeCells, orientRow, curDepth + 1, maxDepth);
|
||||||
|
}
|
||||||
|
title.AddSubTitle(subTitle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < titleRow.Count; i++)
|
||||||
|
{
|
||||||
|
var nameAndAttrs = titleRow[i].Value?.ToString()?.Trim();
|
||||||
|
if (IsIgnoreTitle(nameAndAttrs))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var (titleName, sep) = ParseNameAndMetaAttrs(nameAndAttrs);
|
||||||
|
|
||||||
|
if (title.SubTitles.TryGetValue(titleName, out var oldTitle))
|
||||||
|
{
|
||||||
|
if (oldTitle.FromIndex != i)
|
||||||
|
{
|
||||||
|
throw new Exception($"列:{titleName} 重复");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
title.AddSubTitle(new Title() { Name = titleName, Sep = sep, FromIndex = i, ToIndex = i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RawSheetTableDefInfo LoadSheetTableDefInfo(string rawUrl, string sheetName, Stream stream)
|
||||||
|
{
|
||||||
|
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
|
||||||
|
string ext = Path.GetExtension(rawUrl);
|
||||||
|
//using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration() { FallbackEncoding = DetectCsvEncoding(stream) }))
|
||||||
|
//{
|
||||||
|
// do
|
||||||
|
// {
|
||||||
|
// if (sheetName == null || reader.Name == sheetName)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// var sheet = ReadSheet(rawUrl, reader);
|
||||||
|
// if (sheet != null)
|
||||||
|
// {
|
||||||
|
// _sheets.Add(sheet);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (Exception e)
|
||||||
|
// {
|
||||||
|
// throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败.", e);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } while (reader.NextResult());
|
||||||
|
//}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParseMeta(IExcelDataReader reader, out bool orientRow, out int titleRows, out string tableName)
|
||||||
|
{
|
||||||
|
orientRow = true;
|
||||||
|
titleRows = TITLE_DEFAULT_ROW_NUM;
|
||||||
|
tableName = "";
|
||||||
|
if (!reader.Read() || reader.FieldCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
|
||||||
|
if (reader.GetString(0) != "##")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1, n = reader.FieldCount; i < n; i++)
|
||||||
|
{
|
||||||
|
var attr = reader.GetString(i)?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(attr))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ss = attr.Split('=');
|
||||||
|
if (ss.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
|
||||||
|
}
|
||||||
|
string key = ss[0].Trim().ToLower();
|
||||||
|
string value = ss[1].Trim().ToLower();
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "orientation":
|
||||||
|
{
|
||||||
|
orientRow = DefUtil.ParseOrientation(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "title_rows":
|
||||||
|
{
|
||||||
|
if (!int.TryParse(value, out var v))
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 meta 定义 title_rows:{value} 属性值只能为整数[{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}]");
|
||||||
|
}
|
||||||
|
if (v < TITLE_MIN_ROW_NUM || v > TITLE_MAX_ROW_NUM)
|
||||||
|
{
|
||||||
|
throw new Exception($"单元薄 title_rows 应该在 [{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}] 范围内,默认是{TITLE_DEFAULT_ROW_NUM}");
|
||||||
|
}
|
||||||
|
titleRows = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "table":
|
||||||
|
{
|
||||||
|
tableName = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: orientation=r|row|c|column,title_rows=<number>,table=<tableName>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<Cell>> ParseRawSheetContent(IExcelDataReader reader, bool orientRow)
|
||||||
|
{
|
||||||
|
// TODO 优化性能
|
||||||
|
// 几个思路
|
||||||
|
// 1. 没有 title 的列不加载
|
||||||
|
// 2. 空行优先跳过
|
||||||
|
// 3. 跳过null或者empty的单元格
|
||||||
|
var originRows = new List<List<Cell>>();
|
||||||
|
int rowIndex = 0;
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
++rowIndex; // 第1行是 meta ,标题及数据行从第2行开始
|
||||||
|
var row = new List<Cell>();
|
||||||
|
for (int i = 0, n = reader.FieldCount; i < n; i++)
|
||||||
|
{
|
||||||
|
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
|
||||||
|
}
|
||||||
|
originRows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<Cell>> finalRows;
|
||||||
|
|
||||||
|
if (orientRow)
|
||||||
|
{
|
||||||
|
finalRows = originRows;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 转置这个行列
|
||||||
|
int maxColumn = originRows.Select(r => r.Count).Max();
|
||||||
|
finalRows = new List<List<Cell>>();
|
||||||
|
for (int i = 0; i < maxColumn; i++)
|
||||||
|
{
|
||||||
|
var row = new List<Cell>();
|
||||||
|
for (int j = 0; j < originRows.Count; j++)
|
||||||
|
{
|
||||||
|
row.Add(i < originRows[j].Count ? originRows[j][i] : new Cell(j + 1, i, null));
|
||||||
|
}
|
||||||
|
finalRows.Add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return finalRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static bool IsBlankRow(List<Cell> row)
|
||||||
|
{
|
||||||
|
// 第一列被策划用于表示是否注释掉此行
|
||||||
|
// 忽略此列是否空白
|
||||||
|
return row.GetRange(1, row.Count - 1).All(c => c.Value == null || (c.Value is string s && string.IsNullOrWhiteSpace(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBlankRow(List<Cell> row, int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row.Count - 1); i <= n; i++)
|
||||||
|
{
|
||||||
|
var v = row[i].Value;
|
||||||
|
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSameRow(List<Cell> row1, List<Cell> row2, int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
if (row2.Count < toIndex - 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = Math.Max(1, fromIndex), n = Math.Min(toIndex, row1.Count - 1); i <= n; i++)
|
||||||
|
{
|
||||||
|
var v1 = row1[i].Value;
|
||||||
|
var v2 = row2[i].Value;
|
||||||
|
if (v1 != v2)
|
||||||
|
{
|
||||||
|
if (v1 == null)
|
||||||
|
{
|
||||||
|
if (!(v2 is string s && string.IsNullOrWhiteSpace(s)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v2 == null)
|
||||||
|
{
|
||||||
|
if (!(v1 is string s && string.IsNullOrWhiteSpace(s)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return v1.ToString() == v2.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBlankColumn(List<List<Cell>> rows, int column)
|
||||||
|
{
|
||||||
|
foreach (List<Cell> row in rows)
|
||||||
|
{
|
||||||
|
if (column >= row.Count)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var v = row[column].Value;
|
||||||
|
if (v != null && !(v is string s && string.IsNullOrEmpty(s)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
using Luban.Job.Cfg.Defs;
|
||||||
|
using Luban.Job.Common.Types;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
{
|
||||||
|
class TitleRow
|
||||||
|
{
|
||||||
|
public List<string> Tags { get; }
|
||||||
|
|
||||||
|
|
||||||
|
//public static IEnumerable<TitleRow> CreateMultiRowNamedRow(List<List<Cell>> rows, Title title, TBean bean)
|
||||||
|
//{
|
||||||
|
// if (!((DefBean)bean.Bean).IsMultiRow)
|
||||||
|
// {
|
||||||
|
// foreach (var row in rows)
|
||||||
|
// {
|
||||||
|
// if (Sheet.IsBlankRow(row, title.FromIndex, title.ToIndex))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// yield return new TitleRow(title, row);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// List<DefField> notMultiRowFields = bean.Bean.HierarchyFields.Select(f => (DefField)f).Where(f => !f.IsMultiRow && f.IsRowOrient).ToList();
|
||||||
|
// List<List<Cell>> recordRows = null;
|
||||||
|
// foreach (var row in rows)
|
||||||
|
// {
|
||||||
|
// // 忽略全空的行
|
||||||
|
// if (Sheet.IsBlankRow(row, title.FromIndex, title.ToIndex))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// // 如果非多行数据全空,或者跟记录第一行完全相同说明该行属于多行数据
|
||||||
|
// if (notMultiRowFields.All(f =>
|
||||||
|
// {
|
||||||
|
// var fieldTitle = title.SubTitles[f.Name];
|
||||||
|
// return Sheet.IsBlankRow(row, fieldTitle.FromIndex, fieldTitle.ToIndex);
|
||||||
|
// }) || (title.Root && recordRows != null && notMultiRowFields.All(f =>
|
||||||
|
// {
|
||||||
|
// var fieldTitle = title.SubTitles[f.Name];
|
||||||
|
// return Sheet.IsSameRow(row, recordRows[0], fieldTitle.FromIndex, fieldTitle.ToIndex);
|
||||||
|
// })))
|
||||||
|
// {
|
||||||
|
// if (recordRows == null)
|
||||||
|
// {
|
||||||
|
// recordRows = new List<List<Cell>>();
|
||||||
|
// }
|
||||||
|
// recordRows.Add(row);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (recordRows != null)
|
||||||
|
// {
|
||||||
|
// yield return new TitleRow(title, recordRows);
|
||||||
|
// }
|
||||||
|
// recordRows = new List<List<Cell>>();
|
||||||
|
// recordRows.Add(row);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (recordRows != null)
|
||||||
|
// {
|
||||||
|
// yield return new TitleRow(title, recordRows);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public Title SelfTitle { get; }
|
||||||
|
|
||||||
|
public Cell Current => Row[0];
|
||||||
|
|
||||||
|
public List<Cell> Row { get; }
|
||||||
|
|
||||||
|
public List<List<Cell>> Rows { get; }
|
||||||
|
|
||||||
|
public Dictionary<string, TitleRow> Fields { get; }
|
||||||
|
|
||||||
|
public List<TitleRow> Elements { get; }
|
||||||
|
|
||||||
|
public ExcelStream AsStream(string sep) => new ExcelStream(Row, 0, Row.Count - 1, sep);
|
||||||
|
|
||||||
|
public bool HasSubFields => Fields != null || Elements != null;
|
||||||
|
|
||||||
|
public TitleRow(Title selfTitle, List<Cell> row)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleRow(Title selfTitle, List<List<Cell>> rows)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleRow(Title selfTitle, Dictionary<string, TitleRow> fields)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleRow(Title selfTitle, List<TitleRow> elements)
|
||||||
|
{
|
||||||
|
SelfTitle = selfTitle;
|
||||||
|
Elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RowCount => Rows.Count;
|
||||||
|
|
||||||
|
//private void CheckEmptySinceSecondRow(string name, int fromIndex, int toIndex)
|
||||||
|
//{
|
||||||
|
// for (int i = 1; i < Rows.Count; i++)
|
||||||
|
// {
|
||||||
|
// var row = Rows[i];
|
||||||
|
// if (!IsBlankRow(row, fromIndex, toIndex))
|
||||||
|
// {
|
||||||
|
// throw new Exception($"字段:{name} 不是多行字段,只能第一行填值. {Bright.Common.StringUtil.CollectionToString(row)}");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public Title GetTitle(string name)
|
||||||
|
{
|
||||||
|
return SelfTitle.SubTitles.TryGetValue(name, out var title) ? title : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public ExcelStream GetColumn(string name)
|
||||||
|
//{
|
||||||
|
// var field = GetSubTitleNamedRow(name);
|
||||||
|
// if (field != null)
|
||||||
|
// {
|
||||||
|
// return field.AsStream;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
// }
|
||||||
|
// //if (Titles.TryGetValue(name, out var title))
|
||||||
|
// //{
|
||||||
|
// // // 只有顶级root支持才允许非multi_rows字段与第一行相同时,判定为同个记录
|
||||||
|
// // if (!this.SelfTitle.Root)
|
||||||
|
// // {
|
||||||
|
// // CheckEmptySinceSecondRow(name, title.FromIndex, title.ToIndex);
|
||||||
|
// // }
|
||||||
|
// // var es = new ExcelStream(Rows[0], title.FromIndex, title.ToIndex, sep, namedMode);
|
||||||
|
// // return es;
|
||||||
|
// //}
|
||||||
|
// //else
|
||||||
|
// //{
|
||||||
|
// // throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
// //}
|
||||||
|
//}
|
||||||
|
|
||||||
|
public TitleRow GetSubTitleNamedRow(string name)
|
||||||
|
{
|
||||||
|
//Title title = Titles[name];
|
||||||
|
//return new TitleRow(title, this.Rows);
|
||||||
|
return Fields.TryGetValue(name, out var r) ? r : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public IEnumerable<TitleRow> GenerateSubNameRows(TBean bean)
|
||||||
|
//{
|
||||||
|
// foreach (var row in Rows)
|
||||||
|
// {
|
||||||
|
// if (SelfTitle != null ? IsBlankRow(row, SelfTitle.FromIndex, SelfTitle.ToIndex) : IsBlankRow(row))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// yield return new TitleRow(SelfTitle, row);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public IEnumerable<ExcelStream> GetColumnOfMultiRows(string name, string sep)
|
||||||
|
{
|
||||||
|
foreach (var ele in GetSubTitleNamedRow(name).Elements)
|
||||||
|
{
|
||||||
|
yield return ele.AsStream(sep);
|
||||||
|
}
|
||||||
|
//if (Titles.TryGetValue(name, out var title))
|
||||||
|
//{
|
||||||
|
// if (isRowOrient)
|
||||||
|
// {
|
||||||
|
// foreach (var row in Rows)
|
||||||
|
// {
|
||||||
|
// if (IsBlankRow(row, title.FromIndex, title.ToIndex))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// yield return new ExcelStream(row, title.FromIndex, title.ToIndex, sep, false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// for (int i = title.FromIndex; i <= title.ToIndex; i++)
|
||||||
|
// {
|
||||||
|
// if (!IsBlankColumn(Rows, i))
|
||||||
|
// {
|
||||||
|
// var cells = Rows.Where(r => r.Count > i).Select(r => r[i]).Where(v => !(v.Value == null || (v.Value is string s && string.IsNullOrEmpty(s)))).ToList();
|
||||||
|
// yield return new ExcelStream(cells, 0, cells.Count - 1, sep, false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<ExcelStream> AsMultiRowStream(string sep)
|
||||||
|
{
|
||||||
|
//if (Titles.TryGetValue(name, out var title))
|
||||||
|
//{
|
||||||
|
// if (isRowOrient)
|
||||||
|
// {
|
||||||
|
// var totalCells = Rows.SelectMany(r => r.GetRange(title.FromIndex, title.ToIndex - title.FromIndex + 1))
|
||||||
|
// .Where(c => c.Value != null && !(c.Value is string s && string.IsNullOrWhiteSpace(s))).ToList();
|
||||||
|
// return new ExcelStream(totalCells, 0, totalCells.Count - 1, sep, false);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new NotSupportedException($"bean类型多行数据不支持纵向填写");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// throw new Exception($"单元薄 缺失 列:{name},请检查是否写错或者遗漏");
|
||||||
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,10 +68,11 @@ namespace Luban.Job.Cfg.DataVisitors
|
||||||
|
|
||||||
public void Accept(DString type, DefField x, List<ResourceInfo> y)
|
public void Accept(DString type, DefField x, List<ResourceInfo> y)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(type.Value))
|
//if (!string.IsNullOrEmpty(type.Value))
|
||||||
{
|
//{
|
||||||
y.Add(new ResourceInfo() { Resource = type.Value, Tag = x.ResourceTag });
|
// y.Add(new ResourceInfo() { Resource = type.Value, Tag = x.ResourceTag });
|
||||||
}
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accept(DBytes type, DefField x, List<ResourceInfo> y)
|
public void Accept(DBytes type, DefField x, List<ResourceInfo> y)
|
||||||
|
|
@ -91,26 +92,28 @@ namespace Luban.Job.Cfg.DataVisitors
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = 0;
|
//int index = 0;
|
||||||
foreach (DType fieldData in type.Fields)
|
//foreach (DType fieldData in type.Fields)
|
||||||
{
|
//{
|
||||||
var fieldDef = (DefField)def.HierarchyFields[index++];
|
// var fieldDef = (DefField)def.HierarchyFields[index++];
|
||||||
if (fieldDef.IsResource)
|
// if (fieldDef.IsResource)
|
||||||
{
|
// {
|
||||||
fieldData.Apply(this, fieldDef, y);
|
// fieldData.Apply(this, fieldDef, y);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Accept(DefField def, List<DType> datas, TType elementType, List<ResourceInfo> ress)
|
private void Accept(DefField def, List<DType> datas, TType elementType, List<ResourceInfo> ress)
|
||||||
{
|
{
|
||||||
if (def.IsResource || (elementType is TBean))
|
//if (def.IsResource || (elementType is TBean))
|
||||||
{
|
//{
|
||||||
foreach (var e in datas)
|
// foreach (var e in datas)
|
||||||
{
|
// {
|
||||||
e.Apply(this, def, ress);
|
// e.Apply(this, def, ress);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accept(DArray type, DefField x, List<ResourceInfo> y)
|
public void Accept(DArray type, DefField x, List<ResourceInfo> y)
|
||||||
|
|
@ -130,13 +133,14 @@ namespace Luban.Job.Cfg.DataVisitors
|
||||||
|
|
||||||
public void Accept(DMap type, DefField x, List<ResourceInfo> y)
|
public void Accept(DMap type, DefField x, List<ResourceInfo> y)
|
||||||
{
|
{
|
||||||
if (x.IsResource || (type.Type.ValueType is TBean))
|
//if (x.IsResource || (type.Type.ValueType is TBean))
|
||||||
{
|
//{
|
||||||
foreach (var e in type.Datas.Values)
|
// foreach (var e in type.Datas.Values)
|
||||||
{
|
// {
|
||||||
e.Apply(this, x, y);
|
// e.Apply(this, x, y);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accept(DVector2 type, DefField x, List<ResourceInfo> y)
|
public void Accept(DVector2 type, DefField x, List<ResourceInfo> y)
|
||||||
|
|
|
||||||
|
|
@ -117,165 +117,165 @@ namespace Luban.Job.Cfg.DataVisitors
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var defFields = record.ImplType.HierarchyFields;
|
//var defFields = record.ImplType.HierarchyFields;
|
||||||
int i = 0;
|
//int i = 0;
|
||||||
foreach (var fieldValue in record.Fields)
|
//foreach (var fieldValue in record.Fields)
|
||||||
{
|
//{
|
||||||
var defField = (DefField)defFields[i++];
|
// var defField = (DefField)defFields[i++];
|
||||||
_path.Push(defField.Name);
|
// _path.Push(defField.Name);
|
||||||
switch (defField.CType)
|
// switch (defField.CType)
|
||||||
{
|
// {
|
||||||
case TArray a:
|
// case TArray a:
|
||||||
{
|
// {
|
||||||
if (defField.ValueValidators.Count > 0)
|
// if (defField.ValueValidators.Count > 0)
|
||||||
{
|
// {
|
||||||
var arr = (DArray)fieldValue;
|
// var arr = (DArray)fieldValue;
|
||||||
int index = 0;
|
// int index = 0;
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
_path.Push(index++);
|
// _path.Push(index++);
|
||||||
foreach (var v in defField.ValueValidators)
|
// foreach (var v in defField.ValueValidators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, value, defField.IsNullable);
|
// v.Validate(Ctx, value, defField.IsNullable);
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
if (a.ElementType is TBean)
|
// if (a.ElementType is TBean)
|
||||||
{
|
// {
|
||||||
var arr = (DArray)fieldValue;
|
// var arr = (DArray)fieldValue;
|
||||||
int index = 0;
|
// int index = 0;
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
_path.Push(index++);
|
// _path.Push(index++);
|
||||||
Accept((DBean)value, assembly);
|
// Accept((DBean)value, assembly);
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case TList b:
|
// case TList b:
|
||||||
{
|
// {
|
||||||
if (defField.ValueValidators.Count > 0)
|
// if (defField.ValueValidators.Count > 0)
|
||||||
{
|
// {
|
||||||
var arr = (DList)fieldValue;
|
// var arr = (DList)fieldValue;
|
||||||
int index = 0;
|
// int index = 0;
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
_path.Push(index++);
|
// _path.Push(index++);
|
||||||
foreach (var v in defField.ValueValidators)
|
// foreach (var v in defField.ValueValidators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, value, false);
|
// v.Validate(Ctx, value, false);
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
if (b.ElementType is TBean tb)
|
// if (b.ElementType is TBean tb)
|
||||||
{
|
// {
|
||||||
var arr = (DList)fieldValue;
|
// var arr = (DList)fieldValue;
|
||||||
int index = 0;
|
// int index = 0;
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
_path.Push(index++);
|
// _path.Push(index++);
|
||||||
Accept((DBean)value, assembly);
|
// Accept((DBean)value, assembly);
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
if (defField.IndexField != null)
|
// if (defField.IndexField != null)
|
||||||
{
|
// {
|
||||||
var indexSet = new HashSet<DType>();
|
// var indexSet = new HashSet<DType>();
|
||||||
if (!tb.GetBeanAs<DefBean>().TryGetField(defField.Index, out var _, out var indexOfIndexField))
|
// if (!tb.GetBeanAs<DefBean>().TryGetField(defField.Index, out var _, out var indexOfIndexField))
|
||||||
{
|
// {
|
||||||
throw new Exception("impossible");
|
// throw new Exception("impossible");
|
||||||
}
|
// }
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
_path.Push(index++);
|
// _path.Push(index++);
|
||||||
DType indexValue = ((DBean)value).Fields[indexOfIndexField];
|
// DType indexValue = ((DBean)value).Fields[indexOfIndexField];
|
||||||
if (!indexSet.Add(indexValue))
|
// if (!indexSet.Add(indexValue))
|
||||||
{
|
// {
|
||||||
throw new Exception($"'{TypeUtil.MakeFullName(_path)}' index:'{indexValue}' 重复");
|
// throw new Exception($"'{TypeUtil.MakeFullName(_path)}' index:'{indexValue}' 重复");
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case TSet c:
|
// case TSet c:
|
||||||
{
|
// {
|
||||||
if (defField.ValueValidators.Count > 0)
|
// if (defField.ValueValidators.Count > 0)
|
||||||
{
|
// {
|
||||||
var arr = (DSet)fieldValue;
|
// var arr = (DSet)fieldValue;
|
||||||
foreach (var value in arr.Datas)
|
// foreach (var value in arr.Datas)
|
||||||
{
|
// {
|
||||||
foreach (var v in defField.ValueValidators)
|
// foreach (var v in defField.ValueValidators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, value, false);
|
// v.Validate(Ctx, value, false);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case TMap m:
|
// case TMap m:
|
||||||
{
|
// {
|
||||||
DMap map = (DMap)fieldValue;
|
// DMap map = (DMap)fieldValue;
|
||||||
if (defField.KeyValidators.Count > 0)
|
// if (defField.KeyValidators.Count > 0)
|
||||||
{
|
// {
|
||||||
foreach (var key in map.Datas.Keys)
|
// foreach (var key in map.Datas.Keys)
|
||||||
{
|
// {
|
||||||
_path.Push(key);
|
// _path.Push(key);
|
||||||
foreach (var v in defField.KeyValidators)
|
// foreach (var v in defField.KeyValidators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, key, false);
|
// v.Validate(Ctx, key, false);
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (defField.ValueValidators.Count > 0)
|
// if (defField.ValueValidators.Count > 0)
|
||||||
{
|
// {
|
||||||
foreach (var value in map.Datas.Values)
|
// foreach (var value in map.Datas.Values)
|
||||||
{
|
// {
|
||||||
_path.Push(value);
|
// _path.Push(value);
|
||||||
foreach (var v in defField.ValueValidators)
|
// foreach (var v in defField.ValueValidators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, value, false);
|
// v.Validate(Ctx, value, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (value is DBean dv)
|
// if (value is DBean dv)
|
||||||
{
|
// {
|
||||||
Accept(dv, assembly);
|
// Accept(dv, assembly);
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
case TBean n:
|
// case TBean n:
|
||||||
{
|
// {
|
||||||
Accept((DBean)fieldValue, assembly);
|
// Accept((DBean)fieldValue, assembly);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
default:
|
// default:
|
||||||
{
|
// {
|
||||||
if (defField.Validators.Count > 0)
|
// if (defField.Validators.Count > 0)
|
||||||
{
|
// {
|
||||||
foreach (var v in defField.Validators)
|
// foreach (var v in defField.Validators)
|
||||||
{
|
// {
|
||||||
v.Validate(Ctx, fieldValue, defField.IsNullable);
|
// v.Validate(Ctx, fieldValue, defField.IsNullable);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
_path.Pop();
|
// _path.Pop();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accept(DArray type, DefAssembly x)
|
public void Accept(DArray type, DefAssembly x)
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,21 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DByte Default { get; } = new DByte(0);
|
public static DByte Default { get; } = new DByte(0);
|
||||||
|
|
||||||
|
public static DByte ValueOf(byte x)
|
||||||
|
{
|
||||||
|
if (x == 0)
|
||||||
|
{
|
||||||
|
return Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new DByte(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "byte";
|
public override string TypeName => "byte";
|
||||||
|
|
||||||
public DByte(byte x) : base(x)
|
private DByte(byte x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DDouble Default { get; } = new DDouble(0);
|
public static DDouble Default { get; } = new DDouble(0);
|
||||||
|
|
||||||
|
public static DDouble ValueOf(double x)
|
||||||
|
{
|
||||||
|
return x == 0 ? Default : new DDouble(x);
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "double";
|
public override string TypeName => "double";
|
||||||
|
|
||||||
public DDouble(double x) : base(x)
|
private DDouble(double x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DFint Default { get; } = new DFint(0);
|
public static DFint Default { get; } = new DFint(0);
|
||||||
|
|
||||||
|
public static DFint ValueOf(int x)
|
||||||
|
{
|
||||||
|
return x == 0 ? Default : new DFint(x);
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "fint";
|
public override string TypeName => "fint";
|
||||||
|
|
||||||
public DFint(int x) : base(x)
|
private DFint(int x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public class DFloat : DType<float>
|
public class DFloat : DType<float>
|
||||||
{
|
{
|
||||||
private static DFloat Default { get; } = new DFloat(0);
|
public static DFloat Default { get; } = new DFloat(0);
|
||||||
|
|
||||||
public override string TypeName => "float";
|
public override string TypeName => "float";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DFlong Default { get; } = new DFlong(0);
|
public static DFlong Default { get; } = new DFlong(0);
|
||||||
|
|
||||||
|
public static DFlong ValueOf(long x)
|
||||||
|
{
|
||||||
|
return x == 0 ? Default : new DFlong(x);
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "flong";
|
public override string TypeName => "flong";
|
||||||
|
|
||||||
public DFlong(long x) : base(x)
|
private DFlong(long x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DFshort Default { get; } = new DFshort(0);
|
public static DFshort Default { get; } = new DFshort(0);
|
||||||
|
|
||||||
|
public static DFshort ValueOf(short x)
|
||||||
|
{
|
||||||
|
return x == 0 ? Default : new DFshort(0);
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "fshort";
|
public override string TypeName => "fshort";
|
||||||
|
|
||||||
public DFshort(short x) : base(x)
|
private DFshort(short x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace Luban.Job.Cfg.Datas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DInt Default => s_pool[0];
|
||||||
|
|
||||||
public static DInt ValueOf(int x)
|
public static DInt ValueOf(int x)
|
||||||
{
|
{
|
||||||
if (x >= 0 && x < POOL_SIZE)
|
if (x >= 0 && x < POOL_SIZE)
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ namespace Luban.Job.Cfg.Datas
|
||||||
{
|
{
|
||||||
public static DShort Default { get; } = new DShort(0);
|
public static DShort Default { get; } = new DShort(0);
|
||||||
|
|
||||||
|
public static DShort ValueOf(short x)
|
||||||
|
{
|
||||||
|
return x == 0 ? Default : new DShort(x);
|
||||||
|
}
|
||||||
|
|
||||||
public override string TypeName => "short";
|
public override string TypeName => "short";
|
||||||
|
|
||||||
public DShort(short x) : base(x)
|
private DShort(short x) : base(x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,40 +126,6 @@ namespace Luban.Job.Cfg.Defs
|
||||||
_cfgGroups.Add(new Group() { Names = 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 readonly List<string> _serviceAttrs = new List<string> { "name", "manager", "group" };
|
||||||
|
|
||||||
private void AddService(XElement e)
|
private void AddService(XElement e)
|
||||||
|
|
@ -325,36 +291,36 @@ namespace Luban.Job.Cfg.Defs
|
||||||
var file = inputFileInfos[0];
|
var file = inputFileInfos[0];
|
||||||
var source = new ExcelDataSource();
|
var source = new ExcelDataSource();
|
||||||
var stream = new MemoryStream(await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5));
|
var stream = new MemoryStream(await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5));
|
||||||
var sheet = source.LoadFirstSheet(file.OriginFile, file.SheetName, stream);
|
var tableDefInfo = source.LoadTableDefInfo(file.OriginFile, file.SheetName, stream);
|
||||||
|
|
||||||
var cb = new CfgBean() { Namespace = table.Namespace, Name = table.ValueType, };
|
var cb = new CfgBean() { Namespace = table.Namespace, Name = table.ValueType, };
|
||||||
|
|
||||||
var rc = sheet.RowColumns;
|
//var rc = sheet.RowColumns;
|
||||||
var attrRow = sheet.RowColumns[0];
|
//var attrRow = sheet.RowColumns[0];
|
||||||
if (rc.Count < sheet.AttrRowCount + 1)
|
//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 (name, f) in tableDefInfo.FieldInfos)
|
||||||
{
|
{
|
||||||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} 至少包含 属性行和标题行");
|
var cf = new CfgField() { Name = name, Id = 0 };
|
||||||
}
|
|
||||||
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();
|
string[] attrs = f.Type.Trim().Split('&').Select(s => s.Trim()).ToArray();
|
||||||
|
|
||||||
if (attrs.Length == 0 || string.IsNullOrWhiteSpace(attrs[0]))
|
if (attrs.Length == 0 || string.IsNullOrWhiteSpace(attrs[0]))
|
||||||
{
|
{
|
||||||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' type missing!");
|
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' type missing!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先取desc行,如果为空,则取title行
|
// 优先取desc行,如果为空,则取title行
|
||||||
|
|
||||||
cf.Comment = descRow[f.FromIndex].Value?.ToString();
|
cf.Comment = f.BriefDesc;
|
||||||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
if (string.IsNullOrWhiteSpace(cf.Comment))
|
||||||
{
|
{
|
||||||
cf.Comment = titleRow[f.FromIndex].Value?.ToString();
|
cf.Comment = f.DetailDesc;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
if (string.IsNullOrWhiteSpace(cf.Comment))
|
||||||
{
|
{
|
||||||
|
|
@ -372,7 +338,7 @@ namespace Luban.Job.Cfg.Defs
|
||||||
#endif
|
#endif
|
||||||
if (pair.Length != 2)
|
if (pair.Length != 2)
|
||||||
{
|
{
|
||||||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' attr:'{attrs[i]}' is invalid!");
|
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' attr:'{attrs[i]}' is invalid!");
|
||||||
}
|
}
|
||||||
var attrName = pair[0].Trim();
|
var attrName = pair[0].Trim();
|
||||||
var attrValue = pair[1].Trim();
|
var attrValue = pair[1].Trim();
|
||||||
|
|
@ -383,23 +349,13 @@ namespace Luban.Job.Cfg.Defs
|
||||||
cf.Index = attrValue;
|
cf.Index = attrValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sep":
|
|
||||||
{
|
|
||||||
cf.Sep = attrValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "ref":
|
case "ref":
|
||||||
case "path":
|
case "path":
|
||||||
case "range":
|
case "range":
|
||||||
{
|
{
|
||||||
var validator = new Validator() { Type = attrName, Rule = attrValue };
|
//var validator = new Validator() { Type = attrName, Rule = attrValue };
|
||||||
cf.Validators.Add(validator);
|
//cf.Validators.Add(validator);
|
||||||
cf.ValueValidators.Add(validator);
|
//cf.ValueValidators.Add(validator);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "multi_rows":
|
|
||||||
{
|
|
||||||
cf.IsMultiRow = attrValue == "1" || attrValue.Equals("true", StringComparison.OrdinalIgnoreCase);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "group":
|
case "group":
|
||||||
|
|
@ -412,29 +368,14 @@ namespace Luban.Job.Cfg.Defs
|
||||||
cf.Comment = attrValue;
|
cf.Comment = attrValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "convert":
|
|
||||||
{
|
|
||||||
cf.Converter = attrValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "default":
|
|
||||||
{
|
|
||||||
cf.DefaultValue = attrValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "tags":
|
case "tags":
|
||||||
{
|
{
|
||||||
cf.Tags = attrValue;
|
cf.Tags = attrValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "orientation":
|
|
||||||
{
|
|
||||||
cf.IsRowOrient = DefUtil.ParseOrientation(attrValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{f.Name}' attr:'{attrs[i]}' is invalid!");
|
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' attr:'{attrs[i]}' is invalid!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -665,7 +606,7 @@ namespace Luban.Job.Cfg.Defs
|
||||||
new CfgField() { Name = "sep", Type = "string" },
|
new CfgField() { Name = "sep", Type = "string" },
|
||||||
new CfgField() { Name = "comment", Type = "string" },
|
new CfgField() { Name = "comment", Type = "string" },
|
||||||
new CfgField() { Name = "tags", Type = "string" },
|
new CfgField() { Name = "tags", Type = "string" },
|
||||||
new CfgField() { Name = "fields", Type = "list,__FieldInfo__", IsMultiRow = true },
|
new CfgField() { Name = "fields", Type = "list,__FieldInfo__" },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
|
@ -714,21 +655,10 @@ namespace Luban.Job.Cfg.Defs
|
||||||
(b.GetField("name") as DString).Value.Trim(),
|
(b.GetField("name") as DString).Value.Trim(),
|
||||||
(b.GetField("type") as DString).Value.Trim(),
|
(b.GetField("type") as DString).Value.Trim(),
|
||||||
(b.GetField("index") 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("group") as DString).Value,
|
||||||
"",
|
|
||||||
"",
|
|
||||||
(b.GetField("comment") as DString).Value.Trim(),
|
(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(),
|
(b.GetField("tags") as DString).Value.Trim(),
|
||||||
false,
|
false
|
||||||
DefUtil.ParseOrientation((b.GetField("orientation") as DString).Value)
|
|
||||||
)).ToList(),
|
)).ToList(),
|
||||||
};
|
};
|
||||||
this._beans.Add(curBean);
|
this._beans.Add(curBean);
|
||||||
|
|
@ -771,41 +701,25 @@ namespace Luban.Job.Cfg.Defs
|
||||||
return CreateField(defineFile, XmlUtil.GetRequiredAttribute(e, "name"),
|
return CreateField(defineFile, XmlUtil.GetRequiredAttribute(e, "name"),
|
||||||
XmlUtil.GetRequiredAttribute(e, "type"),
|
XmlUtil.GetRequiredAttribute(e, "type"),
|
||||||
XmlUtil.GetOptionalAttribute(e, "index"),
|
XmlUtil.GetOptionalAttribute(e, "index"),
|
||||||
XmlUtil.GetOptionalAttribute(e, "sep"),
|
|
||||||
XmlUtil.GetOptionBoolAttribute(e, "multi_rows"),
|
|
||||||
XmlUtil.GetOptionalAttribute(e, "group"),
|
XmlUtil.GetOptionalAttribute(e, "group"),
|
||||||
XmlUtil.GetOptionalAttribute(e, "res"),
|
|
||||||
XmlUtil.GetOptionalAttribute(e, "convert"),
|
|
||||||
XmlUtil.GetOptionalAttribute(e, "comment"),
|
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"),
|
XmlUtil.GetOptionalAttribute(e, "tags"),
|
||||||
false,
|
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,
|
private Field CreateField(string defileFile, string name, string type, string index, string group,
|
||||||
string comment, string refs, string path, string range, string keyValidator, string valueValidator, string validator, string tags,
|
string comment, string tags,
|
||||||
bool ignoreNameValidation, bool isRowOrient)
|
bool ignoreNameValidation)
|
||||||
{
|
{
|
||||||
var f = new CfgField()
|
var f = new CfgField()
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Index = index,
|
Index = index,
|
||||||
Sep = sep,
|
|
||||||
IsMultiRow = isMultiRow,
|
|
||||||
Groups = CreateGroups(group),
|
Groups = CreateGroups(group),
|
||||||
Resource = resource,
|
|
||||||
Converter = converter,
|
|
||||||
Comment = comment,
|
Comment = comment,
|
||||||
Tags = tags,
|
Tags = tags,
|
||||||
IgnoreNameValidation = ignoreNameValidation,
|
IgnoreNameValidation = ignoreNameValidation,
|
||||||
IsRowOrient = isRowOrient,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 字段与table的默认组不一样。
|
// 字段与table的默认组不一样。
|
||||||
|
|
@ -822,13 +736,13 @@ namespace Luban.Job.Cfg.Defs
|
||||||
f.Type = type;
|
f.Type = type;
|
||||||
|
|
||||||
|
|
||||||
FillValueValidator(f, refs, "ref");
|
//FillValueValidator(f, refs, "ref");
|
||||||
FillValueValidator(f, path, "path"); // (ue4|unity|normal|regex);xxx;xxx
|
//FillValueValidator(f, path, "path"); // (ue4|unity|normal|regex);xxx;xxx
|
||||||
FillValueValidator(f, range, "range");
|
//FillValueValidator(f, range, "range");
|
||||||
|
|
||||||
FillValidators(defileFile, "key_validator", keyValidator, f.KeyValidators);
|
//FillValidators(defileFile, "key_validator", keyValidator, f.KeyValidators);
|
||||||
FillValidators(defileFile, "value_validator", valueValidator, f.ValueValidators);
|
//FillValidators(defileFile, "value_validator", valueValidator, f.ValueValidators);
|
||||||
FillValidators(defileFile, "validator", validator, f.Validators);
|
//FillValidators(defileFile, "validator", validator, f.Validators);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,23 +109,6 @@ namespace Luban.Job.Cfg.Defs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 List<IValidator> KeyValidators { get; } = new List<IValidator>();
|
|
||||||
|
|
||||||
public List<IValidator> ValueValidators { get; } = new List<IValidator>();
|
|
||||||
|
|
||||||
public List<IValidator> Validators { get; } = new List<IValidator>();
|
|
||||||
|
|
||||||
public string ResourceTag { get; }
|
|
||||||
|
|
||||||
public bool IsResource => !string.IsNullOrEmpty(ResourceTag);
|
|
||||||
|
|
||||||
public string CsRefVarName => $"{CsStyleName}_Ref";
|
public string CsRefVarName => $"{CsStyleName}_Ref";
|
||||||
|
|
||||||
public string JavaRefVarName => $"{JavaStyleName}_Ref";
|
public string JavaRefVarName => $"{JavaStyleName}_Ref";
|
||||||
|
|
@ -150,49 +133,36 @@ namespace Luban.Job.Cfg.Defs
|
||||||
|
|
||||||
public bool HasRecursiveText => HasRecursiveRef;
|
public bool HasRecursiveText => HasRecursiveRef;
|
||||||
|
|
||||||
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)
|
public DefField(DefTypeBase host, CfgField f, int idOffset) : base(host, f, idOffset)
|
||||||
{
|
{
|
||||||
Index = f.Index;
|
Index = f.Index;
|
||||||
Sep = f.Sep;
|
|
||||||
this.IsMultiRow = this.RawIsMultiRow = f.IsMultiRow;
|
|
||||||
ResourceTag = f.Resource;
|
|
||||||
this.Validators.AddRange(f.Validators.Select(v => ValidatorFactory.Create(v)));
|
|
||||||
this.KeyValidators.AddRange(f.KeyValidators.Select(v => ValidatorFactory.Create(v)));
|
|
||||||
this.ValueValidators.AddRange(f.ValueValidators.Select(v => ValidatorFactory.Create(v)));
|
|
||||||
this.Groups = f.Groups;
|
this.Groups = f.Groups;
|
||||||
this.RawDefine = f;
|
this.RawDefine = f;
|
||||||
this.DefaultValue = f.DefaultValue;
|
|
||||||
this.IsRowOrient = f.IsRowOrient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Compile()
|
public override void Compile()
|
||||||
{
|
{
|
||||||
base.Compile();
|
base.Compile();
|
||||||
foreach (var v in this.Validators)
|
//foreach (var v in this.Validators)
|
||||||
{
|
//{
|
||||||
v.Compile(this);
|
// v.Compile(this);
|
||||||
}
|
//}
|
||||||
|
|
||||||
foreach (var v in this.KeyValidators)
|
//foreach (var v in this.KeyValidators)
|
||||||
{
|
//{
|
||||||
v.Compile(this);
|
// v.Compile(this);
|
||||||
}
|
//}
|
||||||
|
|
||||||
foreach (var v in this.ValueValidators)
|
//foreach (var v in this.ValueValidators)
|
||||||
{
|
//{
|
||||||
v.Compile(this);
|
// v.Compile(this);
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(this.DefaultValue))
|
//if (!string.IsNullOrWhiteSpace(this.DefaultValue))
|
||||||
{
|
//{
|
||||||
this.DefalutDtypeValue = CType.Apply(StringDataCreator.Ins, this.DefaultValue);
|
// this.DefalutDtypeValue = CType.Apply(StringDataCreator.Ins, this.DefaultValue);
|
||||||
}
|
//}
|
||||||
|
|
||||||
switch (CType)
|
switch (CType)
|
||||||
{
|
{
|
||||||
|
|
@ -247,10 +217,6 @@ namespace Luban.Job.Cfg.Defs
|
||||||
throw new Exception($"只有容器类型才支持 multi_line 属性");
|
throw new Exception($"只有容器类型才支持 multi_line 属性");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Sep) && CType is TBean bean)
|
|
||||||
{
|
|
||||||
Sep = bean.GetBeanAs<DefBean>().Sep;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Index))
|
if (!string.IsNullOrEmpty(Index))
|
||||||
{
|
{
|
||||||
if ((CType is TArray tarray) && (tarray.ElementType is TBean b))
|
if ((CType is TArray tarray) && (tarray.ElementType is TBean b))
|
||||||
|
|
@ -273,35 +239,35 @@ namespace Luban.Job.Cfg.Defs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CType.IsCollection && !(CType.IsBean))
|
//if (!CType.IsCollection && !(CType.IsBean))
|
||||||
{
|
//{
|
||||||
this.Ref = (RefValidator)this.Validators.FirstOrDefault(v => v is RefValidator);
|
// this.Ref = (RefValidator)this.Validators.FirstOrDefault(v => v is RefValidator);
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(this.RawDefine.Converter))
|
//if (!string.IsNullOrEmpty(this.RawDefine.Converter))
|
||||||
{
|
//{
|
||||||
this.Remapper = AssemblyBase.GetDefTType(HostType.Namespace, this.RawDefine.Converter, this.IsNullable) as TEnum;
|
// this.Remapper = AssemblyBase.GetDefTType(HostType.Namespace, this.RawDefine.Converter, this.IsNullable) as TEnum;
|
||||||
if (this.Remapper == null)
|
// if (this.Remapper == null)
|
||||||
{
|
// {
|
||||||
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' converter:'{this.RawDefine.Converter}' not exists");
|
// throw new Exception($"type:'{HostType.FullName}' field:'{Name}' converter:'{this.RawDefine.Converter}' not exists");
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
// 检查所引用的表是否导出了
|
//// 检查所引用的表是否导出了
|
||||||
if (NeedExport)
|
//if (NeedExport)
|
||||||
{
|
//{
|
||||||
var allValidators = new List<IValidator>(this.Validators);
|
// var allValidators = new List<IValidator>(this.Validators);
|
||||||
allValidators.AddRange(this.KeyValidators);
|
// allValidators.AddRange(this.KeyValidators);
|
||||||
allValidators.AddRange(this.ValueValidators);
|
// allValidators.AddRange(this.ValueValidators);
|
||||||
|
|
||||||
foreach (var val in allValidators)
|
// foreach (var val in allValidators)
|
||||||
{
|
// {
|
||||||
if (val is RefValidator refValidator && !Assembly.GetCfgTable(refValidator.FirstTable).NeedExport)
|
// if (val is RefValidator refValidator && !Assembly.GetCfgTable(refValidator.FirstTable).NeedExport)
|
||||||
{
|
// {
|
||||||
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' ref 引用的表:'{refValidator.FirstTable}' 没有导出");
|
// throw new Exception($"type:'{HostType.FullName}' field:'{Name}' ref 引用的表:'{refValidator.FirstTable}' 没有导出");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PostCompile()
|
public override void PostCompile()
|
||||||
|
|
@ -310,49 +276,49 @@ namespace Luban.Job.Cfg.Defs
|
||||||
|
|
||||||
// 检查 字段类型 与 所引用的表的key是否一致
|
// 检查 字段类型 与 所引用的表的key是否一致
|
||||||
|
|
||||||
foreach (var val in KeyValidators)
|
//foreach (var val in KeyValidators)
|
||||||
{
|
//{
|
||||||
if (val is RefValidator refValidator)
|
// if (val is RefValidator refValidator)
|
||||||
{
|
// {
|
||||||
var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
|
// var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
|
||||||
if (CType is TMap mapType)
|
// if (CType is TMap mapType)
|
||||||
{
|
// {
|
||||||
if (mapType.KeyType.GetType() != cfgTable.KeyTType.GetType())
|
// if (mapType.KeyType.GetType() != cfgTable.KeyTType.GetType())
|
||||||
{
|
// {
|
||||||
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' key类型:'{mapType.KeyType.GetType()}' 与 被引用的表:'{cfgTable.FullName}' key类型:'{cfgTable.KeyTType.GetType()}' 不一致");
|
// throw new Exception($"type:'{HostType.FullName}' field:'{Name}' key类型:'{mapType.KeyType.GetType()}' 与 被引用的表:'{cfgTable.FullName}' key类型:'{cfgTable.KeyTType.GetType()}' 不一致");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' 不是 map类型. 不能指定 key_validator 引用");
|
// throw new Exception($"type:'{HostType.FullName}' field:'{Name}' 不是 map类型. 不能指定 key_validator 引用");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
var remainValidators = new List<IValidator>(this.Validators);
|
//var remainValidators = new List<IValidator>(this.Validators);
|
||||||
remainValidators.AddRange(this.ValueValidators);
|
//remainValidators.AddRange(this.ValueValidators);
|
||||||
foreach (var val in remainValidators)
|
//foreach (var val in remainValidators)
|
||||||
{
|
//{
|
||||||
if (val is RefValidator refValidator)
|
// if (val is RefValidator refValidator)
|
||||||
{
|
// {
|
||||||
var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
|
// var cfgTable = Assembly.GetCfgTable(refValidator.FirstTable);
|
||||||
TType valueType;
|
// TType valueType;
|
||||||
switch (CType)
|
// switch (CType)
|
||||||
{
|
// {
|
||||||
case TArray ta: valueType = ta.ElementType; break;
|
// case TArray ta: valueType = ta.ElementType; break;
|
||||||
case TList tl: valueType = tl.ElementType; break;
|
// case TList tl: valueType = tl.ElementType; break;
|
||||||
case TSet ts: valueType = ts.ElementType; break;
|
// case TSet ts: valueType = ts.ElementType; break;
|
||||||
case TMap tm: valueType = tm.ValueType; break;
|
// case TMap tm: valueType = tm.ValueType; break;
|
||||||
default: valueType = CType; break;
|
// default: valueType = CType; break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (valueType.GetType() != cfgTable.KeyTType.GetType())
|
// if (valueType.GetType() != cfgTable.KeyTType.GetType())
|
||||||
{
|
// {
|
||||||
throw new Exception($"type:'{HostType.FullName}' field:'{Name}' 类型:'{valueType.GetType()}' 与 被引用的表:'{cfgTable.FullName}' key类型:'{cfgTable.KeyTType.GetType()}' 不一致");
|
// throw new Exception($"type:'{HostType.FullName}' field:'{Name}' 类型:'{valueType.GetType()}' 与 被引用的表:'{cfgTable.FullName}' key类型:'{cfgTable.KeyTType.GetType()}' 不一致");
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,36 +3,10 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.RawDefs
|
namespace Luban.Job.Cfg.RawDefs
|
||||||
{
|
{
|
||||||
|
|
||||||
public class Validator
|
|
||||||
{
|
|
||||||
public string Type { get; set; }
|
|
||||||
|
|
||||||
public string Rule { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CfgField : Field
|
public class CfgField : Field
|
||||||
{
|
{
|
||||||
public string Index { get; set; } = "";
|
public string Index { get; set; }
|
||||||
|
|
||||||
public string Sep { get; set; } = "";
|
|
||||||
|
|
||||||
public bool IsMultiRow { get; set; }
|
|
||||||
|
|
||||||
public string Resource { get; set; } = "";
|
|
||||||
|
|
||||||
public string Converter { get; set; } = "";
|
|
||||||
|
|
||||||
public string DefaultValue { get; set; } = "";
|
|
||||||
|
|
||||||
public bool IsRowOrient { get; set; } = true;
|
|
||||||
|
|
||||||
public List<string> Groups { get; set; } = new List<string>();
|
public List<string> Groups { get; set; } = new List<string>();
|
||||||
|
|
||||||
public List<Validator> KeyValidators { get; } = new List<Validator>();
|
|
||||||
|
|
||||||
public List<Validator> ValueValidators { get; } = new List<Validator>();
|
|
||||||
|
|
||||||
public List<Validator> Validators { get; } = new List<Validator>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,23 +55,23 @@ namespace Luban.Job.Cfg.TypeVisitors
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidatorEquals(List<Validator> vs1, List<Validator> vs2)
|
//bool IsValidatorEquals(List<Validator> vs1, List<Validator> vs2)
|
||||||
{
|
//{
|
||||||
if (vs1.Count != vs2.Count)
|
// if (vs1.Count != vs2.Count)
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
for (int i = 0; i < vs1.Count; i++)
|
// for (int i = 0; i < vs1.Count; i++)
|
||||||
{
|
// {
|
||||||
var v1 = vs1[i];
|
// var v1 = vs1[i];
|
||||||
var v2 = vs2[i];
|
// var v2 = vs2[i];
|
||||||
if (v1.Type != v2.Type || v1.Rule != v2.Rule)
|
// if (v1.Type != v2.Type || v1.Rule != v2.Rule)
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return true;
|
// return true;
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (ctx.TryGetValue(a, out var e))
|
if (ctx.TryGetValue(a, out var e))
|
||||||
{
|
{
|
||||||
|
|
@ -103,16 +103,16 @@ namespace Luban.Job.Cfg.TypeVisitors
|
||||||
if (f1.Name != f2.Name
|
if (f1.Name != f2.Name
|
||||||
|| f1.NeedExport != f2.NeedExport
|
|| f1.NeedExport != f2.NeedExport
|
||||||
|| f1.Index != f2.Index
|
|| f1.Index != f2.Index
|
||||||
|| f1.Sep != f2.Sep
|
// || f1.Sep != f2.Sep
|
||||||
#if !LUBAN_LITE
|
//#if !LUBAN_LITE
|
||||||
|| f1.ResourceTag != f2.ResourceTag
|
// || f1.ResourceTag != f2.ResourceTag
|
||||||
#endif
|
//#endif
|
||||||
|| f1.IsMultiRow != f2.IsMultiRow
|
|| f1.IsMultiRow != f2.IsMultiRow
|
||||||
|| f1.CType.IsNullable != f2.CType.IsNullable
|
|| f1.CType.IsNullable != f2.CType.IsNullable
|
||||||
|| f1.CType.GetType() != f2.CType.GetType()
|
|| f1.CType.GetType() != f2.CType.GetType()
|
||||||
|| !IsValidatorEquals(f1.RawDefine.Validators, f2.RawDefine.Validators)
|
//|| !IsValidatorEquals(f1.RawDefine.Validators, f2.RawDefine.Validators)
|
||||||
|| !IsValidatorEquals(f1.RawDefine.KeyValidators, f2.RawDefine.KeyValidators)
|
//|| !IsValidatorEquals(f1.RawDefine.KeyValidators, f2.RawDefine.KeyValidators)
|
||||||
|| !IsValidatorEquals(f1.RawDefine.ValueValidators, f2.RawDefine.ValueValidators)
|
//|| !IsValidatorEquals(f1.RawDefine.ValueValidators, f2.RawDefine.ValueValidators)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return setupNotEqual();
|
return setupNotEqual();
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Luban.Job.Cfg.Utils
|
||||||
|
|
||||||
public static DType CreateVector(TVector2 type, string x)
|
public static DType CreateVector(TVector2 type, string x)
|
||||||
{
|
{
|
||||||
var values = DataUtil.SplitVectorString(x);
|
var values = SplitVectorString(x);
|
||||||
|
|
||||||
return new DVector2(new System.Numerics.Vector2(float.Parse(values[0]), float.Parse(values[1])));
|
return new DVector2(new System.Numerics.Vector2(float.Parse(values[0]), float.Parse(values[1])));
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Luban.Job.Cfg.Utils
|
||||||
|
|
||||||
public static DType CreateVector(TVector3 type, string x)
|
public static DType CreateVector(TVector3 type, string x)
|
||||||
{
|
{
|
||||||
var values = DataUtil.SplitVectorString(x);
|
var values = SplitVectorString(x);
|
||||||
|
|
||||||
return new DVector3(new System.Numerics.Vector3(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2])));
|
return new DVector3(new System.Numerics.Vector3(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2])));
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Luban.Job.Cfg.Utils
|
||||||
|
|
||||||
public static DType CreateVector(TVector4 type, string x)
|
public static DType CreateVector(TVector4 type, string x)
|
||||||
{
|
{
|
||||||
var values = DataUtil.SplitVectorString(x);
|
var values = SplitVectorString(x);
|
||||||
return new DVector4(new System.Numerics.Vector4(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3])));
|
return new DVector4(new System.Numerics.Vector4(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,29 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.Validators
|
namespace Luban.Job.Cfg.Validators
|
||||||
{
|
{
|
||||||
static class ValidatorFactory
|
//static class ValidatorFactory
|
||||||
{
|
//{
|
||||||
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
// private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
public static IValidator Create(Validator validator)
|
// public static IValidator Create(Validator validator)
|
||||||
{
|
// {
|
||||||
s_logger.Debug("== create validator {type}:{rule}", validator.Type, validator.Rule);
|
// s_logger.Debug("== create validator {type}:{rule}", validator.Type, validator.Rule);
|
||||||
switch (validator.Type)
|
// switch (validator.Type)
|
||||||
{
|
// {
|
||||||
case RefValidator.NAME:
|
// case RefValidator.NAME:
|
||||||
{
|
// {
|
||||||
return new RefValidator(validator.Rule.Split(',').ToList());
|
// return new RefValidator(validator.Rule.Split(',').ToList());
|
||||||
}
|
// }
|
||||||
case PathValidator.NAME:
|
// case PathValidator.NAME:
|
||||||
{
|
// {
|
||||||
return new PathValidator(validator.Rule);//.Split(',').ToList());
|
// return new PathValidator(validator.Rule);//.Split(',').ToList());
|
||||||
}
|
// }
|
||||||
case RangeValidator.NAME:
|
// case RangeValidator.NAME:
|
||||||
{
|
// {
|
||||||
return new RangeValidator(validator.Rule);
|
// return new RangeValidator(validator.Rule);
|
||||||
}
|
// }
|
||||||
default:
|
// default:
|
||||||
throw new NotSupportedException("unknown validator type:" + validator.Type);
|
// throw new NotSupportedException("unknown validator type:" + validator.Type);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue