luban/src/Luban.Job.Cfg/Source/DataCreators/SheetDataCreator.cs

597 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

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

using Luban.Common.Utils;
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.RawDefs;
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);
}
private void ThrowIfNonEmpty(TitleRow row)
{
if (row.SelfTitle.NonEmpty)
{
throw new Exception($"字段不允许为空");
}
}
public DType Accept(TBool type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
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;
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;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DShort.Default;
}
return DShort.ValueOf(short.Parse(x.ToString()));
}
public DType Accept(TFshort type, Sheet sheet, TitleRow row)
{
object x = row.Current;
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;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DInt.Default;
}
return DInt.ValueOf(int.Parse(x.ToString()));
}
public DType Accept(TFint type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DFint.Default;
}
return DFint.ValueOf(int.Parse(x.ToString()));
}
public DType Accept(TLong type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DLong.Default;
}
return DLong.ValueOf(long.Parse(x.ToString()));
}
public DType Accept(TFlong type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DFlong.Default;
}
return DFlong.ValueOf(long.Parse(x.ToString()));
}
public DType Accept(TFloat type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DFloat.Default;
}
return DFloat.ValueOf(float.Parse(x.ToString()));
}
public DType Accept(TDouble type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
return DDouble.Default;
}
return DDouble.ValueOf(double.Parse(x.ToString()));
}
public DType Accept(TEnum type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckNull(type.IsNullable, x))
{
return null;
}
if (CheckDefault(x))
{
if (type.DefineEnum.HasZeroValueItem)
{
return new DEnum(type, "0");
}
else
{
throw new InvalidExcelDataException($"枚举类:'{type.DefineEnum.FullName}' 没有value为0的枚举项, 不支持默认值");
}
}
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.UnEscapeRawString(s);
}
else
{
return d.ToString();
}
}
public DType Accept(TString type, Sheet sheet, TitleRow row)
{
object x = row.Current;
if (CheckDefault(x))
{
ThrowIfNonEmpty(row);
}
var s = ParseString(x);
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)
{
//var s = row.AsStream(row.SelfTitle.Sep);
//return type.Apply(ExcelStreamDataCreator.Ins, s);
if (string.IsNullOrEmpty(row.SelfTitle.SepOr(type.GetTag("sep"))))
{
if (row.CellCount != 2)
{
throw new Exception($"text 要求两个字段");
}
int startIndex = row.SelfTitle.FromIndex;
var key = ParseString(row.Row[startIndex].Value);
var text = ParseString(row.Row[startIndex + 1].Value);
if (type.IsNullable && key == null && text == null)
{
return null;
}
DataUtil.ValidateText(key, text);
return new DText(key, text);
}
else
{
var s = row.AsStream(row.SelfTitle.Sep);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
}
public DType Accept(TDateTime type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (d is System.DateTime datetime)
{
return new DDateTime(datetime);
}
return DataUtil.CreateDateTime(d.ToString());
}
public DType Accept(TVector2 type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector2.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector3 type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector3.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
public DType Accept(TVector4 type, Sheet sheet, TitleRow row)
{
var d = row.Current;
if (CheckNull(type.IsNullable, d))
{
return null;
}
if (CheckDefault(d))
{
ThrowIfNonEmpty(row);
return DVector4.Default;
}
return DataUtil.CreateVector(type, d.ToString());
}
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} 位置:{field.Location}");
dce.Push(bean, f);
throw dce;
}
}
return list;
}
public DType Accept(TBean type, Sheet sheet, TitleRow row)
{
string sep = row.SelfTitle.Sep;// type.GetBeanAs<DefBean>().Sep;
if (row.Row != null)
{
var s = row.AsStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
}
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
else if (row.Rows != null)
{
var s = row.AsMultiRowConcatStream(sep);
if (type.IsNullable && s.TryReadEOF())
{
return null;
}
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
else if (row.Fields != null)
{
var originBean = (DefBean)type.Bean;
if (originBean.IsAbstractType)
{
TitleRow typeTitle = row.GetSubTitleNamedRow(DefBean.EXCEL_TYPE_NAME_KEY) ?? row.GetSubTitleNamedRow(DefBean.FALLBACK_TYPE_NAME_KEY);
if (typeTitle == null)
{
throw new Exception($"type:'{originBean.FullName}' 是多态类型,需要定义'{DefBean.EXCEL_TYPE_NAME_KEY}'列来指定具体子类型");
}
string subType = typeTitle.Current?.ToString()?.Trim();
if (subType == null || subType == DefBean.BEAN_NULL_STR)
{
if (!type.IsNullable)
{
throw new Exception($"type:'{originBean.FullName}' 不是可空类型 '{type.Bean.FullName}?' , 不能为空");
}
return null;
}
DefBean implType = DataUtil.GetImplTypeByNameOrAlias(originBean, subType);
return new DBean(type, implType, CreateBeanFields(implType, sheet, row));
}
else
{
if (type.IsNullable)
{
TitleRow typeTitle = row.GetSubTitleNamedRow(DefBean.EXCEL_TYPE_NAME_KEY) ?? row.GetSubTitleNamedRow(DefBean.FALLBACK_TYPE_NAME_KEY);
if (typeTitle == null)
{
throw new Exception($"type:'{originBean.FullName}' 是可空类型,需要定义'{DefBean.EXCEL_TYPE_NAME_KEY}'列来指明是否可空");
}
string subType = typeTitle.Current?.ToString()?.Trim();
if (subType == null || subType == DefBean.BEAN_NULL_STR)
{
return null;
}
else if (subType != DefBean.BEAN_NOT_NULL_STR && subType != originBean.Name)
{
throw new Exception($"type:'{originBean.FullName}' 可空标识:'{subType}' 不合法(只能为'{DefBean.BEAN_NULL_STR}'或'{DefBean.BEAN_NOT_NULL_STR}'或'{originBean.Name}')");
}
}
return new DBean(type, originBean, CreateBeanFields(originBean, sheet, row));
}
}
else if (row.Elements != null)
{
var s = row.AsMultiRowConcatElements(sep);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
else
{
throw new Exception();
}
}
public List<DType> ReadList(TType type, ExcelStream stream)
{
var sep = type.GetTag("sep");
var datas = new List<DType>();
while (!stream.TryReadEOF())
{
if (string.IsNullOrEmpty(sep))
{
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, stream));
}
else
{
datas.Add(type.Apply(ExcelStreamDataCreator.Ins, new ExcelStream(stream.ReadCell(), sep)));
}
}
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;
}
private List<DType> ReadCollectionDatas(TType type, TType elementType, Sheet sheet, TitleRow row)
{
if (row.Row != null)
{
var s = row.AsStream(row.SelfTitle.Sep);
return ExcelStreamDataCreator.Ins.ReadList(type, elementType, s);
}
else if (row.Rows != null)
{
var s = row.AsMultiRowStream(row.SelfTitle.Sep);
return ReadList(elementType, s);
}
else if (row.Fields != null)
{
//throw new Exception($"array 不支持 子字段. 忘记将字段设为多行模式? {row.SelfTitle.Name} => *{row.SelfTitle.Name}");
var datas = new List<DType>(row.Fields.Count);
var sortedFields = row.Fields.Values.ToList();
sortedFields.Sort((a, b) => a.SelfTitle.FromIndex - b.SelfTitle.FromIndex);
foreach (var field in sortedFields)
{
if (field.IsBlank)
{
continue;
}
datas.Add(elementType.Apply(this, sheet, field));
}
return datas;
}
else if (row.Elements != null)
{
return row.Elements.Select(e => elementType.Apply(this, sheet, e)).ToList();
}
else
{
throw new Exception();
}
}
public DType Accept(TArray type, Sheet sheet, TitleRow row)
{
//string sep = DataUtil.GetSep(type);
return new DArray(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TList type, Sheet sheet, TitleRow row)
{
return new DList(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TSet type, Sheet sheet, TitleRow row)
{
return new DSet(type, ReadCollectionDatas(type, type.ElementType, sheet, row));
}
public DType Accept(TMap type, Sheet sheet, TitleRow row)
{
string sep = row.SelfTitle.Sep;
if (row.Row != null)
{
var s = row.AsStream(sep);
return type.Apply(ExcelStreamDataCreator.Ins, s);
}
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);
if (Sheet.IsBlankRow(e.Value.Row, e.Value.SelfTitle.FromIndex, e.Value.SelfTitle.ToIndex))
{
continue;
}
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, e.Value.AsStream(""));
datas.Add(keyData, valueData);
}
return new DMap(type, datas);
}
else if (row.Elements != null)
{
var datas = new Dictionary<DType, DType>();
foreach (var e in row.Elements)
{
var stream = e.AsStream(sep);
var keyData = type.KeyType.Apply(ExcelStreamDataCreator.Ins, stream);
var valueData = type.ValueType.Apply(ExcelStreamDataCreator.Ins, stream);
datas.Add(keyData, valueData);
}
return new DMap(type, datas);
}
else
{
throw new Exception();
}
}
}
}