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