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,v 1.4 2007/04/10 15:01:40 taqua Exp $
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 }