/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.states.datarow;
import java.util.ArrayList;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.ParameterDataRow;
import org.pentaho.reporting.engine.classic.core.ParameterMapping;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.event.ReportEvent;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterDefinitionEntry;
import org.pentaho.reporting.engine.classic.core.states.crosstab.CrosstabSpecification;
import org.pentaho.reporting.engine.classic.core.util.IntegerCache;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchema;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaDefinition;
import org.pentaho.reporting.libraries.base.util.FastStack;
/**
* Creation-Date: 20.02.2006, 15:30:21
*
* @author Thomas Morgner
*/
public final class DefaultFlowController
{
private static class ReportDataContext
{
private boolean advanceRequested;
protected ReportDataContext(final boolean advanceRequested)
{
this.advanceRequested = advanceRequested;
}
public boolean isAdvanceRequested()
{
return advanceRequested;
}
}
private MasterDataRow dataRow;
private boolean advanceRequested;
private FastStack expressionsStack;
private FastStack dataContextStack;
private String exportDescriptor;
private ProcessingContext reportContext;
private ReportParameterValues parameters;
public DefaultFlowController(final ProcessingContext reportContext,
final DataSchemaDefinition schemaDefinition,
final ReportParameterValues parameters,
final ParameterDefinitionEntry[] parameterDefinitionEntries,
final boolean includeStructuralPreprocessing) throws ReportDataFactoryException
{
if (reportContext == null)
{
throw new NullPointerException();
}
if (parameters == null)
{
throw new NullPointerException();
}
if (schemaDefinition == null)
{
throw new NullPointerException();
}
this.reportContext = reportContext;
this.exportDescriptor = reportContext.getExportDescriptor();
this.expressionsStack = new FastStack(10);
this.dataContextStack = new FastStack(10);
this.advanceRequested = false;
this.parameters = parameters;
this.dataRow = GlobalMasterRow.createReportRow
(reportContext, schemaDefinition, new ParameterDataRow(parameters), parameterDefinitionEntries, includeStructuralPreprocessing);
}
private DefaultFlowController(final DefaultFlowController fc,
final MasterDataRow dataRow)
{
if (dataRow == null)
{
throw new NullPointerException();
}
this.reportContext = fc.reportContext;
this.exportDescriptor = fc.exportDescriptor;
this.dataContextStack = (FastStack) fc.dataContextStack.clone();
this.expressionsStack = (FastStack) fc.expressionsStack.clone();
this.advanceRequested = fc.advanceRequested;
this.dataRow = dataRow;
this.parameters = fc.parameters;
}
public DefaultFlowController derive()
{
return new DefaultFlowController(this, dataRow.derive());
}
public DefaultFlowController performAdvance()
{
if (dataRow.isAdvanceable() && advanceRequested == false)
{
final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
fc.advanceRequested = true;
return fc;
}
return this;
}
public DefaultFlowController performCommit()
{
if (isAdvanceRequested())
{
final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
fc.dataRow = dataRow.advance();
fc.advanceRequested = false;
return fc;
}
return this;
}
public MasterDataRow getMasterRow()
{
return dataRow;
}
public boolean isAdvanceRequested()
{
return advanceRequested;
}
/**
* This should be called only once per report processing. A JFreeReport object defines the global master report - all
* other reports are subreport instances.
* <p/>
* The global master report receives its parameter set from the Job-Definition, while subreports will read their
* parameters from the current datarow state.
*
* @param query
* @param queryLimit
* @param queryTimeout
* @return
* @throws ReportDataFactoryException
*/
public DefaultFlowController performQuery(final DataFactory dataFactory,
final String query,
final int queryLimit,
final int queryTimeout,
final ResourceBundleFactory resourceBundleFactory)
throws ReportDataFactoryException
{
if (dataFactory == null)
{
throw new NullPointerException();
}
if (resourceBundleFactory == null)
{
throw new NullPointerException();
}
final MasterDataRow masterRowWithoutData =
dataRow.deriveSubDataRow(reportContext, dataFactory, new ParameterDataRow(parameters),
dataRow.getParameterDefinitionEntries(), resourceBundleFactory);
final ReportDataRow tableData;
if (query != null)
{
tableData = ReportDataRow.createDataRow
(masterRowWithoutData.getDataFactory(), query, queryLimit, queryTimeout,
masterRowWithoutData.getGlobalView());
}
else
{
tableData = new ReportDataRow(new EmptyTableModel());
}
final MasterDataRow masterRow = masterRowWithoutData.deriveWithQueryData(tableData);
final DefaultFlowController fc = new DefaultFlowController(this, masterRow);
fc.dataContextStack.push(new ReportDataContext(advanceRequested));
fc.dataRow = masterRow;
fc.dataRow.resetDataSchema();
return fc;
}
public DefaultFlowController performInitSubreport(final DataFactory dataFactory,
final ParameterMapping[] inputParameters,
final ResourceBundleFactory resourceBundleFactory)
{
if (dataFactory == null)
{
throw new NullPointerException();
}
if (inputParameters == null)
{
throw new NullPointerException();
}
if (resourceBundleFactory == null)
{
throw new NullPointerException();
}
// create a view for the parameters of the report ...
final MasterDataRow subReportDataRow;
if (isGlobalImportOrExport(inputParameters))
{
final ParameterDataRow parameterRow = new ParameterDataRow(dataRow.getGlobalView());
subReportDataRow = dataRow.deriveSubDataRow(reportContext, dataFactory, parameterRow, null, resourceBundleFactory);
}
else
{
final ParameterDataRow parameterRow = new ParameterDataRow(inputParameters, dataRow.getGlobalView());
subReportDataRow = dataRow.deriveSubDataRow(reportContext, dataFactory, parameterRow, null, resourceBundleFactory);
}
final DefaultFlowController fc = new DefaultFlowController(this, subReportDataRow);
fc.dataContextStack.push(new ReportDataContext(advanceRequested));
fc.dataRow = subReportDataRow;
fc.dataRow.resetDataSchema();
return fc;
}
public DefaultFlowController performSubReportQuery(final String query,
final int queryLimit,
final int queryTimeout,
final ParameterMapping[] outputParameters)
throws ReportDataFactoryException
{
if (outputParameters == null)
{
throw new NullPointerException();
}
final MasterDataRow subReportDataRow = this.dataRow;
// perform the query ...
// add the resultset ...
final ReportDataRow tableData;
if (query != null)
{
tableData = ReportDataRow.createDataRow
(subReportDataRow.getDataFactory(), query, queryLimit, queryTimeout, subReportDataRow.getGlobalView());
}
else
{
tableData = new ReportDataRow(new EmptyTableModel());
}
final MasterDataRow masterRow = subReportDataRow.deriveWithQueryData(tableData);
if (isGlobalImportOrExport(outputParameters))
{
if ("true".equals(reportContext.getConfiguration().getConfigProperty
("org.pentaho.reporting.engine.classic.core.EnableGlobalSubReportImports")))
{
masterRow.getParentDataRow().setImportedDataRow(new ImportedVariablesDataRow(masterRow));
}
else
{
masterRow.getParentDataRow().setImportedDataRow
(new ImportedVariablesDataRow(masterRow, filterGlobalImport(outputParameters)));
}
}
else
{
// check and rebuild the parameter mapping from the inner to the outer
// context. Only deep-traversal expressions will be able to see these
// values (unless they have been defined as local variables).
masterRow.getParentDataRow().setImportedDataRow(new ImportedVariablesDataRow(masterRow, outputParameters));
}
final DefaultFlowController fc = new DefaultFlowController(this, masterRow);
fc.dataContextStack.push(new ReportDataContext(advanceRequested));
fc.dataRow = masterRow;
fc.dataRow.resetDataSchema();
return fc;
}
private ParameterMapping[] filterGlobalImport(final ParameterMapping[] parameterMapping)
{
final ArrayList filteredList = new ArrayList(parameterMapping.length);
for (int i = 0; i < parameterMapping.length; i++)
{
final ParameterMapping mapping = parameterMapping[i];
if ("*".equals(mapping.getName()) &&
"*".equals(mapping.getAlias()))
{
continue;
}
filteredList.add(mapping);
}
return (ParameterMapping[]) filteredList.toArray(new ParameterMapping[filteredList.size()]);
}
/**
* Checks whether a global import is defined. A global import effectly overrides all other imports.
*
* @return true, if there is a global import defined, false otherwise.
*/
private boolean isGlobalImportOrExport(final ParameterMapping[] inputParameters)
{
for (int i = 0; i < inputParameters.length; i++)
{
final ParameterMapping inputParameter = inputParameters[i];
if ("*".equals(inputParameter.getName()) &&
"*".equals(inputParameter.getAlias()))
{
return true;
}
}
return false;
}
public DefaultFlowController activateExpressions(final Expression[] expressions,
final boolean preserveState)
throws ReportProcessingException
{
if (expressions == null)
{
throw new NullPointerException();
}
final MasterDataRow dataRow = this.dataRow.derive();
final ExpressionDataRow edr = dataRow.getExpressionDataRow();
edr.pushExpressions(expressions, preserveState);
dataRow.resetDataSchema();
final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
final Integer exCount = IntegerCache.getInteger(expressions.length);
fc.expressionsStack.push(exCount);
return fc;
}
public DefaultFlowController deactivateExpressions()
{
final Integer counter = (Integer) this.expressionsStack.peek();
final int counterRaw = counter.intValue();
if (counterRaw == 0)
{
final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
fc.expressionsStack.pop();
return fc;
}
final MasterDataRow dataRow = this.dataRow.derive();
final ExpressionDataRow edr = dataRow.getExpressionDataRow();
final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
fc.expressionsStack.pop();
edr.popExpressions(counterRaw);
dataRow.resetDataSchema();
return fc;
}
public DefaultFlowController performReturnFromQuery()
{
final ReportDataRow reportDataRow = dataRow.getReportDataRow();
if (reportDataRow == null)
{
return this;
}
// We dont close the report data, as some previously saved states may
// still reference it. (The caching report data factory takes care of
// that later.)
final MasterDataRow innerDr = dataRow.deriveWithReturnFromQuery();
final DefaultFlowController fc = new DefaultFlowController(this, innerDr);
final ReportDataContext context = (ReportDataContext) fc.dataContextStack.pop();
fc.dataRow = dataRow.getParentDataRow();
fc.dataRow = fc.dataRow.derive();
fc.advanceRequested = context.isAdvanceRequested();
innerDr.resetDataSchema();
return fc;
}
public DefaultFlowController performReturnFromSubreport()
{
// first, we undo the call "performSubreportQuery" and unwrap the report-data (and its corresponding stack entry)
final MasterDataRow innerDrFromQuery = dataRow.deriveWithReturnFromQuery();
final DefaultFlowController fc = new DefaultFlowController(this, innerDrFromQuery);
final ReportDataContext context = (ReportDataContext) fc.dataContextStack.pop();
fc.dataRow = fc.dataRow.derive();
fc.advanceRequested = context.isAdvanceRequested();
// second, we undo the call "performInitSubreport" and unwrap the parameter-data (and its corresponding stack entry)
final MasterDataRow innerDr = fc.dataRow.deriveWithReturnFromQuery();
final DefaultFlowController fc2 = new DefaultFlowController(fc, innerDr);
final ReportDataContext context2 = (ReportDataContext) fc2.dataContextStack.pop();
fc2.dataRow = dataRow.getParentDataRow();
fc2.dataRow = fc2.dataRow.derive();
fc2.advanceRequested = context2.isAdvanceRequested();
innerDr.resetDataSchema();
return fc2;
}
public DefaultFlowController performClearExportedParameters()
{
final ImportedVariablesDataRow exportedDataRow = dataRow.getImportedDataRow();
if (exportedDataRow == null)
{
return this;
}
return new DefaultFlowController(this, dataRow.clearExportedParameters());
}
public String getExportDescriptor()
{
return exportDescriptor;
}
public ProcessingContext getReportContext()
{
return reportContext;
}
public DefaultFlowController fireReportEvent(final ReportEvent event)
{
dataRow.fireReportEvent(event);
return this;
}
public int getCursor()
{
if (dataRow.getReportDataRow() != null)
{
return dataRow.getReportDataRow().getCursor();
}
return 0;
}
public DataSchema getDataSchema()
{
return dataRow.getDataSchema();
}
public DataFactory getDataFactory()
{
return dataRow.getDataFactory();
}
public DefaultFlowController startCrosstabMode(final CrosstabSpecification crosstabSpecification)
{
final MasterDataRow dataRow = this.dataRow.startCrosstabMode(crosstabSpecification);
// begin crosstab mode ...
return new DefaultFlowController(this, dataRow);
}
public DefaultFlowController endCrosstabMode()
{
final MasterDataRow dataRow = this.dataRow.endCrosstabMode();
// end crosstab mode ...
return new DefaultFlowController(this, dataRow);
}
public int getCurrentCrosstabPaddingItem()
{
return dataRow.getPrePaddingCount();
}
public DefaultFlowController resetRowCursor()
{
final MasterDataRow dataRow = this.dataRow.resetRowCursor();
return new DefaultFlowController(this, dataRow);
}
public DefaultFlowController restart()
{
final GlobalMasterRow innerDr = dataRow.rebuild();
return new DefaultFlowController(this, innerDr);
}
public DefaultFlowController updateDataSchema(final DataSchemaDefinition dataSchemaDefinition)
{
return new DefaultFlowController(this, dataRow.updateDataSchema(dataSchemaDefinition));
}
}