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,v 1.15 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 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 }