Skip to content

Commit

Permalink
list parse related fix: filter template cells
Browse files Browse the repository at this point in the history
  • Loading branch information
mikheev.a committed Apr 25, 2022
1 parent 5ccdb18 commit e06ab04
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ public void TestLazyParseForArray()
});
}

[Test]
public void TestLazyParseWithBlanks()
{
var model = LazyParse<PriceList>("simpleWithEnumerable_templateWithBlanks.xlsx", "simpleWithEnumerable_target.xlsx");

model.Type.Should().Be("Основной");
model.Items.Should().BeEquivalentTo(new[]
{
new Item {Index = 1, Name = "СЫР ГОЛЛАНДСКИЙ МОЖГА 1КГ", Articul = "123456"},
new Item {Index = 2, Name = "СЫР РОССИЙСКИЙ МОЖГА 1КГ", Articul = "123457"},
});
}

[Test]
public void TestEnumerableWithPrimaryKey()
{
Expand Down
Binary file not shown.
30 changes: 13 additions & 17 deletions Excel.TemplateEngine/ObjectPrinting/LazyParse/LazyClassParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

Expand Down Expand Up @@ -66,9 +67,9 @@ public TModel Parse<TModel>([NotNull] LazyTableReader tableReader, [NotNull] Ren
if (!(enumerableType.IsArray || TypeCheckingHelper.IsList(enumerableType)))
continue;

var templateEnumerableRow = templateRow.SkipWhile(x => x.CellPosition.CellReference != templateCell.CellPosition.CellReference)
.ToArray();
ParseEnumerable(tableReader, model, templateEnumerableRow, enumerableType);
var templateListCells = templateRow.SkipWhile(x => x.CellPosition.CellReference != templateCell.CellPosition.CellReference)
.ToArray();
ParseEnumerable(tableReader, model, templateListCells, enumerableType);
break;
}

Expand All @@ -85,33 +86,28 @@ public TModel Parse<TModel>([NotNull] LazyTableReader tableReader, [NotNull] Ren
/// </summary>
private void ParseEnumerable([NotNull] LazyTableReader tableReader,
[NotNull] object model,
[NotNull] [ItemNotNull] ICell[] templateEnumerableRow,
[NotNull] [ItemNotNull] ICell[] templateListCells,
[NotNull] Type enumerableType)
{
var firstExpression = templateEnumerableRow.First().StringValue;
var arrayAccessEnd = firstExpression.IndexOf(']');
var enumerableExpression = firstExpression.Substring(0, arrayAccessEnd + 1);

var firstEnumerableTemplateCells = templateEnumerableRow.Where(x => !string.IsNullOrEmpty(x.StringValue) && x.StringValue.StartsWith(enumerableExpression))
.Select(x => new SimpleCell(x.CellPosition, x.StringValue))
.ToArray();
var firstEnumerablePath = ExcelTemplatePath.FromRawExpression(templateListCells.First().StringValue)
.SplitForEnumerableExpansion()
.pathToEnumerable;

var modelType = model.GetType();
var pathToEnumerable = ExcelTemplatePath.FromRawExpression(enumerableExpression);
var itemType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, pathToEnumerable);
var itemType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(modelType, firstEnumerablePath);

var items = ParseList(tableReader, itemType, firstEnumerableTemplateCells, logger);
var items = ParseList(tableReader, itemType, templateListCells.Select(x => new SimpleCell(x.CellPosition, x.StringValue)));

var withoutArrayAccess = pathToEnumerable.WithoutArrayAccess();
var withoutArrayAccess = firstEnumerablePath.WithoutArrayAccess();
var enumerableSetter = ObjectChildSetterFactory.GetEnumerableSetter(modelType, withoutArrayAccess, enumerableType, itemType);

enumerableSetter(model, items);
}

private object ParseList([NotNull] LazyTableReader tableReader, [NotNull] Type itemType, [NotNull, ItemNotNull] SimpleCell[] firstEnumerableTemplateCells, ILog log)
private object ParseList([NotNull] LazyTableReader tableReader, [NotNull] Type itemType, [NotNull, ItemNotNull] IEnumerable<SimpleCell> templateListCells)
{
return parseList.MakeGenericMethod(itemType)
.Invoke(null, new object[] {tableReader, firstEnumerableTemplateCells, logger});
.Invoke(null, new object[] {tableReader, templateListCells, true, logger});
}

private void ParseSingleValue([NotNull] SimpleCell cell,
Expand Down
45 changes: 34 additions & 11 deletions Excel.TemplateEngine/ObjectPrinting/LazyParse/ListParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,50 @@ public static class ListParser
/// <summary>
/// Parse tableReader from its current position for List&lt;&gt; until it meets empty row.
/// </summary>
/// <param name="tableReader"></param>
/// <param name="parentType">Base object type.</param>
/// <param name="tableReader">TableReader of target document which will be parsed.</param>
/// <param name="templateListCells">Template cells with list items descriptions.</param>
/// <param name="filterTemplateCells">Determines whether it's needed to filter templateListCells or not.</param>
/// <param name="logger"></param>
public static IReadOnlyList<TItem> Parse<TItem>([NotNull] LazyTableReader tableReader,
[NotNull, ItemNotNull] SimpleCell[] templateListCells,
[NotNull, ItemNotNull] IEnumerable<SimpleCell> templateListCells,
bool filterTemplateCells,
[NotNull] ILog logger)
{
var itemType = typeof(TItem);

var listTemplate = templateListCells.Select(x => (cellPosition : x.CellPosition, itemPropPath : ExcelTemplatePath.FromRawExpression(x.CellValue).SplitForEnumerableExpansion().relativePathToItem))
.OrderBy(x => x.cellPosition.ColumnIndex)
.ToArray();
var template = filterTemplateCells ? FilterTemplateCells(templateListCells) : templateListCells;
var listTemplate = template.Select(x => (CellPosition : x.CellPosition, ItemPropPath : ExcelTemplatePath.FromRawExpression(x.CellValue).SplitForEnumerableExpansion().relativePathToItem))
.ToArray();

var itemPropPaths = listTemplate.Select(x => x.itemPropPath)
var itemPropPaths = listTemplate.Select(x => x.ItemPropPath)
.ToArray();
var dictToObject = ObjectConversionGenerator.BuildDictToObject(itemPropPaths, itemType);

var result = new List<TItem>();

var firstListCellPosition = listTemplate.First().cellPosition;
var firstListCellPosition = listTemplate.First().CellPosition;
var row = tableReader.TryReadRow(firstListCellPosition.RowIndex);
while (row != null)
{
var itemDict = itemPropPaths.ToDictionary(x => x, _ => (object)null);
var rowIsEmpty = true;
foreach (var prop in listTemplate)
{
var cellPosition = new CellPosition(row.RowIndex, prop.cellPosition.ColumnIndex);
var cellPosition = new CellPosition(row.RowIndex, prop.CellPosition.ColumnIndex);
var cell = row.TryReadCell(cellPosition);
if (cell == null || string.IsNullOrEmpty(cell.CellValue))
continue;

rowIsEmpty = false;

var propType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(itemType, prop.itemPropPath);
var propType = ObjectPropertiesExtractor.ExtractChildObjectTypeFromPath(itemType, prop.ItemPropPath);
if (!TextValueParser.TryParse(cell.CellValue, propType, out var parsedValue))
{
logger.Warn("Failed to parse value {CellValue} from {CellReference} with type='{PropType}'", new {CellValue = cell.CellValue, CellReference = cell.CellPosition.CellReference, PropType = propType});
continue;
}

itemDict[prop.itemPropPath] = parsedValue;
itemDict[prop.ItemPropPath] = parsedValue;
}

if (rowIsEmpty)
Expand All @@ -74,5 +75,27 @@ public static IReadOnlyList<TItem> Parse<TItem>([NotNull] LazyTableReader tableR

return result;
}

/// <summary>
/// Returns cells of the first met row and which value describes items of the first met enumerable. Cells is ordered by ColumnIndex.
/// </summary>
/// <param name="templateListCells">Template cells that forms list description.</param>
/// <returns></returns>
public static IEnumerable<SimpleCell> FilterTemplateCells(IEnumerable<SimpleCell> templateListCells)
{
var cellsWithPaths = templateListCells.Where(x => TemplateDescriptionHelper.IsCorrectValueDescription(x.CellValue))
.Select(x => (cell : x, path : ExcelTemplatePath.FromRawExpression(x.CellValue)))
.Where(x => x.path.HasArrayAccess)
.OrderBy(x => x.cell.CellPosition.ColumnIndex)
.ToArray();

var firstTemplateItem = cellsWithPaths[0];
var firstEnumerablePath = firstTemplateItem.path
.SplitForEnumerableExpansion()
.pathToEnumerable;
return cellsWithPaths.Where(x => x.cell.CellPosition.RowIndex == firstTemplateItem.cell.CellPosition.RowIndex &&
x.path.RawPath.StartsWith(firstEnumerablePath.RawPath))
.Select(x => x.cell);
}
}
}

0 comments on commit e06ab04

Please sign in to comment.