fi.hsl.parkandride.core.service.reporting.Excel.java Source code

Java tutorial

Introduction

Here is the source code for fi.hsl.parkandride.core.service.reporting.Excel.java

Source

// Copyright  2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.

package fi.hsl.parkandride.core.service.reporting;

import fi.hsl.parkandride.core.domain.MultilingualString;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import static com.google.common.base.Throwables.getStackTraceAsString;
import static java.lang.Math.max;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static org.apache.poi.ss.usermodel.Cell.*;

class Excel {
    private static final Logger log = LoggerFactory.getLogger(Excel.class);

    private final Workbook wb = new XSSFWorkbook();
    private final Font font12pt = wb.createFont();
    private final Font font12ptGreen = wb.createFont();
    private final Font font12ptRed = wb.createFont();
    private final Font bold = wb.createFont();
    private final CellStyle title = wb.createCellStyle();
    final CellStyle text = wb.createCellStyle();
    final CellStyle multiline = wb.createCellStyle();
    final CellStyle integer = wb.createCellStyle();
    final CellStyle decimal = wb.createCellStyle();
    final CellStyle percent = wb.createCellStyle();
    final CellStyle date = wb.createCellStyle();
    final CellStyle datetime = wb.createCellStyle();
    final CellStyle month = wb.createCellStyle();

    // Colorized cells
    final CellStyle green = wb.createCellStyle();
    final CellStyle red = wb.createCellStyle();
    final CellStyle yellow = wb.createCellStyle();
    final CellStyle orange = wb.createCellStyle();

    private final DataFormat df = wb.createDataFormat();
    private Sheet sheet;

