org.jberet.support.io.ExcelUserModelItemWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.jberet.support.io.ExcelUserModelItemWriter.java

Source

/*
 * Copyright (c) 2014 Red Hat, Inc. and/or its affiliates.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Cheng Fang - Initial API and implementation
 */

package org.jberet.support.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.batch.api.BatchProperty;
import javax.batch.api.chunk.ItemWriter;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.WorkbookUtil;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jberet.support._private.SupportLogger;
import org.jberet.support._private.SupportMessages;

/**
 * An implementation of {@code javax.batch.api.chunk.ItemWriter} for Excel files. This implementation is currently based
 * on Apache POI user model API, and in-memory content generation. For large data set that may cause memory issue,
 * consider using {@link ExcelStreamingItemWriter}.
 *
 * @see     ExcelUserModelItemReader
 * @see     org.jberet.support.io.ExcelItemReaderWriterBase
 * @see     org.jberet.support.io.ExcelStreamingItemWriter
 * @since   1.1.0
 */
@Named
@Dependent
public class ExcelUserModelItemWriter extends ExcelItemReaderWriterBase implements ItemWriter {
    /**
     * Valid writeMode for this writer class is {@link CsvProperties#OVERWRITE} and {@link CsvProperties#FAIL_IF_EXISTS}.
     */
    @Inject
    @BatchProperty
    protected String writeMode;

    /**
     * The resource of an existing Excel file or template file to be used as a base for generating output Excel. Its
     * format is similar to {@link #resource}.
     */
    @Inject
    @BatchProperty
    protected String templateResource;

    /**
     * The sheet name in the template file to be used for generating output Excel. If {@link #templateResource} is
     * specified but this property is not specified, {@link #templateSheetIndex} is used instead.
     */
    @Inject
    @BatchProperty
    protected String templateSheetName;

    /**
     * The sheet index (0-based) in the template file to be used for generating output Excel.
     */
    @Inject
    @BatchProperty
    protected int templateSheetIndex;

    /**
     * The row number (0-based) of the header in the template sheet. If {@link #header} property is provided in
     * job xml file, then this property is ignored. Otherwise, it is used to retrieve header values.
     */
    @Inject
    @BatchProperty
    protected Integer templateHeaderRow;

    protected OutputStream outputStream;

