/* * 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 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; import javax.swing.table.TableModel; 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 { } }