net.sf.jasperreports.components.table.fill.FillTable.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.components.table.fill.FillTable.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.jasperreports.components.table.fill;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import net.sf.jasperreports.components.headertoolbar.HeaderToolbarElement;
import net.sf.jasperreports.components.table.BaseColumn;
import net.sf.jasperreports.components.table.Column;
import net.sf.jasperreports.components.table.ColumnGroup;
import net.sf.jasperreports.components.table.ColumnVisitor;
import net.sf.jasperreports.components.table.TableComponent;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JROrigin;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRPropertyExpression;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.component.BaseFillComponent;
import net.sf.jasperreports.engine.component.FillPrepareResult;
import net.sf.jasperreports.engine.design.JRAbstractCompiler;
import net.sf.jasperreports.engine.design.JRReportCompileData;
import net.sf.jasperreports.engine.export.JRPdfExporterTagHelper;
import net.sf.jasperreports.engine.fill.JRFillCloneFactory;
import net.sf.jasperreports.engine.fill.JRFillComponentElement;
import net.sf.jasperreports.engine.fill.JRFillContext;
import net.sf.jasperreports.engine.fill.JRFillDatasetRun;
import net.sf.jasperreports.engine.fill.JRFillObjectFactory;
import net.sf.jasperreports.engine.fill.JRTemplateFrame;
import net.sf.jasperreports.engine.fill.JRTemplatePrintFrame;
import net.sf.jasperreports.engine.util.JRReportUtils;
import net.sf.jasperreports.engine.xml.JRXmlWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * 
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 */
public class FillTable extends BaseFillComponent {

    private static final Log log = LogFactory.getLog(FillTable.class);

    protected static final String FILL_CACHE_KEY_TABLE_INSTANCE_COUNTER = FillTable.class.getName()
            + "#instanceCounter";
    protected static final String EXCEPTION_MESSAGE_KEY_UNSUPPORTED_REPORT_DATA_TYPE = "components.table.unsupported.report.data.type";

    private final TableComponent table;
    private final JRFillObjectFactory factory;
    private Map<List<FillColumn>, FillTableSubreportFactory> fillSubreportFactories;
    private FillTableSubreport fillSubreport;

    private boolean filling;
    private List<FillColumn> fillColumns;
    private int fillWidth;
    private Map<JRStyle, JRTemplateFrame> printFrameTemplates = new HashMap<JRStyle, JRTemplateFrame>();

    public FillTable(TableComponent table, JRFillObjectFactory factory) {
        this.table = table;
        this.factory = factory;

        // we need to do this for return values with derived variables
        JRFillDatasetRun fillDatasetRun = factory.getDatasetRun(table.getDatasetRun());
        // this is needed for returned variables with evaluationTime=Auto
        factory.registerDatasetRun(fillDatasetRun);

        this.fillSubreportFactories = new HashMap<List<FillColumn>, FillTableSubreportFactory>();
    }

    public FillTable(FillTable table, JRFillCloneFactory factory) {
        super(table, factory);

        this.table = table.table;
        this.factory = table.factory;

        this.fillSubreportFactories = table.fillSubreportFactories;
        this.printFrameTemplates = table.printFrameTemplates;
    }

    @Override
    public void evaluate(byte evaluation) throws JRException {
        if (filling) {
            log.warn("Table fill did not complete, canceling previous table subreport");
            fillSubreport.cancelSubreportFill();
        }

        filling = false;

        evaluateColumns(evaluation);
        if (!fillColumns.isEmpty()) {
            createFillSubreport();
            setTableInstanceCounter();
            fillSubreport.evaluateSubreport(evaluation);
        }
    }

    protected void setTableInstanceCounter() {
        JRFillContext fillerContext = fillContext.getFiller().getFillContext();
        AtomicInteger counter = (AtomicInteger) fillerContext.getFillCache(FILL_CACHE_KEY_TABLE_INSTANCE_COUNTER);
        if (counter == null) {
            // we just need a mutable integer, there's no actual concurrency here
            counter = new AtomicInteger();
            fillerContext.setFillCache(FILL_CACHE_KEY_TABLE_INSTANCE_COUNTER, counter);
        }

        int instanceIndex = counter.getAndIncrement();
        if (log.isDebugEnabled()) {
            log.debug("table instance index is " + instanceIndex);
        }

        fillSubreport.getTableReport().getBaseReport().setTableInstanceIndex(instanceIndex);
    }

