Package org.pentaho.reporting.engine.classic.core.layout.output

Source Code of org.pentaho.reporting.engine.classic.core.layout.output.AbstractReportProcessor

/*
* 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.layout.output;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineCoreModule;
import org.pentaho.reporting.engine.classic.core.EmptyReportException;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportEventException;
import org.pentaho.reporting.engine.classic.core.ReportInterruptedException;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.event.ReportProgressEvent;
import org.pentaho.reporting.engine.classic.core.event.ReportProgressListener;
import org.pentaho.reporting.engine.classic.core.function.OutputFunction;
import org.pentaho.reporting.engine.classic.core.layout.AbstractRenderer;
import org.pentaho.reporting.engine.classic.core.layout.DefaultLayoutSupport;
import org.pentaho.reporting.engine.classic.core.layout.Renderer;
import org.pentaho.reporting.engine.classic.core.states.CollectingReportErrorHandler;
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.ProcessStateHandle;
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.process.PendingPagesHandler;
import org.pentaho.reporting.engine.classic.core.states.process.ProcessState;
import org.pentaho.reporting.engine.classic.core.states.process.RestartOnNewPageHandler;
import org.pentaho.reporting.engine.classic.core.util.IntList;
import org.pentaho.reporting.engine.classic.core.util.StringUtil;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.MemoryUsageMessage;
import org.pentaho.reporting.libraries.docbundle.DocumentBundle;
import org.pentaho.reporting.libraries.docbundle.DocumentMetaData;
import org.pentaho.reporting.libraries.docbundle.MemoryDocumentMetaData;

/**
* Creation-Date: 08.04.2007, 14:52:52
*
* @author Thomas Morgner
* @noinspection HardCodedStringLiteral
*/
public abstract class AbstractReportProcessor implements ReportProcessor
{
  private static final Log logger = LogFactory.getLog(AbstractReportProcessor.class);
  private static final boolean SHOW_ROLLBACKS = logger.isTraceEnabled();

  protected static final int MAX_EVENTS_PER_RUN = 200;
  protected static final int MIN_ROWS_PER_EVENT = 100;
  protected static final int COMMIT_RATE = 10;

  /**
   * A flag defining whether to check for Thread-Interrupts.
   */
  private boolean handleInterruptedState;

  /**
   * Storage for listener references.
   */
  private ArrayList<ReportProgressListener> listeners;

  /**
   * The listeners as object array for faster access.
   */
  private transient Object[] listenersCache;

  private MasterReport report;

  private OutputProcessor outputProcessor;

  private PageStateList stateList;
  private transient ProcessStateHandle activeDataFactory;

  private IntList physicalMapping;
  private IntList logicalMapping;
  private boolean pagebreaksSupported;
  private boolean paranoidChecks;
  private boolean fullStreamingProcessor;

  protected AbstractReportProcessor(final MasterReport report,
                                    final OutputProcessor outputProcessor)
      throws ReportProcessingException
  {
    if (report == null)
    {
      throw new NullPointerException("Report cannot be null.");
    }

    if (outputProcessor == null)
    {
      throw new NullPointerException("OutputProcessor cannot be null");
    }

    try
    {
      // first cloning ... protect the page layouter function ...
      // and any changes we may do to the report instance.

      // a second cloning is done in the start state, to protect the
      // processed data.
      this.report = (MasterReport) report.derive();
    }
    catch (CloneNotSupportedException cne)
    {
      throw new ReportProcessingException("Initial Clone of Report failed");
    }

    this.fullStreamingProcessor = true;
    this.handleInterruptedState = true;
    this.outputProcessor = outputProcessor;
    this.paranoidChecks = "true".equals(outputProcessor.getMetaData().getConfiguration().getConfigProperty
        ("org.pentaho.reporting.engine.classic.core.layout.ParanoidChecks"));
    this.pagebreaksSupported = outputProcessor.getMetaData().isFeatureSupported(OutputProcessorFeature.PAGEBREAKS);
    final Configuration configuration = report.getReportConfiguration();
    final String yieldRateText = configuration.getConfigProperty("org.pentaho.reporting.engine.classic.core.YieldRate");
    final int yieldRate = StringUtil.parseInt(yieldRateText, 0);
    if (yieldRate > 0)
    {
      addReportProgressListener(new YieldReportListener(yieldRate));
    }
    final String profile = configuration.getConfigProperty
        ("org.pentaho.reporting.engine.classic.core.ProfileReportProcessing");
    if ("true".equals(profile))
    {
      final boolean logLevelProgress = "true".equals
          (configuration.getConfigProperty("org.pentaho.reporting.engine.classic.core.performance.LogLevelProgress"));
      final boolean logPageProgress = "true".equals
          (configuration.getConfigProperty("org.pentaho.reporting.engine.classic.core.performance.LogPageProgress"));
      final boolean logRowProgress = "true".equals
          (configuration.getConfigProperty("org.pentaho.reporting.engine.classic.core.performance.LogRowProgress"));
      addReportProgressListener(new PerformanceProgressLogger(logLevelProgress, logPageProgress, logRowProgress));
    }
  }

  protected ProcessStateHandle getProcessStateHandle()
  {
    return activeDataFactory;
  }

  protected MasterReport getReport()
  {
    return report;
  }

  public OutputProcessor getOutputProcessor()
  {
    return outputProcessor;
  }

  protected OutputProcessorMetaData getOutputProcessorMetaData()
  {
    return outputProcessor.getMetaData();
  }


  /**
   * Adds a repagination listener. This listener will be informed of pagination events.
   *
   * @param l the listener.
   */
  public void addReportProgressListener(final ReportProgressListener l)
  {
    if (l == null)
    {
      throw new NullPointerException("Listener == null");
    }
    if (listeners == null)
    {
      listeners = new ArrayList<ReportProgressListener>(5);
    }
    listenersCache = null;
    listeners.add(l);
  }

  /**
   * Removes a repagination listener.
   *
   * @param l the listener.
   */
  public void removeReportProgressListener(final ReportProgressListener l)
  {
    if (l == null)
    {
      throw new NullPointerException("Listener == null");
    }
    if (listeners == null)
    {
      return;
    }
    listenersCache = null;
    listeners.remove(l);
  }


