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

Source Code of org.pentaho.reporting.engine.classic.core.layout.output.DefaultPageStateList$InternalStorageState

/*
* 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.util.WeakReferenceList;
import org.pentaho.reporting.libraries.base.config.ExtendedConfigurationWrapper;


/**
* The ReportState list stores a report states for the beginning of every page. The list is filled on repagination and
* read when a report or a page of the report is printed.
* <p/>
* Important: This list stores page start report states, not arbitary report states. These ReportStates are special:
* they can be reproduced by calling processPage on the report.
* <p/>
* Internally this list is organized as a list of WeakReferenceLists, where every WeakReferenceList stores a certain
* number of page states. The first 20 states are stored in an ordinary list with strong-references, so these states
* never get GarbageCollected (and so they must never be restored by reprocessing them). The next 100 states are stored
* in 4-element ReferenceLists, so if a reference is lost, only 4 states have to be reprocessed. All other states are
* stored in 10-element lists.
*
* @author Thomas Morgner
*/
public class DefaultPageStateList implements PageStateList
{
  private static final Log logger = LogFactory.getLog(DefaultPageStateList.class);
  /**
   * The position of the master element in the list. A greater value will reduce the
   * not-freeable memory used by the list, but restoring a single page will require more
   * time.
   */

  /**
   * The maxmimum masterposition size.
   */
  private static final int MASTERPOSITIONS_MAX = 10;

  /**
   * The medium masterposition size.
   */
  private static final int MASTERPOSITIONS_MED = 4;

  /**
   * The max index that will be stored in the primary list.
   */
  private static final int PRIMARY_MAX = 20;

  /**
   * The max index that will be stored in the master4 list.
   */
  private static final int MASTER4_MAX = 100;

  /**
   * Internal WeakReferenceList that is capable to restore its elements. The elements in this list are page start report
   * states.
   */
  private final class MasterList extends WeakReferenceList<InternalStorageState>
  {
    /**
     * The master list.
     */
    private final DefaultPageStateList master;

    /**
     * Creates a new master list.
     *
     * @param list          the list.
     * @param maxChildCount the maximum number of elements in this list.
     */
    private MasterList(final DefaultPageStateList list, final int maxChildCount)
    {
      super(maxChildCount);
      this.master = list;
    }

    public Object clone() throws CloneNotSupportedException
    {
      return super.clone();
    }

    /**
     * Function to restore the state of a child after the child was garbage collected.
     *
     * @param index the index.
     * @return the restored ReportState of the given index, or null, if the state could not be restored.
     */
    protected InternalStorageState restoreChild(final int index)
    {
      InternalStorageState master = (InternalStorageState) getMaster();
      if (master == null)
      {
        throw new IllegalStateException("We cannot have a master list without a master state");
      }
      try
      {
        final int max = getChildPos(index);
        logger.info("Restoring weak state " + master.getStorePosition() + " for # " + max);
       
        if (master.isValidRestorePoint() == false)
        {
          // not a safe point, so restore the master first ..
          final InternalStorageState state = DefaultPageStateList.this.restoreState
              (master.getStorePosition(), master.getLastSavePosition(), getInternal(master.getLastSavePosition()));
          setMaster(state);
          master = state;
        }

        return restoreState(max, master);
      }
      catch (Exception rpe)
      {
        DefaultPageStateList.logger.debug("Caught exception while restoring a saved page-state", rpe);
        throw new IllegalStateException("Something went wrong while trying to restore the child #" + index);
      }
    }

    /**
     * Internal handler function restore a state. Count denotes the number of pages required to be processed to restore
     * the page, when the reportstate master is used as source element.
     *
     * @param count     the count.
     * @param rootstate the root state.
     * @return the report state.
     * @throws org.pentaho.reporting.engine.classic.core.ReportProcessingException
     *          if there was a problem processing the report.
     */
    private InternalStorageState restoreState(final int count, final InternalStorageState rootstate)
        throws ReportProcessingException
    {
      if (rootstate == null)
      {
        throw new NullPointerException("Master is null");
      }

      InternalStorageState state = rootstate;
      for (int i = 0; i <= count; i++)
      {
        final ReportProcessor pageProcess = master.getPageProcess();
        final PageState pageState = pageProcess.processPage(state.getPageState(), false);
        if (pageState == null)
        {
          throw new IllegalStateException("State returned is null: Report processing reached premature end-point.");
        }
        state = new InternalStorageState
            (pageState, rootstate.getStorePosition() + i, rootstate.getLastSavePosition());
        if (state.isValidRestorePoint())
        {
          set(state, i + 1);
        }
      }
      return state;
    }
  }

