Source for org.jfree.report.flow.paginating.PageStateList

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: PageStateList.java 2754 2007-04-10 15:01:40Z taqua $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: 
  32: package org.jfree.report.flow.paginating;
  33: 
  34: import java.util.ArrayList;
  35: 
  36: import org.jfree.layouting.StateException;
  37: import org.jfree.report.DataSourceException;
  38: import org.jfree.report.ReportDataFactoryException;
  39: import org.jfree.report.ReportProcessingException;
  40: import org.jfree.report.util.WeakReferenceList;
  41: 
  42: 
  43: /**
  44:  * The ReportState list stores a report states for the beginning of every page. The list
  45:  * is filled on repagination and read when a report or a page of the report is printed.
  46:  * <p/>
  47:  * Important: This list stores page start report states, not arbitary report states. These
  48:  * ReportStates are special: they can be reproduced by calling processPage on the report.
  49:  * <p/>
  50:  * Internally this list is organized as a list of WeakReferenceLists, where every
  51:  * WeakReferenceList stores a certain number of page states. The first 20 states are
  52:  * stored in an ordinary list with strong-references, so these states never get
  53:  * GarbageCollected (and so they must never be restored by reprocessing them). The next
  54:  * 100 states are stored in 4-element ReferenceLists, so if a reference is lost, only 4
  55:  * states have to be reprocessed. All other states are stored in 10-element lists.
  56:  *
  57:  * @author Thomas Morgner
  58:  */
  59: public class PageStateList
  60: {
  61:   /**
  62:    * The position of the master element in the list. A greater value will reduce the
  63:    * not-freeable memory used by the list, but restoring a single page will require more
  64:    * time.
  65:    */
  66: 
  67:   /**
  68:    * The maxmimum masterposition size.
  69:    */
  70:   private static final int MASTERPOSITIONS_MAX = 10;
  71: 
  72:   /**
  73:    * The medium masterposition size.
  74:    */
  75:   private static final int MASTERPOSITIONS_MED = 4;
  76: 
  77:   /**
  78:    * The max index that will be stored in the primary list.
  79:    */
  80:   private static final int PRIMARY_MAX = 20;
  81: 
  82:   /**
  83:    * The max index that will be stored in the master4 list.
  84:    */
  85:   private static final int MASTER4_MAX = 120;
  86: 
  87:   /**
  88:    * Internal WeakReferenceList that is capable to restore its elements. The elements in
  89:    * this list are page start report states.
  90:    */
  91:   private static final class MasterList extends WeakReferenceList
  92:   {
  93:     /**
  94:      * The master list.
  95:      */
  96:     private final PageStateList master;
  97: 
  98:     /**
  99:      * Creates a new master list.
 100:      *
 101:      * @param list          the list.
 102:      * @param maxChildCount the maximum number of elements in this list.
 103:      */
 104:     private MasterList (final PageStateList list, final int maxChildCount)
 105:     {
 106:       super(maxChildCount);
 107:       this.master = list;
 108:     }
 109: 
 110:     /**
 111:      * Function to restore the state of a child after the child was garbage collected.
 112:      *
 113:      * @param index the index.
 114:      * @return the restored ReportState of the given index, or null, if the state could
 115:      *         not be restored.
 116:      */
 117:     protected Object restoreChild (final int index)
 118:     {
 119:       final PageState master = (PageState) getMaster();
 120:       if (master == null)
 121:       {
 122:         return null;
 123:       }
 124:       final int max = getChildPos(index);
 125:       try
 126:       {
 127:         return this.restoreState(max, master);
 128:       }
 129:       catch (Exception rpe)
 130:       {
 131:         return null;
 132:       }
 133:     }
 134: 
 135:     /**
 136:      * Internal handler function restore a state. Count denotes the number of pages
 137:      * required to be processed to restore the page, when the reportstate master is used
 138:      * as source element.
 139:      *
 140:      * @param count     the count.
 141:      * @param rootstate the root state.
 142:      * @return the report state.
 143:      *
 144:      * @throws ReportProcessingException if there was a problem processing the report.
 145:      */
 146:     private PageState restoreState (final int count, final PageState rootstate)
 147:         throws ReportProcessingException, StateException,
 148:         ReportDataFactoryException, DataSourceException
 149:     {
 150:       if (rootstate == null)
 151:       {
 152:         throw new NullPointerException("Master is null");
 153:       }
 154:       PageState state = rootstate;
 155:       for (int i = 0; i <= count; i++)
 156:       {
 157:         final PaginatingReportProcessor pageProcess = master.getPageProcess();
 158:         state = pageProcess.processPage(state);
 159:         set(state, i + 1);
 160:         // todo: How to prevent endless loops. Should we prevent them at all?
 161:       }
 162:       return state;
 163:     }
 164:   }
 165: 
 166:   /**
 167:    * The list of master states. This is a list of WeakReferenceLists. These
 168:    * WeakReferenceLists contain their master state as first child. The weakReferenceLists
 169:    * have a maxSize of 10, so every 10th state will protected from being
 170:    * garbageCollected.
 171:    */
 172:   private ArrayList masterStates10; // all states > 120
 173:   /**
 174:    * The list of master states. This is a list of WeakReferenceLists. These
 175:    * WeakReferenceLists contain their master state as first child. The weakReferenceLists
 176:    * have a maxSize of 4, so every 4th state will protected from being garbageCollected.
 177:    */
 178:   private ArrayList masterStates4; // all states from 20 - 120
 179: 
 180:   /**
 181:    * The list of primary states. This is a list of ReportStates and is used to store the
 182:    * first 20 elements of this state list.
 183:    */
 184:   private ArrayList primaryStates; // all states from 0 - 20
 185: 
 186:   /**
 187:    * The number of elements in this list.
 188:    */
 189:   private int size;
 190: 
 191:   private PaginatingReportProcessor pageProcess;
 192: 
 193:   /**
 194:    * Creates a new reportstatelist. The list will be filled using the specified report and
 195:    * output target. Filling of the list is done elsewhere.
 196:    *
 197:    * @param proc the reportprocessor used to restore lost states (null not permitted).
 198:    * @throws NullPointerException if the report processor is <code>null</code>.
 199:    */
 200:   public PageStateList (final PaginatingReportProcessor proc)
 201:   {
 202:     if (proc == null)
 203:     {
 204:       throw new NullPointerException("ReportProcessor null");
 205:     }
 206: 
 207:     this.pageProcess = proc;
 208: 
 209:     primaryStates = new ArrayList();
 210:     masterStates4 = new ArrayList();
 211:     masterStates10 = new ArrayList();
 212: 
 213:   }
 214: 
 215:   /**
 216:    * Returns the index of the WeakReferenceList in the master list.
 217:    *
 218:    * @param pos         the position.
 219:    * @param maxListSize the maximum list size.
 220:    * @return the position within the masterStateList.
 221:    */
 222:   private int getMasterPos (final int pos, final int maxListSize)
 223:   {
 224:     //return (int) Math.floor(pos / maxListSize);
 225:     return (pos / maxListSize);
 226:   }
 227: 
 228:   protected PaginatingReportProcessor getPageProcess ()
 229:   {
 230:     return pageProcess;
 231:   }
 232: 
 233:   /**
 234:    * Returns the number of elements in this list.
 235:    *
 236:    * @return the number of elements in the list.
 237:    */
 238:   public int size ()
 239:   {
 240:     return this.size;
 241:   }
 242: 
 243:   /**
 244:    * Adds this report state to the end of the list.
 245:    *
 246:    * @param state the report state.
 247:    */
 248:   public void add (final PageState state)
 249:   {
 250:     if (state == null)
 251:     {
 252:       throw new NullPointerException();
 253:     }
 254: 
 255:     // the first 20 Elements are stored directly into an ArrayList
 256:     if (size() < PRIMARY_MAX)
 257:     {
 258:       primaryStates.add(state);
 259:       this.size++;
 260:     }
 261:     // the next 100 Elements are stored into a list of 4-element weakReference
 262:     //list. So if an Element gets lost (GCd), only 4 states need to be replayed.
 263:     else if (size() < MASTER4_MAX)
 264:     {
 265:       final int secPos = size() - PRIMARY_MAX;
 266:       final int masterPos = getMasterPos(secPos, MASTERPOSITIONS_MED);
 267:       if (masterPos >= masterStates4.size())
 268:       {
 269:         final MasterList master = new MasterList(this, MASTERPOSITIONS_MED);
 270:         masterStates4.add(master);
 271:         master.add(state);
 272:       }
 273:       else
 274:       {
 275:         final MasterList master = (MasterList) masterStates4.get(masterPos);
 276:         master.add(state);
 277:       }
 278:       this.size++;
 279:     }
 280:     // all other Elements are stored into a list of 10-element weakReference
 281:     //list. So if an Element gets lost (GCd), 10 states need to be replayed.
 282:     else
 283:     {
 284:       final int thirdPos = size() - MASTER4_MAX;
 285:       final int masterPos = getMasterPos(thirdPos, MASTERPOSITIONS_MAX);
 286:       if (masterPos >= masterStates10.size())
 287:       {
 288:         final MasterList master = new MasterList(this, MASTERPOSITIONS_MAX);
 289:         masterStates10.add(master);
 290:         master.add(state);
 291:       }
 292:       else
 293:       {
 294:         final MasterList master = (MasterList) masterStates10.get(masterPos);
 295:         master.add(state);
 296:       }
 297:       this.size++;
 298:     }
 299:   }
 300: 
 301:   /**
 302:    * Removes all elements in the list.
 303:    */
 304:   public void clear ()
 305:   {
 306:     masterStates10.clear();
 307:     masterStates4.clear();
 308:     primaryStates.clear();
 309:     this.size = 0;
 310:   }
 311: 
 312:   /**
 313:    * Retrieves the element on position <code>index</code> in this list.
 314:    *
 315:    * @param index the index.
 316:    * @return the report state.
 317:    */
 318:   public PageState get (int index)
 319:   {
 320:     if (index >= size() || index < 0)
 321:     {
 322:       throw new IndexOutOfBoundsException
 323:               ("Index is invalid. Index was " + index + "; size was " + size());
 324:     }
 325:     if (index < PRIMARY_MAX)
 326:     {
 327:       return (PageState) primaryStates.get(index);
 328:     }
 329:     else if (index < MASTER4_MAX)
 330:     {
 331:       index -= PRIMARY_MAX;
 332:       final MasterList master
 333:               = (MasterList) masterStates4.get(getMasterPos(index, MASTERPOSITIONS_MED));
 334:       return (PageState) master.get(index);
 335:     }
 336:     else
 337:     {
 338:       index -= MASTER4_MAX;
 339:       final MasterList master
 340:               = (MasterList) masterStates10.get(getMasterPos(index, MASTERPOSITIONS_MAX));
 341:       return (PageState) master.get(index);
 342:     }
 343:   }
 344: }