From bb3a5dbbb835c48515d0bb37ec2302dfa8f48541 Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Tue, 27 Feb 2024 14:13:20 -0300 Subject: [PATCH 1/3] feat: add support for providing excel formats per cell Closes #93 --- .../gridexporter/ExcelInputStreamFactory.java | 43 +++++++++++++++---- .../addons/gridexporter/GridExporter.java | 35 +++++++++++++++ .../GridExporterSimpleCustomTemplateDemo.java | 6 +++ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/ExcelInputStreamFactory.java b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/ExcelInputStreamFactory.java index 8150386..c1cca59 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/ExcelInputStreamFactory.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/ExcelInputStreamFactory.java @@ -30,7 +30,9 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.ArrayUtils; @@ -57,6 +59,7 @@ import com.vaadin.flow.data.binder.BeanPropertySet; import com.vaadin.flow.data.binder.PropertySet; import com.vaadin.flow.data.provider.DataProvider; +import com.vaadin.flow.function.ValueProvider; /** * @author mlopez @@ -66,6 +69,7 @@ class ExcelInputStreamFactory extends BaseInputStreamFactory { private static final Logger LOGGER = LoggerFactory.getLogger(ExcelInputStreamFactory.class); private static final String DEFAULT_TEMPLATE = "/template.xlsx"; + private static final String COLUMN_CELLSTYLE_MAP = "colum-cellstyle-map"; public ExcelInputStreamFactory(GridExporter exporter, String template) { super(exporter, template, DEFAULT_TEMPLATE); @@ -279,7 +283,7 @@ private void buildRow(T item, Sheet sheet, Cell startingCell) { configureAlignment(column.getTextAlign(), currentCell); } currentColumn[0] = currentColumn[0] + 1; - buildCell(value, currentCell, column); + buildCell(value, currentCell, column, item); }); } @@ -331,22 +335,35 @@ protected void configureAlignment(ColumnTextAlign columnTextAlign, Cell currentC } } - private void buildCell(Object value, Cell cell, Column column) { - String excelFormat = - (String) ComponentUtil.getData(column, GridExporter.COLUMN_EXCEL_FORMAT_DATA); + @SuppressWarnings("unchecked") + private void buildCell(Object value, Cell cell, Column column, T item) { + ValueProvider provider = null; + provider = (ValueProvider) ComponentUtil.getData(column, GridExporter.COLUMN_EXCEL_FORMAT_DATA_PROVIDER); + String excelFormat; + Map cellStyles = (Map) ComponentUtil.getData(column, COLUMN_CELLSTYLE_MAP); + if (cellStyles==null) { + cellStyles = new HashMap<>(); + ComponentUtil.setData(column, COLUMN_CELLSTYLE_MAP, cellStyles); + } + if (provider!=null) { + excelFormat = provider.apply(item); + } else { + excelFormat = + (String) ComponentUtil.getData(column, GridExporter.COLUMN_EXCEL_FORMAT_DATA); + } if (value == null) { PoiHelper.setBlank(cell); } else if (value instanceof Number) { excelFormat = (excelFormat!=null)?excelFormat:"0"; - applyExcelFormat(cell, excelFormat); cell.setCellValue(((Number) value).doubleValue()); + applyExcelFormat(cell, excelFormat, cellStyles); } else if (value instanceof Date) { excelFormat = (excelFormat!=null)?excelFormat:"dd/MM/yyyy"; - applyExcelFormat(cell, excelFormat); + applyExcelFormat(cell, excelFormat, cellStyles); cell.setCellValue((Date) value); } else if (value instanceof LocalDate) { excelFormat = (excelFormat!=null)?excelFormat:"dd/MM/yyyy"; - applyExcelFormat(cell, excelFormat); + applyExcelFormat(cell, excelFormat, cellStyles); cell.setCellValue( Date.from(((LocalDate) value).atStartOfDay(ZoneId.systemDefault()).toInstant())); } else { @@ -354,8 +371,16 @@ private void buildCell(Object value, Cell cell, Column column) { } } - private void applyExcelFormat(Cell cell, String excelFormat) { + private void applyExcelFormat(Cell cell, String excelFormat, Map cellStyles) { DataFormat format = cell.getSheet().getWorkbook().createDataFormat(); + if (excelFormat!=null && cellStyles.get(excelFormat)==null) { + CellStyle cs = cell.getSheet().getWorkbook().createCellStyle(); + cs.cloneStyleFrom(cell.getCellStyle()); + cellStyles.put(excelFormat, cs); + cell.setCellStyle(cs); + } else if (excelFormat!=null) { + cell.setCellStyle(cellStyles.get(excelFormat)); + } cell.getCellStyle().setDataFormat(format.getFormat(excelFormat)); } @@ -406,7 +431,7 @@ private void fillHeaderOrFooter( (isHeader ? headerOrFooter.getLeft() : transformToType(headerOrFooter.getLeft(), headerOrFooter.getRight())); - buildCell(value, cell, headerOrFooter.getRight()); + buildCell(value, cell, headerOrFooter.getRight(), null); configureAlignment(headerOrFooter.getRight().getTextAlign(), cell); sheet.setActiveCell( new CellAddress( diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java index 1566528..eb94cd1 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java @@ -64,6 +64,7 @@ public class GridExporter implements Serializable { static final String COLUMN_EXPORTED_PROVIDER_DATA = "column-value-exported-data"; static final String COLUMN_PARSING_FORMAT_PATTERN_DATA = "column-parsing-format-pattern-data"; static final String COLUMN_EXCEL_FORMAT_DATA = "column-excel-format-data"; + static final String COLUMN_EXCEL_FORMAT_DATA_PROVIDER = "column-excel-format-data-provider"; static final String COLUMN_TYPE_DATA = "column-type-data"; static final String COLUMN_TYPE_NUMBER = "number"; static final String COLUMN_TYPE_DATE = "date"; @@ -428,6 +429,23 @@ public void setNumberColumnFormat( ComponentUtil.setData(column, COLUMN_TYPE_DATA, COLUMN_TYPE_NUMBER); } + /** + * If the column is based on a String, it configures a DecimalFormat to parse a number from the + * value of the column so it can be converted to a Double, and then allows to specify the excel + * format to be applied to the cell when exported to excel with a provider, so the resulting cell + * is not a string but a number that can be used in formulas. + * + * @param column + * @param decimalFormat + * @param excelFormatProvider + */ + public void setNumberColumnFormatProvider(Column column, DecimalFormat decimalFormat, + ValueProvider excelFormatProvider) { + ComponentUtil.setData(column, COLUMN_PARSING_FORMAT_PATTERN_DATA, decimalFormat); + ComponentUtil.setData(column, COLUMN_EXCEL_FORMAT_DATA_PROVIDER, excelFormatProvider); + ComponentUtil.setData(column, COLUMN_TYPE_DATA, COLUMN_TYPE_NUMBER); + } + /** * If the column is based on a String, it configures a DateFormat to parse a date from the value * of the column so it can be converted to a java.util.Date, and then allows to specify the excel @@ -444,6 +462,23 @@ public void setDateColumnFormat(Column column, DateFormat dateFormat, String ComponentUtil.setData(column, COLUMN_TYPE_DATA, COLUMN_TYPE_DATE); } + /** + * If the column is based on a String, it configures a DateFormat to parse a date from the value + * of the column so it can be converted to a java.util.Date, and then allows to specify the excel + * format to be applied to the cell when exported to excel, so the resulting cell is not a string + * but a date that can be used in formulas. + * + * @param column + * @param dateFormat + * @param excelFormat + */ + public void setDateColumnFormatProvider(Column column, DateFormat dateFormat, + ValueProvider excelFormatProvider) { + ComponentUtil.setData(column, COLUMN_PARSING_FORMAT_PATTERN_DATA, dateFormat); + ComponentUtil.setData(column, COLUMN_EXCEL_FORMAT_DATA_PROVIDER, excelFormatProvider); + ComponentUtil.setData(column, COLUMN_TYPE_DATA, COLUMN_TYPE_DATE); + } + /** * If the column is based on a number attribute of the item, rendered with a NumberRenderer, it * configures the excel format to be applied to the cell when exported to excel, so the resulting diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java index 479bf32..a477991 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java @@ -41,6 +41,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Calendar; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -69,6 +70,10 @@ public GridExporterSimpleCustomTemplateDemo() throws EncryptedDocumentException, grid.addColumn(item -> decimalFormat.format(item.getBudget() + (item.getBudget() / 2))) .setHeader("Max. Budget") .setTextAlign(ColumnTextAlign.END); + Column quantityColumn = + grid.addColumn(item -> decimalFormat.format(item.getBudget() + (new Random().nextInt(10)))) + .setHeader("Quantity") + .setTextAlign(ColumnTextAlign.END); Column dateColumn1 = grid.addColumn( new LocalDateRenderer<>( @@ -106,6 +111,7 @@ public GridExporterSimpleCustomTemplateDemo() throws EncryptedDocumentException, exporter.setCsvExportEnabled(false); exporter.setNumberColumnFormat(minBudgetColumn, "$#.###,##"); exporter.setNumberColumnFormat(maxBudgetColumn, decimalFormat, "$#,###.##"); + exporter.setNumberColumnFormatProvider(quantityColumn, decimalFormat, (person)->person.getBudget()>50000?"#,###.## \"kg\"":"#,###.## \"l\""); exporter.setDateColumnFormat(dateColumn1, "dd/MM/yyyy"); exporter.setDateColumnFormat(dateColumn2, new SimpleDateFormat("dd/MM/yyyy"), "dd/MM/yyyy"); exporter.setFileName( From fe2331576c522513c2ec5d9d722b61c4c7e308db Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Tue, 27 Feb 2024 14:22:52 -0300 Subject: [PATCH 2/3] fix(demo): fix NPE when exporting headers --- .../gridexporter/GridExporterSimpleCustomTemplateDemo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java index a477991..aa7595d 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterSimpleCustomTemplateDemo.java @@ -111,7 +111,8 @@ public GridExporterSimpleCustomTemplateDemo() throws EncryptedDocumentException, exporter.setCsvExportEnabled(false); exporter.setNumberColumnFormat(minBudgetColumn, "$#.###,##"); exporter.setNumberColumnFormat(maxBudgetColumn, decimalFormat, "$#,###.##"); - exporter.setNumberColumnFormatProvider(quantityColumn, decimalFormat, (person)->person.getBudget()>50000?"#,###.## \"kg\"":"#,###.## \"l\""); + exporter.setNumberColumnFormatProvider(quantityColumn, decimalFormat, (person)->person==null?"": + person.getBudget()>50000?"#,###.## \"kg\"":"#,###.## \"l\""); exporter.setDateColumnFormat(dateColumn1, "dd/MM/yyyy"); exporter.setDateColumnFormat(dateColumn2, new SimpleDateFormat("dd/MM/yyyy"), "dd/MM/yyyy"); exporter.setFileName( From bdabaa74e08770a867565c026aa417dd30b70b99 Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Tue, 27 Feb 2024 16:51:32 -0300 Subject: [PATCH 3/3] build: update version to 1.9.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dd9805..ff02419 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.vaadin.addons.flowingcode grid-exporter-addon - 1.8.2-SNAPSHOT + 1.9.0-SNAPSHOT Grid Exporter Add-on Grid Exporter Add-on for Vaadin Flow