  private static class InternalStorageState
  {
    private PageState pageState;
    private int storePosition;
    private int lastSavePosition;

    private InternalStorageState(final PageState pageState,
                                 final int storePosition,
                                 final int lastSavePosition)
    {
      this.pageState = pageState;
      this.storePosition = storePosition;
      this.lastSavePosition = lastSavePosition;
    }

    public PageState getPageState()
    {
      return pageState;
    }

    public int getStorePosition()
    {
      return storePosition;
    }

    public int getLastSavePosition()
    {
      return lastSavePosition;
    }

    public boolean isValidRestorePoint()
    {
      if (pageState == null)
      {
        return false;
      }
      return pageState.isSafeToStoreEarly();
    }
  }

  /**
   * The list of master states. This is a list of WeakReferenceLists. These WeakReferenceLists contain their master
   * state as first child. The weakReferenceLists have a maxSize of 10, so every 10th state will protected from being
   * garbageCollected.
   */
  private ArrayList<MasterList> masterStates10; // all states > 120
  /**
   * The list of master states. This is a list of WeakReferenceLists. These WeakReferenceLists contain their master
   * state as first child. The weakReferenceLists have a maxSize of 4, so every 4th state will protected from being
   * garbageCollected.
   */
  private ArrayList<MasterList> masterStates4; // all states from 20 - 120

  /**
   * The list of primary states. This is a list of ReportStates and is used to store the first 20 elements of this state
   * list.
   */
  private ArrayList<InternalStorageState> primaryStates; // all states from 0 - 20

  /**
   * The number of elements in this list.
   */
  private int size;

  private ReportProcessor pageProcess;

  private int primaryPoolSize;
  private int secondaryPoolSize;
  private int secondaryPoolFrequency;
  private int tertiaryPoolFrequency;
  private int lastSafePosition;

  /**
   * Creates a new reportstatelist. The list will be filled using the specified report and output target. Filling of the
   * list is done elsewhere.
   *
   * @param proc the reportprocessor used to restore lost states (null not permitted).
   * @throws NullPointerException if the report processor is <code>null</code>.
   */
  public DefaultPageStateList(final ReportProcessor proc)
  {
    if (proc == null)
    {
      throw new NullPointerException("ReportProcessor null");
    }

    this.pageProcess = proc;

    final ExtendedConfigurationWrapper config = new ExtendedConfigurationWrapper(proc.getConfiguration());

    this.primaryPoolSize = config.getIntProperty
        ("org.pentaho.reporting.engine.classic.core.performance.pagestates.PrimaryPoolSize", PRIMARY_MAX);
    this.secondaryPoolFrequency = config.getIntProperty
        ("org.pentaho.reporting.engine.classic.core.performance.pagestates.SecondaryPoolFrequency", MASTERPOSITIONS_MED);
    this.secondaryPoolSize = config.getIntProperty
        ("org.pentaho.reporting.engine.classic.core.performance.pagestates.SecondaryPoolSize", MASTER4_MAX) + primaryPoolSize;
    this.tertiaryPoolFrequency = config.getIntProperty
        ("org.pentaho.reporting.engine.classic.core.performance.pagestates.TertiaryPoolFrequency", MASTERPOSITIONS_MAX);

    if (primaryPoolSize < 1)
    {
      throw new IllegalStateException("Invalid configuration: Primary pool must be >= 1");
    }
    if (secondaryPoolSize < primaryPoolSize)
    {
      throw new IllegalStateException("Invalid configuration: Secondary pool must be >= primary pool");
    }
    if (secondaryPoolFrequency < 1)
    {
      throw new IllegalStateException("Invalid configuration: Secondary pool frequency must be >= 1");
    }
    if (tertiaryPoolFrequency < 1)
    {
      throw new IllegalStateException("Invalid configuration: Tertiary pool frequency must be >= 1");
    }
    primaryStates = new ArrayList<InternalStorageState>(primaryPoolSize);
    masterStates4 = new ArrayList<MasterList>(secondaryPoolSize);
    masterStates10 = new ArrayList<MasterList>();

  }

