From e8a09344d4da3629acde15b38a3b4ba54ad14e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=80=D0=B8=D0=BD=20=D0=97=D0=B0=D1=85=D0=B0=D1=80?= =?UTF-8?q?=20=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=87?= Date: Wed, 20 Nov 2024 16:28:11 +0500 Subject: [PATCH] parse multiple worksheets into one model object --- .../ObjectPrintingTests/ExcelParsingTests.cs | 72 ++++- .../Files/withMultipleWorksheets_target.xlsx | Bin 0 -> 14363 bytes .../withMultipleWorksheets_template.xlsx | Bin 0 -> 12561 bytes Excel.TemplateEngine/ITemplateEngine.cs | 24 +- .../ParseCollection/Parsers/IClassParser.cs | 19 +- .../Parsers/Implementations/ClassParser.cs | 259 ++++++++++-------- Excel.TemplateEngine/TemplateEngine.cs | 117 ++++---- 7 files changed, 283 insertions(+), 208 deletions(-) create mode 100644 Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/withMultipleWorksheets_target.xlsx create mode 100644 Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/withMultipleWorksheets_template.xlsx diff --git a/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs b/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs index 97cdec9..fdbc79d 100644 --- a/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs +++ b/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs @@ -84,6 +84,38 @@ public void TestInvalidUris() }); } + [Test] + public void TestParseMultipleWorksheets() + { + var (model, mappingForErrors) = Parse( + "withMultipleWorksheets_template.xlsx", + "withMultipleWorksheets_target.xlsx", + workSheetNumber : 3); + + mappingForErrors["Type"].Should().Be("C3"); + mappingForErrors["Items[0].Id"].Should().Be("B13"); + mappingForErrors["Items[0].Name"].Should().Be("C13"); + mappingForErrors["Items[1].Id"].Should().Be("B14"); + mappingForErrors["Items[1].Name"].Should().Be("C14"); + mappingForErrors["FromSecondWorksheet[0].Id"].Should().Be("Лист2:A2"); + mappingForErrors["FromSecondWorksheet[0].Name"].Should().Be("Лист2:B2"); + mappingForErrors["FromThirdWorksheet"].Should().Be("Лист3:B1"); + + model.Type.Should().Be("Основной"); + model.Items.Should().BeEquivalentTo(new[] + { + new Item {Id = "2311129000009", Name = "СЫР ГОЛЛАНДСКИЙ МОЖГА 1КГ"}, + new Item {Id = "2311131000004", Name = "СЫР РОССИЙСКИЙ МОЖГА 1КГ"}, + }); + model.FromSecondWorksheet.Should().BeEquivalentTo(new[] + { + new Item {Id = "2311129000009", Name = "FromSecondWorksheet1"}, + new Item {Id = "2311131000004", Name = "FromSecondWorksheet2"}, + new Item {Id = "2311131000004", Name = "FromSecondWorksheet3"}, + }); + model.FromThirdWorksheet.Should().Be("FromThirdWorksheet_Value"); + } + [Test] public void TestLazyParse() { @@ -356,28 +388,44 @@ private void CheckCustomProperty(IExcelDocument documentModel, string key, strin printedVersion.Should().Be(value); } - private (TModel model, Dictionary mappingForErrors) Parse(string templateFileName, string targetFileName) + private (TModel model, Dictionary mappingForErrors) Parse(string templateFileName, string targetFileName, int workSheetNumber = 0) where TModel : new() { - return Parse(File.ReadAllBytes(GetFilePath(templateFileName)), File.ReadAllBytes(GetFilePath(targetFileName))); + return Parse(File.ReadAllBytes(GetFilePath(templateFileName)), File.ReadAllBytes(GetFilePath(targetFileName)), workSheetNumber); } - private (TModel model, Dictionary mappingForErrors) Parse(byte[] templateBytes, byte[] targetBytes) + private (TModel model, Dictionary mappingForErrors) Parse(byte[] templateBytes, byte[] targetBytes, int workSheetNumber = 0) where TModel : new() { using (var templateDocument = ExcelDocumentFactory.CreateFromTemplate(templateBytes, logger)) using (var targetDocument = ExcelDocumentFactory.CreateFromTemplate(targetBytes, logger)) { - var template = new ExcelTable(templateDocument.GetWorksheet(0)); - var templateEngine = new TemplateEngine(template, logger); - - var target = new ExcelTable(targetDocument.GetWorksheet(0)); - var tableNavigator = new TableNavigator(new CellPosition("A1"), logger); - var tableParser = new TableParser(target, tableNavigator); - return templateEngine.Parse(tableParser); + var (baseTemplateEngine, baseTableParser) = GetEngineAndParserForWorksheet(templateDocument, targetDocument, 0); + var (model, mappingForErrors) = baseTemplateEngine.Parse(baseTableParser); + for (var i = 1; i < workSheetNumber; i++) + { + var (templateEngine, tableParser) = GetEngineAndParserForWorksheet(templateDocument, targetDocument, i); + var worksheetName = targetDocument.GetWorksheetName(i); + templateEngine.Parse(tableParser, (name, value) => mappingForErrors.Add(name, $"{worksheetName}:{value}"), ref model); + } + return (model, mappingForErrors); } } + private (TemplateEngine templateEngine, TableParser tableParser) GetEngineAndParserForWorksheet( + IExcelDocument templateDocument, + IExcelDocument targetDocument, + int workSheetNumber) + { + var template = new ExcelTable(templateDocument.GetWorksheet(workSheetNumber)); + var templateEngine = new TemplateEngine(template, logger); + + var target = new ExcelTable(targetDocument.GetWorksheet(workSheetNumber)); + var tableNavigator = new TableNavigator(new CellPosition("A1"), logger); + var tableParser = new TableParser(target, tableNavigator); + return (templateEngine, tableParser); + } + private TModel LazyParse(string templateFileName, string targetFileName, ObjectSize offset = null) where TModel : new() { @@ -451,6 +499,10 @@ class PriceList public Dictionary IntBoolDict { get; set; } public PriceList InnerPriceList { get; set; } public Dictionary PriceListsDict { get; set; } + + public Item[] FromSecondWorksheet { get; set; } + + public string FromThirdWorksheet { get; set; } } #endregion diff --git a/Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/withMultipleWorksheets_target.xlsx b/Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/withMultipleWorksheets_target.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5880924e90c90d3edeba4c26902601a602ee734a GIT binary patch literal 14363 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 literal 0 HcmV?d00001 diff --git a/Excel.TemplateEngine/ITemplateEngine.cs b/Excel.TemplateEngine/ITemplateEngine.cs index 7d60fc7..a70f663 100644 --- a/Excel.TemplateEngine/ITemplateEngine.cs +++ b/Excel.TemplateEngine/ITemplateEngine.cs @@ -1,22 +1,24 @@ +using System; using System.Collections.Generic; -using JetBrains.Annotations; - using SkbKontur.Excel.TemplateEngine.ObjectPrinting.LazyParse; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.NavigationPrimitives.Implementations; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.TableBuilder; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.TableParser; -namespace SkbKontur.Excel.TemplateEngine +#nullable enable +namespace SkbKontur.Excel.TemplateEngine; + +public interface ITemplateEngine { - public interface ITemplateEngine - { - void Render([NotNull] ITableBuilder tableBuilder, [NotNull] TModel model); + void Render(ITableBuilder tableBuilder, TModel model) where TModel : notnull; + + (TModel model, Dictionary mappingForErrors) Parse(ITableParser tableParser) + where TModel : new(); - (TModel model, Dictionary mappingForErrors) Parse([NotNull] ITableParser tableParser) - where TModel : new(); + void Parse(ITableParser tableParser, Action mappingForErrors, ref TModel model) + where TModel : new(); - public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null, IFormulaEvaluator formulaEvaluator = null) - where TModel : new(); - } + public TModel LazyParse(LazyTableReader lazyTableReader, ObjectSize? readerOffset = null, IFormulaEvaluator? formulaEvaluator = null) + where TModel : new(); } \ No newline at end of file diff --git a/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/IClassParser.cs b/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/IClassParser.cs index e9b3259..10693b9 100644 --- a/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/IClassParser.cs +++ b/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/IClassParser.cs @@ -1,16 +1,17 @@ using System; -using JetBrains.Annotations; - using SkbKontur.Excel.TemplateEngine.ObjectPrinting.RenderingTemplates; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.TableParser; -namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.ParseCollection.Parsers +#nullable enable + +namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.ParseCollection.Parsers; + +internal interface IClassParser { - internal interface IClassParser - { - [NotNull] - TModel Parse([NotNull] ITableParser tableParser, [NotNull] RenderingTemplate template, [NotNull] Action addFieldMapping) - where TModel : new(); - } + TModel Parse(ITableParser tableParser, RenderingTemplate template, Action addFieldMapping) + where TModel : new(); + + void Parse(ITableParser tableParser, RenderingTemplate template, Action addFieldMapping, ref TModel model) + where TModel : new(); } \ No newline at end of file diff --git a/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/Implementations/ClassParser.cs b/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/Implementations/ClassParser.cs index 737aea5..7f76c2c 100644 --- a/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/Implementations/ClassParser.cs +++ b/Excel.TemplateEngine/ObjectPrinting/ParseCollection/Parsers/Implementations/ClassParser.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; - using SkbKontur.Excel.TemplateEngine.ObjectPrinting.ExcelDocumentPrimitives; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.Helpers; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.RenderingTemplates; @@ -12,161 +10,180 @@ using Vostok.Logging.Abstractions; -namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.ParseCollection.Parsers.Implementations +#nullable enable +namespace SkbKontur.Excel.TemplateEngine.ObjectPrinting.ParseCollection.Parsers.Implementations; + +internal class ClassParser : IClassParser { - internal class ClassParser : IClassParser + public ClassParser(IParserCollection parserCollection, ILog logger) { - public ClassParser(IParserCollection parserCollection, ILog logger) - { - this.parserCollection = parserCollection; - this.logger = logger; - } + this.parserCollection = parserCollection; + this.logger = logger; + } - [NotNull] - public TModel Parse([NotNull] ITableParser tableParser, [NotNull] RenderingTemplate template, [NotNull] Action addFieldMapping) - where TModel : new() - { - var model = new TModel(); + public TModel Parse(ITableParser tableParser, RenderingTemplate template, Action addFieldMapping) + where TModel : new() + { + var model = new TModel(); + Parse(tableParser, template, addFieldMapping, ref model); + return model; + } + + public void Parse(ITableParser tableParser, RenderingTemplate template, Action addFieldMapping, ref TModel model) + where TModel : new() + { + if (model == null) throw new ArgumentNullException(nameof(model)); - var enumerablesLengths = GetEnumerablesLengths(tableParser, template); + var enumerablesLengths = GetEnumerablesLengths(tableParser, template); - foreach (var row in template.Content.Cells) + foreach (var row in template.Content.Cells) + { + foreach (var cell in row) { - foreach (var cell in row) + tableParser.PushState(cell.CellPosition); + + var expression = cell.StringValue; + + if (TemplateDescriptionHelper.IsCorrectValueDescription(expression)) { - tableParser.PushState(cell.CellPosition); - - var expression = cell.StringValue; - - if (TemplateDescriptionHelper.IsCorrectValueDescription(expression)) - { - ParseCellularValue(tableParser, addFieldMapping, model, ExcelTemplatePath.FromRawExpression(expression), enumerablesLengths); - continue; - } - if (TemplateDescriptionHelper.IsCorrectFormValueDescription(expression)) - { - ParseFormValue(tableParser, addFieldMapping, model, cell, ExcelTemplatePath.FromRawExpression(expression)); - continue; - } - - tableParser.PopState(); + ParseCellularValue(tableParser, addFieldMapping, model, ExcelTemplatePath.FromRawExpression(expression), enumerablesLengths); + continue; + } + if (TemplateDescriptionHelper.IsCorrectFormValueDescription(expression)) + { + ParseFormValue(tableParser, addFieldMapping, model, cell, ExcelTemplatePath.FromRawExpression(expression)); + continue; } - } - return model; + tableParser.PopState(); + } } + } - [NotNull] - private Dictionary GetEnumerablesLengths([NotNull] ITableParser tableParser, [NotNull] RenderingTemplate template) + private Dictionary GetEnumerablesLengths(ITableParser tableParser, RenderingTemplate template) + { + var enumerableCellsGroups = new Dictionary>(); + foreach (var row in template.Content.Cells) { - var enumerableCellsGroups = new Dictionary>(); - foreach (var row in template.Content.Cells) + foreach (var cell in row) { - foreach (var cell in row) + var expression = cell.StringValue; + + if (TemplateDescriptionHelper.IsCorrectValueDescription(expression) && ExcelTemplatePath.FromRawExpression(expression).HasArrayAccess) { - var expression = cell.StringValue; - - if (TemplateDescriptionHelper.IsCorrectValueDescription(expression) && ExcelTemplatePath.FromRawExpression(expression).HasArrayAccess) - { - var cleanPathToEnumerable = ExcelTemplatePath.FromRawExpression(expression) - .SplitForEnumerableExpansion() - .pathToEnumerable - .WithoutArrayAccess(); - if (!enumerableCellsGroups.ContainsKey(cleanPathToEnumerable)) - enumerableCellsGroups[cleanPathToEnumerable] = new List(); - enumerableCellsGroups[cleanPathToEnumerable].Add(cell); - } + var cleanPathToEnumerable = ExcelTemplatePath.FromRawExpression(expression) + .SplitForEnumerableExpansion() + .pathToEnumerable + .WithoutArrayAccess(); + if (!enumerableCellsGroups.ContainsKey(cleanPathToEnumerable)) + enumerableCellsGroups[cleanPathToEnumerable] = new List(); + enumerableCellsGroups[cleanPathToEnumerable].Add(cell); } } + } - var enumerablesLengths = new Dictionary(); - - foreach (var enumerableCells in enumerableCellsGroups) - { - var cleanPathToEnumerable = enumerableCells.Key; + var enumerablesLengths = new Dictionary(); - var childEnumerableType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(typeof(TModel), cleanPathToEnumerable); - if (!TypeCheckingHelper.IsIList(childEnumerableType)) - throw new InvalidOperationException($"Only ILists are supported as collections, but tried to use '{childEnumerableType}'. (path: {cleanPathToEnumerable.RawPath})"); + foreach (var enumerableCells in enumerableCellsGroups) + { + var cleanPathToEnumerable = enumerableCells.Key; - var primaryParts = enumerableCells.Value.Where(x => ExcelTemplatePath.FromRawExpression(x.StringValue).HasPrimaryKeyArrayAccess).ToList(); - if (primaryParts.Count == 0) - primaryParts = enumerableCells.Value.Take(1).ToList(); + var childEnumerableType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(typeof(TModel), cleanPathToEnumerable); + if (!TypeCheckingHelper.IsIList(childEnumerableType)) + throw new InvalidOperationException($"Only ILists are supported as collections, but tried to use '{childEnumerableType}'. (path: {cleanPathToEnumerable.RawPath})"); - var measurer = parserCollection.GetEnumerableMeasurer(); - enumerablesLengths[cleanPathToEnumerable] = measurer.GetLength(tableParser, typeof(TModel), primaryParts); - } + var primaryParts = enumerableCells.Value.Where(x => ExcelTemplatePath.FromRawExpression(x.StringValue).HasPrimaryKeyArrayAccess).ToList(); + if (primaryParts.Count == 0) + primaryParts = enumerableCells.Value.Take(1).ToList(); - return enumerablesLengths; + var measurer = parserCollection.GetEnumerableMeasurer(); + enumerablesLengths[cleanPathToEnumerable] = measurer.GetLength(tableParser, typeof(TModel), primaryParts); } - private void ParseCellularValue(ITableParser tableParser, Action addFieldMapping, object model, ExcelTemplatePath path, Dictionary enumerablesLengths) - { - var modelType = model.GetType(); - var leafSetter = ObjectChildSetterFactory.GetChildObjectSetter(modelType, path); - var leafModelType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, path); - - if (path.HasArrayAccess) - ParseEnumerableValue(tableParser, addFieldMapping, model, path, leafSetter, leafModelType, enumerablesLengths); - else - ParseSingleValue(tableParser, addFieldMapping, model, leafSetter, path, leafModelType); - } + return enumerablesLengths; + } - private void ParseSingleValue(ITableParser tableParser, Action addFieldMapping, object model, Action leafSetter, ExcelTemplatePath childModelPath, Type childModelType) - { - addFieldMapping(childModelPath.RawPath, tableParser.CurrentState.Cursor.CellReference); - if (!TextValueParser.TryParse(tableParser.GetCurrentCellText(), childModelType, out var parsedObject)) - { - logger.Error($"Failed to parse value from '{tableParser.CurrentState.Cursor.CellReference}' with childModelType='{childModelType}' via AtomicValueParser"); - return; - } - leafSetter(model, parsedObject); - } + private void ParseCellularValue(ITableParser tableParser, + Action addFieldMapping, + object model, + ExcelTemplatePath path, + Dictionary enumerablesLengths) + { + var modelType = model.GetType(); + var leafSetter = ObjectChildSetterFactory.GetChildObjectSetter(modelType, path); + var leafModelType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, path); + + if (path.HasArrayAccess) + ParseEnumerableValue(tableParser, addFieldMapping, model, path, leafSetter, leafModelType, enumerablesLengths); + else + ParseSingleValue(tableParser, addFieldMapping, model, leafSetter, path, leafModelType); + } - private void ParseEnumerableValue(ITableParser tableParser, Action addFieldMapping, object model, ExcelTemplatePath path, Action leafSetter, Type leafModelType, Dictionary enumerablesLengths) + private void ParseSingleValue(ITableParser tableParser, + Action addFieldMapping, + object model, + Action leafSetter, + ExcelTemplatePath childModelPath, + Type childModelType) + { + addFieldMapping(childModelPath.RawPath, tableParser.CurrentState.Cursor.CellReference); + if (!TextValueParser.TryParse(tableParser.GetCurrentCellText(), childModelType, out var parsedObject)) { - var (rawPathToEnumerable, childPath) = path.SplitForEnumerableExpansion(); + logger.Error($"Failed to parse value from '{tableParser.CurrentState.Cursor.CellReference}' with childModelType='{childModelType}' via AtomicValueParser"); + return; + } + leafSetter(model, parsedObject); + } - var cleanPathToEnumerable = rawPathToEnumerable.WithoutArrayAccess(); + private void ParseEnumerableValue(ITableParser tableParser, + Action addFieldMapping, + object model, + ExcelTemplatePath path, + Action leafSetter, + Type leafModelType, + Dictionary enumerablesLengths) + { + var (rawPathToEnumerable, childPath) = path.SplitForEnumerableExpansion(); - var enumerableType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(model.GetType(), cleanPathToEnumerable); - if (!typeof(IList).IsAssignableFrom(enumerableType)) - throw new Exception($"Only ILists are supported as collections, but tried to use '{enumerableType}'. (path: {cleanPathToEnumerable.RawPath})"); + var cleanPathToEnumerable = rawPathToEnumerable.WithoutArrayAccess(); - var parser = parserCollection.GetEnumerableParser(enumerableType); + var enumerableType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(model.GetType(), cleanPathToEnumerable); + if (!typeof(IList).IsAssignableFrom(enumerableType)) + throw new Exception($"Only ILists are supported as collections, but tried to use '{enumerableType}'. (path: {cleanPathToEnumerable.RawPath})"); - var count = enumerablesLengths[cleanPathToEnumerable]; - var parsedList = parser.Parse(tableParser, leafModelType, count, (name, value) => addFieldMapping($"{cleanPathToEnumerable.RawPath}{name}.{childPath.RawPath}", value)); + var parser = parserCollection.GetEnumerableParser(enumerableType); - leafSetter(model, parsedList); - } + var count = enumerablesLengths[cleanPathToEnumerable]; + var parsedList = parser.Parse(tableParser, leafModelType, count, (name, value) => addFieldMapping($"{cleanPathToEnumerable.RawPath}{name}.{childPath.RawPath}", value)); - private void ParseFormValue(ITableParser tableParser, Action addFieldMapping, object model, ICell cell, ExcelTemplatePath path) - { - var modelType = model.GetType(); - var childSetter = ObjectChildSetterFactory.GetChildObjectSetter(modelType, path); - var childModelType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, path); - var (childFormControlType, childFormControlName) = GetFormControlDescription(cell); + leafSetter(model, parsedList); + } - if (path.HasArrayAccess) - throw new InvalidOperationException("Enumerables are not supported for form controls"); + private void ParseFormValue(ITableParser tableParser, Action addFieldMapping, object model, ICell cell, ExcelTemplatePath path) + { + var modelType = model.GetType(); + var childSetter = ObjectChildSetterFactory.GetChildObjectSetter(modelType, path); + var childModelType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, path); + var (childFormControlType, childFormControlName) = GetFormControlDescription(cell); - var parser = parserCollection.GetFormValueParser(childFormControlType, childModelType); - var parsedObject = parser.ParseOrDefault(tableParser, childFormControlName, childModelType); + if (path.HasArrayAccess) + throw new InvalidOperationException("Enumerables are not supported for form controls"); - childSetter(model, parsedObject); - addFieldMapping(path.RawPath, childFormControlName); - } + var parser = parserCollection.GetFormValueParser(childFormControlType, childModelType); + var parsedObject = parser.ParseOrDefault(tableParser, childFormControlName, childModelType); - private static (string formControlType, string formControlName) GetFormControlDescription([NotNull] ICell cell) - { - var formControlDescription = TemplateDescriptionHelper.TryGetFormControlFromValueDescription(cell.StringValue); - if (string.IsNullOrEmpty(formControlDescription.formControlType) || formControlDescription.formControlName == null) - throw new InvalidOperationException($"Invalid xlsx template. '{cell.StringValue}' is not a valid form control description."); - return formControlDescription; - } + childSetter(model, parsedObject); + addFieldMapping(path.RawPath, childFormControlName); + } - private readonly IParserCollection parserCollection; - private readonly ILog logger; + private static (string formControlType, string formControlName) GetFormControlDescription(ICell cell) + { + var formControlDescription = TemplateDescriptionHelper.TryGetFormControlFromValueDescription(cell.StringValue); + if (string.IsNullOrEmpty(formControlDescription.formControlType) || formControlDescription.formControlName == null) + throw new InvalidOperationException($"Invalid xlsx template. '{cell.StringValue}' is not a valid form control description."); + return formControlDescription; } + + private readonly IParserCollection parserCollection; + private readonly ILog logger; } \ No newline at end of file diff --git a/Excel.TemplateEngine/TemplateEngine.cs b/Excel.TemplateEngine/TemplateEngine.cs index a1830ca..2e8dfd2 100644 --- a/Excel.TemplateEngine/TemplateEngine.cs +++ b/Excel.TemplateEngine/TemplateEngine.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using JetBrains.Annotations; - using SkbKontur.Excel.TemplateEngine.ObjectPrinting.ExcelDocumentPrimitives; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.LazyParse; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.NavigationPrimitives.Implementations; @@ -14,70 +12,75 @@ using Vostok.Logging.Abstractions; -namespace SkbKontur.Excel.TemplateEngine +#nullable enable +namespace SkbKontur.Excel.TemplateEngine; + +public class TemplateEngine : ITemplateEngine { - public class TemplateEngine : ITemplateEngine + public TemplateEngine(ITable templateTable, ILog logger) { - public TemplateEngine([NotNull] ITable templateTable, [NotNull] ILog logger) - { - this.templateTable = templateTable; - templateCollection = new TemplateCollection(templateTable); - rendererCollection = new RendererCollection(templateCollection); - parserCollection = new ParserCollection(logger.ForContext("ExcelObjectPrinter")); - } + this.templateTable = templateTable; + templateCollection = new TemplateCollection(templateTable); + rendererCollection = new RendererCollection(templateCollection); + parserCollection = new ParserCollection(logger.ForContext("ExcelObjectPrinter")); + } - public void Render([NotNull] ITableBuilder tableBuilder, [NotNull] TModel model) - { - var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) - ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); - tableBuilder.CopyFormControlsFrom(templateTable); - tableBuilder.CopyDataValidationsFrom(templateTable); - tableBuilder.CopyWorksheetExtensionListFrom(templateTable); // WorksheetExtensionList contains info about data validations with ranges from other sheets, so copying it to support them. - tableBuilder.CopyCommentsFrom(templateTable); - var render = rendererCollection.GetRenderer(model.GetType()); - render.Render(tableBuilder, model, renderingTemplate); - } + public void Render(ITableBuilder tableBuilder, TModel model) where TModel : notnull + { + var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) + ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); + tableBuilder.CopyFormControlsFrom(templateTable); + tableBuilder.CopyDataValidationsFrom(templateTable); + tableBuilder.CopyWorksheetExtensionListFrom(templateTable); // WorksheetExtensionList contains info about data validations with ranges from other sheets, so copying it to support them. + tableBuilder.CopyCommentsFrom(templateTable); + var render = rendererCollection.GetRenderer(model.GetType()); + render.Render(tableBuilder, model, renderingTemplate); + } - public (TModel model, Dictionary mappingForErrors) Parse([NotNull] ITableParser tableParser) - where TModel : new() - { - var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) - ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); - var parser = parserCollection.GetClassParser(); - var fieldsMappingForErrors = new Dictionary(); - return (model : parser.Parse(tableParser, renderingTemplate, (name, value) => fieldsMappingForErrors.Add(name, value)), mappingForErrors : fieldsMappingForErrors); - } + public (TModel model, Dictionary mappingForErrors) Parse(ITableParser tableParser) + where TModel : new() + { + var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) + ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); + var parser = parserCollection.GetClassParser(); + var fieldsMappingForErrors = new Dictionary(); + return (model : parser.Parse(tableParser, renderingTemplate, (name, value) => fieldsMappingForErrors.Add(name, value)), mappingForErrors : fieldsMappingForErrors); + } - /// - /// Parse only separate cell values and List<> enumerations without and size limitations. - /// - /// Class to parse. - /// LazyTableReader of target xlsx file. - /// Target file offset relative to a template. - /// Target file formula evaluator. - public TModel LazyParse([NotNull] LazyTableReader lazyTableReader, ObjectSize readerOffset = null, IFormulaEvaluator formulaEvaluator = null) - where TModel : new() - { - readerOffset ??= new ObjectSize(0, 0); + public void Parse(ITableParser tableParser, Action mappingForErrors, ref TModel model) + where TModel : new() + { + var renderingTemplate = templateCollection.GetTemplate(rootTemplateName) + ?? throw new InvalidOperationException($"Template with name {rootTemplateName} not found in xlsx"); + var parser = parserCollection.GetClassParser(); + parser.Parse(tableParser, renderingTemplate, mappingForErrors, ref model); + } + + /// + /// Parse only separate cell values and List<> enumerations without and size limitations. + /// + /// Class to parse. + /// LazyTableReader of target xlsx file. + /// Target file offset relative to a template. + /// Target file formula evaluator. + public TModel LazyParse(LazyTableReader lazyTableReader, ObjectSize? readerOffset = null, IFormulaEvaluator? formulaEvaluator = null) + where TModel : new() + { + readerOffset ??= new ObjectSize(0, 0); - 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, formulaEvaluator); - } + 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, formulaEvaluator); + } - private const string rootTemplateName = "RootTemplate"; + private const string rootTemplateName = "RootTemplate"; - [NotNull] - private readonly ITable templateTable; + private readonly ITable templateTable; - [NotNull] - private readonly ITemplateCollection templateCollection; + private readonly ITemplateCollection templateCollection; - [NotNull] - private readonly IRendererCollection rendererCollection; + private readonly IRendererCollection rendererCollection; - [NotNull] - private readonly IParserCollection parserCollection; - } + private readonly IParserCollection parserCollection; } \ No newline at end of file