org.tiefaces.components.websheet.TieWebSheetBean.java Source code

Java tutorial

Introduction

Here is the source code for org.tiefaces.components.websheet.TieWebSheetBean.java

Source

/*
 * Copyright 2017 TieFaces.
 * Licensed under MIT
 */

package org.tiefaces.components.websheet;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ComponentSystemEvent;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.primefaces.context.RequestContext;
import org.primefaces.event.TabChangeEvent;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.tiefaces.components.websheet.chart.ChartHelper;
import org.tiefaces.components.websheet.chart.ChartsData;
import org.tiefaces.components.websheet.configuration.ExpressionEngine;
import org.tiefaces.components.websheet.configuration.SheetConfiguration;
import org.tiefaces.components.websheet.dataobjects.CachedCells;
import org.tiefaces.components.websheet.dataobjects.CellAttributesMap;
import org.tiefaces.components.websheet.dataobjects.CellFormAttributes;
import org.tiefaces.components.websheet.dataobjects.CellMap;
import org.tiefaces.components.websheet.dataobjects.FacesCell;
import org.tiefaces.components.websheet.dataobjects.FacesRow;
import org.tiefaces.components.websheet.dataobjects.HeaderCell;
import org.tiefaces.components.websheet.serializable.SerialDataContext;
import org.tiefaces.components.websheet.serializable.SerialWorkbook;
import org.tiefaces.components.websheet.service.CellHelper;
import org.tiefaces.components.websheet.service.PicturesHelper;
import org.tiefaces.components.websheet.service.ValidationHandler;
import org.tiefaces.components.websheet.service.WebSheetLoader;
import org.tiefaces.components.websheet.utility.CellControlsUtility;
import org.tiefaces.components.websheet.utility.CellUtility;

/**
 * Main class for web sheet.
 * 
 * @author Jason Jiang
 *
 */
public class TieWebSheetBean extends TieWebSheetView implements Serializable {

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 3495468356246589276L;

    /** hold instance for columns in current display sheet. */
    private List<String> columns = new ArrayList<>();
    /** hold instance for each body rows in current display sheet. */
    private List<FacesRow> bodyRows;
    /** hold instance for each header rows in current display sheet. */
    private List<List<HeaderCell>> headerRows;
    /** current workbook. */
    private SerialWorkbook serialWb;
    /** current workbook wrapper for formula parser. */
    private transient XSSFEvaluationWorkbook wbWrapper;
    /** current formula evaluator. */
    private transient FormulaEvaluator formulaEvaluator;
    /** current dataFormatter. */
    private transient DataFormatter dataFormatter;
    /** hold data object context. */
    private SerialDataContext serialDataContext;
    /** hold pictures for current display sheet. */
    private transient Map<String, Picture> picturesMap;
    /**
     * cell attributes map.
     */
    private CellAttributesMap cellAttributesMap = new CellAttributesMap(new HashMap<String, Map<String, String>>(),
            new HashMap<String, String>(), new HashMap<String, List<CellFormAttributes>>(),
            new HashMap<String, Map<String, String>>(), new HashMap<String, String>(),
            new HashMap<String, List<CellFormAttributes>>());

    /**
     * chars data.
     */
    private transient ChartsData chartsData;

    /** hold cached cells in current display sheet. */
    private CachedCells cachedCells;
    /**
     * The max column counts across sheets of this workbook. e.g. sheet1 has 3
     * columns, sheet2 has 5 columns. maxColCounts = 5;
     */
    private int maxColCounts = 0;

    /** hold expressionEngine instance. */
    private transient ExpressionEngine expEngine = null;
    /** hold current objects. */
    private TieWebSheetBeanCurrent current;

    /** submit mode may affect validation process. e.g. validation interface. */
    private Boolean submitMode = false;
    /** create bean's this.getHelper(). */
    private transient TieWebSheetBeanHelper helper = null;

    /**
     * cell default control.
     */
    private Map<String, Map<String, String>> cellDefaultControl = new HashMap<>();

    /** for download file. */
    private transient StreamedContent exportFile;

    /**
     * cells map for current display sheet.
     */

    private CellMap cellsMap = new CellMap(this);

    /** logger. */
    private static final Logger LOG = Logger.getLogger(TieWebSheetBean.class.getName());

