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: PaginatingReportProcessor.java 3525 2007-10-16 11:43:48Z tmorgner $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    package org.jfree.report.flow.paginating;
032    
033    import org.jfree.layouting.ChainingLayoutProcess;
034    import org.jfree.layouting.DefaultLayoutProcess;
035    import org.jfree.layouting.LayoutProcess;
036    import org.jfree.layouting.StateException;
037    import org.jfree.layouting.output.pageable.PageableOutputProcessor;
038    import org.jfree.layouting.util.IntList;
039    import org.jfree.report.DataSourceException;
040    import org.jfree.report.ReportDataFactoryException;
041    import org.jfree.report.ReportProcessingException;
042    import org.jfree.report.flow.AbstractReportProcessor;
043    import org.jfree.report.flow.FlowController;
044    import org.jfree.report.flow.LibLayoutReportTarget;
045    import org.jfree.report.flow.ReportContext;
046    import org.jfree.report.flow.ReportJob;
047    import org.jfree.report.flow.ReportTarget;
048    import org.jfree.report.flow.ReportTargetState;
049    import org.jfree.report.flow.layoutprocessor.LayoutController;
050    import org.jfree.report.flow.layoutprocessor.LayoutControllerFactory;
051    import org.jfree.resourceloader.ResourceKey;
052    import org.jfree.resourceloader.ResourceManager;
053    import org.jfree.util.Log;
054    
055    /**
056     * Paginating report processors are multi-pass processors.
057     * <p/>
058     * This is written to use LibLayout. It will never work with other report
059     * targets.
060     *
061     * Be aware that this class is not synchronized.
062     *
063     * @author Thomas Morgner
064     */
065    public abstract class PaginatingReportProcessor extends AbstractReportProcessor
066    {
067      private PageableOutputProcessor outputProcessor;
068      private PageStateList stateList;
069      private IntList physicalMapping;
070      private IntList logicalMapping;
071    
072      protected PaginatingReportProcessor(final PageableOutputProcessor outputProcessor)
073      {
074        this.outputProcessor = outputProcessor;
075      }
076    
077      public PageableOutputProcessor getOutputProcessor()
078      {
079        return outputProcessor;
080      }
081    
082      protected LibLayoutReportTarget createTarget(final ReportJob job)
083      {
084        if (outputProcessor == null)
085        {
086          throw new IllegalStateException("OutputProcessor is invalid.");
087        }
088        final LayoutProcess layoutProcess =
089            new ChainingLayoutProcess(new DefaultLayoutProcess(outputProcessor));
090        final ResourceManager resourceManager = job.getReportStructureRoot().getResourceManager();
091        final ResourceKey resourceKey = job.getReportStructureRoot().getBaseResource();
092    
093        return new LibLayoutReportTarget
094            (job, resourceKey, resourceManager, layoutProcess);
095      }
096    
097    //  public void processReport(ReportJob job)
098    //      throws ReportDataFactoryException,
099    //      DataSourceException, ReportProcessingException
100    //  {
101    //    prepareReportProcessing(job);
102    //
103    //  }
104    
105      protected void prepareReportProcessing(final ReportJob job)
106          throws ReportDataFactoryException, DataSourceException, ReportProcessingException
107      {
108        if (job == null)
109        {
110          throw new NullPointerException();
111        }
112    
113        final long start = System.currentTimeMillis();
114        // first, compute the globals
115        processReportRun(job, createTarget(job));
116        if (outputProcessor.isGlobalStateComputed() == false)
117        {
118          throw new ReportProcessingException
119              ("Pagination has not yet been finished.");
120        }
121    
122        // second, paginate
123        processPaginationRun(job, createTarget(job));
124        if (outputProcessor.isPaginationFinished() == false)
125        {
126          throw new ReportProcessingException
127              ("Pagination has not yet been finished.");
128        }
129    
130        if (outputProcessor.isContentGeneratable() == false)
131        {
132          throw new ReportProcessingException
133              ("Illegal State.");
134        }
135        final long end = System.currentTimeMillis();
136    //    System.out.println("Pagination-Time: " + (end - start));
137      }
138    
139      protected PageStateList processPaginationRun(final ReportJob job,
140                                                   final LibLayoutReportTarget target)
141          throws ReportDataFactoryException,
142          DataSourceException, ReportProcessingException
143      {
144        if (job == null)
145        {
146          throw new NullPointerException();
147        }
148        stateList = new PageStateList(this);
149        physicalMapping = new IntList(40);
150        logicalMapping = new IntList(20);
151    
152        final ReportContext context = createReportContext(job, target);
153        final LayoutControllerFactory layoutFactory =
154            context.getLayoutControllerFactory();
155    
156        // we have the data and we have our position inside the report.
157        // lets generate something ...
158        final FlowController flowController = createFlowControler(context, job);
159    
160        LayoutController layoutController =
161            layoutFactory.create(flowController, job.getReportStructureRoot(), null);
162    
163        try
164        {
165          stateList.add(new PageState(target.saveState(), layoutController,
166              outputProcessor.getPageCursor()));
167          int logPageCount = outputProcessor.getLogicalPageCount();
168          int physPageCount = outputProcessor.getPhysicalPageCount();
169    
170          while (layoutController.isAdvanceable())
171          {
172            layoutController = layoutController.advance(target);
173            target.commit();
174    
175            while (layoutController.isAdvanceable() == false &&
176                   layoutController.getParent() != null)
177            {
178              final LayoutController parent = layoutController.getParent();
179              layoutController = parent.join(layoutController.getFlowController());
180            }
181    
182            // check whether a pagebreak has been encountered.
183            if (target.isPagebreakEncountered())
184            {
185              // So we hit a pagebreak. Store the state for later reuse.
186              // A single state can refer to more than one physical page.
187    
188              final int newLogPageCount = outputProcessor.getLogicalPageCount();
189              final int newPhysPageCount = outputProcessor.getPhysicalPageCount();
190    
191              final int result = stateList.size() - 1;
192              for (; physPageCount < newPhysPageCount; physPageCount++)
193              {
194                physicalMapping.add(result);
195              }
196    
197              for (; logPageCount < newLogPageCount; logPageCount++)
198              {
199                logicalMapping.add(result);
200              }
201    
202              logPageCount = newLogPageCount;
203              physPageCount = newPhysPageCount;
204    
205              final ReportTargetState targetState = target.saveState();
206              final PageState state =
207                  new PageState (targetState, layoutController,
208                  outputProcessor.getPageCursor());
209              stateList.add(state);
210    
211              // This is an assertation that we do not run into invalid states
212              // later.
213              if (PaginatingReportProcessor.ASSERTATION)
214              {
215                final ReportTarget reportTarget =
216                  targetState.restore(outputProcessor);
217              }
218    
219              target.resetPagebreakFlag();
220            }
221          }
222    
223          // And when we reached the end, add the remaining pages ..
224          final int newLogPageCount = outputProcessor.getLogicalPageCount();
225          final int newPhysPageCount = outputProcessor.getPhysicalPageCount();
226    
227          final int result = stateList.size() - 1;
228          for (; physPageCount < newPhysPageCount; physPageCount++)
229          {
230            physicalMapping.add(result);
231          }
232    
233          for (; logPageCount < newLogPageCount; logPageCount++)
234          {
235            logicalMapping.add(result);
236          }
237        }
238        catch (final StateException e)
239        {
240          throw new ReportProcessingException("Argh, Unable to save the state!");
241        }
242    
243        Log.debug("After pagination we have " + stateList.size() + " states");
244        return stateList;
245      }
246    
247      public boolean isPaginated()
248      {
249        return outputProcessor.isPaginationFinished();
250      }
251    
252      protected PageState getLogicalPageState (final int page)
253      {
254        return stateList.get(logicalMapping.get(page));
255      }
256    
257      protected PageState getPhysicalPageState (final int page)
258      {
259        return stateList.get(physicalMapping.get(page));
260      }
261    
262      public PageState processPage(final PageState previousState)
263          throws StateException, ReportProcessingException,
264          ReportDataFactoryException, DataSourceException
265      {
266        final ReportTargetState targetState = previousState.getTargetState();
267        final LibLayoutReportTarget target =
268            (LibLayoutReportTarget) targetState.restore(outputProcessor);
269        outputProcessor.setPageCursor(previousState.getPageCursor());
270    
271        LayoutController position = previousState.getLayoutController();
272    
273        // we have the data and we have our position inside the report.
274        // lets generate something ...
275    
276        while (position.isAdvanceable())
277        {
278          position = position.advance(target);
279          target.commit();
280    
281          // else check whether this state is finished, and try to join the subflow
282          // with the parent.
283          while (position.isAdvanceable() == false &&
284                 position.getParent() != null)
285          {
286            final LayoutController parent = position.getParent();
287            position = parent.join(position.getFlowController());
288          }
289    
290          // check whether a pagebreak has been encountered.
291          if (target.isPagebreakEncountered())
292          {
293            // So we hit a pagebreak. Store the state for later reuse.
294            // A single state can refer to more than one physical page.
295            final PageState state = new PageState
296                (target.saveState(), position, outputProcessor.getPageCursor());
297            target.resetPagebreakFlag();
298            return state;
299          }
300    
301        }
302    
303        // reached the finish state .. this is bad!
304        return null;
305      }
306    
307      private static final boolean ASSERTATION = true;
308    }