  /**
   * Returns the index of the WeakReferenceList in the master list.
   *
   * @param pos         the position.
   * @param maxListSize the maximum list size.
   * @return the position within the masterStateList.
   */
  private int getMasterPos(final int pos, final int maxListSize)
  {
    //return (int) Math.floor(pos / maxListSize);
    return (pos / maxListSize);
  }

  protected ReportProcessor getPageProcess()
  {
    return pageProcess;
  }

  /**
   * Returns the number of elements in this list.
   *
   * @return the number of elements in the list.
   */
  public int size()
  {
    return this.size;
  }

  /**
   * Adds this report state to the end of the list.
   *
   * @param pageState the report state.
   */
  public void add(final PageState pageState)
  {
    if (pageState == null)
    {
      throw new NullPointerException();
    }

    final int size = size();
    final InternalStorageState state;
    if (pageState.isSafeToStoreEarly() == false)
    {
      state = new InternalStorageState(null, size, lastSafePosition);
    }
    else
    {
      lastSafePosition = size;
      pageState.prepareStorage();
      state = new InternalStorageState(pageState, size, lastSafePosition);
    }

    // the first 20 Elements are stored directly into an ArrayList
    if (size < primaryPoolSize)
    {
      primaryStates.add(state);
      this.size++;
    }
    // the next 100 Elements are stored into a list of 4-element weakReference
    //list. So if an Element gets lost (GCd), only 4 states need to be replayed.
    else if (size < secondaryPoolSize)
    {
      final int secPos = size - primaryPoolSize;
      final int masterPos = getMasterPos(secPos, secondaryPoolFrequency);
      if (masterPos >= masterStates4.size())
      {
        final MasterList master = new MasterList(this, secondaryPoolFrequency);
        masterStates4.add(master);
        master.add(state);
      }
      else
      {
        final MasterList master = masterStates4.get(masterPos);
        master.add(state);
      }
      this.size++;
    }
    // all other Elements are stored into a list of 10-element weakReference
    //list. So if an Element gets lost (GCd), 10 states need to be replayed.
    else
    {
      final int thirdPos = size - secondaryPoolSize;
      final int masterPos = getMasterPos(thirdPos, tertiaryPoolFrequency);
      if (masterPos >= masterStates10.size())
      {
        final MasterList master = new MasterList(this, tertiaryPoolFrequency);
        masterStates10.add(master);
        master.add(state);
      }
      else
      {
        final MasterList master = masterStates10.get(masterPos);
        master.add(state);
      }
      this.size++;
    }
  }

  protected void set(final int index, final InternalStorageState state)
  {
    if (index >= size)
    {
      throw new IndexOutOfBoundsException();
    }

    if (index != state.getStorePosition())
    {
      throw new IllegalArgumentException();
    }
   
    // the first 20 Elements are stored directly into an ArrayList
    if (index < primaryPoolSize)
    {
      final InternalStorageState o = primaryStates.get(index);
      if (o.isValidRestorePoint())
      {
        throw new IllegalArgumentException();
      }

      primaryStates.set(index, state);
    }
    // the next 100 Elements are stored into a list of 4-element weakReference
    //list. So if an Element gets lost (GCd), only 4 states need to be replayed.
    else if (index < secondaryPoolSize)
    {
      final int secPos = index - primaryPoolSize;
      final int masterPos = getMasterPos(secPos, secondaryPoolFrequency);
      if (masterPos >= masterStates4.size())
      {
        throw new IllegalStateException("Replacing an existing entry must not generate a new list slot.");
      }

      final MasterList master = masterStates4.get(masterPos);
      final InternalStorageState o = master.getRaw(secPos);
      if (o != null && o.isValidRestorePoint())
      {
        throw new IllegalArgumentException();
      }

      master.set(state, secPos);
    }
    // all other Elements are stored into a list of 10-element weakReference
    //list. So if an Element gets lost (GCd), 10 states need to be replayed.
    else
    {
      final int thirdPos = index - secondaryPoolSize;
      final int masterPos = getMasterPos(thirdPos, tertiaryPoolFrequency);
      if (masterPos >= masterStates10.size())
      {
        throw new IllegalStateException("Replacing an existing entry must not generate a new list slot.");
      }
      else
      {
        final MasterList master = masterStates10.get(masterPos);
        final InternalStorageState o = master.getRaw(thirdPos);
        if (o != null && o.isValidRestorePoint())
        {
          throw new IllegalArgumentException();
        }

        master.set(state, thirdPos);
      }
    }
  }