    {
        font12pt.setFontHeightInPoints((short) 12);
        font12ptGreen.setFontHeightInPoints((short) 12);
        font12ptGreen.setColor(IndexedColors.GREEN.index);
        font12ptRed.setFontHeightInPoints((short) 12);
        font12ptRed.setColor(IndexedColors.RED.index);
        bold.setFontHeightInPoints((short) 12);
        bold.setBoldweight(Font.BOLDWEIGHT_BOLD);
        title.setFont(bold);
        text.setDataFormat(df.getFormat("TEXT"));
        text.setFont(font12pt);
        multiline.setDataFormat(df.getFormat("TEXT"));
        multiline.setFont(font12pt);
        multiline.setWrapText(true);
        integer.setDataFormat(df.getFormat("0"));
        integer.setFont(font12pt);
        decimal.setDataFormat(df.getFormat("#,####0.0000"));
        decimal.setFont(font12pt);
        percent.setDataFormat(df.getFormat("0.00 %"));
        percent.setFont(font12pt);
        date.setDataFormat(df.getFormat("d.M.yyyy"));
        date.setFont(font12pt);
        datetime.setDataFormat(df.getFormat("d.M.yyyy HH:mm"));
        datetime.setFont(font12pt);
        month.setDataFormat(df.getFormat("M\\/yyyy"));
        month.setFont(font12pt);

        // Colorized
        green.setFont(font12ptGreen);
        green.setFillPattern(CellStyle.SOLID_FOREGROUND);
        green.setFillForegroundColor(IndexedColors.LIGHT_GREEN.index);
        red.setFont(font12ptRed);
        yellow.setFont(font12pt);
        yellow.setFillPattern(CellStyle.SOLID_FOREGROUND);
        yellow.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.index);
        orange.setFont(font12pt);
        orange.setFillPattern(CellStyle.SOLID_FOREGROUND);
        orange.setFillForegroundColor(IndexedColors.CORAL.index);
    }

    static class TableColumn<T> {
        static <T> TableColumn<T> col(String name, Function<T, Object> valueFunction) {
            return col(name, valueFunction, val -> null);
        }

        static <T> TableColumn<T> col(String name, Function<T, Object> valueFunction, CellStyle style) {
            return col(name, valueFunction, v -> style);
        }

        static <T> TableColumn<T> col(String name, Function<T, Object> valueFunction,
                Function<T, CellStyle> styleFn) {
            return new TableColumn<>(name, valueFunction, styleFn);
        }

        public final String name;
        public final Function<T, Object> valueFunction;
        public final Function<T, CellStyle> styleFn;

        private TableColumn(String name, Function<T, Object> valueFunction, Function<T, CellStyle> styleFn) {
            this.name = name;
            this.valueFunction = valueFunction;
            this.styleFn = styleFn;
        }
    }

    public <T> void addSheet(String name, List<T> rows, List<TableColumn<T>> columns) {
        sheet = wb.createSheet(name);
        sheet.createFreezePane(0, 1, 0, 1);

        int maxColumns = 0;
        Row headerRow = sheet.createRow(0);
        for (int column = 0; column < columns.size(); ++column, maxColumns = max(maxColumns, column)) {
            Cell cell = headerRow.createCell(column, CELL_TYPE_STRING);
            TableColumn<T> colType = columns.get(column);
            cell.setCellStyle(title);
            cell.setCellValue(colType.name);
        }

        for (int r = 0; r < rows.size(); ++r) {
            Row row = sheet.createRow(r + 1);
            for (int column = 0; column < columns.size(); ++column) {
                TableColumn<T> colType = columns.get(column);
                Object value;
                Optional<CellStyle> style;
                final T v = rows.get(r);
                try {
                    value = colType.valueFunction.apply(v);
                } catch (RuntimeException ex) {
                    log.error("Failed to generate cell for column " + colType.name, ex);
                    value = cleanExceptionMessage(ex);
                }
                style = Optional.ofNullable(colType.styleFn.apply(v));
                if (value == null) {
                    row.createCell(column, CELL_TYPE_BLANK);
                } else if (value instanceof Double) {
                    Cell cell = row.createCell(column, CELL_TYPE_NUMERIC);
                    cell.setCellStyle(style.orElse(decimal));
                    cell.setCellValue((Double) value);
                } else if (value instanceof Integer) {
                    Cell cell = row.createCell(column, CELL_TYPE_NUMERIC);
                    cell.setCellStyle(style.orElse(integer));
                    cell.setCellValue((Integer) value);
                } else if (value instanceof MultilingualString) {
                    Cell cell = row.createCell(column, CELL_TYPE_STRING);
                    cell.setCellStyle(style.orElse(text));
                    cell.setCellValue(((MultilingualString) value).fi);
                } else if (value instanceof LocalDate) {
                    Cell cell = row.createCell(column, CELL_TYPE_NUMERIC);
                    cell.setCellStyle(style.orElse(date));
                    cell.setCellValue(((LocalDate) value).toDate());
                } else if (value instanceof DateTime) {
                    Cell cell = row.createCell(column, CELL_TYPE_NUMERIC);
                    cell.setCellStyle(style.orElse(datetime));
                    cell.setCellValue(((DateTime) value).toDate());
                } else if (value instanceof Collection) {
                    // currently must be last item in list
                    for (Object o : (Collection<?>) value) {
                        Cell cell = row.createCell(column++, CELL_TYPE_STRING);
                        cell.setCellStyle(text);
                        cell.setCellValue(o.toString());
                    }
                } else {
                    Cell cell = row.createCell(column, CELL_TYPE_STRING);
                    String val = value.toString();
                    if (style.isPresent()) {
                        cell.setCellStyle(style.get());
                    } else if (val.indexOf('\n') > 0) {
                        cell.setCellStyle(multiline);
                    } else {
                        cell.setCellStyle(text);
                    }
                    cell.setCellValue(value.toString());
                }
            }
        }
        if (!rows.isEmpty()) {
            sheet.setAutoFilter(new CellRangeAddress(0, rows.size(), 0, maxColumns));
        }
        autosize(maxColumns);
    }

    private static String cleanExceptionMessage(RuntimeException ex) {
        return asList(getStackTraceAsString(ex).split("\n")).stream().filter(
                l -> !l.matches(".*(sun\\.reflect|java\\.lang\\.reflect|\\$\\$Lambda\\$.*Unknown Source).*"))
                .limit(10).collect(joining("\n"));
    }

    public <T> void addSheet(String name, String... textRows) {
        sheet = wb.createSheet(name);
        int row = 0;
        for (String txt : textRows) {
            Cell cell = sheet.createRow(row++).createCell(0, CELL_TYPE_STRING);
            cell.setCellStyle(text);
            cell.setCellValue(txt);
        }
    }

    private void autosize(int maxColumns) {
        try {
            for (int i = 0; i < maxColumns; ++i) {
                sheet.autoSizeColumn(i);
            }
        } catch (Throwable t) {
            log.warn("Failed to evaluate excel cell widths", t);
        }
    }

    byte[] toBytes() {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
            wb.write(out);
            out.close();
            wb.close();
            return out.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}