001 /** 002 * ======================================== 003 * JFreeReport : a free Java report library 004 * ======================================== 005 * 006 * Project Info: http://reporting.pentaho.org/ 007 * 008 * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors. 009 * 010 * This library is free software; you can redistribute it and/or modify it under the terms 011 * of the GNU Lesser General Public License as published by the Free Software Foundation; 012 * either version 2.1 of the License, or (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public License along with this 019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 020 * Boston, MA 02111-1307, USA. 021 * 022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 023 * in the United States and other countries.] 024 * 025 * ------------ 026 * $Id: PageStateList.java 2754 2007-04-10 15:01:40Z taqua $ 027 * ------------ 028 * (C) Copyright 2000-2005, by Object Refinery Limited. 029 * (C) Copyright 2005-2007, by Pentaho Corporation. 030 */ 031 032 package org.jfree.report.flow.paginating; 033 034 import java.util.ArrayList; 035 036 import org.jfree.layouting.StateException; 037 import org.jfree.report.DataSourceException; 038 import org.jfree.report.ReportDataFactoryException; 039 import org.jfree.report.ReportProcessingException; 040 import org.jfree.report.util.WeakReferenceList; 041 042 043 /** 044 * The ReportState list stores a report states for the beginning of every page. The list 045 * is filled on repagination and read when a report or a page of the report is printed. 046 * <p/> 047 * Important: This list stores page start report states, not arbitary report states. These 048 * ReportStates are special: they can be reproduced by calling processPage on the report. 049 * <p/> 050 * Internally this list is organized as a list of WeakReferenceLists, where every 051 * WeakReferenceList stores a certain number of page states. The first 20 states are 052 * stored in an ordinary list with strong-references, so these states never get 053 * GarbageCollected (and so they must never be restored by reprocessing them). The next 054 * 100 states are stored in 4-element ReferenceLists, so if a reference is lost, only 4 055 * states have to be reprocessed. All other states are stored in 10-element lists. 056 * 057 * @author Thomas Morgner 058 */ 059 public class PageStateList 060 { 061 /** 062 * The position of the master element in the list. A greater value will reduce the 063 * not-freeable memory used by the list, but restoring a single page will require more 064 * time. 065 */ 066 067 /** 068 * The maxmimum masterposition size. 069 */ 070 private static final int MASTERPOSITIONS_MAX = 10; 071 072 /** 073 * The medium masterposition size. 074 */ 075 private static final int MASTERPOSITIONS_MED = 4; 076 077 /** 078 * The max index that will be stored in the primary list. 079 */ 080 private static final int PRIMARY_MAX = 20; 081 082 /** 083 * The max index that will be stored in the master4 list. 084 */ 085 private static final int MASTER4_MAX = 120; 086 087 /** 088 * Internal WeakReferenceList that is capable to restore its elements. The elements in 089 * this list are page start report states. 090 */ 091 private static final class MasterList extends WeakReferenceList 092 { 093 /** 094 * The master list. 095 */ 096 private final PageStateList master; 097 098 /** 099 * 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 }