  /**
   * Removes all elements in the list.
   */
  public void clear()
  {
    masterStates10.clear();
    masterStates4.clear();
    primaryStates.clear();
    this.size = 0;
  }

  /**
   * Retrieves the element on position <code>index</code> in this list.
   *
   * @param index the index.
   * @return the report state.
   */
  public PageState get(final int index)
  {
    if (index >= size() || index < 0)
    {
      throw new IndexOutOfBoundsException
          ("Index is invalid. Index was " + index + "; size was " + size());
    }
    final InternalStorageState internal = getInternal(index);
    if (internal.isValidRestorePoint() == false)
    {
      try
      {
        // From the 'internal' pagestate we know the worst case position where we can find a
        // page state to restore our report processing from. But maybe we have a cheaper solution
        // inbetween, maybe from previous restore runs. So we backtrack from the current target
        // to the worst case and start restoring from any earlier states.

        final int targetPageCursor = internal.getStorePosition();
        final int stateCounter = internal.getLastSavePosition();
        for (int i = targetPageCursor - 1; i >= stateCounter; i -= 1)
        {
          final InternalStorageState startState = getInternal(i);
          if (startState.isValidRestorePoint())
          {
            final InternalStorageState internalStorageState = restoreState(targetPageCursor, i, startState);
            return internalStorageState.getPageState();
          }
        }
        throw new IllegalStateException();
      }
      catch (ReportProcessingException e)
      {
        throw new IllegalStateException(e);
      }
    }
    return internal.getPageState();
  }

  /**
   * Internal handler function restore a state. Count denotes the number of pages required to be processed to restore
   * the page, when the reportstate master is used as source element.
   *
   * @param pageCursor    the page cursor for the end state of the restore sequence.
   * @param lastSaveState the page cursor for the start state of the restore sequence.
   * @param rootstate     the root state.
   * @return the report state.
   * @throws org.pentaho.reporting.engine.classic.core.ReportProcessingException
   *          if there was a problem processing the report.
   */
  private InternalStorageState restoreState(final int pageCursor,
                                            final int lastSaveState,
                                            final InternalStorageState rootstate)
      throws ReportProcessingException
  {
    logger.info("Restoring global state " + pageCursor + " from " + lastSaveState);
    if (rootstate == null)
    {
      throw new NullPointerException("Master is null");
    }

    if (rootstate.isValidRestorePoint() == false)
    {
      throw new IllegalArgumentException();
    }

    InternalStorageState state = rootstate;
    for (int i = lastSaveState; i < pageCursor; i++)
    {
      final ReportProcessor pageProcess = getPageProcess();
      final PageState pageState = pageProcess.processPage(state.getPageState(), false);
      if (pageState == null)
      {
        throw new IllegalStateException("State returned is null: Report processing reached premature end-point.");
      }
      state = new InternalStorageState(pageState, i + 1, rootstate.getLastSavePosition());
      if (pageState.isSafeToStoreEarly())
      {
        set(i + 1, state);
      }
    }
    return state;
  }

  private InternalStorageState getInternal(int index)
  {
    if (index < primaryPoolSize)
    {
      return primaryStates.get(index);
    }
    else if (index < secondaryPoolSize)
    {
      index -= primaryPoolSize;
      final MasterList master = masterStates4.get(getMasterPos(index, secondaryPoolFrequency));
      return (InternalStorageState) master.get(index);
    }
    else
    {
      index -= secondaryPoolSize;
      final MasterList master = masterStates10.get(getMasterPos(index, tertiaryPoolFrequency));
      return (InternalStorageState) master.get(index);
    }
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.layout.output.DefaultPageStateList$InternalStorageState

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.