  /**
   * Sends a repagination update to all registered listeners.
   *
   * @param state the state.
   */
  protected void fireStateUpdate(final ReportProgressEvent state)
  {
    if (listeners == null)
    {
      return;
    }
    if (listenersCache == null)
    {
      listenersCache = listeners.toArray();
    }
    final int length = listenersCache.length;
    for (int i = 0; i < length; i++)
    {
      final ReportProgressListener l = (ReportProgressListener) listenersCache[i];
      l.reportProcessingUpdate(state);
    }
  }


  /**
   * Sends a repagination update to all registered listeners.
   *
   * @param state the state.
   */
  protected void fireProcessingStarted(final ReportProgressEvent state)
  {
    if (listeners == null)
    {
      return;
    }
    if (listenersCache == null)
    {
      listenersCache = listeners.toArray();
    }
    final int length = listenersCache.length;
    for (int i = 0; i < length; i++)
    {
      final ReportProgressListener l = (ReportProgressListener) listenersCache[i];
      l.reportProcessingStarted(state);
    }
  }


  /**
   * Sends a repagination update to all registered listeners.
   *
   * @param state the state.
   */
  protected void fireProcessingFinished(final ReportProgressEvent state)
  {
    if (listeners == null)
    {
      return;
    }
    if (listenersCache == null)
    {
      listenersCache = listeners.toArray();
    }
    final int length = listenersCache.length;
    for (int i = 0; i < length; i++)
    {
      final ReportProgressListener l = (ReportProgressListener) listenersCache[i];
      l.reportProcessingFinished(state);
    }
  }

  /**
   * Returns whether the processor should check the threads interrupted state. If this is set to true and the thread was
   * interrupted, then the report processing is aborted.
   *
   * @return true, if the processor should check the current thread state, false otherwise.
   */
  public boolean isHandleInterruptedState()
  {
    return handleInterruptedState;
  }

  /**
   * Defines, whether the processor should check the threads interrupted state. If this is set to true and the thread
   * was interrupted, then the report processing is aborted.
   *
   * @param handleInterruptedState true, if the processor should check the current thread state, false otherwise.
   */
  public void setHandleInterruptedState(final boolean handleInterruptedState)
  {
    this.handleInterruptedState = handleInterruptedState;
  }

  /**
   * Checks whether the current thread is interrupted.
   *
   * @throws org.pentaho.reporting.engine.classic.core.ReportInterruptedException
   *          if the thread is interrupted to abort the report processing.
   */
  protected final void checkInterrupted()
      throws ReportInterruptedException
  {
    if (isHandleInterruptedState())
    {
      if (Thread.currentThread().isInterrupted())
      {
        throw new ReportInterruptedException("Current thread [" + Thread.currentThread().getName() + "]is interrupted. Returning.");
      }
    }
  }

  public synchronized void close()
  {
    if (activeDataFactory != null)
    {
      this.activeDataFactory.close();
      this.activeDataFactory = null;
      this.stateList = null;
      this.physicalMapping = null;
      this.logicalMapping = null;
    }
  }

  public Configuration getConfiguration()
  {
    return report.getConfiguration();
  }

  protected DefaultProcessingContext createProcessingContext() throws ReportProcessingException
  {
    final OutputProcessorMetaData metaData = getOutputProcessorMetaData();
    final boolean maxLineHeightUsed =
        metaData.isFeatureSupported(OutputProcessorFeature.LEGACY_LINEHEIGHT_CALC) == false;
    final boolean imageResolutionMapping =
        metaData.isFeatureSupported(OutputProcessorFeature.IMAGE_RESOLUTION_MAPPING) == false;
    final MasterReport report = getReport();
    final DocumentMetaData documentMetaData;
    final DocumentBundle bundle = report.getBundle();
    if (bundle != null)
    {
      documentMetaData = bundle.getMetaData();
    }
    else
    {
      documentMetaData = new MemoryDocumentMetaData();
    }
    return new DefaultProcessingContext
        (metaData, new DefaultLayoutSupport(maxLineHeightUsed, imageResolutionMapping),
            report.getResourceBundleFactory(), report.getConfiguration(),
            report.getResourceManager(), report.getContentBase(), documentMetaData,
            report.getReportEnvironment());
  }