    /** constructor. Allow for extension. */
    public TieWebSheetBean() {
        LOG.fine("TieWebSheetBean Constructor");
    }

    /** initialize. */
    @PostConstruct
    public void init() {
        initialLoad();
    }

    /**
     * get body rows.
     * 
     * @return body rows.
     */
    public List<FacesRow> getBodyRows() {
        if (this.bodyRows == null) {
            this.bodyRows = new ArrayList<>();
        }
        return bodyRows;
    }

    /**
     * set body rows.
     * 
     * @param pBodyRows
     *            body rows list.
     */
    public void setBodyRows(final List<FacesRow> pBodyRows) {
        this.bodyRows = pBodyRows;
    }

    /**
     * Gets the header rows.
     *
     * @return return header row list.
     */
    public List<List<HeaderCell>> getHeaderRows() {
        if (this.headerRows == null) {
            this.headerRows = new ArrayList<>();
        }
        return headerRows;
    }

    /**
     * set header rows.
     * 
     * @param pHeaderRows
     *            header rows list.
     */
    public void setHeaderRows(final List<List<HeaderCell>> pHeaderRows) {
        this.headerRows = pHeaderRows;
    }

    /**
     * set columns.
     * 
     * @param pColumns
     *            column list.
     */
    public void setColumns(final List<String> pColumns) {
        this.columns = pColumns;
    }

    /**
     * Gets the serial wb.
     *
     * @return the serial_wb
     */
    public SerialWorkbook getSerialWb() {
        if (serialWb == null) {
            serialWb = new SerialWorkbook();
        }
        return serialWb;
    }

    /**
     * Sets the serial wb.
     *
     * @param pserialWb
     *            the serial_wb to set
     */
    public void setSerialWb(final SerialWorkbook pserialWb) {
        this.serialWb = pserialWb;
    }

    /**
     * Gets the serial data context.
     *
     * @return the serialDataContext
     */
    public SerialDataContext getSerialDataContext() {
        if (serialDataContext == null) {
            serialDataContext = new SerialDataContext();
        }
        return serialDataContext;
    }

    /**
     * Sets the serial data context.
     *
     * @param pserialDataContext
     *            the serialDataContext to set
     */
    public void setSerialDataContext(final SerialDataContext pserialDataContext) {
        this.serialDataContext = pserialDataContext;
    }

    /**
     * get workbook.
     * 
     * @return workbook.
     */
    public Workbook getWb() {
        return this.getSerialWb().getWb();
    }

    /**
     * Set up workbook. Also create evaluation wrapper.
     * 
     * @param pWb
     *            workbook.
     */

    public void setWb(final Workbook pWb) {

        this.getSerialWb().setWb(pWb);
        this.wbWrapper = XSSFEvaluationWorkbook.create((XSSFWorkbook) pWb);
    }

    /**
     * Return evaluation wrapper if needed.
     * 
     * @return wbwrapper.
     */
    public XSSFEvaluationWorkbook getWbWrapper() {
        if ((this.wbWrapper == null) && (this.getWb() != null)) {
            this.wbWrapper = XSSFEvaluationWorkbook.create((XSSFWorkbook) this.getWb());
        }
        return wbWrapper;
    }

    /**
     * get formulaevaluator.
     * 
     * @return formulaevaluator.
     */
    public FormulaEvaluator getFormulaEvaluator() {
        if ((this.formulaEvaluator == null) && (this.getWb() != null)) {
            this.formulaEvaluator = this.getWb().getCreationHelper().createFormulaEvaluator();
        }
        return formulaEvaluator;
    }

    /**
     * set formulaevaluator.
     * 
     * @param pFormulaEvaluator
     *            formulaevaluator.
     */
    public void setFormulaEvaluator(final FormulaEvaluator pFormulaEvaluator) {
        this.formulaEvaluator = pFormulaEvaluator;
    }

    /**
     * get data formatter.
     * 
     * @return dataformatter.
     */
    public DataFormatter getDataFormatter() {
        if (this.dataFormatter == null) {
            this.dataFormatter = new DataFormatter(this.getDefaultLocale());
        }
        return dataFormatter;
    }

    /**
     * set dataformatter.
     * 
     * @param pDataFormatter
     *            dataformatter.
     */
    public void setDataFormatter(final DataFormatter pDataFormatter) {
        this.dataFormatter = pDataFormatter;
    }

