/*
* 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.states.datarow;
import javax.swing.table.TableModel;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.ParameterDataRow;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportEnvironmentDataRow;
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.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.states.CascadingDataFactory;
import org.pentaho.reporting.engine.classic.core.states.EmptyDataFactory;
import org.pentaho.reporting.engine.classic.core.states.ReportState;
import org.pentaho.reporting.engine.classic.core.states.crosstab.CrosstabSpecification;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchema;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaDefinition;
import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataAttributeContext;
public final class GlobalMasterRow implements MasterDataRow
{
private GlobalMasterRow parentRow;
private FastGlobalView globalView;
private DataFactory dataFactory;
private DataSchemaDefinition schemaDefinition;
private ProcessingDataSchemaCompiler schemaCompiler;
private DataSchema dataSchema;
private ResourceBundleFactory resourceBundleFactory;
private OutputProcessorMetaData outputProcessorMetaData;
private ReportEnvironmentDataRow environmentDataRow;
private ParameterDataRow parameterDataRow;
private ExpressionDataRow expressionDataRow;
private ImportedVariablesDataRow importedDataRow;
private DataProcessor paddingData;
private DataProcessor storedPaddingData;
private FastGlobalView storedGlobalView;
private GlobalMasterRow()
{
}
/**
* Creates a new master-row. This is called only once when the report processing starts for the very first time.
*
* @param reportContext
* @param schemaDefinition
* @param parameterDataRow
* @return
*/
public static GlobalMasterRow createReportRow(final ProcessingContext reportContext,
final DataSchemaDefinition schemaDefinition,
final ParameterDataRow parameterDataRow)
{
if (reportContext == null)
{
throw new NullPointerException();
}
if (schemaDefinition == null)
{
throw new NullPointerException();
}
if (parameterDataRow == null)
{
throw new NullPointerException();
}
final GlobalMasterRow gmr = new GlobalMasterRow();
gmr.globalView = new FastGlobalView();
gmr.paddingData = new DataProcessor();
gmr.expressionDataRow = new ExpressionDataRow(gmr.globalView, gmr, reportContext);
gmr.schemaDefinition = schemaDefinition;
gmr.dataFactory = new EmptyDataFactory();
gmr.resourceBundleFactory = reportContext.getResourceBundleFactory();
gmr.outputProcessorMetaData = reportContext.getOutputProcessorMetaData();
final DefaultDataAttributeContext dac = new DefaultDataAttributeContext
(gmr.outputProcessorMetaData, gmr.getResourceBundleFactory().getLocale());
gmr.schemaCompiler =
new ProcessingDataSchemaCompiler(schemaDefinition, dac, reportContext.getResourceManager(), null);
gmr.dataSchema = null;
gmr.setEnvironmentDataRow(new ReportEnvironmentDataRow(reportContext.getEnvironment()));
gmr.setParameterDataRow(parameterDataRow);
return gmr;
}
public void requireStructuralProcessing()
{
expressionDataRow.setIncludeStructuralProcessing(true);
}
public MasterDataRow deriveSubDataRow(final ProcessingContext reportContext,
final DataFactory reportFactory,
final ParameterDataRow parameterDataRow,
final ResourceBundleFactory resourceBundleFactory)
{
if (reportContext == null)
{
throw new NullPointerException();
}
if (reportFactory == null)
{
throw new NullPointerException();
}
if (resourceBundleFactory == null)
{
throw new NullPointerException();
}
if (parameterDataRow == null)
{
throw new NullPointerException();
}
final GlobalMasterRow gmr = new GlobalMasterRow();
gmr.outputProcessorMetaData = outputProcessorMetaData;
gmr.schemaDefinition = schemaDefinition;
gmr.schemaCompiler = schemaCompiler;
gmr.globalView = new FastGlobalView();
gmr.expressionDataRow = new ExpressionDataRow(gmr.globalView, gmr, reportContext);
gmr.expressionDataRow.setIncludeStructuralProcessing(expressionDataRow.isIncludeStructuralProcessing());
gmr.parentRow = this;
gmr.dataSchema = null;
gmr.resourceBundleFactory = resourceBundleFactory;
gmr.paddingData = new DataProcessor();
final CascadingDataFactory dataFactory = new CascadingDataFactory();
dataFactory.add(reportFactory);
dataFactory.add(this.dataFactory);
gmr.dataFactory = dataFactory;
gmr.setEnvironmentDataRow(environmentDataRow);
gmr.setParameterDataRow(parameterDataRow);
return gmr;
}
public MasterDataRow deriveWithQueryData(final TableModel tableData)
{
if (tableData == null)
{
throw new NullPointerException();
}
final GlobalMasterRow derived = derive();
derived.paddingData.setReportDataRow(new ReportDataRow(tableData), derived.globalView);
derived.dataSchema = null;
return derived;
}
public MasterDataRow deriveWithReturnFromQuery()
{
final GlobalMasterRow derived = derive();
derived.paddingData.clearReportDataRow(derived.globalView);
derived.setParameterDataRow(null);
derived.dataSchema = null;
return derived;
}
public DataFactory getDataFactory()
{
return dataFactory;
}
public DataSchema getDataSchema()
{
if (dataSchema == null)
{
try
{
dataSchema = schemaCompiler.compile(this, environmentDataRow.getEnvironment());
}
catch (ReportDataFactoryException re)
{
throw new IllegalStateException("Failed to compile data-schema - aborting report processing", re);
}
}
return dataSchema;
}
public ReportDataRow getReportDataRow()
{
return paddingData.getReportDataRow();
}
public ExpressionDataRow getExpressionDataRow()
{
return expressionDataRow;
}
private void setEnvironmentDataRow(final ReportEnvironmentDataRow environmentDataRow)
{
if (this.environmentDataRow != null)
{
DataRowEventHelper.removeAllColumns(this.environmentDataRow, this.globalView);
}
this.environmentDataRow = environmentDataRow;
if (environmentDataRow != null)
{
DataRowEventHelper.addColumns(environmentDataRow, this.globalView);
}
this.dataSchema = null;
}
public ParameterDataRow getParameterDataRow()
{
return parameterDataRow;
}
private void setParameterDataRow(final ParameterDataRow parameterDataRow)
{
if (this.parameterDataRow != null)
{
DataRowEventHelper.removeAllColumns(this.parameterDataRow, this.globalView);
}
this.parameterDataRow = parameterDataRow;
if (parameterDataRow != null)
{
DataRowEventHelper.addColumns(this.parameterDataRow, globalView);
}
this.dataSchema = null;
}
public DataRow getGlobalView()
{
return globalView;
}
public boolean isAdvanceable()
{
return paddingData.isAdvanceable(globalView);
}
public GlobalMasterRow derive()
{
final GlobalMasterRow o = new GlobalMasterRow();
o.storedGlobalView = storedGlobalView;
o.storedPaddingData = storedPaddingData;
o.environmentDataRow = environmentDataRow;
o.outputProcessorMetaData = outputProcessorMetaData;
o.dataFactory = dataFactory;
o.dataSchema = dataSchema;
o.schemaCompiler = schemaCompiler;
o.schemaDefinition = schemaDefinition;
o.globalView = globalView.derive();
o.parameterDataRow = parameterDataRow;
o.paddingData = paddingData.derive();
o.resourceBundleFactory = resourceBundleFactory;
o.expressionDataRow = expressionDataRow.derive(o.globalView, o, false);
if (parentRow != null)
{
o.parentRow = parentRow.derive();
}
o.importedDataRow = importedDataRow;
return o;
}
public void setImportedDataRow(final ImportedVariablesDataRow dataRow)
{
if (importedDataRow != null)
{
DataRowEventHelper.removeAllColumns(this.importedDataRow, this.globalView);
}
this.importedDataRow = dataRow;
if (importedDataRow != null)
{
DataRowEventHelper.addColumns(importedDataRow, this.globalView);
}
this.dataSchema = null;
}
public MasterDataRow getParentDataRow()
{
return parentRow;
}
/**
* This advances the cursor by one row and updates the flags.
*
* @return
*/
public MasterDataRow advance()
{
return advanceRecursively(false, null);
}
/**
* This method is public as a implementation side effect. It is only intended to be used internally and no matter what
* you intend: If you are not calling it from a MasterDataRow implementation, then you are on the wrong track.
*
* @param deepTraversingOnly
* @param subReportRow
* @return
*/
public GlobalMasterRow advanceRecursively(final boolean deepTraversingOnly,
final MasterDataRow subReportRow)
{
final GlobalMasterRow dataRow = new GlobalMasterRow();
dataRow.storedPaddingData = storedPaddingData;
dataRow.storedGlobalView = storedGlobalView;
dataRow.environmentDataRow = environmentDataRow;
dataRow.outputProcessorMetaData = outputProcessorMetaData;
if (deepTraversingOnly == false)
{
dataRow.globalView = globalView.advance();
}
else
{
dataRow.globalView = globalView.derive();
}
dataRow.dataSchema = dataSchema;
dataRow.dataFactory = dataFactory;
dataRow.schemaCompiler = schemaCompiler;
dataRow.schemaDefinition = schemaDefinition;
dataRow.resourceBundleFactory = resourceBundleFactory;
if (environmentDataRow != null)
{
DataRowEventHelper.refreshDataRow(dataRow.environmentDataRow, dataRow.globalView);
}
if (parameterDataRow != null)
{
dataRow.parameterDataRow = parameterDataRow;
DataRowEventHelper.refreshDataRow(dataRow.parameterDataRow, dataRow.globalView);
}
dataRow.paddingData = paddingData.advance(deepTraversingOnly, dataRow.globalView);
if (expressionDataRow != null)
{
dataRow.expressionDataRow = expressionDataRow.derive(dataRow.globalView, dataRow, true);
}
if (parentRow != null)
{
// the parent row should get a grip on our data as well - just for the
// deep traversing fun and so on ..
dataRow.parentRow = parentRow.advanceRecursively(true, dataRow);
}
if (importedDataRow != null)
{
if (subReportRow != null)
{
dataRow.importedDataRow = importedDataRow.refresh(subReportRow.getGlobalView(), subReportRow.getDataSchema());
DataRowEventHelper.refreshDataRow(dataRow.importedDataRow, dataRow.globalView);
}
else
{
dataRow.importedDataRow = importedDataRow;
DataRowEventHelper.refreshDataRow(dataRow.importedDataRow, dataRow.globalView);
}
}
dataRow.refresh();
dataRow.globalView.validateChangedFlags();
return dataRow;
}
public void fireReportEvent(final ReportEvent event)
{
if (expressionDataRow != null)
{
expressionDataRow.fireReportEvent(event);
}
if ((event.getType() & ReportEvent.NO_PARENT_PASSING_EVENT) == ReportEvent.NO_PARENT_PASSING_EVENT)
{
return;
}
if (parentRow != null)
{
final ReportState parentState = event.getState().getParentSubReportState();
final ReportEvent deepEvent;
if (parentState == null)
{
deepEvent = event;
}
else
{
deepEvent = new ReportEvent
(parentState, event.getState(), event.getType() | ReportEvent.DEEP_TRAVERSING_EVENT);
}
parentRow.fireReportEvent(deepEvent);
if (parentRow.importedDataRow != null)
{
parentRow.importedDataRow = parentRow.importedDataRow.refresh(this.getGlobalView(), this.getDataSchema());
DataRowEventHelper.refreshDataRow(parentRow.importedDataRow, parentRow.globalView);
}
}
}
public MasterDataRow startCrosstabMode(final CrosstabSpecification crosstabSpecification)
{
final GlobalMasterRow retval = derive();
retval.paddingData = paddingData.startCrosstabMode(crosstabSpecification, retval.globalView);
return retval;
}
public MasterDataRow endCrosstabMode()
{
final GlobalMasterRow retval = derive();
retval.paddingData = paddingData.endCrosstabMode();
return retval;
}
public MasterDataRow resetRowCursor()
{
final GlobalMasterRow retval = derive();
retval.paddingData = paddingData.resetRowCursor();
return retval;
}
public MasterDataRow clearExportedParameters()
{
if (importedDataRow == null)
{
return this;
}
final GlobalMasterRow derived = derive();
derived.setImportedDataRow(null);
derived.resetDataSchema();
return derived;
}
public ResourceBundleFactory getResourceBundleFactory()
{
return resourceBundleFactory;
}
public void resetDataSchema()
{
this.dataSchema = null;
}
public GlobalMasterRow rebuild()
{
if (globalView.getColumnNames().length == 0)
{
return this;
}
if (parentRow != null)
{
throw new IllegalStateException
("This should be at the beginning of the master-report processing. No parent allowed.");
}
if (paddingData.getReportDataRow() != null)
{
throw new IllegalStateException
("This should be at the beginning of the master-report processing. No report-data allowed.");
}
final GlobalMasterRow gmr = derive();
gmr.dataSchema = null;
gmr.globalView = new FastGlobalView();
gmr.setEnvironmentDataRow(environmentDataRow);
gmr.setParameterDataRow(parameterDataRow);
return gmr;
}
public MasterDataRow updateDataSchema(final DataSchemaDefinition dataSchemaDefinition)
{
if (dataSchemaDefinition == null)
{
throw new NullPointerException();
}
final DefaultDataAttributeContext dac = new DefaultDataAttributeContext
(outputProcessorMetaData, resourceBundleFactory.getLocale());
final GlobalMasterRow gmr = derive();
gmr.schemaDefinition = dataSchemaDefinition;
gmr.schemaCompiler = new ProcessingDataSchemaCompiler
(dataSchemaDefinition, dac, schemaCompiler.getResourceManager(), schemaCompiler.getGlobalDefaults());
gmr.dataSchema = null;
return gmr;
}
public DataSchemaDefinition getDataSchemaDefinition()
{
return schemaDefinition;
}
public void refresh()
{
if (environmentDataRow != null)
{
DataRowEventHelper.refreshDataRow(environmentDataRow, this.globalView);
}
if (parameterDataRow != null)
{
DataRowEventHelper.refreshDataRow(parameterDataRow, this.globalView);
}
else
{
throw new NullPointerException();
}
if (paddingData != null && !paddingData.isCrosstabActive())
{
paddingData.refresh(this.globalView);
}
if (expressionDataRow != null)
{
expressionDataRow.refresh();
}
if (importedDataRow != null)
{
DataRowEventHelper.refreshDataRow(importedDataRow, this.globalView);
}
}
public ImportedVariablesDataRow getImportedDataRow()
{
return importedDataRow;
}
public TableModel getReportData()
{
return paddingData.getRawData();
}
public int getCursor()
{
return paddingData.getCursor();
}
public int getRawDataCursor()
{
return paddingData.getRawDataCursor();
}
public int getRowCount()
{
// todo: Shall we return the padded count (includes all additional crosstab rows) or shall we just
// return the raw row count?
final TableModel rawData = paddingData.getRawData();
if (rawData != null)
{
return rawData.getRowCount();
}
return 0;
}
public CrosstabSpecification getCrosstabSpecification()
{
return paddingData.getCrosstabSpecification();
}
public boolean isCrosstabActive()
{
return paddingData.isCrosstabActive();
}
public MasterDataRow recordCrosstabRowState()
{
final GlobalMasterRow copy = derive();
copy.storedPaddingData = paddingData.clone();
copy.storedGlobalView = globalView.derive();
return copy;
}
public MasterDataRow clearRecordedCrosstabRowState()
{
final GlobalMasterRow copy = derive();
copy.storedPaddingData = null;
copy.storedGlobalView = null;
return copy;
}
public MasterDataRow replayStoredCrosstabRowState()
{
final GlobalMasterRow copy = derive();
copy.globalView = storedGlobalView.derive();
copy.paddingData = storedPaddingData.clone();
copy.expressionDataRow = expressionDataRow.derive(copy.globalView, copy, false);
copy.refresh();
return copy;
}
public void validateReplayFinished() throws ReportProcessingException
{
}
}