    protected boolean toPrintColumn(BaseColumn column, byte evaluation) throws JRException {
        boolean toPrint;
        JRExpression printWhenExpression = column.getPrintWhenExpression();
        if (printWhenExpression == null) {
            toPrint = true;
        } else {
            Boolean printWhenVal = (Boolean) evaluateExpression(printWhenExpression, evaluation);
            if (printWhenVal == null) {
                toPrint = false;
            } else {
                toPrint = printWhenVal;
            }
        }
        return toPrint;
    }

    protected JRPropertiesMap evaluateProperties(BaseColumn column, byte evaluation) throws JRException {
        JRPropertiesMap staticProperties = column.hasProperties() ? column.getPropertiesMap().cloneProperties()
                : null;
        JRPropertiesMap mergedProperties = null;

        JRPropertyExpression[] propExprs = column.getPropertyExpressions();
        if (propExprs == null || propExprs.length == 0) {
            mergedProperties = staticProperties;
        } else {
            JRPropertiesMap dynamicProperties = new JRPropertiesMap();

            for (int i = 0; i < propExprs.length; i++) {
                JRPropertyExpression prop = propExprs[i];
                String value = (String) evaluateExpression(prop.getValueExpression(), evaluation);
                //if (value != null) //for some properties such as data properties in metadata exporters, the null value is significant
                {
                    dynamicProperties.setProperty(prop.getName(), value);
                }
            }

            mergedProperties = dynamicProperties.cloneProperties();
            mergedProperties.setBaseProperties(staticProperties);
        }

        return mergedProperties;
    }

    protected class FillColumnEvaluator implements ColumnVisitor<FillColumn> {
        final byte evaluation;

        public FillColumnEvaluator(byte evaluation) {
            this.evaluation = evaluation;
        }

        @Override
        public FillColumn visitColumn(Column column) {
            try {
                boolean toPrint = toPrintColumn(column, evaluation);
                if (toPrint) {
                    JRPropertiesMap properties = evaluateProperties(column, evaluation);
                    return new FillColumn(column, properties);
                }
                return null;
            } catch (JRException e) {
                throw new JRRuntimeException(e);
            }
        }

        @Override
        public FillColumn visitColumnGroup(ColumnGroup columnGroup) {
            try {
                boolean toPrint = toPrintColumn(columnGroup, evaluation);
                FillColumn fillColumn;
                if (toPrint) {
                    List<BaseColumn> columns = columnGroup.getColumns();
                    List<FillColumn> subColumns = new ArrayList<FillColumn>(columns.size());
                    int printWidth = 0;
                    for (BaseColumn column : columns) {
                        FillColumn fillSubColumn = column.visitColumn(this);
                        if (fillSubColumn != null) {
                            printWidth += fillSubColumn.getWidth();
                            subColumns.add(fillSubColumn);
                        }
                    }

                    if (subColumns.isEmpty()) {
                        // no sub columns prints
                        // the column group won't print either
                        fillColumn = null;
                    } else {
                        JRPropertiesMap properties = evaluateProperties(columnGroup, evaluation);
                        fillColumn = new FillColumn(columnGroup, printWidth, subColumns, properties);
                    }
                } else {
                    fillColumn = null;
                }
                return fillColumn;
            } catch (JRException e) {
                throw new JRRuntimeException(e);
            }
        }
    }

    protected void evaluateColumns(byte evaluation) {
        FillColumnEvaluator columnEvaluator = new FillColumnEvaluator(evaluation);
        List<BaseColumn> columns = table.getColumns();
        fillColumns = new ArrayList<FillColumn>(columns.size());
        fillWidth = 0;
        for (BaseColumn column : columns) {
            FillColumn fillColumn = column.visitColumn(columnEvaluator);
            if (fillColumn != null) {
                fillColumns.add(fillColumn);
                fillWidth += fillColumn.getWidth();
            }
        }
    }