    /**
     * get columns.
     * 
     * @return list of columns.
     */
    public List<String> getColumns() {
        return columns;
    }

    /**
     * Gets the current.
     *
     * @return the current
     */
    public TieWebSheetBeanCurrent getCurrent() {
        if (current == null) {
            current = new TieWebSheetBeanCurrent();
        }
        return current;
    }

    /**
     * get submit mode.
     * 
     * @return true if it's in submit mode.
     */
    public Boolean getSubmitMode() {
        return this.submitMode;
    }

    /**
     * set submit mode.
     * 
     * @param pSubmitMode
     *            submit mode flag.
     */
    public void setSubmitMde(final Boolean pSubmitMode) {
        this.submitMode = pSubmitMode;
        this.getHelper().getValidationHandler().setSubmitModeInView(this.submitMode);
    }

    /**
     * get bean helper.
     * 
     * @return helper.
     */

    public TieWebSheetBeanHelper getHelper() {
        if (this.helper == null) {
            this.helper = new TieWebSheetBeanHelper(this);
        }
        return this.helper;
    }

    /**
     * get cell this.getHelper().
     * 
     * @return cell this.getHelper().
     */
    public CellHelper getCellHelper() {
        return this.getHelper().getCellHelper();
    }

    /**
     * get expression engine.
     * 
     * @return exp engine.
     */
    public ExpressionEngine getExpEngine() {
        if (this.expEngine == null) {
            this.expEngine = new ExpressionEngine();
        }
        return expEngine;
    }

    /**
     * get websheet loader.
     * 
     * @return websheetloader.
     */
    public WebSheetLoader getWebSheetLoader() {
        return this.getHelper().getWebSheetLoader();
    }

    /**
     * get validationhandler.
     * 
     * @return validation handler.
     */
    public ValidationHandler getValidationHandler() {
        return this.getHelper().getValidationHandler();
    }

    /**
     * get picthis.getHelper().
     * 
     * @return picHelper.
     */
    public PicturesHelper getPicHelper() {
        return this.getHelper().getPicHelper();
    }

    /**
     * get chartthis.getHelper().
     * 
     * @return chartthis.getHelper().
     */
    public ChartHelper getChartHelper() {
        return this.getHelper().getChartHelper();
    }

    /**
     * get pictures map.
     * 
     * @return pictures map.
     */
    public Map<String, Picture> getPicturesMap() {
        if (this.picturesMap == null) {
            this.picturesMap = new HashMap<>();
        }
        return picturesMap;
    }

    /**
     * set pictures map.
     * 
     * @param pPicturesMap
     *            pictures map.
     */
    public void setPicturesMap(final Map<String, Picture> pPicturesMap) {
        this.picturesMap = pPicturesMap;
    }

    /**
     * charts data.
     * 
     * @return charts data
     */
    public ChartsData getCharsData() {
        if (this.chartsData == null) {
            this.chartsData = new ChartsData();
        }
        return this.chartsData;
    }

    /**
     * Gets the cached cells.
     *
     * @return cached cells.
     */
    public CachedCells getCachedCells() {
        if (cachedCells == null) {
            cachedCells = new CachedCells(this);
        }
        return cachedCells;
    }

    /**
     * set cached cells.
     * 
     * @param pCachedCells
     *            cached cells.
     */
    public void setCachedCells(final CachedCells pCachedCells) {
        this.cachedCells = pCachedCells;
    }

    /**
     * Gets the max col counts.
     *
     * @return max column counts.
     */
    public int getMaxColCounts() {
        if (this.maxColCounts == 0) {
            reCalcMaxColCounts();
        }
        return maxColCounts;
    }

    /**
     * recalculate max coulumn count across sheets in the workbook.
     */
    public void reCalcMaxColCounts() {
        if ((this.getSheetConfigMap() == null) || (this.getSheetConfigMap().isEmpty())) {
            this.maxColCounts = 0;
            return;
        }
        int maxColumns = 0;
        for (SheetConfiguration sheetConfig : this.getSheetConfigMap().values()) {
            int counts = sheetConfig.getHeaderCellRange().getRightCol()
                    - sheetConfig.getHeaderCellRange().getLeftCol() + 1;
            if (maxColumns < counts) {
                maxColumns = counts;
            }
        }
        this.maxColCounts = maxColumns;
    }

