From 95d473acbf087e1a6f71c58ce9f7a21597947720 Mon Sep 17 00:00:00 2001 From: Romuald Rousseau Date: Fri, 22 Nov 2024 00:19:22 +0800 Subject: [PATCH] 54 declare a column as a pivot type and use it in the pivot operation (#55) * feat: Add a new header type and process it in IntelliTable * refacto: Can pass pivot types through transformable sheet * fix: Fix header cloning * fix: Fix header cloning * feat: Add a new header type and process it in IntelliTable --------- Co-authored-by: Romuald Rousseau --- .../python/PythonSimpleDateFormat.java | 25 +- .../table/DataTableGroupSubFooterParser.java | 2 +- .../table/DataTableGroupSubHeaderParser.java | 2 +- .../archery/TransformableSheet.java | 34 ++- .../archery/base/BaseCell.java | 15 +- .../archery/base/BaseHeader.java | 27 +- .../archery/base/BaseSheet.java | 35 ++- .../archery/header/DataTableHeader.java | 30 +- .../archery/header/MetaGroupHeader.java | 10 +- .../archery/header/MetaHeader.java | 47 +-- .../archery/header/MetaKeyValueHeader.java | 33 ++- .../archery/header/MetaTableHeader.java | 20 +- .../archery/header/PivotEntry.java | 14 +- .../archery/header/PivotKeyHeader.java | 60 ++-- .../archery/header/PivotTypeHeader.java | 12 +- .../archery/header/PivotValueHeader.java | 13 +- .../archery/intelli/IntelliHeader.java | 41 +-- .../archery/intelli/IntelliTable.java | 267 +++++++++++------- 18 files changed, 415 insertions(+), 272 deletions(-) diff --git a/archery-commons/src/main/java/com/github/romualdrousseau/archery/commons/python/PythonSimpleDateFormat.java b/archery-commons/src/main/java/com/github/romualdrousseau/archery/commons/python/PythonSimpleDateFormat.java index ba9f403f..2544d241 100644 --- a/archery-commons/src/main/java/com/github/romualdrousseau/archery/commons/python/PythonSimpleDateFormat.java +++ b/archery-commons/src/main/java/com/github/romualdrousseau/archery/commons/python/PythonSimpleDateFormat.java @@ -1,25 +1,23 @@ package com.github.romualdrousseau.archery.commons.python; -import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Locale; public class PythonSimpleDateFormat extends SimpleDateFormat { + private final Locale locale; + public PythonSimpleDateFormat() { - super(); + this("", Locale.US); } public PythonSimpleDateFormat(final String pattern) { - super(PythonSimpleDateFormat.toJava(pattern)); - } - - public PythonSimpleDateFormat(final String pattern, DateFormatSymbols formatSymbols) { - super(PythonSimpleDateFormat.toJava(pattern), formatSymbols); + this(pattern, PythonSimpleDateFormat.toJavaLocale(pattern)); } public PythonSimpleDateFormat(final String pattern, Locale locale) { super(PythonSimpleDateFormat.toJava(pattern), locale); + this.locale = locale; } public static String toPython(final String javaPattern) { @@ -68,7 +66,6 @@ public static String toJava(final String pythonPattern) { .replaceAll("%w", "u") .replaceAll("%u", "u") .replaceAll("%U", "ww") - .replaceAll("%V", "ww") .replaceAll("%H", "HH") .replaceAll("%-H", "H") .replaceAll("%I", "hh") @@ -78,4 +75,16 @@ public static String toJava(final String pythonPattern) { .replaceAll("%S", "ss") .replaceAll("%-S", "s"); } + + public static Locale toJavaLocale(final String pythonPattern) { + if (pythonPattern.contains("%w") || pythonPattern.contains("%W")) { + return Locale.UK; + } else { + return Locale.US; + } + } + + public Locale getLocale() { + return this.locale; + } } diff --git a/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubFooterParser.java b/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubFooterParser.java index 05f181cb..e71fd414 100644 --- a/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubFooterParser.java +++ b/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubFooterParser.java @@ -100,7 +100,7 @@ private void processHeader(final BaseCell cell, final String symbol) { this.firstRowHeader = false; } } else if (this.firstRowHeader) { - if (!this.disablePivot && symbol.equals("e") && cell.isPivotHeader() && cell.getColumnIndex() > 0) { + if (!this.disablePivot && symbol.equals("e") && cell.isPivotKeyHeader() && cell.getColumnIndex() > 0) { var foundPivot = this.dataTable.findFirstPivotHeader(); if (foundPivot == null) { foundPivot = new PivotKeyHeader(this.dataTable, cell); diff --git a/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubHeaderParser.java b/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubHeaderParser.java index dcab32d8..3b798bd5 100644 --- a/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubHeaderParser.java +++ b/archery-layex-parser/src/main/java/com/github/romualdrousseau/archery/parser/table/DataTableGroupSubHeaderParser.java @@ -101,7 +101,7 @@ private void processHeader(final BaseCell cell, final String symbol) { this.firstRowHeader = false; } } else if (this.firstRowHeader) { - if (!this.disablePivot && symbol.equals("e") && cell.isPivotHeader() && cell.getColumnIndex() > 0) { + if (!this.disablePivot && symbol.equals("e") && cell.isPivotKeyHeader() && cell.getColumnIndex() > 0) { var foundPivot = this.dataTable.findFirstPivotHeader(); if (foundPivot == null) { foundPivot = new PivotKeyHeader(this.dataTable, cell); diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/TransformableSheet.java b/archery/src/main/java/com/github/romualdrousseau/archery/TransformableSheet.java index ac15e00f..c8cca892 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/TransformableSheet.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/TransformableSheet.java @@ -290,24 +290,46 @@ public void disablePivot() { this.sheet.disablePivot(); } + /** + * This method sets the pivot option for the sheet using the given option + * string. + * + * @param option the option string: "NONE", "WITH_TYPE", "WITH_TYPE_AND_VALUE" + */ + public void setPivotOption(final String option) { + this.sheet.setPivotOption(Enum.valueOf(PivotOption.class, option)); + } + /** * This method sets the pivot entities for the sheet using the given list of * entities. * * @param pivotEntityList the list of entities as a list of string + * + * @deprecated use {@link TransformableSheet#setPivotKeyEntityList(List)} */ public void setPivotEntityList(final List pivotEntityList) { - this.sheet.setPivotEntityList(pivotEntityList); + this.sheet.setPivotKeyEntityList(pivotEntityList); } /** - * This method sets the pivot option for the sheet using the given option - * string. + * This method sets the pivot key for the sheet using the given list of + * entities. * - * @param option the option string: "NONE", "WITH_TYPE", "WITH_TYPE_AND_VALUE" + * @param pivotKeyEntityList the list of entities as a list of string */ - public void setPivotOption(final String option) { - this.sheet.setPivotOption(Enum.valueOf(PivotOption.class, option)); + public void setPivotKeyEntityList(final List pivotKeyEntityList) { + this.sheet.setPivotKeyEntityList(pivotKeyEntityList); + } + + /** + * This method sets the pivot type for the sheet using the given list of + * entities. + * + * @param pivotTypeEntityList the list of entities as a list of string + */ + public void setPivotTypeEntityList(final List pivotTypeEntityList) { + this.sheet.setPivotTypeEntityList(pivotTypeEntityList); } /** diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseCell.java b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseCell.java index 66dfb217..e14e4709 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseCell.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseCell.java @@ -107,14 +107,19 @@ public Tensor getEntityVector() { return this.entityVector; } - public boolean isPivotHeader() { - return this.getPivotEntityAsString().isPresent(); + public boolean isPivotKeyHeader() { + return this.getPivotKeyEntityAsString().isPresent(); } - public Optional getPivotEntityAsString() { + public boolean isPivotTypeHeader() { + final var pivotTypeEntityList = this.sheet.getPivotTypeEntityList(); + return pivotTypeEntityList.stream().anyMatch(x -> this.value.matches(x)); + } + + public Optional getPivotKeyEntityAsString() { if (this.sheet != null) { - final var pivotEntityList = this.sheet.getPivotEntityList(); - return this.entities().stream().filter(x -> pivotEntityList.contains(x)).findFirst(); + final var pivotKeyEntityList = this.sheet.getPivotKeyEntityList(); + return this.entities().stream().filter(x -> pivotKeyEntityList.contains(x)).findFirst(); } else { return Optional.empty(); } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseHeader.java index 095c414e..493d9ac6 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseHeader.java @@ -7,6 +7,12 @@ public abstract class BaseHeader implements Header { + private final BaseCell cell; + + private BaseTable table; + private int colIndex; + private boolean columnEmpty; + public BaseHeader(final BaseTable table, final BaseCell cell) { this.table = table; this.cell = cell; @@ -44,7 +50,7 @@ public boolean isColumnMerged() { return false; } - public void setColumnEmpty(boolean columnEmpty) { + public void setColumnEmpty(final boolean columnEmpty) { this.columnEmpty = columnEmpty; } @@ -72,12 +78,16 @@ public boolean hasRowGroup() { return false; } - public boolean isPivotHeader() { - return this.cell.isPivotHeader(); + public boolean isPivotKeyHeader() { + return this.cell.isPivotKeyHeader(); + } + + public boolean isPivotTypeHeader() { + return this.cell.isPivotTypeHeader(); } - public Optional getPivotEntityAsString() { - return this.cell.getPivotEntityAsString(); + public Optional getPivotKeyEntityAsString() { + return this.cell.getPivotKeyEntityAsString(); } @Override @@ -89,12 +99,7 @@ public boolean equals(final Object o) { return other != null && this.getName().equalsIgnoreCase(other.getName()); } - public abstract String getValue(); - public abstract BaseHeader clone(); - private BaseTable table; - private final BaseCell cell; - private int colIndex; - private boolean columnEmpty; + public abstract String getValue(); } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseSheet.java b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseSheet.java index 89b8c69b..b5b8b1ea 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseSheet.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/base/BaseSheet.java @@ -367,26 +367,40 @@ public void disablePivot() { this.pivotEnabled = false; } - public List getPivotEntityList() { + public PivotOption getPivotOption() { + return this.pivotOption; + } + + public void setPivotOption(final PivotOption option) { + this.pivotOption = option; + } + + public List getPivotKeyEntityList() { if (!this.pivotEnabled) { return Collections.emptyList(); } - if (this.pivotEntityList == null) { + if (this.pivotKeyEntityList == null) { return this.getDocument().getModel().getPivotEntityList(); } - return this.pivotEntityList; + return this.pivotKeyEntityList; } - public void setPivotEntityList(final List pivotEntityList) { - this.pivotEntityList = pivotEntityList; + public void setPivotKeyEntityList(final List pivotKeyEntityList) { + this.pivotKeyEntityList = pivotKeyEntityList; } - public PivotOption getPivotOption() { - return this.pivotOption; + public List getPivotTypeEntityList() { + if (!this.pivotEnabled) { + return Collections.emptyList(); + } + if (this.pivotTypeEntityList == null) { + return Collections.emptyList(); + } + return this.pivotTypeEntityList; } - public void setPivotOption(final PivotOption option) { - this.pivotOption = option; + public void setPivotTypeEntityList(final List pivotTypeEntityList) { + this.pivotTypeEntityList = pivotTypeEntityList; } public String getPivotKeyFormat() { @@ -479,5 +493,6 @@ private int computeLastColumnNum() { private String pivotTypeFormat; private String groupValueFormat; private String columnValueFormat; - private List pivotEntityList; + private List pivotKeyEntityList; + private List pivotTypeEntityList; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/DataTableHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/DataTableHeader.java index 86924bc7..e4cd47ed 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/DataTableHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/DataTableHeader.java @@ -15,14 +15,25 @@ public class DataTableHeader extends BaseHeader { - public DataTableHeader(final BaseHeader parent) { - this(parent.getTable(), parent.getCell()); - } + private String name; + private HeaderTag tag; + private List entities; public DataTableHeader(final BaseTable table, final BaseCell cell) { + this(table, cell, cell.getValue(), null, null); + } + + protected DataTableHeader(final BaseTable table, final BaseCell cell, final String name, final HeaderTag tag, + final List entities) { super(table, cell); - this.name = this.getCell().getValue(); - this.entities = null; + this.name = name; + this.tag = tag; + this.entities = entities; + } + + @Override + public BaseHeader clone() { + return new DataTableHeader(this.getTable(), this.getCell(), this.name, this.tag, this.entities); } @Override @@ -50,11 +61,6 @@ public Iterable entities() { return this.entities; } - @Override - public BaseHeader clone() { - return new DataTableHeader(this); - } - @Override public boolean hasTag() { return this.tag != null; @@ -110,8 +116,4 @@ private List sampleEntities() { .map(i -> entityList.get(i)) .toList(); } - - private String name; - private HeaderTag tag; - private List entities; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaGroupHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaGroupHeader.java index 8555959f..7f998911 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaGroupHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaGroupHeader.java @@ -10,17 +10,13 @@ public MetaGroupHeader(final BaseTable table, final BaseCell cell) { super(table, cell); } - private MetaGroupHeader(final MetaGroupHeader parent) { - super(parent.getTable(), parent.getCell()); + @Override + public BaseHeader clone() { + return new MetaGroupHeader(this.getTable(), this.getCell()); } @Override public String getName() { return String.format(this.getTable().getSheet().getGroupValueFormat(), super.getName()); } - - @Override - public BaseHeader clone() { - return new MetaGroupHeader(this); - } } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaHeader.java index b1849747..465732a2 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaHeader.java @@ -11,21 +11,35 @@ public class MetaHeader extends BaseHeader { + private final String name; + private final BaseCell value; + private final String valueOfValue; + public MetaHeader(final BaseTable table, final BaseCell cell) { + this(table, cell, null, null, null); + } + + protected MetaHeader(final BaseTable table, final BaseCell cell, final String name, final BaseCell value, + final String valueOfValue) { super(table, cell); - final var model = this.getTable().getSheet().getDocument().getModel(); - final var cellValue = this.getCell().getValue(); - this.transformedCell = model - .toEntityValue(cellValue) - .map(x -> new BaseCell(x, this.getCell())) - .orElse(this.getCell()); - this.name = this.getPivotEntityAsString().orElseGet(() -> model.toEntityName(cellValue)); - this.value = this.transformedCell.getValue(); + final var model = table.getSheet().getDocument().getModel(); + final var cellValue = cell.getValue(); + + this.name = (name == null) + ? this.getPivotKeyEntityAsString().orElseGet(() -> model.toEntityName(cellValue)) + : name; + this.value = (value == null) + ? model.toEntityValue(cellValue).map(x -> new BaseCell(x, cell)).orElse(cell) + : value; + this.valueOfValue = (valueOfValue == null) + ? this.value.getValue() + : valueOfValue; } - private MetaHeader(final MetaHeader parent) { - this(parent.getTable(), parent.getCell()); + @Override + public BaseHeader clone() { + return new MetaHeader(this.getTable(), this.getCell(), this.name, this.value, this.valueOfValue); } @Override @@ -35,17 +49,12 @@ public String getName() { @Override public String getValue() { - return this.value; + return this.valueOfValue; } @Override public BaseCell getCellAtRow(final Row row) { - return this.transformedCell; - } - - @Override - public BaseHeader clone() { - return new MetaHeader(this); + return this.value; } @Override @@ -62,8 +71,4 @@ public boolean hasTag() { public HeaderTag getTag() { return null; } - - private final String name; - private final String value; - private final BaseCell transformedCell; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaKeyValueHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaKeyValueHeader.java index 61f69b74..4e034613 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaKeyValueHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaKeyValueHeader.java @@ -7,15 +7,31 @@ public class MetaKeyValueHeader extends MetaHeader { + private final String name; + private final BaseCell value; + private final String valueOfValue; + public MetaKeyValueHeader(final BaseTable table, final BaseCell key, final BaseCell value) { + this(table, key, key.getValue(), value, null); + } + + protected MetaKeyValueHeader(final BaseTable table, final BaseCell key, final String name, final BaseCell value, + final String valueOfValue) { super(table, key); - this.name = this.getCell().getValue(); + + final var model = table.getSheet().getDocument().getModel(); + final var cellValue = value.getValue(); + + this.name = name; this.value = value; - this.valueOfValue = this.getTable().getSheet().getDocument().getModel().toEntityValue(value.getValue()).orElse(value.getValue()); + this.valueOfValue = (valueOfValue == null) + ? model.toEntityValue(cellValue).orElse(cellValue) + : valueOfValue; } - private MetaKeyValueHeader(final MetaKeyValueHeader parent) { - this(parent.getTable(), parent.getCell(), parent.value); + @Override + public BaseHeader clone() { + return new MetaKeyValueHeader(this.getTable(), this.getCell(), this.name, this.value, this.valueOfValue); } @Override @@ -37,13 +53,4 @@ public BaseCell getCellAtRow(final Row row) { public String getEntitiesAsString() { return this.value.getEntitiesAsString(); } - - @Override - public BaseHeader clone() { - return new MetaKeyValueHeader(this); - } - - private final String name; - private final BaseCell value; - private final String valueOfValue; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaTableHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaTableHeader.java index 63a040a0..160dc432 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaTableHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/MetaTableHeader.java @@ -7,23 +7,25 @@ public class MetaTableHeader extends MetaHeader { + private RowGroup rowGroup; + public MetaTableHeader(final BaseTable table, final BaseCell cell) { - super(table, cell); - this.rowGroup = null; + this(table, cell, null); } - private MetaTableHeader(final MetaTableHeader parent) { - this(parent.getTable(), parent.getCell()); + protected MetaTableHeader(final BaseTable table, final BaseCell cell, final RowGroup rowGroup) { + super(table, cell); + this.rowGroup = rowGroup; } @Override - public String getValue() { - return null; + public BaseHeader clone() { + return new MetaTableHeader(this.getTable(), this.getCell(), this.rowGroup); } @Override - public BaseHeader clone() { - return new MetaTableHeader(this); + public String getValue() { + return null; } @Override @@ -34,6 +36,4 @@ public boolean hasRowGroup() { public void assignRowGroup(final RowGroup rowGroup) { this.rowGroup = rowGroup; } - - private RowGroup rowGroup; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotEntry.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotEntry.java index eb3edfb5..d354ee19 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotEntry.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotEntry.java @@ -4,6 +4,11 @@ public class PivotEntry { + private final BaseCell cell; + private final String pivotValue; + + private String typeValue; + public PivotEntry(final BaseCell cell, final String pivotEntityName) { this.cell = cell; this.pivotValue = cell.getSheet().getDocument().getModel().toEntityValue(cell.getValue(), pivotEntityName) @@ -15,7 +20,7 @@ public BaseCell getCell() { return this.cell; } - public String getValue() { + public String getPivotValue() { return this.pivotValue; } @@ -23,11 +28,8 @@ public String getTypeValue() { return this.typeValue; } - public void setTypeValue(final String typeValue) { + public PivotEntry setTypeValue(final String typeValue) { this.typeValue = typeValue; + return this; } - - private final BaseCell cell; - private final String pivotValue; - private String typeValue; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotKeyHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotKeyHeader.java index 314f75f4..a5bcf723 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotKeyHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotKeyHeader.java @@ -11,17 +11,25 @@ public class PivotKeyHeader extends MetaHeader { + private final List entries; + private final String pivotEntityName; + public PivotKeyHeader(final BaseTable table, final BaseCell cell) { + this(table, cell, cell.getPivotKeyEntityAsString().get(), null); + } + + protected PivotKeyHeader(final BaseTable table, final BaseCell cell, final String pivotEntityName, + final List entries) { super(table, cell); - this.entries = new ArrayList<>(); - this.entries.add(new PivotEntry(cell, this.getPivotEntityAsString().get())); - this.valueName = this.getPivotEntityAsString().get(); + this.pivotEntityName = pivotEntityName; + this.entries = (entries == null) + ? new ArrayList<>(List.of(new PivotEntry(cell, pivotEntityName))) + : entries; } - private PivotKeyHeader(final PivotKeyHeader parent) { - super(parent.getTable(), parent.getCell()); - this.entries = parent.entries; - this.valueName = this.getPivotEntityAsString().get(); + @Override + public BaseHeader clone() { + return new PivotKeyHeader(this.getTable(), this.getCell(), this.pivotEntityName, this.entries); } @Override @@ -29,43 +37,37 @@ public String getName() { return String.format(this.getTable().getSheet().getPivotKeyFormat(), super.getName()); } - @Override - public BaseHeader clone() { - return new PivotKeyHeader(this); + public String getPivotEntityName() { + return this.pivotEntityName; } public List getEntries() { return this.entries; } - public Set getEntryTypes() { - return this.entries.stream().map(x -> x.getTypeValue()).collect(Collectors.toSet()); - } - - public Set getEntryValues() { - return this.entries.stream().map(x -> x.getValue()).collect(Collectors.toSet()); + public void addEntry(final BaseCell entry) { + this.entries.add(new PivotEntry(entry, this.pivotEntityName)); } - public String getValueName() { - return this.valueName; + public Set getEntryTypeValues() { + return this.entries.stream().map(x -> x.getTypeValue()).collect(Collectors.toSet()); } - public void updateValueName(final String newName) { - this.valueName = newName; + public void setEntryTypeValues(final Set entryTypeValues) { + this.entries.clear(); + this.entries.addAll(entryTypeValues.stream() + .map(x -> new PivotEntry(this.getCell(), this.pivotEntityName).setTypeValue(x)).toList()); } - public PivotValueHeader getPivotValue() { - return new PivotValueHeader(this, this.valueName); + public Set getEntryPivotValues() { + return this.entries.stream().map(x -> x.getPivotValue()).collect(Collectors.toSet()); } - public PivotTypeHeader getPivotType() { - return new PivotTypeHeader(this, this.valueName); + public PivotTypeHeader getPivotTypeHeader() { + return new PivotTypeHeader(this, this.pivotEntityName); } - public void addEntry(final BaseCell entry) { - this.entries.add(new PivotEntry(entry, this.getPivotEntityAsString().get())); + public PivotValueHeader getPivotValueHeader() { + return new PivotValueHeader(this, this.pivotEntityName); } - - private final List entries; - private String valueName; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotTypeHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotTypeHeader.java index e9fe32f1..4ca8a36b 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotTypeHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotTypeHeader.java @@ -3,10 +3,15 @@ public class PivotTypeHeader extends PivotKeyHeader { public PivotTypeHeader(final PivotKeyHeader parent, final String name) { - super(parent.getTable(), parent.getCell()); + super(parent.getTable(), parent.getCell(), parent.getPivotEntityName(), parent.getEntries()); this.name = name; } + @Override + public PivotTypeHeader clone() { + return new PivotTypeHeader(this, this.name); + } + @Override public String getName() { if(!this.getTable().isLoadCompleted()) { @@ -21,10 +26,5 @@ public PivotTypeHeader setName(final String name) { return this; } - @Override - public PivotTypeHeader clone() { - return new PivotTypeHeader(this, this.name); - } - private String name; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotValueHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotValueHeader.java index 6d4e2297..78a827e6 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotValueHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/header/PivotValueHeader.java @@ -3,10 +3,15 @@ public class PivotValueHeader extends PivotKeyHeader { public PivotValueHeader(final PivotKeyHeader parent, final String name) { - super(parent.getTable(), parent.getCell()); + super(parent.getTable(), parent.getCell(), parent.getPivotEntityName(), parent.getEntries()); this.name = name; } + @Override + public PivotValueHeader clone() { + return new PivotValueHeader(this, this.name); + } + @Override public String getName() { if(!this.getTable().isLoadCompleted()) { @@ -16,9 +21,9 @@ public String getName() { } } - @Override - public PivotValueHeader clone() { - return new PivotValueHeader(this, this.name); + public PivotValueHeader setName(final String name) { + this.name = name; + return this; } private String name; diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliHeader.java b/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliHeader.java index 7b5b24a0..a7d4c419 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliHeader.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliHeader.java @@ -8,17 +8,27 @@ import com.github.romualdrousseau.archery.Row; import com.github.romualdrousseau.archery.base.BaseCell; import com.github.romualdrousseau.archery.base.BaseHeader; +import com.github.romualdrousseau.archery.commons.strings.StringUtils; import com.github.romualdrousseau.archery.config.Settings; import com.github.romualdrousseau.archery.header.DataTableHeader; -import com.github.romualdrousseau.archery.commons.strings.StringUtils; public class IntelliHeader extends DataTableHeader { + public static Optional mergeValues(final List values) { + return StringUtils.merge(Settings.MERGE_SEPARATOR, values); + } + + private final String name; + private final boolean disableAutoName; + + private IntelliHeader prevSibling; + private IntelliHeader nextSibling; + public IntelliHeader(final BaseHeader header, final boolean disableAutoName) { super(header.getTable(), new BaseCell(header.getName(), header.getColumnIndex(), 1, header.getCell().getRawValue(), header.getTable().getSheet())); - final String cellValue = this.getCell().getValue(); + final var cellValue = this.getCell().getValue(); if (StringUtils.isFastBlank(cellValue)) { if (header.isColumnEmpty()) { this.name = ""; @@ -27,7 +37,7 @@ public IntelliHeader(final BaseHeader header, final boolean disableAutoName) { StreamSupport.stream(this.entities().spliterator(), false).findAny() .map(x -> this.getEntitiesAsString()).orElse("VALUE")); } - } else if (this.isPivotHeader() || !disableAutoName) { + } else if (this.isPivotKeyHeader() || !disableAutoName) { this.name = this.getTable().getSheet().getDocument().getModel().toEntityName(cellValue); } else { this.name = cellValue; @@ -39,6 +49,15 @@ public IntelliHeader(final BaseHeader header, final boolean disableAutoName) { this.setColumnEmpty(StringUtils.isFastBlank(this.name) && header.isColumnEmpty()); } + protected IntelliHeader(final IntelliHeader parent) { + this(parent, parent.disableAutoName); + } + + @Override + public BaseHeader clone() { + return new IntelliHeader(this); + } + @Override public String getName() { return this.name; @@ -70,12 +89,7 @@ public BaseCell getCellAtRow(final Row row, final boolean merged) { .map(x -> new BaseCell(x, this.getColumnIndex(), 1, this.getTable().getSheet())) .orElseGet(() -> this.getCellAtRow(row)); } - - @Override - public BaseHeader clone() { - return new IntelliHeader(this, this.disableAutoName); - } - + @Override public void resetTag() { super.resetTag(); @@ -96,13 +110,4 @@ public void mergeTo(final IntelliHeader other) { other.prevSibling = e; e.nextSibling = other; } - - public static Optional mergeValues(final List values) { - return StringUtils.merge(Settings.MERGE_SEPARATOR, values); - } - - private final String name; - private final boolean disableAutoName; - private IntelliHeader prevSibling; - private IntelliHeader nextSibling; } diff --git a/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliTable.java b/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliTable.java index 0a4071a1..b0cf0e85 100644 --- a/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliTable.java +++ b/archery/src/main/java/com/github/romualdrousseau/archery/intelli/IntelliTable.java @@ -1,43 +1,59 @@ package com.github.romualdrousseau.archery.intelli; -import java.util.List; -import java.util.Optional; import java.io.IOException; import java.io.UncheckedIOException; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.StreamSupport; import com.github.romualdrousseau.archery.PivotOption; import com.github.romualdrousseau.archery.base.BaseHeader; +import com.github.romualdrousseau.archery.base.BaseRow; import com.github.romualdrousseau.archery.base.BaseSheet; import com.github.romualdrousseau.archery.base.BaseTableGraph; import com.github.romualdrousseau.archery.base.DataTable; -import com.github.romualdrousseau.archery.base.BaseRow; import com.github.romualdrousseau.archery.base.RowGroup; -import com.github.romualdrousseau.archery.header.PivotEntry; -import com.github.romualdrousseau.archery.header.PivotKeyHeader; import com.github.romualdrousseau.archery.commons.collections.DataFrame; import com.github.romualdrousseau.archery.commons.collections.DataFrameWriter; import com.github.romualdrousseau.archery.commons.collections.Row; import com.github.romualdrousseau.archery.commons.strings.StringUtils; +import com.github.romualdrousseau.archery.header.DataTableHeader; +import com.github.romualdrousseau.archery.header.PivotEntry; +import com.github.romualdrousseau.archery.header.PivotKeyHeader; public class IntelliTable extends DataTable { private static final int BATCH_SIZE = 10000; + private final ArrayList tmpHeaders = new ArrayList<>(); + private final DataFrameWriter writer; + private final DataFrame rows; + public IntelliTable(final BaseSheet sheet, final BaseTableGraph root, final boolean headerAutoNameEnabled) { super(sheet); + // Collect pivot types + + final var pivotEntryTypes = new HashSet(); + root.parse(e -> e.getTable().headers().forEach(h -> this.addPivotEntryTypes((BaseHeader) h, pivotEntryTypes))); + // Collect headers - root.parse(e -> e.getTable().headers().forEach(h -> this.addTmpHeader((BaseHeader) h))); + root.parse(e -> e.getTable().headers().forEach(h -> this.addTmpHeader((BaseHeader) h, pivotEntryTypes))); + + final var pivotKeyHeader = this.findKeyPivotHeader(); + final var pivotTypeHeader = this.findPivotTypeHeader(); // Build tables try { this.writer = new DataFrameWriter(BATCH_SIZE, this.tmpHeaders.size()); - final var pivot = this.findPivotHeader(); root.parseIf( - e -> this.buildRowsForOneTable((BaseTableGraph) e, (DataTable) e.getTable(), pivot), + e -> this.emitAllRowsForOneTable((BaseTableGraph) e, (DataTable) e.getTable(), pivotKeyHeader, + pivotTypeHeader), e -> e instanceof BaseTableGraph && e.getTable() instanceof DataTable); this.setLoadCompleted(true); this.rows = this.writer.getDataFrame(); @@ -74,36 +90,81 @@ public BaseRow getRowAt(final int rowIndex) { return new IntelliRow(this, rowIndex, this.rows.getRow(rowIndex)); } - private void addTmpHeader(final BaseHeader header) { + private void addPivotEntryTypes(final BaseHeader header, final Set pivotEntryTypes) { + if (!header.isPivotTypeHeader()) { + return; + } + + pivotEntryTypes.addAll(StreamSupport.stream(header.getTable().rows().spliterator(), false) + .filter(x -> header.getCellAtRow(x).hasValue()) + .map(x -> header.getCellAtRow(x).getValue()) + .distinct() + .toList()); + } + + private void addTmpHeader(final BaseHeader header, final Set pivotEntryTypes) { if (this.tmpHeaders.contains(header)) { return; } if (header instanceof PivotKeyHeader) { - final var pivot = (PivotKeyHeader) header; + final var pivotHeader = (PivotKeyHeader) header.clone(); if (this.getSheet().getPivotOption() == PivotOption.WITH_TYPE_AND_VALUE) { - this.addHeaderIntoTmpHeaders(pivot.clone(), false); - for (final String typeValue : pivot.getEntryTypes()) { - this.addHeaderIntoTmpHeaders(pivot.getPivotType().clone().setName(typeValue), false); + if (!pivotEntryTypes.isEmpty()) { + pivotHeader.setEntryTypeValues(pivotEntryTypes); } + this.addHeaderIntoTmpHeaders(pivotHeader, false); + pivotHeader.getEntryTypeValues().forEach( + x -> this.addHeaderIntoTmpHeaders(pivotHeader.getPivotTypeHeader().clone().setName(x), false)); } else if (this.getSheet().getPivotOption() == PivotOption.WITH_TYPE) { - this.addHeaderIntoTmpHeaders(pivot.clone(), false); - this.addHeaderIntoTmpHeaders(pivot.getPivotType().clone(), false); - this.addHeaderIntoTmpHeaders(pivot.getPivotValue().clone(), false); + this.addHeaderIntoTmpHeaders(pivotHeader, false); + this.addHeaderIntoTmpHeaders(pivotHeader.getPivotTypeHeader().clone(), false); + this.addHeaderIntoTmpHeaders(pivotHeader.getPivotValueHeader().clone(), false); } else { - this.addHeaderIntoTmpHeaders(pivot.clone(), false); - this.addHeaderIntoTmpHeaders(pivot.getPivotValue().clone(), false); + this.addHeaderIntoTmpHeaders(pivotHeader, false); + this.addHeaderIntoTmpHeaders(pivotHeader.getPivotValueHeader().clone(), false); } } else { this.addHeaderIntoTmpHeaders(header.clone(), true); } } - private void buildRowsForOneTable(final BaseTableGraph graph, final DataTable orgTable, - final PivotKeyHeader pivot) { + private PivotKeyHeader findKeyPivotHeader() { + for (final var header : this.tmpHeaders) { + if (header instanceof PivotKeyHeader) { + return (PivotKeyHeader) header; + } + } + return null; + } + + private DataTableHeader findPivotTypeHeader() { + for (final var header : this.tmpHeaders) { + if (header.isPivotTypeHeader()) { + return (DataTableHeader) header; + } + } + return null; + } + + private String findTypeValue(final DataTable orgTable, final BaseRow orgRow, + final DataTableHeader pivotTypeHeader) { + if (pivotTypeHeader != null) { + final var orgHeaders = orgTable.findAllHeaders(pivotTypeHeader); + if (orgHeaders.size() > 0) { + return orgHeaders.get(0).getCellAtRow(orgRow).getValue(); + } + } + return null; + } + + private void emitAllRowsForOneTable(final BaseTableGraph graph, final DataTable orgTable, + final PivotKeyHeader pivotKeyHeader, final DataTableHeader pivotTypeHeader) { if (orgTable.getNumberOfRowGroups() == 0) { for (final var orgRow : orgTable.rows()) { - final var newRows = buildRowsForOneRow(graph, orgTable, (BaseRow) orgRow, pivot, null); + final var newRows = emitAllRowsForOneRow(graph, orgTable, (BaseRow) orgRow, pivotKeyHeader, + pivotTypeHeader, + null); this.addRows(newRows); } } else { @@ -111,7 +172,9 @@ private void buildRowsForOneTable(final BaseTableGraph graph, final DataTable or for (int i = 0; i < rowGroup.getNumberOfRows(); i++) { if (rowGroup.getRow() + i < orgTable.getNumberOfRows()) { final var orgRow = orgTable.getRowAt(rowGroup.getRow() + i); - final var newRows = buildRowsForOneRow(graph, orgTable, orgRow, pivot, rowGroup); + final var newRows = emitAllRowsForOneRow(graph, orgTable, orgRow, pivotKeyHeader, + pivotTypeHeader, + rowGroup); this.addRows(newRows); } } @@ -119,156 +182,160 @@ private void buildRowsForOneTable(final BaseTableGraph graph, final DataTable or } } - private List buildRowsForOneRow(final BaseTableGraph graph, final DataTable orgTable, + private List emitAllRowsForOneRow(final BaseTableGraph graph, final DataTable orgTable, final BaseRow orgRow, - final PivotKeyHeader pivot, final RowGroup rowGroup) { + final PivotKeyHeader pivotKeyHeader, final DataTableHeader pivotTypeHeader, final RowGroup rowGroup) { final var newRows = new ArrayList(); if (orgRow.isIgnored()) { return newRows; } - - if (pivot == null) { - buildOneRowWithoutPivot(graph, orgTable, orgRow, rowGroup).ifPresent(newRows::add); + if (pivotKeyHeader == null) { + this.emitOneRowWithoutPivot(graph, orgTable, orgRow, rowGroup).ifPresent(newRows::add); } else if (this.getSheet().getPivotOption() == PivotOption.WITH_TYPE_AND_VALUE) { - pivot.getEntryValues() - .forEach(x -> buildOneRowWithPivotTypeAndValue(graph, orgTable, orgRow, pivot, x, rowGroup) + pivotKeyHeader.getEntryPivotValues() + .forEach(x -> this + .emitOneRowWithPivotTypeAndValue(graph, orgTable, orgRow, rowGroup, pivotKeyHeader, x, + this.findTypeValue(orgTable, orgRow, pivotTypeHeader)) .ifPresent(newRows::add)); } else if (this.getSheet().getPivotOption() == PivotOption.WITH_TYPE) { - pivot.getEntries() - .forEach(x -> buildOneRowWithPivotAndType(graph, orgTable, orgRow, x, rowGroup) + pivotKeyHeader.getEntries() + .forEach(x -> this.emitOneRowWithPivotAndType(graph, orgTable, orgRow, rowGroup, x) .ifPresent(newRows::add)); } else { - pivot.getEntries() - .forEach(x -> buildOneRowWithPivot(graph, orgTable, orgRow, x, rowGroup) + pivotKeyHeader.getEntries() + .forEach(x -> this.emitOneRowWithPivot(graph, orgTable, orgRow, rowGroup, x) .ifPresent(newRows::add)); } return newRows; } - private Optional buildOneRowWithoutPivot(final BaseTableGraph graph, final DataTable orgTable, + private Optional emitOneRowWithoutPivot(final BaseTableGraph graph, final DataTable orgTable, final BaseRow orgRow, final RowGroup rowGroup) { final var newRow = new Row(this.tmpHeaders.size()); - for (final var abstractHeader : this.tmpHeaders) { - final var orgHeaders = orgTable.findAllHeaders(abstractHeader); - this.generateCells(graph, orgHeaders, abstractHeader, rowGroup, orgRow, newRow); + for (final var tmpHeader : this.tmpHeaders) { + this.emitAllCells(graph, orgTable, orgRow, rowGroup, tmpHeader, newRow); } return Optional.of(newRow); } - private Optional buildOneRowWithPivotTypeAndValue(final BaseTableGraph graph, final DataTable orgTable, - final BaseRow orgRow, final PivotKeyHeader pivot, final String value, final RowGroup rowGroup) { - final var newRow = new Row(this.tmpHeaders.size()); + private Optional emitOneRowWithPivotTypeAndValue(final BaseTableGraph graph, final DataTable orgTable, + final BaseRow orgRow, final RowGroup rowGroup, final PivotKeyHeader pivotKeyHeader, final String pivotValue, + final String typeValue) { boolean hasPivotedValues = false; - for (final var abstractHeader : this.tmpHeaders) { - final var orgHeaders = orgTable.findAllHeaders(abstractHeader); - if (abstractHeader instanceof PivotKeyHeader) { + + final var newRow = new Row(this.tmpHeaders.size()); + for (final var tmpHeader : this.tmpHeaders) { + if (tmpHeader instanceof PivotKeyHeader) { + final var orgHeaders = orgTable.findAllHeaders(tmpHeader); if (orgHeaders.size() > 0) { - newRow.set(abstractHeader.getColumnIndex(), value); + final var ci = tmpHeader.getColumnIndex(); + newRow.set(ci, pivotValue); int i = 1; - for (final var typeValue : pivot.getEntryTypes()) { - final var ci = abstractHeader.getColumnIndex() + i; - hasPivotedValues |= pivot.getEntries().stream() - .filter(x -> x.getValue().equals(value) && x.getTypeValue().equals(typeValue)) + for (final var entryTypeValue : pivotKeyHeader.getEntryTypeValues()) { + final var tv_i = entryTypeValue; + final var pv_i = pivotValue; + final var ci_i = ci + i++; + hasPivotedValues |= pivotKeyHeader.getEntries().stream() + .filter(x -> x.getPivotValue().equals(pv_i) && + (typeValue == null && x.getTypeValue().equals(tv_i) + || typeValue != null && typeValue.equals(tv_i))) .findFirst() .map(x -> { - newRow.set(ci, orgRow.getCellAt(x.getCell().getColumnIndex()).getValue()); - return orgRow.getCellAt(x.getCell().getColumnIndex()).hasValue(); + final var cell = orgRow.getCellAt(x.getCell().getColumnIndex()); + newRow.set(ci_i, cell.getValue()); + return cell.hasValue(); }) .orElse(false); - - i++; } } } else { - this.generateCells(graph, orgHeaders, abstractHeader, rowGroup, orgRow, newRow); + this.emitAllCells(graph, orgTable, orgRow, rowGroup, tmpHeader, newRow); } } + return hasPivotedValues ? Optional.of(newRow) : Optional.empty(); } - private Optional buildOneRowWithPivotAndType(final BaseTableGraph graph, final DataTable orgTable, - final BaseRow orgRow, final PivotEntry pivotEntry, final RowGroup rowGroup) { + private Optional emitOneRowWithPivotAndType(final BaseTableGraph graph, final DataTable orgTable, + final BaseRow orgRow, final RowGroup rowGroup, final PivotEntry pivotEntry) { if (!orgRow.getCellAt(pivotEntry.getCell().getColumnIndex()).hasValue()) { return Optional.empty(); } + final var newRow = new Row(this.tmpHeaders.size()); - for (final var abstractHeader : this.tmpHeaders) { - final var orgHeaders = orgTable.findAllHeaders(abstractHeader); - if (abstractHeader instanceof PivotKeyHeader) { + for (final var tmpHeader : this.tmpHeaders) { + if (tmpHeader instanceof PivotKeyHeader) { + + final var orgHeaders = orgTable.findAllHeaders(tmpHeader); if (orgHeaders.size() > 0) { - final var ci = abstractHeader.getColumnIndex(); - newRow.set(ci + 0, pivotEntry.getValue()); + final var ci = tmpHeader.getColumnIndex(); + newRow.set(ci + 0, pivotEntry.getPivotValue()); newRow.set(ci + 1, pivotEntry.getTypeValue()); newRow.set(ci + 2, orgRow.getCellAt(pivotEntry.getCell().getColumnIndex()).getValue()); } + } else { - this.generateCells(graph, orgHeaders, abstractHeader, rowGroup, orgRow, newRow); + this.emitAllCells(graph, orgTable, orgRow, rowGroup, tmpHeader, newRow); } } + return Optional.of(newRow); } - private Optional buildOneRowWithPivot(final BaseTableGraph graph, final DataTable orgTable, - final BaseRow orgRow, final PivotEntry pivotEntry, final RowGroup rowGroup) { + private Optional emitOneRowWithPivot(final BaseTableGraph graph, final DataTable orgTable, + final BaseRow orgRow, final RowGroup rowGroup, final PivotEntry pivotEntry) { if (!orgRow.getCellAt(pivotEntry.getCell().getColumnIndex()).hasValue()) { return Optional.empty(); } + final var newRow = new Row(this.tmpHeaders.size()); - for (final var abstractHeader : this.tmpHeaders) { - final var orgHeaders = orgTable.findAllHeaders(abstractHeader); - if (abstractHeader instanceof PivotKeyHeader) { + for (final var tmpHeader : this.tmpHeaders) { + if (tmpHeader instanceof PivotKeyHeader) { + final var orgHeaders = orgTable.findAllHeaders(tmpHeader); if (orgHeaders.size() > 0) { - newRow.set(abstractHeader.getColumnIndex() + 0, pivotEntry.getCell().getValue()); - newRow.set(abstractHeader.getColumnIndex() + 1, - orgRow.getCellAt(pivotEntry.getCell().getColumnIndex()).getValue()); + final var ci = tmpHeader.getColumnIndex(); + newRow.set(ci + 0, pivotEntry.getCell().getValue()); + newRow.set(ci + 1, orgRow.getCellAt(pivotEntry.getCell().getColumnIndex()).getValue()); } } else { - this.generateCells(graph, orgHeaders, abstractHeader, rowGroup, orgRow, newRow); + this.emitAllCells(graph, orgTable, orgRow, rowGroup, tmpHeader, newRow); } } + return Optional.of(newRow); } - private void generateCells(final BaseTableGraph graph, final List orgHeaders, - final BaseHeader abstractHeader, final RowGroup rowGroup, final BaseRow orgRow, final Row newRow) { + private void emitAllCells(final BaseTableGraph graph, final DataTable orgTable, final BaseRow orgRow, + final RowGroup rowGroup, + final BaseHeader tmpHeader, final Row newRow) { + final var orgHeaders = orgTable.findAllHeaders(tmpHeader); if (orgHeaders.size() > 0) { for (final var orgHeader : orgHeaders) { - final var orgAbstractHeader = (BaseHeader) orgHeader; - if (rowGroup == null || !orgAbstractHeader.hasRowGroup()) { - final var currValue = orgAbstractHeader.getCellAtRow(orgRow).getValue(); - final var prevValue = newRow.get(abstractHeader.getColumnIndex()); - this.generateCell(abstractHeader, prevValue, currValue, newRow); + if (rowGroup == null || !orgHeader.hasRowGroup()) { + final var oldValue = newRow.get(tmpHeader.getColumnIndex()); + final var newValue = orgHeader.getCellAtRow(orgRow).getValue(); + this.emitOneCell(tmpHeader, oldValue, newValue, newRow); } else { - final var currValue = rowGroup.getCell().getValue(); - this.generateCell(abstractHeader, null, currValue, newRow); + final var newValue = rowGroup.getCell().getValue(); + this.emitOneCell(tmpHeader, null, newValue, newRow); } - } } else { - final var orgAbstractHeader = graph.getParent().findClosestHeader(abstractHeader); - final var currValue = orgAbstractHeader.getValue(); - final var prevValue = newRow.get(abstractHeader.getColumnIndex()); - this.generateCell(abstractHeader, prevValue, currValue, newRow); + final var orgHeader = graph.getParent().findClosestHeader(tmpHeader); + final var oldValue = newRow.get(tmpHeader.getColumnIndex()); + final var newValue = orgHeader.getValue(); + this.emitOneCell(tmpHeader, oldValue, newValue, newRow); } } - private void generateCell(final BaseHeader abstractHeader, final String prevValue, final String currValue, + private void emitOneCell(final BaseHeader tmpHeader, final String oldValue, final String newValue, final Row newRow) { - abstractHeader.setColumnEmpty(abstractHeader.isColumnEmpty() && StringUtils.isFastBlank(currValue)); - if (prevValue == null) { - newRow.set(abstractHeader.getColumnIndex(), currValue); + tmpHeader.setColumnEmpty(tmpHeader.isColumnEmpty() && StringUtils.isFastBlank(newValue)); + if (oldValue == null) { + newRow.set(tmpHeader.getColumnIndex(), newValue); } else { - newRow.set(abstractHeader.getColumnIndex(), prevValue + currValue); - } - } - - private PivotKeyHeader findPivotHeader() { - for (final var header : this.tmpHeaders) { - if (header instanceof PivotKeyHeader) { - return (PivotKeyHeader) header; - } + newRow.set(tmpHeader.getColumnIndex(), oldValue + newValue); } - return null; } private void addHeaderIntoTmpHeaders(final BaseHeader newHeader, final boolean columnEmpty) { @@ -287,8 +354,4 @@ private void addRows(final List newRows) { } }); } - - private final ArrayList tmpHeaders = new ArrayList<>(); - private final DataFrameWriter writer; - private final DataFrame rows; }