    protected void createFillSubreport() throws JRException {
        FillTableSubreportFactory subreportFactory = fillSubreportFactories.get(fillColumns);
        if (subreportFactory == null) {
            subreportFactory = createFillTableSubreportFactory();
            fillSubreportFactories.put(fillColumns, subreportFactory);
        }

        fillSubreport = subreportFactory.createFillSubreport();
    }

    protected FillTableSubreportFactory createFillTableSubreportFactory() throws JRException {
        JasperReport parentReport = fillContext.getFiller().getJasperReport();
        JasperReport containingReport = containingReport(parentReport);
        JRDataset reportSubdataset = JRReportUtils.findSubdataset(table.getDatasetRun(), containingReport);

        BuiltinExpressionEvaluatorFactory builtinEvaluatorFactory = new BuiltinExpressionEvaluatorFactory();

        String tableReportName = JRAbstractCompiler.getUnitName(containingReport, reportSubdataset);

        // clone the table subdataset in order to have a different instance for other
        // elements that might be using it.
        // we're cloning the subdataset via an object factory in order to preserve
        // the relationship between objects (e.g. variables and groups) in the cloned
        // dataset
        JRDataset tableSubdataset = DatasetCloneObjectFactory.cloneDataset(reportSubdataset);
        TableReportDataset reportDataset = new TableReportDataset(tableSubdataset, tableReportName);

        TableReport tableReport = new TableReport(fillContext, table, reportDataset, fillColumns,
                builtinEvaluatorFactory);

        if (log.isDebugEnabled()) {
            String tableReportXml = new JRXmlWriter(fillContext.getFiller().getJasperReportsContext())
                    .write(tableReport, "UTF-8");
            log.debug("Generated table report:\n" + tableReportXml);
        }

        JRReportCompileData tableReportCompileData = createTableReportCompileData(containingReport,
                reportSubdataset);

        TableJasperReport compiledTableReport = new TableJasperReport(parentReport, tableReport,
                tableReportCompileData, new TableReportBaseObjectFactory(reportDataset), "");// no suffix as already included in the report name

        TableSubreport subreport = new TableSubreport(table.getDatasetRun(),
                ((JRFillComponentElement) fillContext.getComponentElement()).getParent());
        return new FillTableSubreportFactory(subreport, compiledTableReport, builtinEvaluatorFactory);
    }

    protected JasperReport containingReport(JasperReport parentReport) {
        JasperReport containingReport = parentReport;
        while (containingReport instanceof TableJasperReport) {
            containingReport = ((TableJasperReport) containingReport).getParentReport();
        }
        return containingReport;
    }

    protected JRReportCompileData createTableReportCompileData(JasperReport parentReport,
            JRDataset reportSubdataset) throws JRException {
        Serializable reportCompileDataObj = parentReport.getCompileData();
        if (!(reportCompileDataObj instanceof JRReportCompileData)) {
            throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_UNSUPPORTED_REPORT_DATA_TYPE,
                    new Object[] { reportCompileDataObj.getClass().getName() });
        }

        JRReportCompileData reportCompileData = (JRReportCompileData) reportCompileDataObj;
        Serializable datasetCompileData = reportCompileData.getDatasetCompileData(reportSubdataset);

        TableReportCompileData tableReportCompileData = new TableReportCompileData(parentReport);
        tableReportCompileData.setMainDatasetCompileData(datasetCompileData);

