diff --git a/Excel.TemplateEngine/ITemplateEngine.cs b/Excel.TemplateEngine/ITemplateEngine.cs index f0b4364..7d60fc7 100644 --- a/Excel.TemplateEngine/ITemplateEngine.cs +++ b/Excel.TemplateEngine/ITemplateEngine.cs @@ -16,7 +16,7 @@ public interface ITemplateEngine (TModel model, Dictionary mappingForErrors) Parse([NotNull] ITableParser tableParser) where TModel : new(); - public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null) + public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null, IFormulaEvaluator formulaEvaluator = null) where TModel : new(); } } \ No newline at end of file diff --git a/Excel.TemplateEngine/ObjectPrinting/LazyParse/IFormulaEvaluator.cs b/Excel.TemplateEngine/ObjectPrinting/LazyParse/IFormulaEvaluator.cs new file mode 100644 index 0000000..3e88b09 --- /dev/null +++ b/Excel.TemplateEngine/ObjectPrinting/LazyParse/IFormulaEvaluator.cs @@ -0,0 +1,12 @@ +#nullable enable + +using DocumentFormat.OpenXml.Spreadsheet; + +namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.LazyParse +{ + public interface IFormulaEvaluator + { + string? TryEvaluate( + Cell cell); + } +} \ No newline at end of file diff --git a/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyClassParser.cs b/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyClassParser.cs index 898ddbe..8026a51 100644 --- a/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyClassParser.cs +++ b/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyClassParser.cs @@ -20,7 +20,7 @@ namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.LazyParse /// internal class LazyClassParser { - public LazyClassParser(ILog logger) + public LazyClassParser([NotNull] ILog logger) { this.logger = logger; } @@ -32,8 +32,13 @@ public LazyClassParser(ILog logger) /// Target document LazyTableReader. /// /// Target file offset relative to a template. + /// Target document formula evaluator. [NotNull] - public TModel Parse([NotNull] LazyTableReader tableReader, [NotNull] RenderingTemplate template, ObjectSize readerOffset) + public TModel Parse( + [NotNull] LazyTableReader tableReader, + [NotNull] RenderingTemplate template, + [NotNull] ObjectSize readerOffset, + [CanBeNull] IFormulaEvaluator formulaEvaluator = null) where TModel : new() { var model = new TModel(); @@ -52,7 +57,10 @@ public TModel Parse([NotNull] LazyTableReader tableReader, [NotNull] Ren { foreach (var templateCell in templateRow) { - var targetCell = targetRowReader.TryReadCell(templateCell.CellPosition.Add(readerOffset)); + var targetCell = targetRowReader.TryReadCell( + templateCell.CellPosition.Add(readerOffset), + formulaEvaluator); + if (targetCell == null) continue; @@ -71,7 +79,7 @@ public TModel Parse([NotNull] LazyTableReader tableReader, [NotNull] Ren var templateListCells = templateRow.SkipWhile(x => x.CellPosition.CellReference != templateCell.CellPosition.CellReference) .ToArray(); - ParseEnumerable(tableReader, model, templateListCells, enumerableType, readerOffset); + ParseEnumerable(tableReader, model, templateListCells, enumerableType, readerOffset, formulaEvaluator); break; } @@ -90,7 +98,8 @@ private void ParseEnumerable([NotNull] LazyTableReader tableReader, [NotNull] object model, [NotNull] [ItemNotNull] ICell[] templateListCells, [NotNull] Type enumerableType, - [NotNull] ObjectSize readerOffset) + [NotNull] ObjectSize readerOffset, + [CanBeNull] IFormulaEvaluator formulaEvaluator) { var firstEnumerablePath = ExcelTemplatePath.FromRawExpression(templateListCells.First().StringValue) .SplitForEnumerableExpansion() @@ -99,7 +108,12 @@ private void ParseEnumerable([NotNull] LazyTableReader tableReader, var modelType = model.GetType(); var itemType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, firstEnumerablePath); - var items = ParseList(tableReader, itemType, templateListCells.Select(x => new SimpleCell(x.CellPosition, x.StringValue)), readerOffset); + var items = ParseList( + tableReader, + itemType, + templateListCells.Select(x => new SimpleCell(x.CellPosition, x.StringValue)), + readerOffset, + formulaEvaluator); var withoutArrayAccess = firstEnumerablePath.WithoutArrayAccess(); var enumerableSetter = ObjectChildSetterFactory.GetEnumerableSetter(modelType, withoutArrayAccess, enumerableType, itemType); @@ -107,10 +121,15 @@ private void ParseEnumerable([NotNull] LazyTableReader tableReader, enumerableSetter(model, items); } - private object ParseList([NotNull] LazyTableReader tableReader, [NotNull] Type itemType, [NotNull, ItemNotNull] IEnumerable templateListCells, ObjectSize readerOffset) + private object ParseList( + [NotNull] LazyTableReader tableReader, + [NotNull] Type itemType, + [NotNull, ItemNotNull] IEnumerable templateListCells, + [NotNull] ObjectSize readerOffset, + [CanBeNull] IFormulaEvaluator formulaEvaluator) { return parseList.MakeGenericMethod(itemType) - .Invoke(null, new object[] {tableReader, templateListCells, true, logger, readerOffset}); + .Invoke(null, new object[] {tableReader, templateListCells, true, logger, readerOffset, formulaEvaluator}); } private void ParseSingleValue([NotNull] SimpleCell cell, diff --git a/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyRowReader.cs b/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyRowReader.cs index 5f59da4..492e2b7 100644 --- a/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyRowReader.cs +++ b/Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyRowReader.cs @@ -24,14 +24,17 @@ public LazyRowReader([NotNull] Row row, [NotNull] IReadOnlyList sharedSt } [NotNull] - private SimpleCell LoadCurrentCell() + private SimpleCell LoadCurrentCell( + [CanBeNull] IFormulaEvaluator formulaEvaluator) { var cell = (Cell)reader.LoadCurrentElement(); - return ToSimpleCell(cell!); + return ToSimpleCell(cell!, formulaEvaluator); } [CanBeNull] - public SimpleCell TryReadCell([NotNull] ICellPosition cellPosition) + public SimpleCell TryReadCell( + [NotNull] ICellPosition cellPosition, + [CanBeNull] IFormulaEvaluator formulaEvaluator = null) { if (cellPosition.RowIndex != RowIndex) throw new ArgumentException($"Incorrect cell reference. Target cell reference: {cellPosition}. Current row index: {RowIndex}."); @@ -50,7 +53,8 @@ public SimpleCell TryReadCell([NotNull] ICellPosition cellPosition) if (reader.ElementType != typeof(Cell)) continue; - currentCell = LoadCurrentCell(); + currentCell = LoadCurrentCell( + formulaEvaluator); if (cellPosition.ColumnIndex > currentCell!.CellPosition.ColumnIndex) continue; @@ -66,15 +70,29 @@ public SimpleCell TryReadCell([NotNull] ICellPosition cellPosition) } [NotNull] - private SimpleCell ToSimpleCell([NotNull] Cell cell) + private SimpleCell ToSimpleCell( + [NotNull] Cell cell, + [CanBeNull] IFormulaEvaluator formulaEvaluator) { var cellIndex = new CellPosition(cell.CellReference); + var cellValue = cell.CellValue?.InnerText; if (cell.DataType?.Value == CellValues.SharedString && cellValue != null) { var i = int.Parse(cellValue); cellValue = sharedStrings[i]; } + else if ( + cell.CellFormula != null && + formulaEvaluator != null && + string.IsNullOrEmpty(cellValue)) + { + var formulaEvaluatedValue = formulaEvaluator.TryEvaluate( + cell); + + if (!string.IsNullOrEmpty(formulaEvaluatedValue)) + cellValue = formulaEvaluatedValue; + } return new SimpleCell(cellIndex, cellValue); } diff --git a/Excel.TemplateEngine/ObjectPrinting/LazyParse/ListParser.cs b/Excel.TemplateEngine/ObjectPrinting/LazyParse/ListParser.cs index 4fc0faa..5549294 100644 --- a/Excel.TemplateEngine/ObjectPrinting/LazyParse/ListParser.cs +++ b/Excel.TemplateEngine/ObjectPrinting/LazyParse/ListParser.cs @@ -26,7 +26,8 @@ public static IReadOnlyList Parse([NotNull] LazyTableReader tableR [NotNull, ItemNotNull] IEnumerable templateListCells, bool filterTemplateCells, [NotNull] ILog logger, - [NotNull] ObjectSize readerOffset) + [NotNull] ObjectSize readerOffset, + [CanBeNull] IFormulaEvaluator formulaEvaluator = null) { var itemType = typeof(TItem); @@ -49,7 +50,7 @@ public static IReadOnlyList Parse([NotNull] LazyTableReader tableR while (row != null) { var itemDict = itemPropPaths.ToDictionary(x => x, _ => (object)null); - FillInItemDict(itemTemplate, row, itemType, itemDict, readerOffset, logger); + FillInItemDict(itemTemplate, row, itemType, itemDict, readerOffset, logger, formulaEvaluator); if (IsRowEmpty(itemDict, impotentItemProps)) { @@ -71,12 +72,13 @@ private static void FillInItemDict((ICellPosition CellPosition, ExcelTemplatePat Type itemType, Dictionary itemDict, ObjectSize readerOffset, - ILog logger) + ILog logger, + IFormulaEvaluator formulaEvaluator) { foreach (var prop in itemTemplate) { var cellPosition = new CellPosition(row.RowIndex, prop.CellPosition.ColumnIndex + readerOffset.Width); - var cell = row.TryReadCell(cellPosition); + var cell = row.TryReadCell(cellPosition, formulaEvaluator); if (cell == null || string.IsNullOrWhiteSpace(cell.CellValue)) continue; diff --git a/Excel.TemplateEngine/TemplateEngine.cs b/Excel.TemplateEngine/TemplateEngine.cs index c1fe905..a1830ca 100644 --- a/Excel.TemplateEngine/TemplateEngine.cs +++ b/Excel.TemplateEngine/TemplateEngine.cs @@ -54,7 +54,8 @@ public void Render([NotNull] ITableBuilder tableBuilder, [NotNull] TMode /// Class to parse. /// LazyTableReader of target xlsx file. /// Target file offset relative to a template. - public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null) + /// Target file formula evaluator. + public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null, IFormulaEvaluator formulaEvaluator = null) where TModel : new() { readerOffset ??= new ObjectSize(0, 0); @@ -62,7 +63,7 @@ public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, Objec var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); var parser = parserCollection.GetLazyClassParser(); - return parser.Parse(lazyTableReader, renderingTemplate, readerOffset); + return parser.Parse(lazyTableReader, renderingTemplate, readerOffset, formulaEvaluator); } private const string rootTemplateName = "RootTemplate";