diff --git a/pom.xml b/pom.xml
index 39d877b..99b1ee5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,6 +140,11 @@
4.8.1
test
+
+ org.apache.poi
+ poi-ooxml
+ 3.17
+
diff --git a/src/main/java/org/vaadin/crudui/crud/AbstractCrud.java b/src/main/java/org/vaadin/crudui/crud/AbstractCrud.java
index 2580c58..f35d800 100755
--- a/src/main/java/org/vaadin/crudui/crud/AbstractCrud.java
+++ b/src/main/java/org/vaadin/crudui/crud/AbstractCrud.java
@@ -1,10 +1,17 @@
package org.vaadin.crudui.crud;
-import com.vaadin.ui.Composite;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.vaadin.crudui.form.CrudFormFactory;
import org.vaadin.crudui.layout.CrudLayout;
+import org.vaadin.crudui.support.BeanExcelBuilder;
+import org.vaadin.crudui.support.ExcelOnDemandStreamResource;
-import java.util.Collections;
+import com.vaadin.server.Resource;
+import com.vaadin.ui.Composite;
/**
* @author Alejandro Duarte
@@ -17,6 +24,7 @@ public abstract class AbstractCrud extends Composite implements Crud {
protected AddOperationListener addOperation = t -> null;
protected UpdateOperationListener updateOperation = t -> null;
protected DeleteOperationListener deleteOperation = t -> { };
+ protected Map exportOperations = new HashMap<>();
protected CrudLayout crudLayout;
protected CrudFormFactory crudFormFactory;
@@ -25,6 +33,13 @@ public AbstractCrud(Class domainType, CrudLayout crudLayout, CrudFormFactory<
this.domainType = domainType;
this.crudLayout = crudLayout;
this.crudFormFactory = crudFormFactory;
+ exportOperations.put("EXCEL", new ExcelOnDemandStreamResource() {
+
+ @Override
+ protected XSSFWorkbook getWorkbook() {
+ return new BeanExcelBuilder(domainType).createExcelDocument(findAllOperation.findAll());
+ }
+ });
if (crudListener != null) {
setCrudListener(crudListener);
@@ -89,4 +104,15 @@ public void setCrudListener(CrudListener crudListener) {
setFindAllOperation(crudListener::findAll);
}
+ public void addExporter(String name, Resource exporter) {
+ exportOperations.put(name, exporter);
+ }
+
+ public void removeExporter(String name) {
+ exportOperations.remove(name);
+ }
+
+ public Resource getExporter(String name) {
+ return exportOperations.get(name);
+ }
}
diff --git a/src/main/java/org/vaadin/crudui/crud/impl/GridCrud.java b/src/main/java/org/vaadin/crudui/crud/impl/GridCrud.java
index 538994c..83083fa 100755
--- a/src/main/java/org/vaadin/crudui/crud/impl/GridCrud.java
+++ b/src/main/java/org/vaadin/crudui/crud/impl/GridCrud.java
@@ -1,11 +1,8 @@
package org.vaadin.crudui.crud.impl;
-import com.vaadin.data.provider.Query;
-import com.vaadin.icons.VaadinIcons;
-import com.vaadin.ui.Button;
-import com.vaadin.ui.Component;
-import com.vaadin.ui.Grid;
-import com.vaadin.ui.Notification;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+
import org.vaadin.crudui.crud.AbstractCrud;
import org.vaadin.crudui.crud.CrudListener;
import org.vaadin.crudui.crud.CrudOperation;
@@ -15,7 +12,14 @@
import org.vaadin.crudui.layout.CrudLayout;
import org.vaadin.crudui.layout.impl.WindowBasedCrudLayout;
-import java.util.Collection;
+import com.vaadin.data.provider.Query;
+import com.vaadin.icons.VaadinIcons;
+import com.vaadin.server.FileDownloader;
+import com.vaadin.server.FontAwesome;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Notification;
/**
* @author Alejandro Duarte
@@ -32,6 +36,7 @@ public class GridCrud extends AbstractCrud {
protected Button deleteButton;
protected Grid grid;
+ protected LinkedHashMap exporterButtons = new LinkedHashMap<>();
protected Collection items;
public GridCrud(Class domainType) {
@@ -64,7 +69,7 @@ protected void initLayout() {
findAllButton.setDescription("Refresh list");
findAllButton.setIcon(VaadinIcons.REFRESH);
crudLayout.addToolbarComponent(findAllButton);
-
+
addButton = new Button("", e -> addButtonClicked());
addButton.setDescription("Add");
addButton.setIcon(VaadinIcons.PLUS);
@@ -84,6 +89,10 @@ protected void initLayout() {
grid.setSizeFull();
grid.addSelectionListener(e -> gridSelectionChanged());
crudLayout.setMainComponent(grid);
+
+ Button btn = new Button(FontAwesome.FILE_EXCEL_O.getHtml());
+ btn.setCaptionAsHtml(true);
+ addExporterMenu("EXCEL", btn);
updateButtons();
}
@@ -249,9 +258,20 @@ public void setRowCountCaption(String rowCountCaption) {
public void setSavedMessage(String savedMessage) {
this.savedMessage = savedMessage;
}
-
+
public void setDeletedMessage(String deletedMessage) {
this.deletedMessage = deletedMessage;
}
+
+ public Button getExporterMenu(String name) {
+ return exporterButtons.get(name);
+ }
+ public GridCrud addExporterMenu(String name, Button exporterButton) {
+ exporterButtons.put(name, exporterButton);
+ new FileDownloader(getExporter(name)).extend(exporterButton);
+ crudLayout.addToolbarComponent(exporterButton);
+
+ return this;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/org/vaadin/crudui/support/BeanExcelBuilder.java b/src/main/java/org/vaadin/crudui/support/BeanExcelBuilder.java
new file mode 100644
index 0000000..216a9f4
--- /dev/null
+++ b/src/main/java/org/vaadin/crudui/support/BeanExcelBuilder.java
@@ -0,0 +1,285 @@
+package org.vaadin.crudui.support;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.vaadin.data.BeanPropertySet;
+import com.vaadin.data.PropertyDefinition;
+import com.vaadin.data.PropertySet;
+import com.vaadin.shared.util.SharedUtil;
+
+public class BeanExcelBuilder {
+
+ public static final String dateCellStyleFormat = "m/d/yy";
+ public static final String dateTimeCellStyleFormat = "m/d/yy hh:mm:ss";
+
+ private Class clazz;
+ private int startCol;
+ private int startRow;
+ private int currCol;
+ private String sheetName;
+
+ private List properties;
+ private List columnsHeaders;
+
+ private Map formats = new HashMap<>();
+
+ public BeanExcelBuilder(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ protected XSSFSheet buildSheet(XSSFWorkbook wb) {
+ if (sheetName!=null) {
+ return wb.createSheet(sheetName);
+ } else {
+ return wb.createSheet();
+ }
+ }
+
+ protected XSSFWorkbook buildWorkBook() {
+ return new XSSFWorkbook();
+ }
+
+ public XSSFWorkbook createExcelDocument(Collection beans) {
+ XSSFWorkbook wb = buildWorkBook();
+ XSSFSheet s = buildSheet(wb);
+ PropertySet propertySet = BeanPropertySet.get(clazz);
+
+ int rn=0;
+
+ int cn=0;
+ XSSFRow r = s.createRow(rn++);
+
+ List headers = columnsHeaders;
+ if (headers==null) {
+ headers = propertySet.getProperties().map(pd -> SharedUtil.propertyIdToHumanFriendly(pd.getName())).collect(Collectors.toList());
+ }
+ startHeaderRow(r);
+ for (String ch : headers) {
+ XSSFCell cell = r.createCell(cn++);
+ cell.setCellValue(ch);
+ setHeaderStyle(cell, ch);
+ }
+ endHeaderRow(r);
+
+ for(T bean: beans) {
+ r = s.createRow(rn++);
+ cn=0;
+
+ currCol = startCol;
+ doStarRow(bean, r);
+ List props = properties;
+ if (props==null) {
+ props = propertySet.getProperties().map(pd -> pd.getName()).collect(Collectors.toList());
+ }
+ for(String propertyName : props) {
+
+ PropertyDefinition definition = propertySet
+ .getProperty(propertyName)
+ .orElseThrow(() -> new IllegalArgumentException(
+ "Could not resolve property name " + propertyName
+ ));
+
+ Object value = definition.getGetter().apply(bean);
+
+ Cell cell = buildCell(r, currCol, definition);
+ setCellValue(cell, value, definition);
+ setCellStyle(cell, value, definition, formats.get(propertyName));
+ currCol++;
+
+ }
+ doEndRow(bean, r);
+
+ }
+
+ return wb;
+ }
+
+ protected void endHeaderRow(XSSFRow r) {
+ }
+
+ protected void startHeaderRow(XSSFRow r) {
+ }
+
+ protected void setHeaderStyle(XSSFCell cell, String headerName) {
+ }
+
+ protected void doEndRow(T object, Row row) {
+
+ }
+
+ protected Cell buildCell(Row row, int colNumber, PropertyDefinition definition) {
+ Cell cell = row.createCell(colNumber);
+
+ return cell;
+ }
+
+ protected void doStarRow(T object, Row row) {
+ }
+
+ protected CellStyle getCellStyle(Cell cell, String format) {
+ Workbook wb = cell.getRow().getSheet().getWorkbook();
+ CreationHelper createHelper = wb.getCreationHelper();
+ CellStyle dateCellStyle = wb.createCellStyle();
+ dateCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(format));
+
+ return dateCellStyle;
+ }
+
+ protected void setCellValue(Cell cell, Object value, PropertyDefinition definition) {
+
+ if (String.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.STRING);
+ } else if (Date.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (Double.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (Number.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (Boolean.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.BOOLEAN);
+ } else if (LocalDate.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (LocalDateTime.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (definition.getType().isEnum()) {
+ cell.setCellType(CellType.STRING);
+ } else if (Calendar.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.NUMERIC);
+ } else if (RichTextString.class.isAssignableFrom(definition.getType())) {
+ cell.setCellType(CellType.STRING);
+ }
+
+
+ if (value==null)
+ return;
+
+ if (String.class.isInstance(value)) {
+ cell.setCellValue((String) value);
+ } else if (Date.class.isInstance(value)) {
+ cell.setCellValue((Date) value);
+ } else if (Double.class.isInstance(value)) {
+ cell.setCellValue((Double) value);
+ } else if (Number.class.isInstance(value)) {
+ cell.setCellValue(((Number) value).doubleValue());
+ } else if (Boolean.class.isInstance(value)) {
+ cell.setCellValue((Boolean) value);
+
+ } else if (LocalDate.class.isInstance(value)) {
+ cell.setCellValue(DateUtil.getExcelDate(java.sql.Date.valueOf((LocalDate)value)));
+ } else if (LocalDateTime.class.isInstance(value)) {
+ cell.setCellValue(DateUtil.getExcelDate(java.sql.Timestamp.valueOf((LocalDateTime)value)));
+
+ } else if (definition.getType().isEnum()) {
+ cell.setCellValue(((Enum>)value).name());
+
+ } else if (Calendar.class.isInstance(value)) {
+ cell.setCellValue((Calendar) value);
+ } else if (RichTextString.class.isInstance(value)) {
+ cell.setCellValue((RichTextString) value);
+ }
+
+ }
+
+ protected void setCellStyle(Cell cell, Object value, PropertyDefinition definition, String format) {
+
+ CellStyle cs = null;
+
+ if (format==null) {
+
+ if (String.class.isAssignableFrom(definition.getType())) {
+ } else if (Date.class.isAssignableFrom(definition.getType())) {
+ cs = getCellStyle(cell, dateTimeCellStyleFormat);
+ } else if (Double.class.isAssignableFrom(definition.getType())) {
+ } else if (Number.class.isAssignableFrom(definition.getType())) {
+ } else if (Boolean.class.isAssignableFrom(definition.getType())) {
+ } else if (LocalDate.class.isAssignableFrom(definition.getType())) {
+ cs = getCellStyle(cell, dateCellStyleFormat);
+ } else if (LocalDateTime.class.isAssignableFrom(definition.getType())) {
+ cs = getCellStyle(cell, dateTimeCellStyleFormat);
+ } else if (definition.getType().isEnum()) {
+ } else if (Calendar.class.isAssignableFrom(definition.getType())) {
+ cs = getCellStyle(cell, dateTimeCellStyleFormat);
+ } else if (RichTextString.class.isAssignableFrom(definition.getType())) {
+ }
+ } else {
+ cs = getCellStyle(cell, format);
+ }
+
+ if (cs!=null)
+ cell.setCellStyle(cs);
+ }
+
+ public int getStartCol() {
+ return startCol;
+ }
+
+ public void setStartCol(int startCol) {
+ this.startCol = startCol;
+ }
+
+ public int getStartRow() {
+ return startRow;
+ }
+
+ public void setStartRow(int startRow) {
+ this.startRow = startRow;
+ }
+
+ public Collection getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List properties) {
+ this.properties = properties;
+ }
+
+ public void setProperties(String... properties) {
+ this.properties = new ArrayList<>();
+ Collections.addAll(this.properties, properties);
+ }
+
+ public Collection getColumnsHeaders() {
+ return columnsHeaders;
+ }
+
+ public void setColumnsHeaders(List columnsHeaders) {
+ this.columnsHeaders = columnsHeaders;
+ }
+
+ public void setColumnsHeaders(String... columnsHeaders) {
+ this.columnsHeaders = new ArrayList<>();
+ Collections.addAll(this.columnsHeaders, columnsHeaders);
+ }
+
+ public Map getFormats() {
+ return formats;
+ }
+
+ public void setFormats(Map formats) {
+ this.formats = formats;
+ }
+
+}
diff --git a/src/main/java/org/vaadin/crudui/support/ExcelOnDemandStreamResource.java b/src/main/java/org/vaadin/crudui/support/ExcelOnDemandStreamResource.java
new file mode 100644
index 0000000..784eb13
--- /dev/null
+++ b/src/main/java/org/vaadin/crudui/support/ExcelOnDemandStreamResource.java
@@ -0,0 +1,41 @@
+package org.vaadin.crudui.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.vaadin.server.ConnectorResource;
+import com.vaadin.server.DownloadStream;
+
+public abstract class ExcelOnDemandStreamResource implements ConnectorResource {
+
+ protected abstract XSSFWorkbook getWorkbook();
+
+ @Override
+ public DownloadStream getStream() {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ getWorkbook().write(baos);
+ baos.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return new DownloadStream(new ByteArrayInputStream(baos.toByteArray()), getMIMEType(), getFilename());
+ }
+
+ @Override
+ public String getFilename() {
+ return "export.xlsx";
+ }
+
+ @Override
+ public String getMIMEType() {
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ }
+
+
+}