    @Override
    public void open(final Serializable checkpoint) throws Exception {
        //if template is used, create workbook based on template resource, and try to get header from template
        if (templateResource != null) {
            InputStream templateInputStream = null;
            try {
                templateInputStream = getInputStream(templateResource, false);

                //for SXSSF (streaming), the original templateWorkbook is wrapped inside this.workbook, and these 2
                // workbook instances are different.  For XSSF and HSSF, the two are the same.
                // SXSSF workbook does not support reading, so we have to use the original templateWorkbook to read
                // header, and then reassign sheet to that of this.workbook, which is SXSSFWorkbook
                final Workbook templateWorkbook = createWorkbook(templateInputStream);
                if (templateSheetName != null) {
                    sheet = templateWorkbook.getSheet(templateSheetName);
                }
                if (sheet == null) {
                    sheet = templateWorkbook.getSheetAt(templateSheetIndex);
                }
                //if header property is already injected from job.xml, use it and no need to check templateHeaderRow
                if (header == null) {
                    if (templateHeaderRow == null) {
                        throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, null, "templateHeaderRow");
                    }
                    final Row headerRow = sheet.getRow(templateHeaderRow);
                    if (headerRow == null) {
                        throw SupportMessages.MESSAGES.failToReadExcelHeader(templateResource, templateSheetName);
                    }
                    header = getCellStringValues(headerRow);
                }
                currentRowNum = sheet.getLastRowNum();
                if (workbook != templateWorkbook) {
                    sheet = workbook.getSheet(sheet.getSheetName());
                }
                workbook.setActiveSheet(workbook.getSheetIndex(sheet));
            } finally {
                if (templateInputStream != null) {
                    try {
                        templateInputStream.close();
                    } catch (final Exception e) {
                        SupportLogger.LOGGER.tracef(e,
                                "Failed to close template InputStream %s for template resource %s%n",
                                templateInputStream, templateResource);
                    }
                }
            }
        } else { // no template is specified
            createWorkbook(null);
            sheet = sheetName == null ? workbook.createSheet()
                    : workbook.createSheet(WorkbookUtil.createSafeSheetName(sheetName));

            if (header == null) {
                throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, null, "header");
            }
            //write header row
            final Row headerRow = sheet.createRow(0);
            for (int i = 0, j = header.length; i < j; ++i) {
                headerRow.createCell(i, Cell.CELL_TYPE_STRING).setCellValue(header[i]);
            }
            currentRowNum = 0;
        }
        outputStream = getOutputStream(writeMode);
    }

    @Override
    public void writeItems(final List<Object> items) throws Exception {
        int nextRowNum = currentRowNum + 1;
        Row row = null;
        if (List.class.isAssignableFrom(beanType)) {
            for (int i = 0, j = items.size(); i < j; ++i, ++nextRowNum) {
                @SuppressWarnings("unchecked")
                final List<Object> item = (List<Object>) items.get(i);
                row = sheet.createRow(nextRowNum);
                for (int x = 0, y = item.size(); x < y; ++x) {
                    createCell(row, x, item.get(x));
                }
            }
        } else if (Map.class.isAssignableFrom(beanType)) {
            for (int i = 0, j = items.size(); i < j; ++i, ++nextRowNum) {
                @SuppressWarnings("unchecked")
                final Map<String, Object> item = (Map<String, Object>) items.get(i);
                row = sheet.createRow(nextRowNum);
                for (int x = 0, y = header.length; x < y; ++x) {
                    createCell(row, x, item.get(header[x]));
                }
            }
        } else {
            if (objectMapper == null) {
                initJsonFactoryAndObjectMapper();
            }
            for (int i = 0, j = items.size(); i < j; ++i, ++nextRowNum) {
                final Object item = items.get(i);

                @SuppressWarnings("unchecked")
                final Map<String, Object> itemAsMap = objectMapper.convertValue(item, Map.class);

                row = sheet.createRow(nextRowNum);
                for (int x = 0, y = header.length; x < y; ++x) {
                    createCell(row, x, itemAsMap.get(header[x]));
                }
            }
        }
        currentRowNum = row.getRowNum();
        if (sheet instanceof SXSSFSheet) {
            ((SXSSFSheet) sheet).flushRows();
        }
    }

    @Override
    public Serializable checkpointInfo() throws Exception {
        return null;
    }

    @Override
    public void close() throws Exception {
        if (workbook != null) {
            if (outputStream != null) {
                try {
                    workbook.write(outputStream);
                } catch (final IOException e) {
                    SupportLogger.LOGGER.failToWriteWorkbook(e, workbook.toString(), resource);
                }

                try {
                    outputStream.close();
                } catch (final IOException e) {
                    SupportLogger.LOGGER.tracef(e, "Failed to close OutputStream %s for resource %s%n",
                            outputStream, resource);
                }
                outputStream = null;
            }

            if (workbook instanceof SXSSFWorkbook) {
                ((SXSSFWorkbook) workbook).dispose();
            }
            workbook = null;
        }
    }

    /**
     * Creates the workbook for this writer.
     * @param templateInputStream    java.io.InputStream for the template excel file, if any
     * @return    the template workbook if a template is specified; otherwise null
     * @throws IOException
     * @throws InvalidFormatException
     */
    protected Workbook createWorkbook(final InputStream templateInputStream)
            throws IOException, InvalidFormatException {
        if (templateInputStream != null) {
            return workbook = WorkbookFactory.create(templateInputStream);
        } else {
            workbook = resource.endsWith("xls") ? new HSSFWorkbook() : new XSSFWorkbook();
            return null;
        }
    }

    protected void createCell(final Row row, final int columnIndex, final Object val) throws Exception {
        final Cell cell;
        if (val instanceof String) {
            cell = row.createCell(columnIndex, Cell.CELL_TYPE_STRING);
            cell.setCellValue((String) val);
        } else if (val instanceof Number) {
            cell = row.createCell(columnIndex, Cell.CELL_TYPE_NUMERIC);
            cell.setCellValue(((Number) val).doubleValue());
        } else if (val instanceof Boolean) {
            cell = row.createCell(columnIndex, Cell.CELL_TYPE_BOOLEAN);
            cell.setCellValue((Boolean) val);
        } else if (val instanceof Character) {
            cell = row.createCell(columnIndex, Cell.CELL_TYPE_STRING);
            cell.setCellValue(val.toString());
        } else if (val == null) {
            row.createCell(columnIndex, Cell.CELL_TYPE_BLANK);
        } else {
            cell = row.createCell(columnIndex, Cell.CELL_TYPE_STRING);
            cell.setCellValue(val.toString());
        }
    }
}