    /**
     * load web sheet from inputStream file.
     * 
     * @param inputStream
     *            input stream file.
     * @return 1 (success) -1 (failed)
     */
    public int loadWebSheet(final InputStream inputStream) {
        return loadWebSheet(inputStream, null);
    }

    /**
     * load web sheet from inputStream file with data object.
     * 
     * @param inputStream
     *            input stream file.
     * @param pDataContext
     *            data object.
     * @return 1 (success) -1 (failed)
     */
    public int loadWebSheet(final InputStream inputStream, final Map<String, Object> pDataContext) {
        return this.getHelper().getWebSheetLoader().loadWorkbook(inputStream, pDataContext);
    }

    /**
     * load web sheet from giving workbook.
     * 
     * @param pWb
     *            workbook.
     * @return 1 (success) -1 (failed)
     */

    public int loadWebSheet(final Workbook pWb) {
        return loadWebSheet(pWb, null);
    }

    /**
     * load web sheet from giving workbook with data object.
     * 
     * @param pWb
     *            workbook.
     * @param pDataContext
     *            data object.
     * @return 1 (success) -1 (failed)
     */

    public int loadWebSheet(final Workbook pWb, final Map<String, Object> pDataContext) {
        return this.getHelper().getWebSheetLoader().loadWorkbook(pWb, pDataContext);
    }

    /**
     * Triggered when user switch the tab. This will load different tab(sheet)
     * as the current sheet.
     * 
     * @param event
     *            tabchange event.
     */
    public void onTabChange(final TabChangeEvent event) {
        String tabName = event.getTab().getTitle();
        loadWorkSheetByTabName(tabName);
    }

    /**
     * load worksheet by tab name.
     * 
     * @param tabName
     *            tab name.
     * @return 1 success. -1 failed.
     */

    public int loadWorkSheetByTabName(final String tabName) {

        try {
            int sheetId = this.getHelper().getWebSheetLoader().findTabIndexWithName(tabName);
            if ((getSheetConfigMap() != null) && (sheetId < getSheetConfigMap().size())) {
                this.getHelper().getWebSheetLoader().loadWorkSheet(tabName);
                setActiveTabIndex(sheetId);
            }
            return 1;
        } catch (Exception ex) {
            LOG.log(Level.SEVERE, "loadWorkSheetByTabName failed. error = " + ex.getMessage(), ex);

        }
        return -1;
    }

    /**
     * get export file.
     * 
     * @return exportfile.
     */

    public StreamedContent getExportFile() {
        return exportFile;
    }

    /** download current workbook. */
    public void doExport() {
        try {

            String fileName = this.getExportFileName();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            this.getWb().write(out);
            InputStream stream = new BufferedInputStream(new ByteArrayInputStream(out.toByteArray()));
            exportFile = new DefaultStreamedContent(stream, "application/force-download", fileName);

        } catch (Exception e) {
            LOG.log(Level.SEVERE, "Error in export file : " + e.getLocalizedMessage(), e);
        }
        return;
    }

    /**
     * Save the current workbooks.
     */
    public void doSave() {

        this.setSubmitMde(false);
        if (!this.getHelper().getValidationHandler().preValidation()) {
            LOG.fine("Validation failded before saving");
            return;
        }
        processSave();
        this.getHelper().getWebSheetLoader().setUnsavedStatus(RequestContext.getCurrentInstance(), false);
    }

    /**
     * save process. User need override this method to save into db.
     * 
     */
    public void processSave() {
        return;
    }

    /**
     * Submit the current workbooks.
     */
    public void doSubmit() {

        this.setSubmitMde(true);
        // validation may behavior differently depend on the submit mode.
        // e.g. when submit mode = false, empty fields or value not changed cells
        // don't need to pass the validation rule. This allow partial save the form. 
        // when submit mode = true, all cells need to pass the validation.
        if (!this.getHelper().getValidationHandler().preValidation()) {
            LOG.fine("Validation failed before saving");
            return;
        }
        processSubmit();
        this.getHelper().getWebSheetLoader().setUnsavedStatus(RequestContext.getCurrentInstance(), false);
        this.setSubmitMde(false);
    }