        JRDataset[] datasets = parentReport.getDatasets();
        if (datasets != null) {
            for (JRDataset dataset : datasets) {
                Serializable compileData = reportCompileData.getDatasetCompileData(dataset);
                tableReportCompileData.setDatasetCompileData(dataset, compileData);
            }
        }
        tableReportCompileData.copyCrosstabCompileData(reportCompileData);
        return tableReportCompileData;
    }

    @Override
    public FillPrepareResult prepare(int availableHeight) {
        try {
            if (fillColumns.isEmpty()) {
                //no columns to print
                return FillPrepareResult.NO_PRINT_NO_OVERFLOW;
            }

            JRTemplatePrintFrame printFrame = new JRTemplatePrintFrame(getFrameTemplate(), printElementOriginator);
            JRLineBox lineBox = printFrame.getLineBox();
            int verticalPadding = lineBox.getTopPadding() + lineBox.getBottomPadding();

            FillPrepareResult result = fillSubreport.prepareSubreport(availableHeight - verticalPadding, filling);

            if (verticalPadding != 0) {
                result = result.addStretch(verticalPadding);
            }

            filling = result.willOverflow();
            return result;
        } catch (JRException e) {
            throw new JRRuntimeException(e);
        }
    }

    @Override
    public JRPrintElement fill() {
        JRTemplatePrintFrame printFrame = new JRTemplatePrintFrame(getFrameTemplate(), printElementOriginator);

        if (fillSubreport.getTableReport().getBaseReport().isInteractiveTable()) {
            printFrame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_TABLE_UUID,
                    fillContext.getComponentElement().getUUID().toString());
        }

        JRLineBox lineBox = printFrame.getLineBox();

        printFrame.setUUID(fillContext.getComponentElement().getUUID());
        printFrame.setX(fillContext.getComponentElement().getX());
        printFrame.setY(fillContext.getElementPrintY());
        printFrame.setWidth(fillWidth + lineBox.getLeftPadding() + lineBox.getRightPadding());
        printFrame.setHeight(
                fillSubreport.getContentsStretchHeight() + lineBox.getTopPadding() + lineBox.getBottomPadding());
        if (fillSubreport.getTableReport().getBaseReport().isGeneratePdfTags()) {
            printFrame.getPropertiesMap().setProperty(JRPdfExporterTagHelper.PROPERTY_TAG_TABLE,
                    JRPdfExporterTagHelper.TAG_FULL);
        }

        List<JRStyle> styles = fillSubreport.getSubreportStyles();
        for (Iterator<JRStyle> it = styles.iterator(); it.hasNext();) {
            JRStyle style = it.next();
            try {
                fillContext.getFiller().addPrintStyle(style);
            } catch (JRException e) {
                throw new JRRuntimeException(e);
            }
        }

        List<JROrigin> origins = fillSubreport.getSubreportOrigins();
        for (Iterator<JROrigin> it = origins.iterator(); it.hasNext();) {
            JROrigin origin = it.next();
            fillContext.getFiller().getJasperPrint().addOrigin(origin);
        }

        Collection<JRPrintElement> elements = fillSubreport.getPrintElements();
        if (elements != null) {
            for (Iterator<JRPrintElement> it = elements.iterator(); it.hasNext();) {
                JRPrintElement element = it.next();
                printFrame.addElement(element);
            }
        }

        fillSubreport.subreportPageFilled();

        return printFrame;
    }

    protected JRTemplateFrame getFrameTemplate() {
        JRStyle style = fillContext.getElementStyle();
        JRTemplateFrame frameTemplate = printFrameTemplates.get(style);
        if (frameTemplate == null) {
            frameTemplate = new JRTemplateFrame(fillContext.getElementOrigin(),
                    fillContext.getDefaultStyleProvider());
            frameTemplate.setElement(fillContext.getComponentElement());
            frameTemplate = deduplicate(frameTemplate);

            printFrameTemplates.put(style, frameTemplate);
        }

        return frameTemplate;
    }

    @Override
    public void rewind() {
        if (filling) {
            if (log.isDebugEnabled()) {
                log.debug("Rewinding table subreport");
            }

            try {
                fillSubreport.rewind();
            } catch (JRException e) {
                throw new JRRuntimeException(e);
            }

            filling = false;
        }
    }

    protected class FillTableSubreportFactory {
        private final TableSubreport subreport;
        private final TableJasperReport compiledTableReport;
        private final BuiltinExpressionEvaluatorFactory builtinEvaluatorFactory;

        public FillTableSubreportFactory(TableSubreport subreport, TableJasperReport compiledTableReport,
                BuiltinExpressionEvaluatorFactory builtinEvaluatorFactory) {
            this.subreport = subreport;
            this.compiledTableReport = compiledTableReport;
            this.builtinEvaluatorFactory = builtinEvaluatorFactory;
        }

        public FillTableSubreport createFillSubreport() {
            return new FillTableSubreport(fillContext, subreport, factory, compiledTableReport,
                    builtinEvaluatorFactory);
        }
    }

}