Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cell styles and data formats #29

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ Exporter.exportAsExcel(grid, gridHeaderMap)

Exporter works as using reflection to read property from each item, any column without a valid key will be ignored.

When grid is created using bean type create map where Column is key and header text is value. Pass that map as argument to Exporter subclass constructor.
If map is null then default column key will be used for creating column header in excel.
```
private Map<Column<MyEntity>, String> gridHeaderMap;
...
Exporter.exportAsExcel(grid, gridHeaderMap)
```

In case when grid is created without bean type (adding columns with value providers) then map with column headers is mandatory.

### Data formats, excel built-in and custom data formats

By default excel exporter will use default excel data formats for date, time and numbers. To set custom formats:
```
DataFormats formats = new TypeDataFormats();
formats.localDateFormat("DDD-MMM-YY");
Exporter.exportAsExcel(grid, gridHeaderMap, formats);
```


## Development instructions

Starting the test/demo server:
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/vaadin/haijian/CellValueTypeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.vaadin.haijian;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;

import org.apache.poi.ss.usermodel.Cell;

/**
*
* @author Krunoslav Magazin
* Oct 5, 2019
*/
public class CellValueTypeConverter {

public void setValue(Cell cell, LocalDateTime localDateTime) {
cell.setCellValue(convertLocalDateTimeToDate(localDateTime));
}

public void setValue(Cell cell, LocalDate localDateTime) {
cell.setCellValue(convertLocalDateToDate(localDateTime));
}

public void setValue(Cell cell, Calendar localDateTime) {
cell.setCellValue(localDateTime.getTime());
}

public Date convertLocalDateToDate(LocalDate dateToConvert) {
return Date.from(dateToConvert.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}

public Date convertLocalDateTimeToDate(LocalDateTime dateToConvert) {
return Date.from(dateToConvert.atZone(ZoneId.systemDefault()).toInstant());
}

}
24 changes: 24 additions & 0 deletions src/main/java/org/vaadin/haijian/DataFormats.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.vaadin.haijian;

import java.util.Map;

/**
*
* @author Krunoslav Magazin
* Oct 5, 2019
*/
public interface DataFormats {

void numberFormat(Class<? extends Number> clazz, String format);

void localDateTimeFormat( String format);

void localDateFormat( String format);

void calendarFormat( String format);

void dateFormat( String format);

public Map<Class<?>, String> getTypeFormatsMap();

}
86 changes: 75 additions & 11 deletions src/main/java/org/vaadin/haijian/ExcelFileBuilder.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
package org.vaadin.haijian;

import com.vaadin.flow.component.grid.Grid;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.LoggerFactory;

import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.LoggerFactory;

import com.vaadin.flow.component.grid.Grid;

/**
*
* Modified Oct 5, 2019 by Krunoslav Magazin - added data formats and type
* conversion
*
*/
public class ExcelFileBuilder<T> extends FileBuilder<T> {
private static final String EXCEL_FILE_EXTENSION = ".xls";

Expand All @@ -20,8 +36,30 @@ public class ExcelFileBuilder<T> extends FileBuilder<T> {
private Cell cell;
private CellStyle boldStyle;

private DataFormats dataFormats;
private CellValueTypeConverter converter;
private WorkbookStyles styles;

ExcelFileBuilder(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders) {
super(grid, columnHeaders);
this.dataFormats = new TypeDataFormats();
this.converter = new CellValueTypeConverter();
}

ExcelFileBuilder(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, DataFormats dataFormats) {
this(grid, columnHeaders);
this.dataFormats = dataFormats;
}

ExcelFileBuilder(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, CellValueTypeConverter converter) {
this(grid, columnHeaders);
this.converter = converter;
}

ExcelFileBuilder(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, DataFormats dataFormats,
CellValueTypeConverter converter) {
this(grid, columnHeaders, dataFormats);
this.converter = converter;
}

@Override
Expand Down Expand Up @@ -58,17 +96,37 @@ protected void buildCell(Object value) {
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
} else if (value instanceof Number) {
buildNumericCell((Number) value);
} else if (value instanceof Calendar) {
Calendar calendar = (Calendar) value;
cell.setCellValue(calendar.getTime());
cell.setCellType(Cell.CELL_TYPE_STRING);
} else if (value instanceof Double) {
cell.setCellValue((Double) value);
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
converter.setValue(cell, (Calendar) value);
} else if (value instanceof Date) {
Date date = (Date) value;
cell.setCellValue(date);
} else if (value instanceof LocalDate) {
converter.setValue(cell, (LocalDate) value);
} else if (value instanceof LocalDateTime) {
converter.setValue(cell, (LocalDateTime) value);
} else {
cell.setCellValue(value.toString());
cell.setCellType(Cell.CELL_TYPE_STRING);
}

styles.setStyle(cell, value.getClass());

}

protected void buildNumericCell(Number value) {
cell.setCellType(Cell.CELL_TYPE_NUMERIC);

if (value instanceof BigDecimal) {
cell.setCellValue(((BigDecimal) value).doubleValue());
} else {
cell.setCellValue(value.doubleValue());
}

styles.setStyle(cell, value.getClass());

}

@Override
Expand Down Expand Up @@ -104,5 +162,11 @@ protected void resetContent() {
row = null;
cell = null;
boldStyle = null;

createCellStyleDataFormats();
}

private void createCellStyleDataFormats() {
styles = new WorkbookStyles(workbook, dataFormats);
}
}
28 changes: 25 additions & 3 deletions src/main/java/org/vaadin/haijian/Exporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,35 @@

public class Exporter {

private Exporter(){}
private Exporter() {
}

public static <T> InputStreamFactory exportAsExcel(Grid<T> grid) {
return new ExcelFileBuilder<>(grid, null)::build;
}

public static <T> InputStreamFactory exportAsExcel(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders){
public static <T> InputStreamFactory exportAsExcel(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders) {
return new ExcelFileBuilder<>(grid, columnHeaders)::build;
}

public static <T> InputStreamFactory exportAsCSV(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders){
public static <T> InputStreamFactory exportAsExcel(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, DataFormats dataFormats) {
return new ExcelFileBuilder<>(grid, columnHeaders, dataFormats)::build;
}

public static <T> InputStreamFactory exportAsExcel(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, CellValueTypeConverter converter) {
return new ExcelFileBuilder<>(grid, columnHeaders, converter)::build;
}

public static <T> InputStreamFactory exportAsExcel(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders, DataFormats dataFormats,
CellValueTypeConverter converter) {
return new ExcelFileBuilder<>(grid, columnHeaders, dataFormats, converter)::build;
}

public static <T> InputStreamFactory exportAsCSV(Grid<T> grid) {
return new CSVFileBuilder<>(grid, null)::build;
}

public static <T> InputStreamFactory exportAsCSV(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders) {
return new CSVFileBuilder<>(grid, columnHeaders)::build;
}
}
79 changes: 42 additions & 37 deletions src/main/java/org/vaadin/haijian/FileBuilder.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package org.vaadin.haijian;

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.data.binder.BeanPropertySet;
import com.vaadin.flow.data.binder.PropertyDefinition;
import com.vaadin.flow.data.binder.PropertySet;
import com.vaadin.flow.data.provider.DataCommunicator;
import com.vaadin.flow.data.provider.Query;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
Expand All @@ -20,19 +12,28 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.LoggerFactory;

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.data.binder.BeanPropertySet;
import com.vaadin.flow.data.binder.PropertyDefinition;
import com.vaadin.flow.data.binder.PropertySet;
import com.vaadin.flow.data.provider.DataCommunicator;
import com.vaadin.flow.data.provider.Query;

public abstract class FileBuilder<T> {
private static final String TMP_FILE_NAME = "tmp";

File file;
private Grid<T> grid;
private Map<Grid.Column<T>, String> columnHeaders;
private Map<Grid.Column<T>, String> customColumnHeaders;
private Collection<Grid.Column> columns;
private PropertySet<T> propertySet;

@SuppressWarnings("unchecked")
FileBuilder(Grid<T> grid, Map<Grid.Column<T>, String> columnHeaders) {
this.grid = grid;
this.columnHeaders = columnHeaders;
this.customColumnHeaders = columnHeaders;
columns = grid.getColumns().stream().filter(this::isExportable).collect(Collectors.toList());
try {
Field field = Grid.class.getDeclaredField("propertySet");
Expand All @@ -41,6 +42,9 @@ public abstract class FileBuilder<T> {
if (propertySetRaw != null) {
propertySet = (PropertySet<T>) propertySetRaw;
}
if (propertySet == null && columnHeaders == null) {
throw new ExporterException("Create Grid with bean type or create Grid with adding columns and columns headers map.");
}
} catch (Exception e) {
throw new ExporterException("couldn't read propertyset information from grid", e);
}
Expand Down Expand Up @@ -83,29 +87,32 @@ protected void resetContent() {
}

private void buildHeaderRow() {
onNewRow();
if (columnHeaders == null) {
columns.forEach(column -> {
Optional<PropertyDefinition<T, ?>> propertyDefinition = propertySet.getProperty(column.getKey());
if (propertyDefinition.isPresent()) {
onNewCell();
buildColumnHeaderCell(propertyDefinition.get().getCaption());
} else {
LoggerFactory.getLogger(this.getClass()).warn(String.format("Column key %s is a property which cannot be found", column.getKey()));
}
});
} else {
columns.forEach(column -> {
String columnHeader = columnHeaders.get(column);
if (columnHeader != null) {
onNewCell();
buildColumnHeaderCell(columnHeader);
} else {
LoggerFactory.getLogger(this.getClass()).warn(String.format("Column with key %s have not column header value defined in map", column.getKey()));
}
});
onNewRow();
if (customColumnHeaders == null) {
columns.forEach(column -> {
// when grid created with bean type
if (propertySet != null) {
Optional<PropertyDefinition<T, ?>> propertyDefinition = propertySet.getProperty(column.getKey());
if (propertyDefinition.isPresent()) {
onNewCell();
buildColumnHeaderCell(propertyDefinition.get().getCaption());
} else {
LoggerFactory.getLogger(this.getClass()).warn(String.format("Column key %s is a property which cannot be found", column.getKey()));
}
}
});
} else {
columns.forEach(column -> {
String columnHeader = customColumnHeaders.get(column);
if (columnHeader != null) {
onNewCell();
buildColumnHeaderCell(columnHeader);
} else {
LoggerFactory.getLogger(this.getClass()).warn(String.format("Column with key %s have not column header value defined in map", column.getKey()));
}
});
}
}
}


void buildColumnHeaderCell(String header) {
Expand Down Expand Up @@ -177,14 +184,12 @@ int getNumberOfColumns() {
return columns.size();
}

@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings({ "unchecked", "rawtypes" })
private Stream<T> getDataStream(Query newQuery) {
Stream<T> stream = grid.getDataProvider().fetch(newQuery);
if (stream.isParallel()) {
LoggerFactory.getLogger(DataCommunicator.class)
.debug("Data provider {} has returned "
+ "parallel stream on 'fetch' call",
grid.getDataProvider().getClass());
LoggerFactory.getLogger(DataCommunicator.class).debug("Data provider {} has returned " + "parallel stream on 'fetch' call",
grid.getDataProvider().getClass());
stream = stream.collect(Collectors.toList()).stream();
assert !stream.isParallel();
}
Expand Down
Loading