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     */
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    }