    /**
     * submit process. User need override this method to submit the form.
     * 
     */
    public void processSubmit() {
        return;
    }

    /**
     * Triggered when value in cells changed. e.g. user edit cell.
     * 
     * @param event
     *            ajax event.
     */
    public void valueChangeEvent(final AjaxBehaviorEvent event) {
        this.getHelper().getValidationHandler().valueChangeEvent(event);
    }

    /**
     * check whether current workbook contain multiple pages.
     * 
     * @return true (multiple pages) false ( single page).
     */
    public boolean isMultiplePage() {
        return ((bodyRows != null) && (bodyRows.size() > this.getMaxRowsPerPage()));
    }

    /**
     * called before bean gone.
     */
    @PreDestroy
    public void finish() {
        if (FacesContext.getCurrentInstance() == null) {
            LOG.info("session has gone");
        }

    }

    /**
     * get sheet config map.
     * 
     * @return sheet config map.
     */
    public Map<String, SheetConfiguration> getSheetConfigMap() {
        return this.getSerialWb().getSheetConfigMap();
    }

    /**
     * set sheet config map.
     * 
     * @param pSheetConfigMap
     *            sheet config map.
     */
    public void setSheetConfigMap(final Map<String, SheetConfiguration> pSheetConfigMap) {
        this.getSerialWb().setSheetConfigMap(pSheetConfigMap);
    }

    /**
     * Gets the cells map.
     *
     * @return the cells map
     */
    @SuppressWarnings("rawtypes")
    public Map getCellsMap() {
        return cellsMap;
    }

    /**
     * initial load process. designed for extension.
     */
    public void initialLoad() {
        // designed for extension.
    }

    /**
     * Triggered when user click add row button.
     * 
     * @param rowIndex
     *            row index.
     */
    public void addRepeatRow(final int rowIndex) {
        this.getHelper().getWebSheetLoader().addRepeatRow(rowIndex);
    }

    /**
     * Triggered when user click delete row button.
     * 
     * @param rowIndex
     *            row index.
     */
    public void deleteRepeatRow(final int rowIndex) {
        this.getHelper().getWebSheetLoader().deleteRepeatRow(rowIndex);
    }

    /**
     * get cell attributes map.
     * 
     * @return cell attributes map.
     */
    public CellAttributesMap getCellAttributesMap() {
        return cellAttributesMap;
    }

    /**
     * get cell default control.
     * 
     * @return cell default control.
     */
    public Map<String, Map<String, String>> getCellDefaultControl() {
        return cellDefaultControl;
    }

    /**
     * populate component.
     * 
     * @param event
     *            component system event.
     */
    public void populateComponent(final ComponentSystemEvent event) {
        UIComponent component = event.getComponent();
        int[] rowcol = CellUtility.getRowColFromComponentAttributes(component);
        int row = rowcol[0];
        int col = rowcol[1];
        FacesCell fcell = CellUtility.getFacesCellFromBodyRow(row, col, this.getBodyRows(),
                this.getCurrent().getCurrentTopRow(), this.getCurrent().getCurrentLeftColumn());
        CellControlsUtility.populateAttributes(component, fcell, this.getCellDefaultControl());
    }

    /**
     * Gets the current sheet config.
     *
     * @return the currentSheetConfig
     */
    public SheetConfiguration getCurrentSheetConfig() {
        String currentTabName = this.getCurrent().getCurrentTabName();
        if (currentTabName == null) {
            return null;
        }
        return this.getSheetConfigMap().get(currentTabName);
    }

    /**
     * load the bean from saving.
     * 
     * @param in
     *            inputstream.
     * @throws IOException
     *             io exception.
     */
    private void readObject(final java.io.ObjectInputStream in) throws IOException {
        try {
            in.defaultReadObject();
            recover();
        } catch (EncryptedDocumentException | ClassNotFoundException e) {
            LOG.log(Level.SEVERE, " error in readObject of serialWorkbook : " + e.getLocalizedMessage(), e);
        }
    }

    /**
     * recover objects after deserilize.
     */
    private void recover() {
        if (this.getWb() != null) {
            this.getChartHelper().loadChartsMap();
            this.getPicHelper().loadPicturesMap();
        }
    }

    /**
     * Refresh data.
     */
    public void refreshData() {
        this.getWebSheetLoader().refreshData();
    }

}