blob: 1867901a2ade938ebf49fdc8a66e0b33d7e20e08 [file] [log] [blame]
/**
* Copyright 2011-2019 Jonathan Nash and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Jonathan Nash - initial implementation
* Compex Systemhaus GmbH - adaptions for OSBP
*/
package org.eclipse.osbp.xtext.table.common.export;
import com.vaadin.data.Container;
import com.vaadin.data.Property;
import com.vaadin.data.util.ObjectProperty;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.util.IOUtils;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService.ThemeResourceType;
import org.eclipse.osbp.xtext.table.common.CellSetImage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tepi.filtertable.FilterTable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* The Class ExcelExport. Implementation of TableExport to export Vaadin Tables to Excel .xls files.
*
* @author jnash
* @version $Revision: 1.2 $
*/
public class ExcelExport extends TableExport {
/**
* The Constant serialVersionUID.
*/
private static final long serialVersionUID = -8404407996727936497L;
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExport.class);
/**
* The name of the sheet in the workbook the table contents will be written to.
*/
protected String sheetName;
/**
* The title of the "report" of the table contents.
*/
protected String reportTitle;
/**
* The filename of the workbook that will be sent to the user.
*/
protected String exportFileName;
/**
* Flag indicating whether we will add a totals row to the FilterTable. A totals row in the FilterTable is
* typically implemented as a footer and therefore is not part of the data source.
*/
protected boolean displayTotals;
/**
* Flag indicating whether the first column should be treated as row headers. They will then be
* formatted either like the column headers or a special row headers CellStyle can be specified.
*/
protected boolean rowHeaders = false;
/**
* Flag indicating whether we should use table.formatPropertyValue() as the cell value instead
* of the property value using the specified data formats.
*/
protected boolean useTableFormatPropertyValue = false;
/**
* The workbook that contains the sheet containing the report with the table contents.
*/
protected final transient Workbook workbook;
/**
* The Sheet object that will contain the table contents report.
*/
protected transient Sheet sheet;
protected transient Sheet hierarchicalTotalsSheet = null;
/**
* The POI cell creation helper.
*/
protected transient CreationHelper createHelper;
protected transient DataFormat dataFormat;
protected transient Drawing drawing;
/**
* Various styles that are used in report generation. These can be set by the user if the
* default style is not desired to be used.
*/
protected transient CellStyle dateCellStyle;
protected transient CellStyle doubleCellStyle;
protected transient CellStyle integerCellStyle;
protected transient CellStyle totalsDoubleCellStyle;
protected transient CellStyle totalsIntegerCellStyle;
protected transient CellStyle columnHeaderCellStyle;
protected transient CellStyle titleCellStyle;
protected Short dateDataFormat;
protected Short doubleDataFormat;
protected Short integerDataFormat;
protected transient Map<Short, CellStyle> dataFormatCellStylesMap = new HashMap<>();
/**
* The default row header style is null and, if row headers are specified with
* setRowHeaders(true), then the column headers style is used. setRowHeaderStyle() allows the
* user to specify a different row header style.
*/
protected transient CellStyle rowHeaderCellStyle = null;
/**
* The totals row.
*/
protected transient Row titleRow;
protected transient Row headerRow;
protected transient Row totalsRow;
protected transient Row hierarchicalTotalsRow;
// This let's the user specify the data format of the property in case the formatting of the property
// will not be properly identified by the class of the property. In this case, the specified format is
// used. However, all other cell stylings will be those of the
protected transient Map<Object, String> propertyExcelFormatMap = new HashMap<>();
/**
* At minimum, we need a FilterTable to export. Everything else has default settings.
*
* @param table the table
*/
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService) {
this(table, themeResourceService, null);
}
/**
* Instantiates a new TableExport class.
*
* @param table the table
* @param sheetName the sheet name
*/
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService, final String sheetName) {
this(table, themeResourceService, sheetName, null);
}
/**
* Instantiates a new TableExport class.
*
* @param table the table
* @param sheetName the sheet name
* @param reportTitle the report title
*/
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService, final String sheetName, final String reportTitle) {
this(table, themeResourceService, sheetName, reportTitle, null);
}
/**
* Instantiates a new TableExport class.
*
* @param table the table
* @param sheetName the sheet name
* @param reportTitle the report title
* @param exportFileName the export file name
*/
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService, final String sheetName, final String reportTitle,
final String exportFileName) {
this(table, themeResourceService, sheetName, reportTitle, exportFileName, true);
}
/**
* Instantiates a new TableExport class. This is the final constructor that all other
* constructors end up calling. If the other constructors were called then they pass in the
* default parameters.
*
* @param table the table
* @param sheetName the sheet name
* @param reportTitle the report title
* @param exportFileName the export file name
* @param hasTotalsRow flag indicating whether we should create a totals row
*/
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService, final String sheetName, final String reportTitle,
final String exportFileName, final boolean hasTotalsRow) {
this(table, themeResourceService, new HSSFWorkbook(), sheetName, reportTitle, exportFileName, hasTotalsRow);
}
public ExcelExport(final FilterTable table, IThemeResourceService themeResourceService, final Workbook wkbk, final String shtName, final String rptTitle,
final String xptFileName, final boolean hasTotalsRow) {
super(table, themeResourceService);
workbook = wkbk;
init(shtName, rptTitle, xptFileName, hasTotalsRow);
format();
}
/**
* At minimum, we need a FilterTable to export. Everything else has default settings.
*
* @param tableHolder the tableHolder
*/
public ExcelExport(final TableHolder tableHolder) {
this(tableHolder, null);
}
/**
* Instantiates a new TableExport class.
*
* @param tableHolder the tableHolder
* @param sheetName the sheet name
*/
public ExcelExport(final TableHolder tableHolder, final String sheetName) {
this(tableHolder, sheetName, null);
}
/**
* Instantiates a new TableExport class.
*
* @param tableHolder the tableHolder
* @param sheetName the sheet name
* @param reportTitle the report title
*/
public ExcelExport(final TableHolder tableHolder, final String sheetName, final String reportTitle) {
this(tableHolder, sheetName, reportTitle, null);
}
/**
* Instantiates a new TableExport class.
*
* @param tableHolder the tableHolder
* @param sheetName the sheet name
* @param reportTitle the report title
* @param exportFileName the export file name
*/
public ExcelExport(final TableHolder tableHolder, final String sheetName, final String reportTitle,
final String exportFileName) {
this(tableHolder, sheetName, reportTitle, exportFileName, true);
}
/**
* Instantiates a new TableExport class. This is the final constructor that all other
* constructors end up calling. If the other constructors were called then they pass in the
* default parameters.
*
* @param tableHolder the tableHolder
* @param sheetName the sheet name
* @param reportTitle the report title
* @param exportFileName the export file name
* @param hasTotalsRow flag indicating whether we should create a totals row
*/
public ExcelExport(final TableHolder tableHolder, final String sheetName, final String reportTitle,
final String exportFileName, final boolean hasTotalsRow) {
this(tableHolder, new HSSFWorkbook(), sheetName, reportTitle, exportFileName, hasTotalsRow);
}
public ExcelExport(final TableHolder tableHolder, final Workbook wkbk, final String shtName,
final String rptTitle, final String xptFileName, final boolean hasTotalsRow) {
super(tableHolder);
workbook = wkbk;
init(shtName, rptTitle, xptFileName, hasTotalsRow);
}
private void init(final String shtName, final String rptTitle, final String xptFileName,
final boolean hasTotalsRow) {
if ((null == shtName) || ("".equals(shtName))) {
sheetName = "FilterTable Export";
} else {
sheetName = shtName;
}
if (null == rptTitle) {
reportTitle = "";
} else {
reportTitle = rptTitle;
}
if ((null == xptFileName) || ("".equals(xptFileName))) {
exportFileName = "FilterTable-Export.xls";
} else {
exportFileName = xptFileName;
}
displayTotals = hasTotalsRow;
sheet = workbook.createSheet(sheetName);
createHelper = workbook.getCreationHelper();
dataFormat = workbook.createDataFormat();
drawing = sheet.createDrawingPatriarch();
dateDataFormat = defaultDateDataFormat();
doubleDataFormat = defaultDoubleDataFormat();
integerDataFormat = defaultIntegerDataFormat();
doubleCellStyle = defaultDataCellStyle(workbook);
doubleCellStyle.setDataFormat(doubleDataFormat);
dataFormatCellStylesMap.put(doubleDataFormat, doubleCellStyle);
integerCellStyle = defaultDataCellStyle(workbook);
integerCellStyle.setDataFormat(integerDataFormat);
dataFormatCellStylesMap.put(integerDataFormat, integerCellStyle);
dateCellStyle = defaultDataCellStyle(workbook);
dateCellStyle.setDataFormat(dateDataFormat);
dataFormatCellStylesMap.put(dateDataFormat, dateCellStyle);
totalsDoubleCellStyle = defaultTotalsDoubleCellStyle(workbook);
totalsIntegerCellStyle = defaultTotalsIntegerCellStyle(workbook);
columnHeaderCellStyle = defaultHeaderCellStyle(workbook);
titleCellStyle = defaultTitleCellStyle(workbook);
}
/*
* Set a new table to be exported in another workbook tab / sheet.
*/
public void setNextTable(final FilterTable table, final String sheetName) {
setTable(table);
sheet = workbook.createSheet(sheetName);
}
public void setNextTableHolder(final TableHolder tableHolder, final String sheetName) {
setTableHolder(tableHolder);
sheet = workbook.createSheet(sheetName);
}
/*
* This will exclude columns from the export that are not visible due to them being collapsed.
* This should be called before convertTable() is called.
*/
public void excludeCollapsedColumns() {
final Iterator<Object> iterator = getPropIds().iterator();
while (iterator.hasNext()) {
final Object propId = iterator.next();
if (getTableHolder().isColumnCollapsed(propId)) {
iterator.remove();
}
}
}
/**
* Creates the workbook containing the exported table data, without exporting it to the user.
*/
@Override
public void convertTable() {
final int startRow;
// initial setup
initialSheetSetup();
// add title row
startRow = addTitleRow();
int row = startRow;
// add header row
addHeaderRow(row);
row++;
// add data rows
if (isHierarchical()) {
row = addHierarchicalDataRows(sheet, row);
} else {
row = addDataRows(sheet, row);
}
// add totals row
if (displayTotals) {
addTotalsRow(row, startRow);
}
// final sheet format before export
finalSheetFormat();
}
/**
* Export the workbook to the end-user.
* <p/>
* Code obtained from: http://vaadin.com/forum/-/message_boards/view_message/159583
*
* @return true, if successful
*/
@Override
public boolean sendConverted() {
File tempFile = null;
FileOutputStream fileOut = null;
try {
tempFile = File.createTempFile("tmp", ".xls");
fileOut = new FileOutputStream(tempFile);
workbook.write(fileOut);
if (null == mimeType) {
setMimeType(EXCEL_MIME_TYPE);
}
return super.sendConvertedFileToUser(getTableHolder().getUI(), tempFile, getReportTitle()+".xls");
} catch (final IOException e) {
LOGGER.error("Converting to XLS failed with IOException {}", e);
return false;
} finally {
if(tempFile != null) {
tempFile.deleteOnExit();
}
try {
if(fileOut != null) {
fileOut.close();
}
} catch (final IOException e) {
// nothing to do
}
}
}
/**
* Initial sheet setup. Override this method to specifically change initial, sheet-wide,
* settings.
*/
protected void initialSheetSetup() {
final PrintSetup printSetup = sheet.getPrintSetup();
printSetup.setLandscape(true);
sheet.setFitToPage(true);
sheet.setHorizontallyCenter(true);
if ((isHierarchical()) && (displayTotals)) {
hierarchicalTotalsSheet = workbook.createSheet("tempHts");
}
}
/**
* Adds the title row. Override this method to change title-related aspects of the workbook.
* Alternately, the title Row Object is accessible via getTitleRow() after report creation. To
* change title text use setReportTitle(). To change title CellStyle use setTitleStyle().
*
* @return the int
*/
protected int addTitleRow() {
if ((null == reportTitle) || ("".equals(reportTitle))) {
return 0;
}
titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(45);
final Cell titleCell;
final CellRangeAddress cra;
if (rowHeaders) {
titleCell = titleRow.createCell(1);
cra = new CellRangeAddress(0, 0, 1, getPropIds().size() - 1);
sheet.addMergedRegion(cra);
} else {
titleCell = titleRow.createCell(0);
cra = new CellRangeAddress(0, 0, 0, getPropIds().size() - 1);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, getPropIds().size() - 1));
}
titleCell.setCellValue(reportTitle);
titleCell.setCellStyle(titleCellStyle);
// cell borders don't work on merged ranges so, if there are borders
// we apply them to the merged range here.
if (titleCellStyle.getBorderLeft() != CellStyle.BORDER_NONE) {
RegionUtil.setBorderLeft(titleCellStyle.getBorderLeft(), cra, sheet, workbook);
}
if (titleCellStyle.getBorderRight() != CellStyle.BORDER_NONE) {
RegionUtil.setBorderRight(titleCellStyle.getBorderRight(), cra, sheet, workbook);
}
if (titleCellStyle.getBorderTop() != CellStyle.BORDER_NONE) {
RegionUtil.setBorderTop(titleCellStyle.getBorderTop(), cra, sheet, workbook);
}
if (titleCellStyle.getBorderBottom() != CellStyle.BORDER_NONE) {
RegionUtil.setBorderBottom(titleCellStyle.getBorderBottom(), cra, sheet, workbook);
}
return 1;
}
/**
* Adds the header row. Override this method to change header-row-related aspects of the
* workbook. Alternately, the header Row Object is accessible via getHeaderRow() after report
* creation. To change header CellStyle, though, use setHeaderStyle().
*
* @param row the row
*/
protected void addHeaderRow(final int row) {
headerRow = sheet.createRow(row);
Cell headerCell;
Object propId;
headerRow.setHeightInPoints(40);
for (int col = 0; col < getPropIds().size(); col++) {
propId = getPropIds().get(col);
headerCell = headerRow.createCell(col);
headerCell.setCellValue(createHelper.createRichTextString(getTableHolder().getColumnHeader(propId)));
headerCell.setCellStyle(getColumnHeaderStyle(row, col));
final Short poiAlignment = getTableHolder().getCellAlignment(propId);
CellUtil.setAlignment(headerCell, workbook, poiAlignment);
}
}
/**
* This method is called by addTotalsRow() to determine what CellStyle to use. By default we
* just return totalsCellStyle which is either set to the default totals style, or can be
* overriden by the user using setTotalsStyle(). However, if the user wants to have different
* total items have different styles, then this method should be overriden. The parameters
* passed in are all potentially relevant items that may be used to determine what formatting to
* return, that are not accessible globally.
*
* @param row the row
* @param col the current column
* @return the header style
*/
protected CellStyle getColumnHeaderStyle(final int row, final int col) {
if ((rowHeaders) && (col == 0)) {
return titleCellStyle;
}
return columnHeaderCellStyle;
}
/**
* For Hierarchical Containers, this method recursively adds root items and child items. The
* child items are appropriately grouped using grouping/outlining sheet functionality. Override
* this method to make any changes. To change the CellStyle used for all FilterTable data use
* setDataStyle(). For different data cells to have different CellStyles, override
* getDataStyle().
*
* @param row the row
* @return the int
*/
protected int addHierarchicalDataRows(final Sheet sheetToAddTo, final int row) {
final Collection<?> roots;
int localRow = row;
roots = ((Container.Hierarchical) getTableHolder().getContainerDataSource()).rootItemIds();
/*
* For Hierarchical Containers, the outlining/grouping in the sheet is with the summary row
* at the top and the grouped/outlined subcategories below.
*/
sheet.setRowSumsBelow(false);
int count = 0;
for (final Object rootId : roots) {
count = addDataRowRecursively(sheetToAddTo, rootId, localRow);
// for totals purposes, we just want to add rootIds which contain totals
// so we store just the totals in a separate sheet.
if (displayTotals) {
addDataRow(hierarchicalTotalsSheet, rootId, localRow);
}
if (count > 1) {
sheet.groupRow(localRow + 1, (localRow + count) - 1);
sheet.setRowGroupCollapsed(localRow + 1, true);
}
localRow = localRow + count;
}
return localRow;
}
/**
* this method adds row items for non-Hierarchical Containers. Override this method to make any
* changes. To change the CellStyle used for all FilterTable data use setDataStyle(). For different
* data cells to have different CellStyles, override getDataStyle().
*
* @param row the row
* @return the int
*/
protected int addDataRows(final Sheet sheetToAddTo, final int row) {
final Collection<?> itemIds = getTableHolder().getContainerDataSource().getItemIds();
int localRow = row;
int count = 0;
for (final Object itemId : itemIds) {
addDataRow(sheetToAddTo, itemId, localRow);
count = 1;
if (count > 1) {
sheet.groupRow(localRow + 1, (localRow + count) - 1);
sheet.setRowGroupCollapsed(localRow + 1, true);
}
localRow = localRow + count;
}
return localRow;
}
/**
* Used by addHierarchicalDataRows() to implement the recursive calls.
*
* @param rootItemId the root item id
* @param row the row
* @return the int
*/
private int addDataRowRecursively(final Sheet sheetToAddTo, final Object rootItemId, final int row) {
int numberAdded = 0;
int localRow = row;
addDataRow(sheetToAddTo, rootItemId, row);
numberAdded++;
if (((Container.Hierarchical) getTableHolder().getContainerDataSource()).hasChildren(rootItemId)) {
final Collection<?> children = ((Container.Hierarchical) getTableHolder().getContainerDataSource())
.getChildren(rootItemId);
for (final Object child : children) {
localRow++;
numberAdded = numberAdded + addDataRowRecursively(sheetToAddTo, child, localRow);
}
}
return numberAdded;
}
/**
* This method is ultimately used by either addDataRows() or addHierarchicalDataRows() to
* actually add the data to the Sheet.
*
* @param rootItemId the root item id
* @param row the row
*/
protected void addDataRow(final Sheet sheetToAddTo, final Object rootItemId, final int row) {
final Row sheetRow = sheetToAddTo.createRow(row);
Property<?> prop;
Object propId;
Object value;
Cell sheetCell;
for (int col = 0; col < getPropIds().size(); col++) {
propId = getPropIds().get(col);
prop = getProperty(rootItemId, propId);
if (null == prop) {
value = null;
} else {
value = prop.getValue();
}
sheetCell = sheetRow.createCell(col);
final CellStyle cs = getCellStyle(rootItemId, row, col, false);
sheetCell.setCellStyle(cs);
final Short poiAlignment = getTableHolder().getCellAlignment(propId);
CellUtil.setAlignment(sheetCell, workbook, poiAlignment);
if (null != value) {
if (!isNumeric(prop.getType())) {
if (java.util.Date.class.isAssignableFrom(prop.getType())) {
sheetCell.setCellValue((Date) value);
} else if(CellSetImage.class.isAssignableFrom(prop.getType())) {
ClientAnchor anchor = createHelper.createClientAnchor();
anchor.setAnchorType( ClientAnchor.MOVE_AND_RESIZE );
InputStream stream = getThemeResourceService().getThemeResourceInputStream(((CellSetImage)prop.getValue()).getResourceName(), ThemeResourceType.ICON);
try {
int pictureIndex = workbook.addPicture(IOUtils.toByteArray(stream), Workbook.PICTURE_TYPE_PNG);
anchor.setCol1( col );
anchor.setRow1( row );
anchor.setRow2( row );
anchor.setCol2( col+1 );
final Picture pict = drawing.createPicture( anchor, pictureIndex );
pict.resize();
} catch (IOException e) {
LOGGER.error("{}", e);
}
} else {
sheetCell.setCellValue(createHelper.createRichTextString(value.toString()));
}
} else {
try {
// parse all numbers as double, the format will determine how they appear
final Double d = Double.parseDouble(value.toString());
sheetCell.setCellValue(d);
} catch (final NumberFormatException nfe) {
LOGGER.error("NumberFormatException parsing a numeric value: {}", nfe);
sheetCell.setCellValue(createHelper.createRichTextString(value.toString()));
}
}
}
}
}
private Property<?> getProperty(final Object rootItemId, final Object propId) {
Property<?> prop;
if (getTableHolder().isGeneratedColumn(propId)) {
prop = getTableHolder().getPropertyForGeneratedColumn(propId, rootItemId);
} else {
prop = getTableHolder().getContainerDataSource().getContainerProperty(rootItemId, propId);
if (useTableFormatPropertyValue) {
if (getTableHolder().isExportableFormattedProperty()) {
final String formattedProp = getTableHolder().getFormattedPropertyValue(rootItemId, propId, prop);
if (null == prop) {
prop = new ObjectProperty<>(formattedProp, String.class);
} else {
final Object val = prop.getValue();
if (null == val) {
prop = new ObjectProperty<>(formattedProp, String.class);
} else {
if (!val.toString().equals(formattedProp)) {
prop = new ObjectProperty<>(formattedProp, String.class);
}
}
}
} else {
LOGGER.error("{}", "Cannot use FilterTable formatted property unless FilterTable is instance of ExportableFormattedProperty");
}
}
}
return prop;
}
private Class<?> getPropertyType(final Object propId) {
Class<?> classType;
if (getTableHolder().isGeneratedColumn(propId)) {
classType = getTableHolder().getPropertyTypeForGeneratedColumn(propId);
} else {
classType = getTableHolder().getContainerDataSource().getType(propId);
}
return classType;
}
public void setExcelFormatOfProperty(final Object propertyId, final String excelFormat) {
if (propertyExcelFormatMap.containsKey(propertyId)) {
propertyExcelFormatMap.remove(propertyId);
}
propertyExcelFormatMap.put(propertyId.toString(), excelFormat);
}
/**
* This method is called by addDataRow() to determine what CellStyle to use. By default we just
* return dataStyle which is either set to the default data style, or can be overriden by the
* user using setDataStyle(). However, if the user wants to have different data items have
* different styles, then this method should be overriden. The parameters passed in are all
* potentially relevant items that may be used to determine what formatting to return, that are
* not accessible globally.
*
* @param rootItemId the root item id
* @param row the row
* @param col the col
* @return the data style
*/
protected CellStyle getCellStyle(final Object rootItemId, final int row, final int col, final boolean totalsRow) {
final Object propId = getPropIds().get(col);
// get the basic style for the type of cell (i.e. data, header, total)
if ((rowHeaders) && (col == 0)) {
if (null == rowHeaderCellStyle) {
return columnHeaderCellStyle;
}
return rowHeaderCellStyle;
}
final Class<?> propType = getPropertyType(propId);
if (totalsRow) {
if (propertyExcelFormatMap.containsKey(propId)) {
final short df = dataFormat.getFormat(propertyExcelFormatMap.get(propId));
final CellStyle customTotalStyle = workbook.createCellStyle();
customTotalStyle.cloneStyleFrom(totalsDoubleCellStyle);
customTotalStyle.setDataFormat(df);
return customTotalStyle;
}
if (isIntegerLongShortOrBigDecimal(propType)) {
return totalsIntegerCellStyle;
}
return totalsDoubleCellStyle;
}
// Check if the user has over-ridden that data format of this property
if (propertyExcelFormatMap.containsKey(propId)) {
final short df = dataFormat.getFormat(propertyExcelFormatMap.get(propId));
if (dataFormatCellStylesMap.containsKey(df)) {
return dataFormatCellStylesMap.get(df);
}
// if it hasn't already been created for re-use, we create a cell style and override the data format
// For data cells, each data format corresponds to a single complete cell style
final CellStyle retStyle = workbook.createCellStyle();
retStyle.cloneStyleFrom(dataFormatCellStylesMap.get(doubleDataFormat));
retStyle.setDataFormat(df);
dataFormatCellStylesMap.put(df, retStyle);
return retStyle;
}
// if not over-ridden, use the overall setting
if (isDoubleOrFloat(propType)) {
return dataFormatCellStylesMap.get(doubleDataFormat);
} else {
if (isIntegerLongShortOrBigDecimal(propType)) {
return dataFormatCellStylesMap.get(integerDataFormat);
} else {
if (java.util.Date.class.isAssignableFrom(propType)) {
return dataFormatCellStylesMap.get(dateDataFormat);
}
}
}
return dataFormatCellStylesMap.get(doubleDataFormat);
}
/**
* Adds the totals row to the report. Override this method to make any changes. Alternately, the
* totals Row Object is accessible via getTotalsRow() after report creation. To change the
* CellStyle used for the totals row, use setFormulaStyle. For different totals cells to have
* different CellStyles, override getTotalsStyle().
*
* @param currentRow the current row
* @param startRow the start row
*/
protected void addTotalsRow(final int currentRow, final int startRow) {
totalsRow = sheet.createRow(currentRow);
totalsRow.setHeightInPoints(30);
Cell cell;
CellRangeAddress cra;
for (int col = 0; col < getPropIds().size(); col++) {
final Object propId = getPropIds().get(col);
cell = totalsRow.createCell(col);
cell.setCellStyle(getCellStyle(currentRow, startRow, col, true));
final Short poiAlignment = getTableHolder().getCellAlignment(propId);
CellUtil.setAlignment(cell, workbook, poiAlignment);
final Class<?> propType = getPropertyType(propId);
if (isNumeric(propType)) {
cra = new CellRangeAddress(startRow, currentRow - 1, col, col);
if (isHierarchical()) {
// 9 & 109 are for sum. 9 means include hidden cells, 109 means exclude.
// this will show the wrong value if the user expands an outlined category, so
// we will range value it first
cell.setCellFormula("SUM(" + cra.formatAsString(hierarchicalTotalsSheet.getSheetName(),
true) + ")");
} else {
cell.setCellFormula("SUM(" + cra.formatAsString() + ")");
}
} else {
if (0 == col) {
cell.setCellValue(createHelper.createRichTextString("Total"));
}
}
}
}
/**
* Final formatting of the sheet upon completion of writing the data. For example, we can only
* size the column widths once the data is in the report and the sheet knows how wide the data
* is.
*/
protected void finalSheetFormat() {
final FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
if (isHierarchical()) {
/*
* evaluateInCell() is equivalent to paste special -> value. The formula refers to cells
* in the other sheet we are going to delete. We sum in the other sheet because if we
* summed in the main sheet, we would double count. Subtotal with hidden rows is not yet
* implemented in POI.
*/
for (final Row r : sheet) {
for (final Cell c : r) {
if (c.getCellType() == Cell.CELL_TYPE_FORMULA) {
evaluator.evaluateInCell(c);
}
}
}
workbook.setActiveSheet(workbook.getSheetIndex(sheet));
if (hierarchicalTotalsSheet != null) {
workbook.removeSheetAt(workbook.getSheetIndex(hierarchicalTotalsSheet));
}
} else {
evaluator.evaluateAll();
}
for (int col = 0; col < getPropIds().size(); col++) {
sheet.autoSizeColumn(col);
}
}
/**
* Returns the default title style. Obtained from: http://svn.apache.org/repos/asf/poi
* /trunk/src/examples/src/org/apache/poi/ss/examples/TimesheetDemo.java
*
* @param wb the wb
* @return the cell style
*/
protected CellStyle defaultTitleCellStyle(final Workbook wb) {
CellStyle style;
final Font titleFont = wb.createFont();
titleFont.setFontHeightInPoints((short) 18);
titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setFont(titleFont);
return style;
}
/**
* Returns the default header style. Obtained from: http://svn.apache.org/repos/asf/poi
* /trunk/src/examples/src/org/apache/poi/ss/examples/TimesheetDemo.java
*
* @param wb the wb
* @return the cell style
*/
protected CellStyle defaultHeaderCellStyle(final Workbook wb) {
CellStyle style;
final Font monthFont = wb.createFont();
monthFont.setFontHeightInPoints((short) 11);
monthFont.setColor(IndexedColors.WHITE.getIndex());
style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setFont(monthFont);
style.setWrapText(true);
return style;
}
/**
* Returns the default data cell style. Obtained from: http://svn.apache.org/repos/asf/poi
* /trunk/src/examples/src/org/apache/poi/ss/examples/TimesheetDemo.java
*
* @param wb the wb
* @return the cell style
*/
protected CellStyle defaultDataCellStyle(final Workbook wb) {
CellStyle style;
style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setWrapText(true);
style.setBorderRight(CellStyle.BORDER_THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderTop(CellStyle.BORDER_THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderBottom(CellStyle.BORDER_THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setDataFormat(doubleDataFormat);
return style;
}
/**
* Returns the default totals row style for Double data. Obtained from: http://svn.apache.org/repos/asf/poi
* /trunk/src/examples/src/org/apache/poi/ss/examples/TimesheetDemo.java
*
* @param wb the wb
* @return the cell style
*/
protected CellStyle defaultTotalsDoubleCellStyle(final Workbook wb) {
CellStyle style;
style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setDataFormat(doubleDataFormat);
return style;
}
/**
* Returns the default totals row style for Integer data. Obtained from: http://svn.apache.org/repos/asf/poi
* /trunk/src/examples/src/org/apache/poi/ss/examples/TimesheetDemo.java
*
* @param wb the wb
* @return the cell style
*/
protected CellStyle defaultTotalsIntegerCellStyle(final Workbook wb) {
CellStyle style;
style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setDataFormat(integerDataFormat);
return style;
}
protected short defaultDoubleDataFormat() {
return createHelper.createDataFormat().getFormat("0.00");
}
protected short defaultIntegerDataFormat() {
return createHelper.createDataFormat().getFormat("0");
}
protected short defaultDateDataFormat() {
return createHelper.createDataFormat().getFormat("mm/dd/yyyy");
}
public void setDoubleDataFormat(final String excelDoubleFormat) {
CellStyle prevDoubleDataStyle = null;
if (dataFormatCellStylesMap.containsKey(doubleDataFormat)) {
prevDoubleDataStyle = dataFormatCellStylesMap.get(doubleDataFormat);
dataFormatCellStylesMap.remove(doubleDataFormat);
}
doubleDataFormat = createHelper.createDataFormat().getFormat(excelDoubleFormat);
if (null != prevDoubleDataStyle) {
doubleCellStyle = prevDoubleDataStyle;
doubleCellStyle.setDataFormat(doubleDataFormat);
dataFormatCellStylesMap.put(doubleDataFormat, doubleCellStyle);
}
}
public void setIntegerDataFormat(final String excelIntegerFormat) {
CellStyle prevIntegerDataStyle = null;
if (dataFormatCellStylesMap.containsKey(integerDataFormat)) {
prevIntegerDataStyle = dataFormatCellStylesMap.get(integerDataFormat);
dataFormatCellStylesMap.remove(integerDataFormat);
}
integerDataFormat = createHelper.createDataFormat().getFormat(excelIntegerFormat);
if (null != prevIntegerDataStyle) {
integerCellStyle = prevIntegerDataStyle;
integerCellStyle.setDataFormat(integerDataFormat);
dataFormatCellStylesMap.put(integerDataFormat, integerCellStyle);
}
}
public void setDateDataFormat(final String excelDateFormat) {
CellStyle prevDateDataStyle = null;
if (dataFormatCellStylesMap.containsKey(dateDataFormat)) {
prevDateDataStyle = dataFormatCellStylesMap.get(dateDataFormat);
dataFormatCellStylesMap.remove(dateDataFormat);
}
dateDataFormat = createHelper.createDataFormat().getFormat(excelDateFormat);
if (null != prevDateDataStyle) {
dateCellStyle = prevDateDataStyle;
dateCellStyle.setDataFormat(dateDataFormat);
dataFormatCellStylesMap.put(dateDataFormat, dateCellStyle);
}
}
/**
* Utility method to determine whether value being put in the Cell is numeric.
*
* @param type the type
* @return true, if is numeric
*/
private boolean isNumeric(final Class<?> type) {
if (isIntegerLongShortOrBigDecimal(type)) {
return true;
}
return isDoubleOrFloat(type);
}
/**
* Utility method to determine whether value being put in the Cell is integer-like type.
*
* @param type the type
* @return true, if is integer-like
*/
private boolean isIntegerLongShortOrBigDecimal(final Class<?> type) {
if ((Integer.class.equals(type) || (int.class.equals(type)))) {
return true;
}
if ((Long.class.equals(type) || (long.class.equals(type)))) {
return true;
}
if ((Short.class.equals(type)) || (short.class.equals(type))) {
return true;
}
return BigDecimal.class.equals(type);
}
/**
* Utility method to determine whether value being put in the Cell is double-like type.
*
* @param type the type
* @return true, if is double-like
*/
private boolean isDoubleOrFloat(final Class<?> type) {
if ((Double.class.equals(type)) || (double.class.equals(type))) {
return true;
}
return ((Float.class.equals(type)) || (float.class.equals(type)));
}
/**
* Gets the workbook.
*
* @return the workbook
*/
public Workbook getWorkbook() {
return workbook;
}
/**
* Gets the sheet name.
*
* @return the sheet name
*/
public String getSheetName() {
return sheetName;
}
/**
* Gets the report title.
*
* @return the report title
*/
public String getReportTitle() {
return reportTitle;
}
/**
* Gets the export file name.
*
* @return the export file name
*/
public String getExportFileName() {
return exportFileName;
}
/**
* Gets the cell style used for report data..
*
* @return the cell style
*/
public CellStyle getDoubleDataStyle() {
return doubleCellStyle;
}
/**
* Gets the cell style used for report data..
*
* @return the cell style
*/
public CellStyle getIntegerDataStyle() {
return integerCellStyle;
}
public CellStyle getDateDataStyle() {
return dateCellStyle;
}
/**
* Gets the cell style used for the report headers.
*
* @return the column header style
*/
public CellStyle getColumnHeaderStyle() {
return columnHeaderCellStyle;
}
/**
* Gets the cell title used for the report title.
*
* @return the title style
*/
public CellStyle getTitleStyle() {
return titleCellStyle;
}
/**
* Sets the text used for the report title.
*
* @param reportTitle the new report title
*/
public void setReportTitle(final String reportTitle) {
this.reportTitle = reportTitle;
}
/**
* Sets the export file name.
*
* @param exportFileName the new export file name
*/
public void setExportFileName(final String exportFileName) {
this.exportFileName = exportFileName;
}
/**
* Sets the cell style used for report data.
*
* @param doubleDataStyle the new data style
*/
public void setDoubleDataStyle(final CellStyle doubleDataStyle) {
this.doubleCellStyle = doubleDataStyle;
}
/**
* Sets the cell style used for report data.
*
* @param integerDataStyle the new data style
*/
public void setIntegerDataStyle(final CellStyle integerDataStyle) {
this.integerCellStyle = integerDataStyle;
}
/**
* Sets the cell style used for report data.
*
* @param dateDataStyle the new data style
*/
public void setDateDataStyle(final CellStyle dateDataStyle) {
this.dateCellStyle = dateDataStyle;
}
/**
* Sets the cell style used for the report headers.
*
* @param columnHeaderStyle CellStyle
*/
public void setColumnHeaderStyle(final CellStyle columnHeaderStyle) {
this.columnHeaderCellStyle = columnHeaderStyle;
}
/**
* Sets the cell style used for the report title.
*
* @param titleStyle the new title style
*/
public void setTitleStyle(final CellStyle titleStyle) {
this.titleCellStyle = titleStyle;
}
/**
* Gets the title row.
*
* @return the title row
*/
public Row getTitleRow() {
return titleRow;
}
/**
* Gets the header row.
*
* @return the header row
*/
public Row getHeaderRow() {
return headerRow;
}
/**
* Gets the totals row.
*
* @return the totals row
*/
public Row getTotalsRow() {
return totalsRow;
}
/**
* Gets the cell style used for the totals row.
*
* @return the totals style
*/
public CellStyle getTotalsDoubleStyle() {
return totalsDoubleCellStyle;
}
/**
* Sets the cell style used for the totals row.
*
* @param totalsDoubleStyle the new totals style
*/
public void setTotalsDoubleStyle(final CellStyle totalsDoubleStyle) {
this.totalsDoubleCellStyle = totalsDoubleStyle;
}
/**
* Gets the cell style used for the totals row.
*
* @return the totals style
*/
public CellStyle getTotalsIntegerStyle() {
return totalsIntegerCellStyle;
}
/**
* Sets the cell style used for the totals row.
*
* @param totalsIntegerStyle the new totals style
*/
public void setTotalsIntegerStyle(final CellStyle totalsIntegerStyle) {
this.totalsIntegerCellStyle = totalsIntegerStyle;
}
/**
* Flag indicating whether a totals row will be added to the report or not.
*
* @return true, if totals row will be added
*/
public boolean isDisplayTotals() {
return displayTotals;
}
/**
* Sets the flag indicating whether a totals row will be added to the report or not.
*
* @param displayTotals boolean
*/
public void setDisplayTotals(final boolean displayTotals) {
this.displayTotals = displayTotals;
}
public void setUseTableFormatPropertyValue(final boolean useFormatPropertyValue) {
this.useTableFormatPropertyValue = useFormatPropertyValue;
}
/**
* See value of flag indicating whether the first column should be treated as row headers.
*
* @return boolean
*/
public boolean hasRowHeaders() {
return rowHeaders;
}
/**
* Method getRowHeaderStyle.
*
* @return CellStyle
*/
public CellStyle getRowHeaderStyle() {
return rowHeaderCellStyle;
}
/**
* Set value of flag indicating whether the first column should be treated as row headers.
*
* @param rowHeaders boolean
*/
public void setRowHeaders(final boolean rowHeaders) {
this.rowHeaders = rowHeaders;
}
/**
* Method setRowHeaderStyle.
*
* @param rowHeaderStyle CellStyle
*/
public void setRowHeaderStyle(final CellStyle rowHeaderStyle) {
this.rowHeaderCellStyle = rowHeaderStyle;
}
private void format() {
setRowHeaders(true);
CellStyle style;
style = getTitleStyle();
setStyle(style, HSSFColor.DARK_BLUE.index, 18, HSSFColor.WHITE.index, true,
CellStyle.ALIGN_CENTER_SELECTION);
style = getColumnHeaderStyle();
setStyle(style, HSSFColor.LIGHT_BLUE.index, 12, HSSFColor.BLACK.index, true,
CellStyle.ALIGN_CENTER);
style = getDateDataStyle();
setStyle(style, HSSFColor.LIGHT_CORNFLOWER_BLUE.index, 12, HSSFColor.BLACK.index, false,
CellStyle.ALIGN_RIGHT);
style = getDoubleDataStyle();
setStyle(style, HSSFColor.LIGHT_CORNFLOWER_BLUE.index, 12, HSSFColor.BLACK.index, false,
CellStyle.ALIGN_RIGHT);
setTotalsDoubleStyle(style);
style = getIntegerDataStyle();
setStyle(style, HSSFColor.LIGHT_CORNFLOWER_BLUE.index, 12, HSSFColor.BLACK.index, false,
CellStyle.ALIGN_RIGHT);
setTotalsIntegerStyle(style);
// we want the rowHeader style to be like the columnHeader style, just centered differently.
final CellStyle newStyle = workbook.createCellStyle();
newStyle.cloneStyleFrom(style);
newStyle.setAlignment(CellStyle.ALIGN_LEFT);
setRowHeaderStyle(newStyle);
}
private void setStyle(CellStyle style, short foregroundColor, int fontHeight, short fontColor,
boolean isBold, short alignment) {
style.setFillForegroundColor(foregroundColor);
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
Font f = workbook.getFontAt(style.getFontIndex());
f.setFontHeightInPoints((short) fontHeight);
f.setFontName(HSSFFont.FONT_ARIAL);
f.setColor(fontColor);
if(isBold) {
f.setBoldweight(Font.BOLDWEIGHT_BOLD);
} else {
f.setBoldweight(Font.BOLDWEIGHT_NORMAL);
}
style.setAlignment(alignment);
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setBorderRight(CellStyle.BORDER_THIN);
style.setBorderTop(CellStyle.BORDER_THIN);
style.setBorderBottom(CellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.BLACK.index);
style.setRightBorderColor(HSSFColor.BLACK.index);
style.setTopBorderColor(HSSFColor.BLACK.index);
style.setBottomBorderColor(HSSFColor.BLACK.index);
}
}