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 3048 2007-07-28 18:02:42Z tmorgner $
027: * ------------
028: * (C) Copyright 2000-2005, by Object Refinery Limited.
029: * (C) Copyright 2005-2007, by Pentaho Corporation.
030: */package org.jfree.report.flow.paginating;
031:
032: import java.util.ArrayList;
033:
034: import org.jfree.layouting.StateException;
035: import org.jfree.report.DataSourceException;
036: import org.jfree.report.ReportDataFactoryException;
037: import org.jfree.report.ReportProcessingException;
038: import org.jfree.report.util.WeakReferenceList;
039:
040: /**
041: * The ReportState list stores a report states for the beginning of every page. The list
042: * is filled on repagination and read when a report or a page of the report is printed.
043: * <p/>
044: * Important: This list stores page start report states, not arbitary report states. These
045: * ReportStates are special: they can be reproduced by calling processPage on the report.
046: * <p/>
047: * Internally this list is organized as a list of WeakReferenceLists, where every
048: * WeakReferenceList stores a certain number of page states. The first 20 states are
049: * stored in an ordinary list with strong-references, so these states never get
050: * GarbageCollected (and so they must never be restored by reprocessing them). The next
051: * 100 states are stored in 4-element ReferenceLists, so if a reference is lost, only 4
052: * states have to be reprocessed. All other states are stored in 10-element lists.
053: *
054: * @author Thomas Morgner
055: */
056: public class PageStateList {
057: /**
058: * The position of the master element in the list. A greater value will reduce the
059: * not-freeable memory used by the list, but restoring a single page will require more
060: * time.
061: */
062:
063: /**
064: * The maxmimum masterposition size.
065: */
066: private static final int MASTERPOSITIONS_MAX = 10;
067:
068: /**
069: * The medium masterposition size.
070: */
071: private static final int MASTERPOSITIONS_MED = 4;
072:
073: /**
074: * The max index that will be stored in the primary list.
075: */
076: private static final int PRIMARY_MAX = 20;
077:
078: /**
079: * The max index that will be stored in the master4 list.
080: */
081: private static final int MASTER4_MAX = 120;
082:
083: /**
084: * Internal WeakReferenceList that is capable to restore its elements. The elements in
085: * this list are page start report states.
086: */
087: private static final class MasterList extends WeakReferenceList {
088: /**
089: * The master list.
090: */
091: private final PageStateList master;
092:
093: /**
094: * Creates a new master list.
095: *
096: * @param list the list.
097: * @param maxChildCount the maximum number of elements in this list.
098: */
099: private MasterList(final PageStateList list,
100: final int maxChildCount) {
101: super (maxChildCount);
102: this .master = list;
103: }
104:
105: /**
106: * Function to restore the state of a child after the child was garbage collected.
107: *
108: * @param index the index.
109: * @return the restored ReportState of the given index, or null, if the state could
110: * not be restored.
111: */
112: protected Object restoreChild(final int index) {
113: final PageState master = (PageState) getMaster();
114: if (master == null) {
115: return null;
116: }
117: final int max = getChildPos(index);
118: try {
119: return this .restoreState(max, master);
120: } catch (Exception rpe) {
121: return null;
122: }
123: }
124:
125: /**
126: * Internal handler function restore a state. Count denotes the number of pages
127: * required to be processed to restore the page, when the reportstate master is used
128: * as source element.
129: *
130: * @param count the count.
131: * @param rootstate the root state.
132: * @return the report state.
133: *
134: * @throws ReportProcessingException if there was a problem processing the report.
135: */
136: private PageState restoreState(final int count,
137: final PageState rootstate)
138: throws ReportProcessingException, StateException,
139: ReportDataFactoryException, DataSourceException {
140: if (rootstate == null) {
141: throw new NullPointerException("Master is null");
142: }
143: PageState state = rootstate;
144: for (int i = 0; i <= count; i++) {
145: final PaginatingReportProcessor pageProcess = master
146: .getPageProcess();
147: state = pageProcess.processPage(state);
148: set(state, i + 1);
149: // todo: How to prevent endless loops. Should we prevent them at all?
150: }
151: return state;
152: }
153: }
154:
155: /**
156: * The list of master states. This is a list of WeakReferenceLists. These
157: * WeakReferenceLists contain their master state as first child. The weakReferenceLists
158: * have a maxSize of 10, so every 10th state will protected from being
159: * garbageCollected.
160: */
161: private ArrayList masterStates10; // all states > 120
162: /**
163: * The list of master states. This is a list of WeakReferenceLists. These
164: * WeakReferenceLists contain their master state as first child. The weakReferenceLists
165: * have a maxSize of 4, so every 4th state will protected from being garbageCollected.
166: */
167: private ArrayList masterStates4; // all states from 20 - 120
168:
169: /**
170: * The list of primary states. This is a list of ReportStates and is used to store the
171: * first 20 elements of this state list.
172: */
173: private ArrayList primaryStates; // all states from 0 - 20
174:
175: /**
176: * The number of elements in this list.
177: */
178: private int size;
179:
180: private PaginatingReportProcessor pageProcess;
181:
182: /**
183: * Creates a new reportstatelist. The list will be filled using the specified report and
184: * output target. Filling of the list is done elsewhere.
185: *
186: * @param proc the reportprocessor used to restore lost states (null not permitted).
187: * @throws NullPointerException if the report processor is <code>null</code>.
188: */
189: public PageStateList(final PaginatingReportProcessor proc) {
190: if (proc == null) {
191: throw new NullPointerException("ReportProcessor null");
192: }
193:
194: this .pageProcess = proc;
195:
196: primaryStates = new ArrayList();
197: masterStates4 = new ArrayList();
198: masterStates10 = new ArrayList();
199:
200: }
201:
202: /**
203: * Returns the index of the WeakReferenceList in the master list.
204: *
205: * @param pos the position.
206: * @param maxListSize the maximum list size.
207: * @return the position within the masterStateList.
208: */
209: private int getMasterPos(final int pos, final int maxListSize) {
210: //return (int) Math.floor(pos / maxListSize);
211: return (pos / maxListSize);
212: }
213:
214: protected PaginatingReportProcessor getPageProcess() {
215: return pageProcess;
216: }
217:
218: /**
219: * Returns the number of elements in this list.
220: *
221: * @return the number of elements in the list.
222: */
223: public int size() {
224: return this .size;
225: }
226:
227: /**
228: * Adds this report state to the end of the list.
229: *
230: * @param state the report state.
231: */
232: public void add(final PageState state) {
233: if (state == null) {
234: throw new NullPointerException();
235: }
236:
237: // the first 20 Elements are stored directly into an ArrayList
238: if (size() < PRIMARY_MAX) {
239: primaryStates.add(state);
240: this .size++;
241: }
242: // the next 100 Elements are stored into a list of 4-element weakReference
243: //list. So if an Element gets lost (GCd), only 4 states need to be replayed.
244: else if (size() < MASTER4_MAX) {
245: final int secPos = size() - PRIMARY_MAX;
246: final int masterPos = getMasterPos(secPos,
247: MASTERPOSITIONS_MED);
248: if (masterPos >= masterStates4.size()) {
249: final MasterList master = new MasterList(this ,
250: MASTERPOSITIONS_MED);
251: masterStates4.add(master);
252: master.add(state);
253: } else {
254: final MasterList master = (MasterList) masterStates4
255: .get(masterPos);
256: master.add(state);
257: }
258: this .size++;
259: }
260: // all other Elements are stored into a list of 10-element weakReference
261: //list. So if an Element gets lost (GCd), 10 states need to be replayed.
262: else {
263: final int thirdPos = size() - MASTER4_MAX;
264: final int masterPos = getMasterPos(thirdPos,
265: MASTERPOSITIONS_MAX);
266: if (masterPos >= masterStates10.size()) {
267: final MasterList master = new MasterList(this ,
268: MASTERPOSITIONS_MAX);
269: masterStates10.add(master);
270: master.add(state);
271: } else {
272: final MasterList master = (MasterList) masterStates10
273: .get(masterPos);
274: master.add(state);
275: }
276: this .size++;
277: }
278: }
279:
280: /**
281: * Removes all elements in the list.
282: */
283: public void clear() {
284: masterStates10.clear();
285: masterStates4.clear();
286: primaryStates.clear();
287: this .size = 0;
288: }
289:
290: /**
291: * Retrieves the element on position <code>index</code> in this list.
292: *
293: * @param index the index.
294: * @return the report state.
295: */
296: public PageState get(int index) {
297: if (index >= size() || index < 0) {
298: throw new IndexOutOfBoundsException(
299: "Index is invalid. Index was " + index
300: + "; size was " + size());
301: }
302: if (index < PRIMARY_MAX) {
303: return (PageState) primaryStates.get(index);
304: } else if (index < MASTER4_MAX) {
305: index -= PRIMARY_MAX;
306: final MasterList master = (MasterList) masterStates4
307: .get(getMasterPos(index, MASTERPOSITIONS_MED));
308: return (PageState) master.get(index);
309: } else {
310: index -= MASTER4_MAX;
311: final MasterList master = (MasterList) masterStates10
312: .get(getMasterPos(index, MASTERPOSITIONS_MAX));
313: return (PageState) master.get(index);
314: }
315: }
316: }
|