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 }