  /**
   * Processes all prepare levels to compute the function values.
   *
   * @param state   the state state with which we beginn the processing.
   * @param maxRows the number of rows in the table model.
   * @return the finish state for the current level.
   * @throws ReportProcessingException if processing failed or if there are exceptions during the function execution.
   */
  protected ProcessState processPrepareLevels(ProcessState state, final int maxRows)
      throws ReportProcessingException
  {
    final boolean failOnError = isStrictErrorHandling(getReport().getReportConfiguration());
    final ReportProcessingErrorHandler errorHandler = new CollectingReportErrorHandler();
    state.setErrorHandler(errorHandler);

    int lastRow = -1;
    int eventCount = 0;
    final int eventTrigger;
    if (maxRows <= 0)
    {
      eventTrigger = Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT);
    }
    else
    {
      eventTrigger = Math.min(maxRows, Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT));
    }

    final ReportProgressEvent repaginationState = new ReportProgressEvent(this);
    // Function processing does not use the PageLayouter, so we don't need
    // the expensive cloning ...
    while (!state.isFinish())
    {
      checkInterrupted();
      if (lastRow != state.getCurrentDataItem())
      {
        lastRow = state.getCurrentDataItem();
        if (eventCount == 0)
        {
          repaginationState.reuse(ReportProgressEvent.PRECOMPUTING_VALUES, state.getCurrentDataItem(), state.getNumberOfRows(),
              state.getCurrentPage(), state.getProgressLevel(), state.getProgressLevelCount());
          fireStateUpdate(repaginationState);
          eventCount += 1;
        }
        else
        {
          if (eventCount == eventTrigger)
          {
            eventCount = 0;
          }
          else
          {
            eventCount += 1;
          }
        }
      }

      //progress = state.createStateProgress(progress);
      final ProcessState nextState = state.advance();
      state.setErrorHandler(IgnoreEverythingReportErrorHandler.INSTANCE);
      state = nextState.commit();

      if (errorHandler.isErrorOccured() == true)
      {
        final List childExceptions = Arrays.asList(errorHandler.getErrors());
        errorHandler.clearErrors();
        if (failOnError)
        {
          throw new ReportEventException("Failed to dispatch an event.", childExceptions);
        }
        else
        {
          final ReportEventException exception =
              new ReportEventException("Failed to dispatch an event.", childExceptions);
          AbstractReportProcessor.logger.error("Failed to dispatch an event.", exception);
        }
      }
    }
    return state;
  }

  protected abstract OutputFunction createLayoutManager();


  protected void prepareReportProcessing() throws ReportProcessingException
  {
    if (stateList != null)
    {
      // is already paginated.
      return;
    }

    final long start = System.currentTimeMillis();

    // every report processing starts with an StartState.
    final DefaultProcessingContext processingContext = createProcessingContext();
    final MasterReport report = getReport();
    final OutputFunction lm = createLayoutManager();

    final ProcessState startState = new ProcessState();
    try
    {
      startState.initializeForMasterReport
          (report, processingContext, new InitialLayoutProcess((OutputFunction) lm.getInstance()));
    }
    finally
    {
      activeDataFactory = startState.getProcessHandle();
    }
    ProcessState state = startState;
    final int maxRows = startState.getNumberOfRows();

    // the report processing can be split into 2 separate processes.
    // The first is the ReportPreparation; all function values are resolved and
    // a dummy run is done to calculate the final layout. This dummy run is
    // also necessary to resolve functions which use or depend on the PageCount.

    // the second process is the printing of the report, this is done in the
    // processReport() method.
    processingContext.setPrepareRun(true);

    // now process all function levels.
    // there is at least one level defined, as we added the PageLayouter
    // to the report.
    // the levels are defined from +inf to 0
    // we don't draw and we do not collect states in a StateList yet
    final int[] levels;
    if (state.isStructuralPreprocessingNeeded())
    {
      state = performStructuralPreprocessing(state, processingContext);
      levels = state.getRequiredRuntimeLevels();
    }
    else
    {
      levels = state.getRequiredRuntimeLevels();
    }

    if (levels.length == 0)
    {
      throw new IllegalStateException("Assertation Failed: No functions defined, invalid implementation.");
    }
    processingContext.setProgressLevelCount(levels.length);
    int index = 0;
    int level = levels[index];
    // outer loop: process all function levels
    boolean hasNext;
    do
    {
      processingContext.setProcessingLevel(level);
      processingContext.setProgressLevel(index);

      // if the current level is the output-level, then save the report state.
      // The state is used later to restart the report processing.
      if (level == LayoutProcess.LEVEL_PAGINATE)
      {
        if (isFullStreamingProcessor())
        {
          stateList = new FastPageStateList(this);
        }
        else
        {
          stateList = new DefaultPageStateList(this);
        }
        physicalMapping = new IntList(40);
        logicalMapping = new IntList(20);
        AbstractReportProcessor.logger.debug("Pagination started ..");
        state = processPaginationLevel(state, stateList, maxRows);
      }
      else
      {
        state = processPrepareLevels(state, maxRows);
      }

      // if there is an other level to process, then use the finish state to
      // create a new start state, which will continue the report processing on
      // the next higher level.
      hasNext = (index < (levels.length - 1));
      if (hasNext)
      {
        index += 1;
        level = levels[index];
        processingContext.setProcessingLevel(level);
        processingContext.setProgressLevel(index);
        if (state.isFinish())
        {
          state = state.restart();
          // this is a paranoid check ...
          if (state.getCurrentPage() != ReportState.BEFORE_FIRST_PAGE)
          {
            throw new IllegalStateException("State was not set up properly");
          }
        }
        else
        {
          throw new IllegalStateException(
              "Repaginate did not produce an finish state");
        }
      }
    }
    while (hasNext == true);

    // finally return the saved page states.
    processingContext.setPrepareRun(false);

    final long end = System.currentTimeMillis();
    AbstractReportProcessor.logger.debug("Pagination-Time: " + (end - start));

  }

  public void setFullStreamingProcessor(final boolean fullStreamingProcessor)
  {
    this.fullStreamingProcessor = fullStreamingProcessor;
  }

  public boolean isFullStreamingProcessor()
  {
    return fullStreamingProcessor;
  }

  private ProcessState performStructuralPreprocessing(final ProcessState startState,
                                                      final DefaultProcessingContext processingContext)
      throws ReportProcessingException
  {
    processingContext.setProcessingLevel(Integer.MAX_VALUE);
    processingContext.setProgressLevel(-1);

    final int maxRows = startState.getNumberOfRows();
    ProcessState state = processPrepareLevels(startState, maxRows);
    if (state.isFinish())
    {
      state = state.restart();
      // this is a paranoid check ...
      if (state.getCurrentPage() != ReportState.BEFORE_FIRST_PAGE)
      {
        throw new IllegalStateException("State was not set up properly");
      }
    }
    else
    {
      throw new IllegalStateException(
          "Repaginate did not produce an finish state");
    }
    return state;
  }


  /**
   * Processes the print level for the current report. This function will fill the report state list while performing
   * the repagination.
   *
   * @param startState the start state for the print level.
   * @param pageStates the list of report states that should receive the created page states.
   * @param maxRows    the number of rows in the report (used to estaminate the current progress).
   * @return the finish state for the report.
   * @throws ReportProcessingException if there was a problem processing the report.
   */
  private ProcessState processPaginationLevel(final ProcessState startState,
                                              final PageStateList pageStates,
                                              final int maxRows)
      throws ReportProcessingException
  {
    try
    {
      final boolean failOnError = isStrictErrorHandling(getReport().getReportConfiguration());
      final ReportProcessingErrorHandler errorHandler = new CollectingReportErrorHandler();
      final DefaultLayoutPagebreakHandler pagebreakHandler = new DefaultLayoutPagebreakHandler();

      final ProcessState initialReportState = startState.deriveForStorage();
      final PageState initialPageState = new PageState(initialReportState, outputProcessor.getPageCursor());
      pageStates.add(initialPageState);

      final ReportProgressEvent repaginationState = new ReportProgressEvent(this);

      // inner loop: process the complete report, calculate the function values
      // for the current level. Higher level functions are not available in the
      // dataRow.
      final int eventTrigger;
      if (maxRows <= 0)
      {
        eventTrigger = Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT);
      }
      else
      {
        eventTrigger = Math.min(maxRows, Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT));
      }

      ProcessState state = startState.deriveForStorage();
      state.setErrorHandler(errorHandler);
      validate(state);

      int pageEventCount = 0;
      // First and last derive of a page must be a storage derivate - this clones everything and does
      // not rely on the more complicated transactional layouting ..
      ProcessState fallBackState = state.deriveForPagebreak();
      ProcessState globalState = state.deriveForStorage();

      ReportStateKey rollbackPageState = null;
      boolean isInRollBackMode = false;
      final boolean pagebreaksSupported = isPagebreaksSupported();

      int eventCount = 0;
      int lastRow = -1;
      while (!state.isFinish())
      {
        int logPageCount = outputProcessor.getLogicalPageCount();
        int physPageCount = outputProcessor.getPhysicalPageCount();

        checkInterrupted();
        if (lastRow != state.getCurrentDataItem())
        {
          lastRow = state.getCurrentDataItem();
          if (eventCount == 0)
          {
            if (isPagebreaksSupported() && fallBackState != null)
            {
              repaginationState.reuse(ReportProgressEvent.PAGINATING,
                  fallBackState.getCurrentDataItem(), fallBackState.getNumberOfRows(), fallBackState.getCurrentPage(),
                  fallBackState.getProgressLevel(), fallBackState.getProgressLevelCount());
            }
            else
            {
              repaginationState.reuse(ReportProgressEvent.PAGINATING,
                  state.getCurrentDataItem(), state.getNumberOfRows(), state.getCurrentPage(),
                  state.getProgressLevel(), state.getProgressLevelCount());
            }
            fireStateUpdate(repaginationState);
            eventCount += 1;
          }
          else
          {
            if (eventCount == eventTrigger)
            {
              eventCount = 0;
            }
            else
            {
              eventCount += 1;
            }
          }
        }

        // Do not try to layout on a artificial state. Those states are not valid on
        // generating page events and cannot be trusted.
        ProcessState realFallbackState = fallBackState;
        final ProcessState restoreState;
        if (pagebreaksSupported && state.isArtifcialState() == false)
        {
          restoreState = fallBackState;
          if (isInRollBackMode == false)
          {
            if (pageEventCount >= AbstractReportProcessor.COMMIT_RATE)
            {
              final OutputFunction outputFunction = state.getLayoutProcess().getOutputFunction();
              if (outputFunction.createRollbackInformation())
              {
                realFallbackState = state.deriveForPagebreak();
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  logger.debug("Paginate: Try to generate new fallback state after commit count reached: " + state.getProcessKey());
                }
                validate(state);
              }
              else
              {
                realFallbackState = null;
              }
            }
          }
        }
        else
        {
          restoreState = null;
        }

        final ProcessState nextState = state.advance();
        state.setErrorHandler(IgnoreEverythingReportErrorHandler.INSTANCE);
        state = nextState;
        validate(state);

        final ReportStateKey nextStateKey = state.getProcessKey();

        if (errorHandler.isErrorOccured() == true)
        {
          final List childExceptions = Arrays.asList(errorHandler.getErrors());
          errorHandler.clearErrors();
          final ReportEventException exception =
              new ReportEventException("Failed to dispatch an event.", childExceptions);
          if (failOnError)
          {
            throw exception;
          }
          else
          {
            AbstractReportProcessor.logger.error("Failed to dispatch an event.", exception);
          }
        }

        if (state.isArtifcialState())
        {
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Paginate: Silent commit as we are in an artificial state: " + state.getProcessKey());
          }
          state = state.commit();
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Paginate: Post Silent commit as we are in an artificial state: " + state.getProcessKey());
          }
          continue;
        }

        final OutputFunction outputFunction = state.getLayoutProcess().getOutputFunction();
        if (outputFunction instanceof DefaultOutputFunction == false)
        {
          state = state.commit();
          if (state.isFinish() && pageStates.size() == 1)
          {
            physicalMapping.add(0);
            logicalMapping.add(0);
          }
          continue;
        }

        final DefaultOutputFunction lm = (DefaultOutputFunction) outputFunction;
        final Renderer renderer = lm.getRenderer();
        pagebreakHandler.setReportState(state);

        boolean assertExpectPagebreak = false;
        if (isInRollBackMode)
        {
          // todo: Could be that we have to use the other key here..
          // was: state.getProcessKey()
          if (nextStateKey.equals(rollbackPageState))
          {
            // reached the border case. We have to insert a manual pagebreak here or at least
            // we have to force the renderer to end the page right now.
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              AbstractReportProcessor.logger.debug(
                  "Paginate: Found real pagebreak position. This might be the last state we process: " + rollbackPageState);
              AbstractReportProcessor.logger.debug(
                  "Paginate:                                         : " + state.getProcessKey());
              if (restoreState != null)
              {
                AbstractReportProcessor.logger.debug(
                    "Paginate:                                         : " + restoreState.getProcessKey());
              }
              AbstractReportProcessor.logger.debug(
                  "Paginate: (Handler)                               : " + state.getAdvanceHandler());
            }
            assertExpectPagebreak = true;
            renderer.addPagebreak(state.getProcessKey());
          }
        }

        final Renderer.LayoutResult pagebreakEncountered = renderer.validatePages();
        if (AbstractReportProcessor.SHOW_ROLLBACKS)
        {
          AbstractReportProcessor.logger.debug("Validate Page returned " + pagebreakEncountered);
        }
        if (assertExpectPagebreak == true && pagebreakEncountered != Renderer.LayoutResult.LAYOUT_PAGEBREAK)
        {
          if (SHOW_ROLLBACKS)
          {
            AbstractReportProcessor.logger.debug("Paginate: Missed the pagebreak. This smells fishy!");
          }
        }

        if (pagebreakEncountered != Renderer.LayoutResult.LAYOUT_UNVALIDATABLE)
        {
          if (pagebreaksSupported && state.isArtifcialState() == false)
          {
            if (isInRollBackMode == false)
            {
              if (pageEventCount >= AbstractReportProcessor.COMMIT_RATE)
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  logger.debug("Paginate: Try to apply new fallback state after commit count reached: " + state.getProcessKey());
                  logger.debug("Paginate:        : " + renderer.getLastStateKey());
                }

                fallBackState = realFallbackState;
                pageEventCount = 0;
              }
              else
              {
                pageEventCount += 1;
              }
            }
          }
        }

        if (pagebreakEncountered == Renderer.LayoutResult.LAYOUT_PAGEBREAK)
        {
          final ReportStateKey lastVisibleStateKey = renderer.getLastStateKey();
          if (isPagebreaksSupported() &&
              isInRollBackMode == false &&
              lastVisibleStateKey != null &&
              renderer.isOpen())
          {
            if (lastVisibleStateKey.equals(nextStateKey) == false)
            {
              // Roll back to the last known to be good position and process the states up to, but not
              // including the current state. This way, we can fire the page-events *before* this band
              // gets printed.
              rollbackPageState = lastVisibleStateKey;

              final ReportStateKey restoreStateProcessKey = restoreState.getProcessKey();
              if (AbstractReportProcessor.SHOW_ROLLBACKS)
              {
                AbstractReportProcessor.logger.debug(
                    "Paginate: Encountered bad break, need to roll-back: " + rollbackPageState);
                AbstractReportProcessor.logger.debug(
                    "Paginate:    Next StateKey                        : " + state.getProcessKey());
                AbstractReportProcessor.logger.debug(
                    "Paginate:    Restored Key                         : " + restoreStateProcessKey);
                AbstractReportProcessor.logger.debug(
                    "Paginate:    Position in event chain              : " + restoreState.getSequenceCounter());
              }
              if (lastVisibleStateKey.getSequenceCounter() < restoreStateProcessKey.getSequenceCounter())
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  AbstractReportProcessor.logger.debug(
                      "Print: Fall back to start of page              : " + restoreStateProcessKey);
                }
                state = globalState.deriveForPagebreak();
              }
              else
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  AbstractReportProcessor.logger.debug(
                      "Print: Fall back to save-state                 : " + restoreStateProcessKey);
                }
                state = restoreState.deriveForPagebreak();
              }

              final DefaultOutputFunction rollbackOutputFunction = (DefaultOutputFunction) state.getLayoutProcess().getOutputFunction();
              final Renderer rollbackRenderer = rollbackOutputFunction.getRenderer();
              rollbackRenderer.rollback();

              validate(state);

              isInRollBackMode = true;
              fallBackState = null; // there is no way we can fall-back inside a roll-back ..
              continue;
            }
            else
            {
              // The current state printed content partially on the now finished page and there is more
              // content on the currently open page. This is a in-between pagebreak, we invoke a pagebreak
              // after this state has been processed.
              if (AbstractReportProcessor.SHOW_ROLLBACKS)
              {
                AbstractReportProcessor.logger.debug("Paginate: Encountered on-going break " + lastVisibleStateKey);
              }
            }
          }
          else
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              if (isInRollBackMode)
              {
                if (assertExpectPagebreak == false)
                {
                  if (nextStateKey.equals(rollbackPageState) == false)
                  {
                    AbstractReportProcessor.logger.debug("X1: " + nextStateKey);
                    AbstractReportProcessor.logger.debug("X2: " + rollbackPageState);
                  }
                }
                AbstractReportProcessor.logger.debug("Paginate: Encountered a roll-back break: " + isInRollBackMode);
              }
              else
              {
                AbstractReportProcessor.logger.debug("Paginate: Encountered a good break: " + isInRollBackMode);
              }
              AbstractReportProcessor.logger.debug
                  ("Paginate:                                              : " + state.getProcessKey());
            }
            isInRollBackMode = false;
            rollbackPageState = null;
          }

          if (isPagebreaksSupported() == false)
          {
            // The commit causes all closed-nodes to become finishable. This allows the process-page
            // and the incremental-update methods to remove the nodes. For non-streaming targets (where
            // pagebreaks are possible) the commit state is managed manually
            renderer.applyAutoCommit();
          }

          if (renderer.processPage(pagebreakHandler, state.getProcessKey(), true) == false)
          {
            throw new IllegalStateException
                ("This cannot be. If the validation said we get a new page, how can we now get lost here");
          }

          if (isPagebreaksSupported() && renderer.isPendingPageHack() &&
              renderer.isCurrentPageEmpty() == false && renderer.isPageStartPending() == false)
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              logger.debug("Paginate: Delaying next event to allow pending pages to be processed: " + state.getProcessKey());
            }
            state = PendingPagesHandler.create(state);
          }
          else if (isPagebreaksSupported())
          {
            state = RestartOnNewPageHandler.create(state.commit());
          }
          else
          {
            state = state.commit();
          }

          // can continue safely ..
          final int newLogPageCount = outputProcessor.getLogicalPageCount();
          final int newPhysPageCount = outputProcessor.getPhysicalPageCount();

          final int result = pageStates.size() - 1;
          for (; physPageCount < newPhysPageCount; physPageCount++)
          {
            physicalMapping.add(result);
          }

          for (; logPageCount < newLogPageCount; logPageCount++)
          {
            logicalMapping.add(result);
          }

          if (state.isFinish() == false)
          {
            // A pagebreak has occured ...
            // We add all but the last state ..
            final PageState pageState = new PageState(state, outputProcessor.getPageCursor());
            pageStates.add(pageState);
          }

          if (isPagebreaksSupported())
          {
            fallBackState = state.deriveForPagebreak();
            globalState = state.deriveForStorage();
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              logger.debug("Paginate: Generating new fallback state after pagebreak found: " + state.getProcessKey());
            }
            pageEventCount = 0;
            eventCount = 0;
          }
        }
        else
        {
          if (isPagebreaksSupported() == false)
          {
            renderer.applyAutoCommit();
          }

          // PageEventCount is zero on streaming exports and zero after a new rollback event is created.
          if (pageEventCount == 0 && isInRollBackMode == false &&
              pagebreakEncountered == Renderer.LayoutResult.LAYOUT_NO_PAGEBREAK)
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              logger.debug("Paginate: Perform incremental update: " + state.getProcessKey());
            }
            renderer.processIncrementalUpdate(false);
          }
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Paginate: Commit: " + state.getProcessKey() + " " + state.getAdvanceHandler());
          }
          state = state.commit();
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Paginate: Post Commit: " + state.getProcessKey() + " " + state.getAdvanceHandler());
          }

          if (pagebreaksSupported && fallBackState != restoreState)
          {
            final DefaultOutputFunction commitableOutputFunction =
                (DefaultOutputFunction) state.getLayoutProcess().getOutputFunction();
            final Renderer commitableRenderer = commitableOutputFunction.getRenderer();
            commitableRenderer.applyRollbackInformation();
          }
        }

      }
      return initialReportState;
    }
    catch (ContentProcessingException e)
    {
      throw new ReportProcessingException("Content-Processing failed.", e);
    }
  }

  private void validate(final ProcessState state)
  {
    if (paranoidChecks)
    {
      final OutputFunction outputFunction = state.getLayoutProcess().getOutputFunction();
      if (outputFunction instanceof DefaultOutputFunction == false)
      {
        return;
      }

      final DefaultOutputFunction of = (DefaultOutputFunction) outputFunction;
      final AbstractRenderer r = (AbstractRenderer) of.getRenderer();
      r.performParanoidModelCheck();
    }
  }

  public boolean isPaginated()
  {
    return stateList != null;
  }

  protected PageState getLogicalPageState(final int page)
  {
    final int index = logicalMapping.get(page);
    final PageState pageState = stateList.get(index);
    if (pageState == null)
    {
      throw new IndexOutOfBoundsException(
          "The logical mapping between page " + page + " and index " + index + " is invalid.");
    }
    return pageState;
  }

  protected PageState getPhysicalPageState(final int page)
  {
    final int index = physicalMapping.get(page);
    final PageState pageState = stateList.get(index);
    if (pageState == null)
    {
      throw new IndexOutOfBoundsException(
          "The physical mapping between page " + page + " and index " + index + " is invalid.");
    }
    return pageState;
  }


  /**
   * Checks whether report processing should be aborted when an exception occurs.
   *
   * @param config the configuration.
   * @return if strict error handling is enabled.
   */
  protected static boolean isStrictErrorHandling(final Configuration config)
  {
    final String strictError = config.getConfigProperty(ClassicEngineCoreModule.STRICT_ERROR_HANDLING_KEY);
    return "true".equals(strictError);
  }

  public PageState processPage(final PageState pageState,
                               final boolean performOutput)
      throws ReportProcessingException
  {
    if (pageState == null)
    {
      throw new NullPointerException("PageState must not be null.");
    }
    final boolean failOnError = isStrictErrorHandling(getReport().getReportConfiguration());
    final ReportProcessingErrorHandler errorHandler = new CollectingReportErrorHandler();
    if (SHOW_ROLLBACKS)
    {
      AbstractReportProcessor.logger.debug(
          "Process Page: Starting with : " + pageState.getReportState().getProcessKey());
    }

    try
    {
      final ProcessState startState = pageState.getReportState();
      outputProcessor.setPageCursor(pageState.getPageCursor());
      final int maxRows = startState.getNumberOfRows();
      final ReportProgressEvent repaginationState = new ReportProgressEvent(this);
      final DefaultLayoutPagebreakHandler pagebreakHandler = new DefaultLayoutPagebreakHandler();
      // inner loop: process the complete report, calculate the function values
      // for the current level. Higher level functions are not available in the
      // dataRow.
      final int eventTrigger;
      if (maxRows <= 0)
      {
        eventTrigger = Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT);
      }
      else
      {
        eventTrigger = Math.min(maxRows, Math.max(maxRows / MAX_EVENTS_PER_RUN, MIN_ROWS_PER_EVENT));
      }

      final boolean pagebreaksSupported = isPagebreaksSupported();

      ReportStateKey rollbackPageState = null;

      ProcessState state = startState.deriveForStorage();
      ProcessState fallBackState = state.deriveForPagebreak();
      final ProcessState globalState = state.deriveForStorage();
      state.setErrorHandler(errorHandler);

      boolean isInRollBackMode = false;
      int lastRow = -1;
      int eventCount = 0;
      int pageEventCount = 0;
      while (!state.isFinish())
      {
        checkInterrupted();
        if (lastRow != state.getCurrentDataItem())
        {
          lastRow = state.getCurrentDataItem();
          if (eventCount == 0)
          {
            repaginationState.reuse(ReportProgressEvent.GENERATING_CONTENT,
                state.getCurrentDataItem(), maxRows, state.getCurrentPage(),
                state.getProgressLevel(), state.getProgressLevelCount());
            fireStateUpdate(repaginationState);
            eventCount += 1;
          }
          else
          {
            if (eventCount == eventTrigger)
            {
              eventCount = 0;
            }
            else
            {
              eventCount += 1;
            }
          }
        }

        ProcessState realFallbackState = fallBackState;
        final ProcessState restoreState;
        if (pagebreaksSupported && state.isArtifcialState() == false)
        {
          restoreState = fallBackState;
          if (isInRollBackMode == false)
          {
            if (pageEventCount >= AbstractReportProcessor.COMMIT_RATE)
            {
              final OutputFunction outputFunction = state.getLayoutProcess().getOutputFunction();
              if (outputFunction.createRollbackInformation())
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  logger.debug("Print: Try to generate new fallback state after commit count reached: " + state.getProcessKey());
                }
                realFallbackState = state.deriveForPagebreak();
              }
              else
              {
                realFallbackState = null;
              }
            }
          }
        }
        else
        {
          restoreState = null;
        }

        final ProcessState nextState = state.advance();
        state.setErrorHandler(IgnoreEverythingReportErrorHandler.INSTANCE);
        state = nextState;

        final ReportStateKey nextStateKey = state.getProcessKey();

        if (errorHandler.isErrorOccured() == true)
        {
          final List childExceptions = Arrays.asList(errorHandler.getErrors());
          errorHandler.clearErrors();
          if (failOnError)
          {
            throw new ReportEventException("Failed to dispatch an event.", childExceptions);
          }
          else
          {
            final ReportEventException exception =
                new ReportEventException("Failed to dispatch an event.", childExceptions);
            AbstractReportProcessor.logger.error("Failed to dispatch an event.", exception);
          }
        }

        if (state.isArtifcialState())
        {
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Print: Silent commit as we are in an artificial state: " + state.getProcessKey());
          }
          state = state.commit();
          continue;
        }

        final OutputFunction outputFunction = state.getLayoutProcess().getOutputFunction();
        if (outputFunction instanceof DefaultOutputFunction == false)
        {
          if (AbstractReportProcessor.SHOW_ROLLBACKS)
          {
            logger.debug("Print: Silent commit as we are have no access to the renderer: " + state.getProcessKey());
          }
          state = state.commit();
          continue;
        }

        final DefaultOutputFunction lm = (DefaultOutputFunction) outputFunction;
        final Renderer renderer = lm.getRenderer();
        pagebreakHandler.setReportState(state);

        boolean assertExpectPagebreak = false;
        if (isInRollBackMode)
        {
          if (nextStateKey.equals(rollbackPageState))
          {
            // reached the border case. We have to insert a manual pagebreak here or at least
            // we have to force the renderer to end the page right now.
//            Log.debug ("HERE: Found real pagebreak position. This might be the last state we process.");
            assertExpectPagebreak = true;
            renderer.addPagebreak(state.getProcessKey());
          }
        }

        final Renderer.LayoutResult pagebreakEncountered = renderer.validatePages();
        if (assertExpectPagebreak == true && pagebreakEncountered != Renderer.LayoutResult.LAYOUT_PAGEBREAK)
        {
          if (SHOW_ROLLBACKS)
          {
            AbstractReportProcessor.logger.debug("Paginate: Missed the pagebreak. This smells fishy!");
          }
        }

        if (pagebreakEncountered == Renderer.LayoutResult.LAYOUT_UNVALIDATABLE)
        {
          if (pagebreaksSupported && state.isArtifcialState() == false)
          {
            if (isInRollBackMode == false)
            {
              if (pageEventCount >= AbstractReportProcessor.COMMIT_RATE)
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  logger.debug("Paginate: Try to apply new fallback state after commit count reached: " + state.getProcessKey());
                }
                fallBackState = realFallbackState;
                pageEventCount = 0;
              }
              else
              {
                pageEventCount += 1;
              }
            }
          }
        }

        if (pagebreakEncountered == Renderer.LayoutResult.LAYOUT_PAGEBREAK)
        {
          final ReportStateKey lastVisibleStateKey = renderer.getLastStateKey();
          if (pagebreaksSupported &&
              isInRollBackMode == false &&
              renderer.isOpen() &&
              lastVisibleStateKey != null)
          {
            if (lastVisibleStateKey.equals(nextStateKey) == false)
            {
//              Log.debug ("HERE: Encountered bad break, need to roll-back");

              // Roll back to the last known to be good position and process the states up to, but not
              // including the current state. This way, we can fire the page-events *before* this band
              // gets printed.
              rollbackPageState = lastVisibleStateKey;

              final ReportStateKey restoreStateProcessKey = restoreState.getProcessKey();
              if (AbstractReportProcessor.SHOW_ROLLBACKS)
              {
                AbstractReportProcessor.logger.debug(
                    "Print: Encountered bad break, need to roll-back: " + rollbackPageState);
                AbstractReportProcessor.logger.debug(
                    "Print:                                         : " + state.getProcessKey());
                AbstractReportProcessor.logger.debug(
                    "Print:                                         : " + restoreStateProcessKey);
              }

              if (lastVisibleStateKey.getSequenceCounter() < restoreStateProcessKey.getSequenceCounter())
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  AbstractReportProcessor.logger.debug(
                      "Print: Fall back to start of page              : " + restoreStateProcessKey);
                }
                state = globalState.deriveForPagebreak();
              }
              else
              {
                if (AbstractReportProcessor.SHOW_ROLLBACKS)
                {
                  AbstractReportProcessor.logger.debug(
                      "Print: Fall back to save-state                 : " + restoreStateProcessKey);
                }
                state = restoreState.deriveForPagebreak();
              }

              final DefaultOutputFunction rollbackOutputFunction = (DefaultOutputFunction) state.getLayoutProcess().getOutputFunction();
              final Renderer rollbackRenderer = rollbackOutputFunction.getRenderer();
              rollbackRenderer.rollback();

              validate(state);

              isInRollBackMode = true;
              continue;
            }
            else
            {
              // The current state printed content partially on the now finished page and there is more
              // content on the currently open page. This is a in-between pagebreak, we invoke a pagebreak
              // after this state has been processed.
              if (AbstractReportProcessor.SHOW_ROLLBACKS)
              {
                AbstractReportProcessor.logger.debug("Print: Encountered on-going break " + lastVisibleStateKey);
              }
            }
          }
          else
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              AbstractReportProcessor.logger.debug(
                  "Print: Encountered a good break or a roll-back break: " + isInRollBackMode);
              AbstractReportProcessor.logger.debug(
                  "Print:                                              : " + state.getProcessKey());
            }
          }
          if (pagebreaksSupported == false)
          {
            // The commit causes all closed-nodes to become finishable. This allows the process-page
            // and the incremental-update methods to remove the nodes. For non-streaming targets (where
            // pagebreaks are possible) the commit state is managed manually
            renderer.applyAutoCommit();
          }

          if (renderer.processPage(pagebreakHandler, state.getProcessKey(), performOutput) == false)
          {
            throw new IllegalStateException("This must not be.");
          }

          if (isPagebreaksSupported() && renderer.isPendingPageHack() &&
              renderer.isCurrentPageEmpty() == false && renderer.isPageStartPending() == false)
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              logger.debug("Print: Delaying next event to allow pending pages to be processed: " + state.getProcessKey());
            }
            state = PendingPagesHandler.create(state);
          }
          else if (isPagebreaksSupported())
          {
            if (AbstractReportProcessor.SHOW_ROLLBACKS)
            {
              logger.debug("Print: Adding RestartOnNewPageHandler to open Page in time: " + state.getProcessKey());
            }
            state = RestartOnNewPageHandler.create(state.commit());
          }
          else
          {
            state = state.commit();
          }

          if (renderer.isOpen())
          {
            // No need to create a copy here. It is part of the contract that the resulting page state must be
            // cloned before it can be used again. The only place where it is used is this method, so we can
            // be pretty sure that this contract is valid.
            return new PageState(state, outputProcessor.getPageCursor());
          }
        }
        else
        {
          if (pagebreaksSupported == false)
          {
            // The commit causes all closed-nodes to become finishable. This allows the process-page
            // and the incremental-update methods to remove the nodes. For non-streaming targets (where
            // pagebreaks are possible) the commit state is managed manually
            renderer.applyAutoCommit();
          }

          if (pageEventCount == 0 && isInRollBackMode == false &&
              pagebreakEncountered == Renderer.LayoutResult.LAYOUT_NO_PAGEBREAK)
          {
            renderer.processIncrementalUpdate(performOutput);
          }
          state = state.commit();

          // Expected a pagebreak now, but did not encounter one.
//          if (nextStateKey.equals(rollbackPageState))
//          {
//            // reached the border case. We have to insert a manual pagebreak here or at least
//            // we have to force the renderer to end the page right now.
//            Log.debug ("HERE: Ups, Found real pagebreak position. but where is my break?");
//            //renderer.addPagebreak(state.getProcessKey());
//          }
          if (pagebreaksSupported && fallBackState != restoreState)
          {
            final DefaultOutputFunction commitableOutputFunction =
                (DefaultOutputFunction) state.getLayoutProcess().getOutputFunction();
            final Renderer commitableRenderer = commitableOutputFunction.getRenderer();
            commitableRenderer.applyRollbackInformation();
          }
        }
      }

      // We should never reach this point, if this function has been called by the PageStateList.
      //throw new ReportProcessingException("Content processing failed with an illegal state");
      return null;
    }
    catch (ContentProcessingException e)
    {
      throw new ReportProcessingException("Content-Processing failed.", e);
    }
  }

  public boolean paginate() throws ReportProcessingException
  {
    fireProcessingStarted(new ReportProgressEvent(this));

    try
    {
      if (isPaginated() == false)
      {
        // Processes the whole report ..
        prepareReportProcessing();
      }
    }
    finally
    {
      fireProcessingFinished(new ReportProgressEvent(this));
    }
    return true;
  }

  public void processReport() throws ReportProcessingException
  {
    if (AbstractReportProcessor.logger.isDebugEnabled())
    {
      AbstractReportProcessor.logger.debug(new MemoryUsageMessage(System.identityHashCode(
          Thread.currentThread()) + ": Report processing time: Starting: "));
    }
    try
    {
      final long startTime = System.currentTimeMillis();

      fireProcessingStarted(new ReportProgressEvent(this));

      if (isPaginated() == false)
      {
        // Processes the whole report ..
        prepareReportProcessing();
      }

      final long paginateTime = System.currentTimeMillis();
      if (AbstractReportProcessor.logger.isDebugEnabled())
      {
        AbstractReportProcessor.logger.debug(new MemoryUsageMessage
            (System.identityHashCode(Thread.currentThread()) +
                ": Report processing time: Pagination time: " + ((paginateTime - startTime) / 1000.0)));
      }
      if (getLogicalPageCount() == 0)
      {
        throw new EmptyReportException("Report did not generate any content.");
      }

      // Start from scratch ...
      PageState state = getLogicalPageState(0);
      while (state != null)
      {
        state = processPage(state, true);
      }
      final long endTime = System.currentTimeMillis();
      if (AbstractReportProcessor.logger.isDebugEnabled())
      {
        AbstractReportProcessor.logger.debug(new MemoryUsageMessage
            (System.identityHashCode(Thread.currentThread()) +
                ": Report processing time: " + ((endTime - startTime) / 1000.0)));
      }
    }
    catch (EmptyReportException re)
    {
      throw re;
    }
    catch (ReportProcessingException re)
    {
      AbstractReportProcessor.logger.error(System.identityHashCode(
          Thread.currentThread()) + ": Report processing failed.");
      throw re;
    }
    catch (Exception e)
    {
      AbstractReportProcessor.logger.error(System.identityHashCode(
          Thread.currentThread()) + ": Report processing failed.");
      throw new ReportProcessingException("Failed to process the report", e);
    }
    fireProcessingFinished(new ReportProgressEvent(this, getPhysicalPageCount()));
    if (AbstractReportProcessor.logger.isDebugEnabled())
    {
      AbstractReportProcessor.logger.debug(System.identityHashCode(
          Thread.currentThread()) + ": Report processing finished.");
    }
  }

  public int getLogicalPageCount()
  {
    if (logicalMapping == null)
    {
      return -1;
    }
    return logicalMapping.size();
  }

  public int getPhysicalPageCount()
  {
    if (physicalMapping == null)
    {
      return -1;
    }
    return physicalMapping.size();
  }

  /**
   * Checks whether the output mode may generate pagebreaks. If we have to deal with pagebreaks, we may have to perform
   * roll-backs and commits to keep the pagebreaks in sync with the state-processing. This is ugly, expensive and you
   * better dont try this at home.
   * <p/>
   * The roll-back is done for paginated and flow-report outputs, but if we have no autmoatic and manual pagebreaks,
   * there is no need to even consider to roll-back to a state before the pagebreak (which will never occur).
   *
   * @return a flag indicating whether the output target supports pagebreaks.
   */
  protected boolean isPagebreaksSupported()
  {
    return pagebreaksSupported;
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.layout.output.AbstractReportProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.