/*
* 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.process;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.CrosstabGroup;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.Group;
import org.pentaho.reporting.engine.classic.core.InvalidReportStateException;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ParameterMapping;
import org.pentaho.reporting.engine.classic.core.ReportDefinition;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.ReportParameterValidationException;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.RootLevelBand;
import org.pentaho.reporting.engine.classic.core.Section;
import org.pentaho.reporting.engine.classic.core.SubReport;
import org.pentaho.reporting.engine.classic.core.cache.CachingDataFactory;
import org.pentaho.reporting.engine.classic.core.event.ReportEvent;
import org.pentaho.reporting.engine.classic.core.filter.types.ExternalElementType;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.function.ExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.function.OutputFunction;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.function.ProcessingDataFactoryContext;
import org.pentaho.reporting.engine.classic.core.function.StructureFunction;
import org.pentaho.reporting.engine.classic.core.function.sys.AttributeExpressionsEvaluator;
import org.pentaho.reporting.engine.classic.core.function.sys.CellFormatFunction;
import org.pentaho.reporting.engine.classic.core.function.sys.MetaDataStyleEvaluator;
import org.pentaho.reporting.engine.classic.core.function.sys.SheetNameFunction;
import org.pentaho.reporting.engine.classic.core.function.sys.StyleExpressionsEvaluator;
import org.pentaho.reporting.engine.classic.core.function.sys.StyleResolvingEvaluator;
import org.pentaho.reporting.engine.classic.core.function.sys.WizardItemHideFunction;
import org.pentaho.reporting.engine.classic.core.layout.InlineSubreportMarker;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.sorting.SortConstraint;
import org.pentaho.reporting.engine.classic.core.parameters.DefaultParameterContext;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterDefinition;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterValidator;
import org.pentaho.reporting.engine.classic.core.parameters.ValidationResult;
import org.pentaho.reporting.engine.classic.core.sorting.SortingDataFactory;
import org.pentaho.reporting.engine.classic.core.states.DataFactoryManager;
import org.pentaho.reporting.engine.classic.core.states.DefaultGroupSizeRecorder;
import org.pentaho.reporting.engine.classic.core.states.DefaultGroupingState;
import org.pentaho.reporting.engine.classic.core.states.DesignTimeDataFactory;
import org.pentaho.reporting.engine.classic.core.states.EmptyDataFactory;
import org.pentaho.reporting.engine.classic.core.states.EmptyGroupSizeRecorder;
import org.pentaho.reporting.engine.classic.core.states.FunctionStorage;
import org.pentaho.reporting.engine.classic.core.states.FunctionStorageKey;
import org.pentaho.reporting.engine.classic.core.states.GroupSizeRecorder;
import org.pentaho.reporting.engine.classic.core.states.GroupStartRecord;
import org.pentaho.reporting.engine.classic.core.states.GroupingState;
import org.pentaho.reporting.engine.classic.core.states.IgnoreEverythingReportErrorHandler;
import org.pentaho.reporting.engine.classic.core.states.InitialLayoutProcess;
import org.pentaho.reporting.engine.classic.core.states.LayoutProcess;
import org.pentaho.reporting.engine.classic.core.states.PerformanceMonitorContext;
import org.pentaho.reporting.engine.classic.core.states.ProcessStateHandle;
import org.pentaho.reporting.engine.classic.core.states.ReportDefinitionImpl;
import org.pentaho.reporting.engine.classic.core.states.ReportProcessingErrorHandler;
import org.pentaho.reporting.engine.classic.core.states.ReportState;
import org.pentaho.reporting.engine.classic.core.states.ReportStateKey;
import org.pentaho.reporting.engine.classic.core.states.StateUtilities;
import org.pentaho.reporting.engine.classic.core.states.StructureFunctionComparator;
import org.pentaho.reporting.engine.classic.core.states.SubLayoutProcess;
import org.pentaho.reporting.engine.classic.core.states.SubReportStorage;
import org.pentaho.reporting.engine.classic.core.states.crosstab.CrosstabProcessorFunction;
import org.pentaho.reporting.engine.classic.core.states.datarow.DefaultFlowController;
import org.pentaho.reporting.engine.classic.core.states.datarow.InlineDataRowRuntime;
import org.pentaho.reporting.engine.classic.core.states.datarow.MasterDataRow;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.util.IntegerCache;
import org.pentaho.reporting.engine.classic.core.util.LongSequence;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.engine.classic.core.util.beans.BeanException;
import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry;
import org.pentaho.reporting.engine.classic.core.wizard.ProxyDataSchemaDefinition;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.config.HierarchicalConfiguration;
import org.pentaho.reporting.libraries.base.config.ModifiableConfiguration;
import org.pentaho.reporting.libraries.base.util.ArgumentNullException;
import org.pentaho.reporting.libraries.base.util.FastStack;
import org.pentaho.reporting.libraries.base.util.PerformanceLoggingStopWatch;
@SuppressWarnings("HardCodedStringLiteral")
public class ProcessState implements ReportState
{
private static class InternalProcessHandle implements ProcessStateHandle
{
private PerformanceMonitorContext monitorContext;
private DataFactoryManager manager;
private InternalProcessHandle(final DataFactoryManager manager,
final PerformanceMonitorContext monitorContext)
{
this.manager = manager;
this.monitorContext = monitorContext;
}
public void close()
{
// close the data-factory manager ...
monitorContext.close();
manager.close();
}
}
private static class InternalPerformanceMonitorContext implements PerformanceMonitorContext
{
private PerformanceMonitorContext parent;
private EventListenerList listeners;
private InternalPerformanceMonitorContext(final PerformanceMonitorContext parent)
{
this.parent = parent;
this.listeners = new EventListenerList();
}
public PerformanceLoggingStopWatch createStopWatch(final String tag)
{
return parent.createStopWatch(tag);
}
public PerformanceLoggingStopWatch createStopWatch(final String tag, final Object message)
{
return parent.createStopWatch(tag, message);
}
public void addChangeListener(final ChangeListener listener)
{
listeners.add(ChangeListener.class, listener);
}
public void removeChangeListener(final ChangeListener listener)
{
listeners.remove(ChangeListener.class, listener);
}
public void close()
{
ChangeEvent event = new ChangeEvent(this);
for (final ChangeListener changeListener : listeners.getListeners(ChangeListener.class))
{
changeListener.stateChanged(event);
}
}
}
public static final int ARTIFICIAL_EVENT_CODE = ReportEvent.ARTIFICIAL_EVENT_CODE;
private static final Log logger = LogFactory.getLog(ProcessState.class);
private int currentGroupIndex;
private int currentPresentationGroupIndex;
private ReportDefinitionImpl report;
private int currentSubReport;
private InlineSubreportMarker[] subReports;
private ProcessState suspendedState;
private ProcessState parentSubReportState;
private FunctionStorage functionStorage;
private FunctionStorage structureFunctionStorage;
private DataFactoryManager dataFactoryManager;
private InternalProcessHandle processHandle;
private DefaultFlowController flowController;
private LayoutProcess layoutProcess;
private ReportStateKey processKey;
private AdvanceHandler advanceHandler;
private ReportProcessingErrorHandler errorHandler;
private int sequenceCounter;
private boolean inItemGroup;
private InlineSubreportMarker currentSubReportMarker;
private boolean inlineProcess;
private FastStack<GroupStartRecord> groupStarts;
private boolean structuralPreprocessingNeeded;
private HashSet<Integer> processLevels;
private SubReportStorage subReportStorage;
private String query;
private Integer queryLimit;
private Integer queryTimeout;
private GroupSizeRecorder recorder;
private boolean reportInstancesShareConnection;
private AdvanceHandler postSummaryRowAdvanceHandler;
private int replayStoredCrosstabGroup;
private LongSequence groupSequenceCounter;
private LongSequence crosstabColumnSequenceCounter;
private boolean designtime;
private PerformanceMonitorContext performanceMonitorContext;
public ProcessState()
{
}
public void initializeForMasterReport(final MasterReport report,
final ProcessingContext processingContext,
final OutputFunction outputFunction)
throws ReportProcessingException
{
ArgumentNullException.validate("report", report);
ArgumentNullException.validate("processingContext", processingContext);
ArgumentNullException.validate("outputFunction", outputFunction);
final ReportParameterDefinition parameters = report.getParameterDefinition();
final DefaultParameterContext parameterContext = new DefaultParameterContext(report);
// pre-init the output-processor-metadata.
initializeProcessingContext(processingContext, report);
this.designtime =
processingContext.getOutputProcessorMetaData().isFeatureSupported(OutputProcessorFeature.DESIGNTIME);
final ReportParameterValues parameterValues;
if (designtime == false)
{
try
{
final ReportParameterValidator reportParameterValidator = parameters.getValidator();
final ValidationResult validationResult =
reportParameterValidator.validate(new ValidationResult(), parameters, parameterContext);
if (validationResult.isEmpty() == false)
{
throw new ReportParameterValidationException
("The parameters provided for this report are not valid.", validationResult);
}
parameterValues = validationResult.getParameterValues();
}
finally
{
parameterContext.close();
}
}
else
{
parameterValues = new ReportParameterValues();
}
final PerformanceMonitorContext rawPerformanceMonitorContext =
ClassicEngineBoot.getInstance().getObjectFactory().get(PerformanceMonitorContext.class);
this.performanceMonitorContext = new InternalPerformanceMonitorContext(rawPerformanceMonitorContext);
final InitialLayoutProcess layoutProcess = new InitialLayoutProcess(outputFunction, performanceMonitorContext);
this.reportInstancesShareConnection = "true".equals(processingContext.getConfiguration().getConfigProperty
("org.pentaho.reporting.engine.classic.core.ReportInstancesShareConnections"));
this.processLevels = new HashSet<Integer>();
this.groupStarts = new FastStack<GroupStartRecord>();
this.errorHandler = IgnoreEverythingReportErrorHandler.INSTANCE;
this.advanceHandler = BeginReportHandler.HANDLER;
this.currentSubReport = -1;
this.currentGroupIndex = ReportState.BEFORE_FIRST_GROUP;
this.currentPresentationGroupIndex = ReportState.BEFORE_FIRST_GROUP;
this.functionStorage = new FunctionStorage();
this.structureFunctionStorage = new FunctionStorage();
this.sequenceCounter = 0;
this.processKey = new ReportStateKey
(null, ReportState.BEFORE_FIRST_ROW, 0, ReportState.BEFORE_FIRST_GROUP, -1, sequenceCounter, false, false);
this.dataFactoryManager = new DataFactoryManager();
this.subReportStorage = new SubReportStorage();
this.processHandle = new InternalProcessHandle(dataFactoryManager, performanceMonitorContext);
this.crosstabColumnSequenceCounter = new LongSequence(10, -1);
this.groupSequenceCounter = new LongSequence(10, -1);
this.groupSequenceCounter.set(0, -1);
final DefaultFlowController startFlowController =
new DefaultFlowController(processingContext, report.getDataSchemaDefinition(),
StateUtilities.computeParameterValueSet(report, parameterValues), performanceMonitorContext);
final MasterReportProcessPreprocessor processPreprocessor = new MasterReportProcessPreprocessor(startFlowController);
final MasterReport processedReport = processPreprocessor.invokePreDataProcessing(report);
final DefaultFlowController flowController = processPreprocessor.getFlowController();
final Object dataCacheEnabledRaw =
processedReport.getAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.DATA_CACHE);
final boolean dataCacheEnabled = designtime == false && Boolean.FALSE.equals(dataCacheEnabledRaw) == false;
final DataFactory sortingDataFactory =
new SortingDataFactory(lookupDataFactory(processedReport), performanceMonitorContext);
final CachingDataFactory dataFactory = new CachingDataFactory(sortingDataFactory, dataCacheEnabled);
dataFactory.initialize(new ProcessingDataFactoryContext(processingContext, dataFactory));
final FunctionStorageKey functionStorageKey = FunctionStorageKey.createKey(null, processedReport);
this.dataFactoryManager.store(functionStorageKey, dataFactory, true);
// eval query, query-limit and query-timeout
this.flowController = flowController;
final Integer queryLimitDefault = IntegerCache.getInteger(processedReport.getQueryLimit());
final Integer queryTimeoutDefault = IntegerCache.getInteger(processedReport.getQueryTimeout());
final String queryDefined = designtime ? "design-time-query" : processedReport.getQuery();
final Object queryRaw = evaluateExpression(processedReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY), queryDefined);
final Object queryLimitRaw = evaluateExpression(processedReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY_LIMIT), queryLimitDefault);
final Object queryTimeoutRaw = evaluateExpression(processedReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY_TIMEOUT), queryTimeoutDefault);
final List<SortConstraint> sortOrder = lookupSortOrder(processedReport);
this.query = (String) ConverterRegistry.convert(queryRaw, String.class, processedReport.getQuery());
this.queryLimit = (Integer) ConverterRegistry.convert(queryLimitRaw, Integer.class, queryLimitDefault);
this.queryTimeout = (Integer) ConverterRegistry.convert(queryTimeoutRaw, Integer.class, queryTimeoutDefault);
DefaultFlowController postQueryFlowController = flowController.performQuery
(dataFactory, query, queryLimit.intValue(), queryTimeout.intValue(),
processingContext.getResourceBundleFactory(), sortOrder);
final MasterReportProcessPreprocessor postProcessor = new MasterReportProcessPreprocessor(postQueryFlowController);
final MasterReport fullReport = postProcessor.invokePreProcessing(processedReport);
postQueryFlowController = postProcessor.getFlowController();
if (isStructureRunNeeded(processedReport) == false)
{
// Perform a static analysis on whether there is an External-element or Inline-Subreports or Crosstabs
// if none, return unchanged
this.structuralPreprocessingNeeded = false;
}
else
{
// otherwise process the report one time to walk through all eligible states. Record all subreports,
// and then compute the runlevels based on what we have in the caches.
this.structuralPreprocessingNeeded = true;
this.processLevels.add(LayoutProcess.LEVEL_STRUCTURAL_PREPROCESSING);
postQueryFlowController.requireStructuralProcessing();
}
final Expression[] expressions;
if (designtime)
{
expressions = new Expression[0];
}
else
{
expressions = fullReport.getExpressions().getExpressions();
}
this.flowController = postQueryFlowController.activateExpressions(expressions, false);
this.report = new ReportDefinitionImpl(fullReport, fullReport.getPageDefinition());
this.layoutProcess = new SubLayoutProcess(layoutProcess,
computeStructureFunctions(fullReport.getStructureFunctions(),
getFlowController().getReportContext().getOutputProcessorMetaData()), fullReport.getObjectID());
if (StateUtilities.computeLevels(this.flowController, this.layoutProcess, processLevels))
{
this.recorder = new DefaultGroupSizeRecorder();
}
else
{
this.recorder = new EmptyGroupSizeRecorder();
}
this.processKey = createKey();
}
private List<SortConstraint> lookupSortOrder(final AbstractReportDefinition report)
{
Object attribute = report.getAttribute
(AttributeNames.Internal.NAMESPACE, AttributeNames.Internal.COMPUTED_SORT_CONSTRAINTS);
if (attribute instanceof List<?>) {
return (List<SortConstraint>) attribute;
}
return Collections.emptyList();
}
private void initializeProcessingContext(final ProcessingContext processingContext, final MasterReport report)
{
Configuration configuration = wrapForCompatibility(processingContext);
processingContext.getOutputProcessorMetaData().initialize(mapStaticMetaData(configuration, report));
}
private Configuration mapStaticMetaData(final Configuration configuration, final MasterReport report)
{
HierarchicalConfiguration hc = new HierarchicalConfiguration(configuration);
setConfigurationIfDefined(hc,
"org.pentaho.reporting.engine.classic.core.layout.fontrenderer.ComplexTextLayout",
report.getAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.COMPLEX_TEXT));
return hc;
}
private void setConfigurationIfDefined(final ModifiableConfiguration config, final String configKey, final Object value)
{
if (value == null)
{
return;
}
try
{
String valueText = ConverterRegistry.toAttributeValue(value);
config.setConfigProperty(configKey, valueText);
}
catch (final BeanException e)
{
logger.info(String.format("Ignoring invalid attribute-value override for configuration '%s' with value '%s'", configKey, value));
}
}
private Configuration wrapForCompatibility(final ProcessingContext processingContext)
{
final int compatibilityLevel = processingContext.getCompatibilityLevel();
if (compatibilityLevel < 0)
{
return processingContext.getConfiguration();
}
if (compatibilityLevel < ClassicEngineBoot.computeVersionId(3, 999, 999))
{
// enable strict compatibility mode for reports older than 4.0.
final HierarchicalConfiguration config = new HierarchicalConfiguration(processingContext.getConfiguration());
config.setConfigProperty("org.pentaho.reporting.engine.classic.core.legacy.WrapProgressMarkerInSection", "true");
config.setConfigProperty("org.pentaho.reporting.engine.classic.core.legacy.StrictCompatibility", "true");
return config;
}
// this is a trunk or 4.0 or newer report.
return processingContext.getConfiguration();
}
private boolean isDesigntime()
{
return designtime;
}
private boolean isReportsShareConnections(final ReportDefinition report)
{
final Object attribute = report.getAttribute
(AttributeNames.Internal.NAMESPACE, AttributeNames.Internal.SHARED_CONNECTIONS);
if (Boolean.TRUE.equals(attribute))
{
return true;
}
if (Boolean.FALSE.equals(attribute))
{
return false;
}
return reportInstancesShareConnection;
}
public void initializeForSubreport(final InlineSubreportMarker[] subReports,
final int subReportIndex,
final ProcessState parentState) throws ReportProcessingException
{
if (parentState == null)
{
throw new NullPointerException();
}
this.designtime = parentState.designtime;
this.crosstabColumnSequenceCounter = new LongSequence(10, -1);
this.groupSequenceCounter = new LongSequence(10, -1);
this.groupSequenceCounter.set(0, -1);
this.recorder = (GroupSizeRecorder) parentState.recorder.clone();
this.recorder.reset();
this.performanceMonitorContext = parentState.performanceMonitorContext;
this.reportInstancesShareConnection = parentState.reportInstancesShareConnection;
this.groupStarts = new FastStack<GroupStartRecord>();
this.parentSubReportState = parentState;
this.advanceHandler = BeginReportHandler.HANDLER;
this.errorHandler = parentState.errorHandler;
this.functionStorage = parentState.functionStorage;
this.structureFunctionStorage = parentState.structureFunctionStorage;
this.currentGroupIndex = ReportState.BEFORE_FIRST_GROUP;
this.currentPresentationGroupIndex = ReportState.BEFORE_FIRST_GROUP;
this.currentSubReport = subReportIndex;
this.subReports = subReports.clone();
this.dataFactoryManager = parentState.dataFactoryManager;
this.subReportStorage = parentState.subReportStorage;
this.structuralPreprocessingNeeded = parentState.structuralPreprocessingNeeded;
this.processLevels = parentState.processLevels;
this.sequenceCounter = parentState.getSequenceCounter() + 1;
this.currentSubReportMarker = subReports[subReportIndex];
this.inlineProcess =
parentState.isInlineProcess() || currentSubReportMarker.getProcessType() == SubReportProcessType.INLINE;
final SubReport subreportFromMarker = currentSubReportMarker.getSubreport();
final FunctionStorageKey functionStorageKey = FunctionStorageKey.createKey
(parentSubReportState.getProcessKey(), subreportFromMarker);
final boolean needPreProcessing;
final SubReport initialSubReport;
if (subReportStorage.contains(functionStorageKey))
{
initialSubReport = subReportStorage.restore(functionStorageKey);
initialSubReport.reconnectParent(subreportFromMarker.getParentSection());
applyCurrentStyleAndAttributes(subreportFromMarker, initialSubReport);
needPreProcessing = false;
}
else
{
initialSubReport = subreportFromMarker.derive(true);
initialSubReport.reconnectParent(subreportFromMarker.getParentSection());
needPreProcessing = true;
}
final DefaultFlowController parentStateFlowController = parentState.getFlowController();
final ResourceBundleFactory resourceBundleFactory = parentState.getResourceBundleFactory();
if (isSubReportInvisible(initialSubReport, parentStateFlowController))
{
// make it a minimum effort report, but still enter the loop.
final ReportDefinition parentReport = parentState.getReport();
final SubReport dummyReport = new SubReport(functionStorageKey.getReportId());
this.report = new ReportDefinitionImpl(dummyReport, parentReport.getPageDefinition(), subreportFromMarker.getParentSection());
this.flowController = parentStateFlowController.derive();
this.advanceHandler = EndSubReportHandler.HANDLER;
this.layoutProcess = new SubLayoutProcess
(parentState.layoutProcess, computeStructureFunctions(initialSubReport.getStructureFunctions(),
flowController.getReportContext().getOutputProcessorMetaData()), this.report.getObjectID());
}
else
{
DataFactory dataFactory = dataFactoryManager.restore(functionStorageKey, isReportsShareConnections(initialSubReport));
final DefaultFlowController postPreProcessingFlowController;
final SubReport preDataSubReport;
if (dataFactory == null)
{
final SubReportProcessPreprocessor preprocessor = new SubReportProcessPreprocessor(parentStateFlowController);
preDataSubReport = preprocessor.invokePreDataProcessing(initialSubReport);
postPreProcessingFlowController = preprocessor.getFlowController();
final DataFactory subreportDf = lookupDataFactory(preDataSubReport);
final boolean dataCacheEnabled = isCacheEnabled(preDataSubReport);
if (subreportDf == null)
{
// subreport does not define a own factory, we reuse the parent's data-factory in the master-row.
dataFactory = new EmptyDataFactory();
}
else
{
// subreport comes with an own factory, so open the gates ..
final DataFactory sortingDataFactory = new SortingDataFactory(subreportDf, performanceMonitorContext);
final CachingDataFactory cdataFactory = new CachingDataFactory(sortingDataFactory, dataCacheEnabled);
final ProcessingContext context = postPreProcessingFlowController.getReportContext();
cdataFactory.initialize(new ProcessingDataFactoryContext(context, cdataFactory));
dataFactoryManager.store(functionStorageKey, cdataFactory, isReportsShareConnections(preDataSubReport));
dataFactory = cdataFactory;
}
}
else
{
preDataSubReport = initialSubReport;
postPreProcessingFlowController = parentStateFlowController;
}
// And now initialize the sub-report.
final ParameterMapping[] inputMappings = preDataSubReport.getInputMappings();
final ParameterMapping[] exportMappings = preDataSubReport.getExportMappings();
// eval query, query-limit and query-timeout
this.flowController = postPreProcessingFlowController.performInitSubreport
(dataFactory, inputMappings, resourceBundleFactory);
final Integer queryLimitDefault = IntegerCache.getInteger(preDataSubReport.getQueryLimit());
final Integer queryTimeoutDefault = IntegerCache.getInteger(preDataSubReport.getQueryTimeout());
final Object queryRaw = evaluateExpression(preDataSubReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY), preDataSubReport.getQuery());
final Object queryLimitRaw = evaluateExpression(preDataSubReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY_LIMIT), queryLimitDefault);
final Object queryTimeoutRaw = evaluateExpression(preDataSubReport.getAttributeExpression(AttributeNames.Internal.NAMESPACE,
AttributeNames.Internal.QUERY_TIMEOUT), queryTimeoutDefault);
final String queryDefined = designtime ? "design-time-query" : preDataSubReport.getQuery();
this.query = (String) ConverterRegistry.convert(queryRaw, String.class, queryDefined);
this.queryLimit = (Integer) ConverterRegistry.convert(queryLimitRaw, Integer.class, queryLimitDefault);
this.queryTimeout = (Integer) ConverterRegistry.convert(queryTimeoutRaw, Integer.class, queryTimeoutDefault);
final List<SortConstraint> sortOrder = lookupSortOrder(preDataSubReport);
DefaultFlowController postQueryFlowController = flowController.performSubReportQuery
(query, queryLimit.intValue(), queryTimeout.intValue(), exportMappings, sortOrder);
final ProxyDataSchemaDefinition schemaDefinition =
new ProxyDataSchemaDefinition(preDataSubReport.getDataSchemaDefinition(),
postQueryFlowController.getMasterRow().getDataSchemaDefinition());
postQueryFlowController = postQueryFlowController.updateDataSchema(schemaDefinition);
SubReport fullReport = preDataSubReport;
DefaultFlowController fullFlowController = postQueryFlowController;
if (needPreProcessing)
{
final SubReportProcessPreprocessor preprocessor = new SubReportProcessPreprocessor(postQueryFlowController);
fullReport = preprocessor.invokePreProcessing(preDataSubReport);
fullFlowController = preprocessor.getFlowController();
subReportStorage.store(functionStorageKey, fullReport);
}
this.report = new ReportDefinitionImpl(fullReport, fullReport.getPageDefinition(), subreportFromMarker.getParentSection());
final Expression[] structureFunctions = getStructureFunctionStorage().restore(functionStorageKey);
if (structureFunctions != null)
{
final StructureFunction[] functions = new StructureFunction[structureFunctions.length];
//noinspection SuspiciousSystemArraycopy
System.arraycopy(structureFunctions, 0, functions, 0, structureFunctions.length);
this.layoutProcess = new SubLayoutProcess(parentState.layoutProcess, functions, this.report.getObjectID());
}
else
{
final StructureFunction[] functions = computeStructureFunctions(fullReport.getStructureFunctions(),
fullFlowController.getReportContext().getOutputProcessorMetaData());
this.layoutProcess = new SubLayoutProcess(parentState.layoutProcess, functions, this.report.getObjectID());
}
boolean preserve = true;
Expression[] expressions = getFunctionStorage().restore(functionStorageKey);
if (expressions == null)
{
// ok, it seems we have entered a new subreport ..
// we use the expressions from the report itself ..
if (designtime)
{
expressions = new Expression[0];
}
else
{
expressions = fullReport.getExpressions().getExpressions();
}
preserve = false;
}
this.flowController = fullFlowController.activateExpressions(expressions, preserve);
this.flowController = this.flowController.refreshDataRow();
// now a bunch of paranoid assertions, just in case I missed something.
if (this.report.getParentSection() == null)
{
throw new InvalidReportStateException();
}
if (this.report.getParentSection().getReportDefinition() != this.parentSubReportState.getReport())
{
throw new InvalidReportStateException();
}
final int processingLevel = flowController.getReportContext().getProcessingLevel();
if (processingLevel == LayoutProcess.LEVEL_PAGINATE)
{
if (this.parentSubReportState.isInItemGroup())
{
if (this.parentSubReportState.getReport().getDetailsFooter().getComputedStyle() == null)
{
throw new InvalidReportStateException();
}
}
}
}
StateUtilities.computeLevels(this.flowController, this.layoutProcess, processLevels);
this.processKey = createKey();
}
private DataFactory lookupDataFactory(final AbstractReportDefinition report)
{
if (designtime)
{
return new DesignTimeDataFactory();
}
return report.getDataFactory();
}
private void applyCurrentStyleAndAttributes(final SubReport subreportFromMarker, final SubReport report)
{
// derive would regenerate instance-IDs, which is not advisable.
report.getStyle().copyFrom(subreportFromMarker.getStyle());
report.copyAttributes(subreportFromMarker.getAttributes());
}
private boolean isSubReportInvisible(final SubReport report,
final DefaultFlowController flowController)
{
final int processingLevel = flowController.getReportContext().getProcessingLevel();
if (processingLevel != LayoutProcess.LEVEL_PAGINATE)
{
// outside
return false;
}
if (designtime)
{
return false;
}
if (flowController.getReportContext().getOutputProcessorMetaData().isFeatureSupported(OutputProcessorFeature.DESIGNTIME))
{
final Object attribute = report.getAttribute(AttributeNames.Designtime.NAMESPACE,
AttributeNames.Designtime.HIDE_IN_LAYOUT_GUI_ATTRIBUTE);
if (Boolean.TRUE.equals(attribute))
{
return true;
}
return false;
}
else
{
final StyleSheet computedStyle = report.getComputedStyle();
if (computedStyle.getBooleanStyleProperty(ElementStyleKeys.VISIBLE))
{
return false;
}
return true;
}
}
private static boolean isCacheEnabled(ReportDefinition reportDefinition)
{
while (reportDefinition != null)
{
final Object dataCacheEnabledRaw =
reportDefinition.getAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.DATA_CACHE);
if (Boolean.FALSE.equals(dataCacheEnabledRaw))
{
return false;
}
final Section parentSection = reportDefinition.getParentSection();
if (parentSection == null)
{
break;
}
reportDefinition = parentSection.getReportDefinition();
}
return true;
}
private Object evaluateExpression(final Expression expression, final Object defaultValue)
{
if (expression == null)
{
return defaultValue;
}
if (designtime)
{
return defaultValue;
}
final Expression evalExpression = expression.getInstance();
final InlineDataRowRuntime runtime = new InlineDataRowRuntime();
runtime.setState(this);
final ExpressionRuntime oldRuntime = evalExpression.getRuntime();
try
{
evalExpression.setRuntime(runtime);
return evalExpression.getValue();
}
catch (final Exception e)
{
logger.debug("Failed to evaluate expression " + expression, e);
return defaultValue;
}
finally
{
evalExpression.setRuntime(oldRuntime);
}
}
public int[] getRequiredRuntimeLevels()
{
processLevels.add(IntegerCache.getInteger(LayoutProcess.LEVEL_PAGINATE));
final int[] retval = new int[processLevels.size()];
final Integer[] levels = processLevels.toArray(new Integer[processLevels.size()]);
Arrays.sort(levels, new StateUtilities.DescendingComparator<Integer>());
for (int i = 0; i < levels.length; i++)
{
final Integer level = levels[i];
retval[i] = level.intValue();
}
return retval;
}
private StructureFunction[] computeStructureFunctions(final StructureFunction[] fromReport,
final OutputProcessorMetaData metaData)
{
final ArrayList<StructureFunction> e = new ArrayList<StructureFunction>(Arrays.asList(fromReport));
if (structuralPreprocessingNeeded)
{
e.add(new CrosstabProcessorFunction());
}
if (isDesigntime() == false)
{
e.add(new AttributeExpressionsEvaluator());
e.add(new SheetNameFunction());
e.add(new StyleExpressionsEvaluator());
e.add(new CellFormatFunction());
e.add(new WizardItemHideFunction());
}
e.add(new MetaDataStyleEvaluator());
e.add(new StyleResolvingEvaluator());
Collections.sort(e, new StructureFunctionComparator());
return e.toArray(new StructureFunction[e.size()]);
}
public boolean isSubReportExecutable()
{
final Expression expression =
getReport().getAttributeExpression(AttributeNames.Core.NAMESPACE, AttributeNames.Core.SUBREPORT_ACTIVE);
if (expression != null)
{
// the master-report state will only be non-null for subreports.
final InlineDataRowRuntime dataRowRuntime = new InlineDataRowRuntime();
dataRowRuntime.setState(this);
expression.setRuntime(dataRowRuntime);
try
{
final Object value = expression.getValue();
// the expression has to explicitly return false as a value to disable the report processing of
// subreports. Just returning null or a non-boolean value is not enough. This is a safety measure
// so that if in doubt we print more data than to little.
if (Boolean.FALSE.equals(value))
{
return false;
}
if ("false".equals(String.valueOf(value)))
{
return false;
}
return true;
}
finally
{
expression.setRuntime(null);
}
}
return true;
}
public ProcessState returnFromSubReport(final LayoutProcess layoutProcess) throws ReportProcessingException
{
final ProcessState state = deriveForAdvance();
state.layoutProcess = (LayoutProcess) layoutProcess.clone();
return state;
}
public ProcessState restart() throws ReportProcessingException
{
if (getParentState() != null)
{
throw new IllegalStateException("Cannot reset a state that is a subreport state");
}
final ProcessState state = this.deriveForStorage();
state.crosstabColumnSequenceCounter.clear();
state.groupSequenceCounter.clear();
state.groupSequenceCounter.set(0, -1);
state.recorder.reset();
state.currentSubReport = -1;
state.currentGroupIndex = ReportState.BEFORE_FIRST_GROUP;
state.currentPresentationGroupIndex = ReportState.BEFORE_FIRST_GROUP;
if (state.groupStarts.isEmpty() == false)
{
throw new IllegalStateException();
}
state.setAdvanceHandler(BeginReportHandler.HANDLER);
final ReportStateKey parentStateKey;
final ReportState parentState = this.getParentSubReportState();
if (parentState == null)
{
parentStateKey = null;
}
else
{
parentStateKey = parentState.getProcessKey();
}
final CachingDataFactory dataFactory = state.dataFactoryManager.restore
(FunctionStorageKey.createKey(parentStateKey, state.getReport()), isReportsShareConnections(report));
if (dataFactory == null)
{
throw new ReportProcessingException("No data factory on restart()? Somewhere we went wrong.");
}
final DefaultFlowController fc = state.getFlowController();
final DefaultFlowController cfc = fc.restart();
final DefaultFlowController qfc = cfc.performQuery
(dataFactory, query, queryLimit.intValue(), queryTimeout.intValue(), fc.getMasterRow().getResourceBundleFactory());
final Expression[] expressions = getFunctionStorage().restore
(FunctionStorageKey.createKey(null, state.getReport()));
final DefaultFlowController efc = qfc.activateExpressions(expressions, true);
state.setFlowController(efc);
state.sequenceCounter += 1;
state.processKey = state.createKey();
return state;
}
public ReportProcessingErrorHandler getErrorHandler()
{
return errorHandler;
}
public void setErrorHandler(final ReportProcessingErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
}
public void setSequenceCounter(final int sequenceCounter)
{
this.sequenceCounter = sequenceCounter;
this.processKey = this.createKey();
}
public int getSequenceCounter()
{
return sequenceCounter;
}
public InlineSubreportMarker getCurrentSubReportMarker()
{
return currentSubReportMarker;
}
public boolean isInlineProcess()
{
return inlineProcess;
}
public SubReportProcessType getSubreportProcessingType()
{
InlineSubreportMarker cm = getCurrentSubReportMarker();
if (cm == null)
{
return SubReportProcessType.BANDED;
}
return cm.getProcessType();
}
/**
* This is a more expensive version of the ordinary derive. This method creates a separate copy of the layout-process
* so that this operation is expensive in memory and CPU usage.
*
* @return the derived state.
*/
public ProcessState deriveForPagebreak()
{
try
{
final ProcessState processState = clone();
processState.flowController = flowController.derive();
processState.report = report.clone();
processState.layoutProcess = layoutProcess.deriveForPagebreak();
return processState;
}
catch (final CloneNotSupportedException e)
{
throw new IllegalStateException("Clone failed but I dont know why ..");
}
}
public ProcessState deriveForAdvance()
{
try
{
final ProcessState processState = clone();
processState.sequenceCounter += 1;
processState.processKey = processState.createKey();
return processState;
}
catch (final CloneNotSupportedException e)
{
throw new IllegalStateException("Clone failed but I dont know why ..");
}
}
public ProcessState deriveForStorage()
{
try
{
final ProcessState result = clone();
result.flowController = flowController.derive();
result.report = report.clone();
result.layoutProcess = layoutProcess.deriveForStorage();
return result;
}
catch (final CloneNotSupportedException e)
{
throw new IllegalStateException("Clone failed but I dont know why ..");
}
}
public ProcessState clone() throws CloneNotSupportedException
{
final ProcessState result = (ProcessState) super.clone();
result.groupSequenceCounter = (LongSequence) groupSequenceCounter.clone();
result.crosstabColumnSequenceCounter = (LongSequence) crosstabColumnSequenceCounter.clone();
result.groupStarts = groupStarts.clone();
result.processKey = result.createKey();
result.recorder = (GroupSizeRecorder) recorder.clone();
return result;
}
public AdvanceHandler getAdvanceHandler()
{
return advanceHandler;
}
private ReportStateKey createKey()
{
final ProcessState parent = (ProcessState) getParentState();
if (parent != null)
{
return new ReportStateKey(parent.createKey(),
getCurrentRow(), getEventCode(),
getCurrentGroupIndex(), getCurrentSubReport(),
sequenceCounter, advanceHandler.isRestoreHandler(),
isInlineProcess());
}
return new ReportStateKey(null, getCurrentRow(),
getEventCode(), getCurrentGroupIndex(), getCurrentSubReport(),
sequenceCounter, advanceHandler.isRestoreHandler(), false);
}
public void setAdvanceHandler(final AdvanceHandler advanceHandler)
{
if (advanceHandler == null)
{
throw new NullPointerException();
}
this.advanceHandler = advanceHandler;
this.processKey = null;
}
public final ProcessState advance() throws ReportProcessingException
{
return advanceHandler.advance(this);
}
public final ProcessState commit() throws ReportProcessingException
{
final ProcessState commit = advanceHandler.commit(this);
commit.processKey = commit.createKey();
return commit;
}
public int getCurrentRow()
{
return this.flowController.getMasterRow().getCursor();
}
public int getCurrentDataItem()
{
return this.flowController.getMasterRow().getRawDataCursor();
}
public int getProgressLevel()
{
return flowController.getReportContext().getProgressLevel();
}
public int getProgressLevelCount()
{
return flowController.getReportContext().getProgressLevelCount();
}
public boolean isPrepareRun()
{
return flowController.getReportContext().isPrepareRun();
}
public int getLevel()
{
return flowController.getReportContext().getProcessingLevel();
}
public boolean isFinish()
{
return advanceHandler.isFinish();
}
public int getEventCode()
{
return advanceHandler.getEventCode();
}
public int getCurrentGroupIndex()
{
return currentGroupIndex;
}
public void enterGroup()
{
recorder.enterGroup();
currentGroupIndex += 1;
final Group group = report.getGroup(currentGroupIndex);
groupStarts.push(new GroupStartRecord(getCurrentRow(), group.getName(), group.getGeneratedName()));
groupSequenceCounter.increment(currentGroupIndex);
groupSequenceCounter.set(currentGroupIndex + 1, 0);
if (groupStarts.size() != currentGroupIndex + 1)
{
throw new IllegalStateException();
}
}
public void leaveGroup()
{
recorder.leaveGroup();
if (groupStarts.size() != currentGroupIndex + 1)
{
throw new IllegalStateException();
}
currentGroupIndex -= 1;
groupStarts.pop();
}
public int getPresentationGroupIndex()
{
return currentPresentationGroupIndex;
}
public void enterPresentationGroup()
{
currentPresentationGroupIndex += 1;
}
public void leavePresentationGroup()
{
currentPresentationGroupIndex -= 1;
}
public ReportDefinition getReport()
{
return report;
}
public int getCurrentSubReport()
{
return currentSubReport;
}
public ReportState getParentState()
{
if (suspendedState != null)
{
return suspendedState;
}
if (parentSubReportState != null)
{
return parentSubReportState;
}
return null;
}
public ReportState getParentSubReportState()
{
return parentSubReportState;
}
public FunctionStorage getStructureFunctionStorage()
{
return structureFunctionStorage;
}
public FunctionStorage getFunctionStorage()
{
return functionStorage;
}
public DefaultFlowController getFlowController()
{
return flowController;
}
public void setFlowController(final DefaultFlowController flowController)
{
if (flowController == null)
{
throw new NullPointerException();
}
this.flowController = flowController;
this.processKey = null;
}
public LayoutProcess getLayoutProcess()
{
return layoutProcess;
}
public ReportStateKey getProcessKey()
{
if (processKey == null)
{
processKey = createKey();
}
return processKey;
}
public DataRow getDataRow()
{
return flowController.getMasterRow().getGlobalView();
}
public int getNumberOfRows()
{
final MasterDataRow masterRow = flowController.getMasterRow();
return masterRow.getRowCount();
}
/**
* Fires a 'page-started' event.
*
* @param baseEvent the type of the base event which caused the page start to be triggered.
*/
public void firePageStartedEvent(final int baseEvent)
{
final ReportEvent event = new ReportEvent(this, ReportEvent.PAGE_STARTED | baseEvent);
flowController = flowController.fireReportEvent(event);
layoutProcess.fireReportEvent(event);
}
/**
* Fires a '<code>page-finished</code>' event. The <code>pageFinished(...)</code> method is called for every report
* function.
*/
public void firePageFinishedEvent(final boolean noParentPassing)
{
final int eventCode = ReportEvent.PAGE_FINISHED | (noParentPassing ? ReportEvent.NO_PARENT_PASSING_EVENT : 0);
final ReportEvent event = new ReportEvent(this, eventCode);
flowController = flowController.fireReportEvent(event);
layoutProcess.fireReportEvent(event);
}
protected void fireReportEvent()
{
final int eventCode = advanceHandler.getEventCode();
if ((eventCode & ProcessState.ARTIFICIAL_EVENT_CODE) == ProcessState.ARTIFICIAL_EVENT_CODE)
{
throw new IllegalStateException("Cannot fire artificial events.");
}
final ReportEvent event = new ReportEvent(this, eventCode);
flowController = flowController.fireReportEvent(event);
layoutProcess.fireReportEvent(event);
}
/**
* Returns true if this is the last item in the group, and false otherwise. This checks the group condition and all
* conditions of all subgroups.
*
* @param rootGroup the root group that should be checked.
* @param currentDataRow the current data row.
* @param nextDataRow the next data row, or null, if this is the last datarow.
* @return A flag indicating whether or not the current item is the last in its group.
*/
public static boolean isLastItemInGroup(final Group rootGroup,
final MasterDataRow currentDataRow,
final MasterDataRow nextDataRow)
{
// return true if this is the last row in the model.
if (currentDataRow.isAdvanceable() == false || nextDataRow == null)
{
return true;
}
final DataRow nextView = nextDataRow.getGlobalView();
Group g = rootGroup;
while (g != null)
{
if (g.isGroupChange(nextView))
{
return true;
}
// groups are never directly nested into each other. They always have a group-body between each group instance.
final Section parentSection = g.getParentSection();
if (parentSection == null)
{
return false;
}
final Section maybeGroup = parentSection.getParentSection();
if (maybeGroup instanceof Group)
{
g = (Group) maybeGroup;
}
else
{
g = null;
}
}
return false;
}
public boolean isSubReportEvent()
{
return getParentSubReportState() != null;
}
public InlineSubreportMarker[] getSubReports()
{
return subReports.clone();
}
public ProcessStateHandle getProcessHandle()
{
return processHandle;
}
public void setInItemGroup(final boolean inItemGroup)
{
if (inItemGroup)
{
recorder.enterItems();
}
else
{
recorder.leaveItems();
}
this.inItemGroup = inItemGroup;
}
public boolean isInItemGroup()
{
return inItemGroup;
}
public ResourceBundleFactory getResourceBundleFactory()
{
return flowController.getMasterRow().getResourceBundleFactory();
}
public boolean isArtifcialState()
{
return (advanceHandler.getEventCode() & ReportEvent.ARTIFICIAL_EVENT_CODE) != 0;
}
public GroupingState createGroupingState()
{
return new DefaultGroupingState(currentGroupIndex, groupStarts.clone());
}
private boolean isStructureRunNeeded(final Section section)
{
final int count = section.getElementCount();
for (int i = 0; i < count; i++)
{
final ReportElement element = section.getElement(i);
final Object type = element.getAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.ELEMENT_TYPE);
if (type instanceof ExternalElementType)
{
return true;
}
if (element instanceof CrosstabGroup)
{
return true;
}
else if (element instanceof SubReport)
{
return true;
}
else if (element instanceof RootLevelBand)
{
final RootLevelBand band = (RootLevelBand) element;
if (band.getSubReportCount() > 0)
{
return true;
}
if (isStructureRunNeeded((Section) element))
{
return true;
}
}
else if (element instanceof Section)
{
if (isStructureRunNeeded((Section) element))
{
return true;
}
}
}
return false;
}
public boolean isStructuralPreprocessingNeeded()
{
return structuralPreprocessingNeeded;
}
public void advanceCursor()
{
recorder.advanceItems();
}
public Integer getPredictedStateCount()
{
return recorder.getPredictedStateCount();
}
public boolean isCrosstabActive()
{
return flowController.isCrosstabActive();
}
public String toString()
{
final StringBuilder b = new StringBuilder();
b.append("ProcessState={");
b.append("runLevel=").append(getLevel());
b.append(", key=").append(getProcessKey());
b.append("}");
return b.toString();
}
public ProcessState recordCrosstabRowState()
{
// record the flow controller and all expressions for a later temporary rollback.
final ProcessState next = deriveForAdvance();
next.flowController = flowController.recordCrosstabRowState();
next.crosstabColumnSequenceCounter.clear();
next.crosstabColumnSequenceCounter.fill(-1);
return next;
}
/**
* Reset the state to use the stored flow-controller for the summary calculation.
*
* @return
*/
public ProcessState replayStoredCrosstabRowState()
{
final ProcessState next = deriveForAdvance();
next.replayStoredCrosstabGroup = currentGroupIndex + 1;
next.suspendedState = this;
next.flowController = flowController.replayStoredCrosstabRowState();
next.processKey = next.createKey();
// DebugLog.log("ProcessState:replay " + processKey);
return next;
}
public int getReplayStoredCrosstabGroup()
{
return replayStoredCrosstabGroup;
}
public AdvanceHandler getPostSummaryRowAdvanceHandler()
{
return postSummaryRowAdvanceHandler;
}
public void setPostSummaryRowAdvanceHandler(final AdvanceHandler postSummaryRowAdvanceHandler)
{
this.postSummaryRowAdvanceHandler = postSummaryRowAdvanceHandler;
}
public ProcessState finishReplayingStoredCrosstabRowState() throws ReportProcessingException
{
final ProcessState next = this.suspendedState.deriveForAdvance();
next.layoutProcess = (LayoutProcess) this.layoutProcess.clone();
next.flowController = flowController.derive();
next.advanceHandler = this.advanceHandler;
next.flowController.getMasterRow().validateReplayFinished();
// DebugLog.log("ProcessState:finish-replay " + processKey);
return next;
}
public void clearStoredCrosstabRowState()
{
this.flowController = flowController.clearRecordedCrosstabRowState();
}
public long getGroupSequenceCounter(final int groupIndex)
{
return groupSequenceCounter.get(groupIndex);
}
public long getCrosstabColumnSequenceCounter(final int groupIndex)
{
return crosstabColumnSequenceCounter.get(groupIndex);
}
public void crosstabResetColumnIndices()
{
crosstabColumnSequenceCounter.clear();
crosstabColumnSequenceCounter.fill(-1);
}
public void crosstabIncrementColumnCounter()
{
crosstabColumnSequenceCounter.increment(currentGroupIndex);
}
public PerformanceMonitorContext getPerformanceMonitorContext()
{
return performanceMonitorContext;
}
}