1 /*
2  * JasperReports - Free Java Reporting Library.
3  * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
4  * http://www.jaspersoft.com
5  *
6  * Unless you have purchased a commercial license agreement from Jaspersoft,
7  * the following license terms apply:
8  *
9  * This program is part of JasperReports.
10  *
11  * JasperReports is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * JasperReports is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
23  */

24 package net.sf.jasperreports.engine.fill;
25
26 import org.apache.commons.javaflow.api.continuable;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import net.sf.jasperreports.engine.JRException;
31 import net.sf.jasperreports.engine.JRExpression;
32 import net.sf.jasperreports.engine.JRGroup;
33 import net.sf.jasperreports.engine.JRRuntimeException;
34 import net.sf.jasperreports.engine.JasperReport;
35 import net.sf.jasperreports.engine.JasperReportsContext;
36 import net.sf.jasperreports.engine.type.FooterPositionEnum;
37 import net.sf.jasperreports.engine.type.IncrementTypeEnum;
38 import net.sf.jasperreports.engine.type.ResetTypeEnum;
39 import net.sf.jasperreports.engine.type.RunDirectionEnum;
40
41
42 /**
43  * @author Teodor Danciu (teodord@users.sourceforge.net)
44  */

45 public class JRHorizontalFiller extends JRBaseFiller
46 {
47
48     private static final Log log = LogFactory.getLog(JRHorizontalFiller.class);
49
50     private int lastDetailOffsetX = -1;
51     private int lastDetailOffsetY = -1;
52     private int currentDetailOffsetY;
53     private int maxDetailOffsetY;
54     
55
56     /**
57      *
58      */

59     protected JRHorizontalFiller(
60         JasperReportsContext jasperReportsContext, 
61         JasperReport jasperReport
62         ) throws JRException
63     {
64         this(jasperReportsContext, jasperReport, null);
65     }
66
67     /**
68      *
69      */

70     public JRHorizontalFiller(
71         JasperReportsContext jasperReportsContext, 
72         JasperReport jasperReport, 
73         BandReportFillerParent parent 
74         ) throws JRException
75     {
76         this(jasperReportsContext, SimpleJasperReportSource.from(jasperReport), parent);
77     }
78
79     public JRHorizontalFiller(
80         JasperReportsContext jasperReportsContext, 
81         JasperReportSource reportSource,
82         BandReportFillerParent parent 
83         ) throws JRException
84     {
85         super(jasperReportsContext, reportSource, parent);
86
87         setPageHeight(pageHeight);
88     }
89
90
91     @Override
92     protected void setPageHeight(int pageHeight)
93     {
94         this.pageHeight = pageHeight;
95
96         columnFooterOffsetY = pageHeight - bottomMargin - pageFooter.getHeight() - columnFooter.getHeight();
97         lastPageColumnFooterOffsetY = pageHeight - bottomMargin - lastPageFooter.getHeight() - columnFooter.getHeight();
98         
99         if (log.isDebugEnabled())
100         {
101             log.debug("Filler " + fillerId + " - pageHeight: " + pageHeight
102                     + ", columnFooterOffsetY: " + columnFooterOffsetY
103                     + ", lastPageColumnFooterOffsetY: " + lastPageColumnFooterOffsetY);
104         }
105     }
106
107
108     @Override
109     @continuable
110     protected synchronized void fillReport() throws JRException
111     {
112         setLastPageFooter(false);
113
114         if (next())
115         {
116             fillReportStart();
117
118             while (next())
119             {
120                 fillReportContent();
121             }
122
123             fillReportEnd();
124         }
125         else
126         {
127             if (log.isDebugEnabled())
128             {
129                 log.debug("Fill " + fillerId + ": no data");
130             }
131
132             switch (getWhenNoDataType())
133             {
134                 case ALL_SECTIONS_NO_DETAIL :
135                 {
136                     if (log.isDebugEnabled())
137                     {
138                         log.debug("Fill " + fillerId + ": all sections");
139                     }
140
141                     scriptlet.callBeforeReportInit();
142                     calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
143                     scriptlet.callAfterReportInit();
144
145                     printPage = newPage();
146                     printPageContentsWidth = 0;
147                     addPage(printPage);
148                     setFirstColumn();
149                     offsetY = topMargin;
150                     isFirstPageBand = true;
151                     isFirstColumnBand = true;
152
153                     fillBackground();
154
155                     fillTitle();
156
157                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
158
159                     fillColumnHeaders(JRExpression.EVALUATION_DEFAULT);
160
161                     fillGroupHeaders(true);
162
163                     fillGroupFooters(true);
164
165                     fillSummary();
166
167                     break;
168                 }
169                 case BLANK_PAGE :
170                 {
171                     if (log.isDebugEnabled())
172                     {
173                         log.debug("Fill " + fillerId + ": blank page");
174                     }
175
176                     printPage = newPage();
177                     addPage(printPage);
178                     break;
179                 }
180                 case NO_DATA_SECTION:
181                 {
182                     if (log.isDebugEnabled())
183                     {
184                         log.debug("Fill " + fillerId + ": NoData section");
185                     }
186
187                     scriptlet.callBeforeReportInit();
188                     calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
189                     scriptlet.callAfterReportInit();
190
191                     printPage = newPage();
192                     addPage(printPage);
193                     setFirstColumn();
194                     offsetY = topMargin;
195                     isFirstPageBand = true;
196                     isFirstColumnBand = true;
197
198                     fillBackground();
199
200                     fillNoData();
201
202                     break;
203
204                 }
205                 case NO_PAGES :
206                 default :
207                 {
208                     if (log.isDebugEnabled())
209                     {
210                         log.debug("Fill " + fillerId + ": no pages");
211                     }
212                 }
213             }
214         }
215
216         recordUsedPageHeight(offsetY + bottomMargin);
217         if (ignorePagination)
218         {
219             jasperPrint.setPageHeight(usedPageHeight);
220         }
221
222         if (isSubreport())
223         {
224             addPageToParent(true);
225         }
226         else
227         {
228             addLastPageBookmarks();
229         }
230         
231         if (bookmarkHelper != null)
232         {
233             jasperPrint.setBookmarks(bookmarkHelper.getRootBookmarks());
234         }
235     }
236
237
238     /**
239      *
240      */

241     @continuable
242     private void fillReportStart() throws JRException
243     {
244         scriptlet.callBeforeReportInit();
245         calculator.initializeVariables(ResetTypeEnum.REPORT, IncrementTypeEnum.REPORT);
246         scriptlet.callAfterReportInit();
247
248         printPage = newPage();
249         printPageContentsWidth = 0;
250         addPage(printPage);
251         setFirstColumn();
252         offsetY = topMargin;
253         isFirstPageBand = true;
254         isFirstColumnBand = true;
255
256         fillBackground();
257
258         fillTitle();
259
260         fillPageHeader(JRExpression.EVALUATION_DEFAULT);
261
262         fillColumnHeaders(JRExpression.EVALUATION_DEFAULT);
263
264         fillGroupHeaders(true);
265
266         fillDetail();
267     }
268
269
270     private void setFirstColumn()
271     {
272         columnIndex = 0;
273         offsetX = leftMargin;
274         setColumnNumberVariable();
275     }
276
277     /**
278      *
279      */

280     @continuable
281     private void fillReportContent() throws JRException
282     {
283         calculator.estimateGroupRuptures();
284
285         fillGroupFooters(false);
286
287         resolveGroupBoundElements(JRExpression.EVALUATION_OLD, false);
288         scriptlet.callBeforeGroupInit();
289         calculator.initializeVariables(ResetTypeEnum.GROUP, IncrementTypeEnum.GROUP);
290         scriptlet.callAfterGroupInit();
291
292         fillGroupHeaders(false);
293
294         fillDetail();
295     }
296
297
298     /**
299      *
300      */

301     @continuable
302     private void fillReportEnd() throws JRException
303     {
304         fillGroupFooters(true);
305
306         fillSummary();
307     }
308
309
310     /**
311      *
312      */

313     @continuable
314      private void fillTitle() throws JRException
315      {
316         if (log.isDebugEnabled() && !title.isEmpty())
317         {
318             log.debug("Fill " + fillerId + ": title at " + offsetY);
319         }
320
321         title.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
322
323         if (title.isToPrint())
324         {
325             while (
326                 title.getBreakHeight() > pageHeight - bottomMargin - offsetY
327                 )
328             {
329                 addPage(false);
330             }
331
332             title.evaluate(JRExpression.EVALUATION_DEFAULT);
333
334             JRPrintBand printBand = title.fill(pageHeight - bottomMargin - offsetY);
335
336             if (title.willOverflow() && title.isSplitPrevented() && !title.isSplitTypePreventInhibited())
337             {
338                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
339                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
340                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
341                 scriptlet.callBeforePageInit();
342                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
343                 scriptlet.callAfterPageInit();
344
345                 addPage(false);
346
347                 printBand = 
348                     title.refill(
349                         JRExpression.EVALUATION_DEFAULT,
350                         pageHeight - bottomMargin - offsetY
351                         );
352             }
353
354             fillBand(printBand);
355             offsetY += printBand.getHeight();
356             isCrtRecordOnPage = true;
357             isCrtRecordOnColumn = true;
358
359             while (title.willOverflow())
360             {
361                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
362                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
363                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
364                 scriptlet.callBeforePageInit();
365                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
366                 scriptlet.callAfterPageInit();
367
368                 addPage(false);
369
370                 printBand = title.fill(pageHeight - bottomMargin - offsetY);
371
372                 fillBand(printBand);
373                 offsetY += printBand.getHeight();
374                 isCrtRecordOnPage = true;
375                 isCrtRecordOnColumn = true;
376             }
377
378             resolveBandBoundElements(title, JRExpression.EVALUATION_DEFAULT);
379
380             if (isTitleNewPage)
381             {
382                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
383                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
384                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
385                 scriptlet.callBeforePageInit();
386                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
387                 scriptlet.callAfterPageInit();
388
389                 addPage(false);
390             }
391         }
392     }
393
394
395     /**
396      *
397      */

398     @continuable
399     private void fillPageHeader(byte evaluation) throws JRException
400     {
401         if (log.isDebugEnabled() && !pageHeader.isEmpty())
402         {
403             log.debug("Fill " + fillerId + ": page header at " + offsetY);
404         }
405
406         setNewPageColumnInBands();
407
408         pageHeader.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
409
410         if (pageHeader.isToPrint())
411         {
412             int reattempts = getMasterColumnCount();
413             if (isCreatingNewPage)
414             {
415                 --reattempts;
416             }
417
418             boolean filled = fillBandNoOverflow(pageHeader, evaluation);
419
420             for (int i = 0; !filled && i < reattempts; ++i)
421             {
422                 resolveGroupBoundElements(evaluation, false);
423                 resolveColumnBoundElements(evaluation);
424                 resolvePageBoundElements(evaluation);
425                 scriptlet.callBeforePageInit();
426                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
427                 scriptlet.callAfterPageInit();
428
429                 addPage(false);
430
431                 filled = fillBandNoOverflow(pageHeader, evaluation);
432             }
433
434             if (!filled)
435             {
436                 throw 
437                     new JRRuntimeException(
438                         EXCEPTION_MESSAGE_KEY_PAGE_HEADER_OVERFLOW_INFINITE_LOOP,
439                         (Object[])null);
440             }
441         }
442
443         columnHeaderOffsetY = offsetY;
444
445         isNewPage = true;
446     }
447
448
449     private boolean fillBandNoOverflow(JRFillBand band, byte evaluation) throws JRException
450     {
451         int availableHeight = columnFooterOffsetY - offsetY;
452         boolean overflow = availableHeight < band.getHeight();
453
454         if (!overflow)
455         {
456             band.evaluate(evaluation);
457             JRPrintBand printBand = band.fill(availableHeight);
458
459             overflow = band.willOverflow();
460             if (overflow)
461             {
462                 band.rewind();
463             }
464             else
465             {
466                 fillBand(printBand);
467                 offsetY += printBand.getHeight();
468                 isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
469                 isCrtRecordOnColumn = isCrtRecordOnPage;
470
471                 resolveBandBoundElements(band, evaluation);
472             }
473         }
474
475         return !overflow;
476     }
477
478
479     /**
480      *
481      */

482     @continuable
483     private void fillColumnHeaders(byte evaluation) throws JRException
484     {
485         if (log.isDebugEnabled() && !columnHeader.isEmpty())
486         {
487             log.debug("Fill " + fillerId + ": column headers at " + offsetY);
488         }
489
490         setNewPageColumnInBands();
491         isFirstColumnBand = true;
492
493         for(columnIndex = 0; columnIndex < columnCount; columnIndex++)
494         {
495             setColumnNumberVariable();
496
497             columnHeader.evaluatePrintWhenExpression(evaluation);
498
499             if (columnHeader.isToPrint())
500             {
501                 int reattempts = getMasterColumnCount();
502                 if (isCreatingNewPage)
503                 {
504                     --reattempts;
505                 }
506
507                 boolean fits = columnHeader.getHeight() <= columnFooterOffsetY - columnHeaderOffsetY;
508                 for (int i = 0; !fits && i < reattempts; ++i)
509                 {
510                     fillPageFooter(evaluation);
511
512                     resolveGroupBoundElements(evaluation, false);
513                     resolveColumnBoundElements(evaluation);
514                     resolvePageBoundElements(evaluation);
515                     scriptlet.callBeforePageInit();
516                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
517                     scriptlet.callAfterPageInit();
518
519                     addPage(false);
520
521                     fillPageHeader(evaluation);
522
523                     fits = columnHeader.getHeight() <= columnFooterOffsetY - columnHeaderOffsetY;
524                 }
525
526                 if (!fits)
527                 {
528                     throw 
529                         new JRRuntimeException(
530                             EXCEPTION_MESSAGE_KEY_COLUMN_HEADER_OVERFLOW_INFINITE_LOOP,
531                             (Object[])null);
532                 }
533
534                 setOffsetX();
535                 offsetY = columnHeaderOffsetY;
536
537                 fillFixedBand(columnHeader, evaluation, false);
538             }
539         }
540
541         setFirstColumn();
542
543         isNewColumn = true;
544     }
545
546
547     /**
548      *
549      */

550     @continuable
551     private void fillGroupHeaders(boolean isFillAll) throws JRException
552     {
553         if (groups != null && groups.length > 0)
554         {
555             for (int i = 0; i < groups.length; i++)
556             {
557                 JRFillGroup group = groups[i];
558
559                 if (isFillAll || group.hasChanged())
560                 {
561                     fillGroupHeader(group);
562                 }
563             }
564         }
565     }
566
567
568     /**
569      *
570      */

571     @continuable
572     private void fillGroupHeader(JRFillGroup group) throws JRException
573     {
574         JRFillSection groupHeaderSection = (JRFillSection)group.getGroupHeaderSection();
575
576         if (log.isDebugEnabled() && !groupHeaderSection.isEmpty())
577         {
578             log.debug("Fill " + fillerId + ": " + group.getName() + " header at " + offsetY);
579         }
580
581         //byte evalPrevPage = (group.isTopLevelChange()?JRExpression.EVALUATION_OLD:JRExpression.EVALUATION_DEFAULT);
582
583         if (
584             (group.isStartNewPage() || group.isResetPageNumber()) && !isNewPage
585             || ( group.isStartNewColumn() && !isNewColumn )
586             )
587         {
588             fillPageBreak(
589                 group.isResetPageNumber(),
590                 isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD, //evalPrevPage,
591                 JRExpression.EVALUATION_DEFAULT,
592                 true
593                 );
594         }
595
596         boolean isFirstHeaderBandToPrint = true;
597         boolean isGroupReset = false;
598         
599         JRFillBand[] groupHeaderBands = groupHeaderSection.getFillBands();
600         for (int i = 0; i < groupHeaderBands.length; i++)
601         {
602             JRFillBand groupHeaderBand = groupHeaderBands[i];
603
604             groupHeaderBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
605
606             if (groupHeaderBand.isToPrint())
607             {
608                 while (
609                     groupHeaderBand.getBreakHeight() > columnFooterOffsetY - offsetY ||
610                     (isFirstHeaderBandToPrint && group.getMinHeightToStartNewPage() > columnFooterOffsetY - offsetY)
611                     )
612                 {
613                     fillPageBreak(
614                         false,
615                         isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD, //evalPrevPage,
616                         JRExpression.EVALUATION_DEFAULT,
617                         true
618                         );
619                 }
620             }
621
622             if (!isGroupReset && (isFirstHeaderBandToPrint || i == groupHeaderBands.length - 1))
623             {
624                 // perform this group reset before the first header band prints, 
625                 // or before the last header band, regardless if it prints or not 
626                 setNewGroupInBands(group);
627
628                 group.setFooterPrinted(false);
629                 group.resetDetailsCount();
630                 
631                 isGroupReset = true;
632             }
633
634             ElementRange elementRange = null;
635             
636             if (
637                 (group.isKeepTogether() && !isNewColumn)
638                 || group.getMinDetailsToStartFromTop() > 0
639                 )
640             {
641                 elementRange = group.getKeepTogetherElementRange();
642                 
643                 if (elementRange == null)
644                 {
645                     // we need to set a keep together element range for the group
646                     // even if its header does not print,
647                     // but only if the column is not already new
648                     elementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
649                     
650                     group.setKeepTogetherElementRange(elementRange);
651                     // setting a non-null element range here would cause the group header band to be
652                     // refilled below and thus kept together, in case a split occurs in it;
653                     // the non-null element range will be also moved onto the new page/column in the process,
654                     // but it will contain no elements as the already mentioned non-splitting behavior of the group header band
655                     // would not add any element to it;
656                     // so the keep together element range set here is more like flag to signal the group header itself
657                     // should be prevented from splitting in the fillColumnBand call below
658                 }
659             }
660
661             if (groupHeaderBand.isToPrint())
662             {
663                 setFirstColumn();
664
665                 fillColumnBand(groupHeaderBand, JRExpression.EVALUATION_DEFAULT);
666                 
667                 ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
668                 
669                 // in case a column/page break occurred during the filling of the band above,
670                 // the provided element range is discarded/ignored,
671                 // but that should not be a problem because the discarded element range was already dealt with during the break
672                 // because it was a keep together element range
673                 ElementRangeUtil.expandOrIgnore(elementRange, newElementRange);
674
675                 isFirstPageBand = false;
676                 isFirstColumnBand = true;
677                 
678                 isFirstHeaderBandToPrint = false;
679             }
680         }
681
682         group.setHeaderPrinted(true);
683     }
684
685
686     /**
687      *
688      */

689     @continuable
690     private void fillGroupHeadersReprint(byte evaluation) throws JRException
691     {
692         if (groups != null && groups.length > 0)
693         {
694             for (int i = 0; i < groups.length; i++)
695             {
696                 JRFillGroup group = groups[i];
697                 
698                 if (
699                     group.getKeepTogetherElementRange() != null
700                     && (group.isKeepTogether() || !group.hasMinDetails())
701                     )
702                 {
703                     // we reprint headers only for groups that are "outer" to the one which 
704                     // triggered a potential "keep together" move 
705                     break;
706                 }
707
708                 if (
709                     group.isReprintHeaderOnEachPage()
710                     && (!group.hasChanged() || (group.hasChanged() && group.isHeaderPrinted()))
711                     )
712                 {
713                     fillGroupHeaderReprint(groups[i], evaluation);
714                 }
715             }
716         }
717     }
718
719
720     /**
721      *
722      */

723     @continuable
724      private void fillGroupHeaderReprint(JRFillGroup group, byte evaluation) throws JRException
725      {
726         JRFillSection groupHeaderSection = (JRFillSection)group.getGroupHeaderSection();
727
728         JRFillBand[] groupHeaderBands = groupHeaderSection.getFillBands();
729         for (int i = 0; i < groupHeaderBands.length; i++)
730         {
731             JRFillBand groupHeaderBand = groupHeaderBands[i];
732
733             groupHeaderBand.evaluatePrintWhenExpression(evaluation);
734
735             if (groupHeaderBand.isToPrint())
736             {
737                 setFirstColumn();
738
739                 while (groupHeaderBand.getBreakHeight() > columnFooterOffsetY - offsetY)
740                 {
741                     fillPageBreak(false, evaluation, evaluation, true); // using same evaluation for both side of the break is ok here
742                 }
743
744                 fillColumnBand(groupHeaderBand, evaluation);
745
746                 //isFirstPageBand = false;
747                 isFirstColumnBand = true;
748             }
749         }
750     }
751
752
753     /**
754      *
755      */

756     @continuable
757     private void fillDetail() throws JRException
758     {
759         if (log.isDebugEnabled() && !detailSection.isEmpty())
760         {
761             log.debug("Fill " + fillerId + ": detail at " + offsetY);
762         }
763
764         if (
765             offsetX == lastDetailOffsetX
766             && offsetY == lastDetailOffsetY
767             )
768         {
769             if (columnIndex == columnCount - 1)
770             {
771                 columnIndex = 0;
772
773                 maxDetailOffsetY = 0;
774             }
775             else
776             {
777                 columnIndex++;
778
779                 offsetY = currentDetailOffsetY;
780             }
781
782         }
783
784         if (!detailSection.areAllPrintWhenExpressionsNull())
785         {
786             calculator.estimateVariables();
787         }
788
789         JRFillBand[] detailBands = detailSection.getFillBands();
790         for (int i = 0; i < detailBands.length; i++)
791         {
792             JRFillBand detailBand = detailBands[i];
793
794             detailBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_ESTIMATED);
795
796             if (detailBand.isToPrint())
797             {
798                 while (detailBand.getHeight() > columnFooterOffsetY - offsetY)
799                 {
800                     fillPageBreak(
801                         false,
802                         isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD,
803                         JRExpression.EVALUATION_DEFAULT,
804                         true
805                         );
806                 }
807                 
808                 break;
809             }
810         }
811
812         scriptlet.callBeforeDetailEval();
813         calculator.calculateVariables(true);
814         scriptlet.callAfterDetailEval();
815                 
816         setColumnNumberVariable();
817
818         setOffsetX();
819
820         currentDetailOffsetY = offsetY;
821
822         detailElementRange = null;
823
824         boolean keepDetailElementRangeForOrphanFooter = true;
825         boolean atLeastOneDetailBandPrinted = false;
826         
827         for (int i = 0; i < detailBands.length; i++)
828         {
829             JRFillBand detailBand = detailBands[i];
830
831             detailBand.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
832
833             if (detailBand.isToPrint())
834             {
835                 if (
836                     keepDetailElementRangeForOrphanFooter
837                     && detailElementRange == null
838                     )
839                 {
840                     detailElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
841                 }
842                 
843                 while (detailBand.getHeight() > columnFooterOffsetY - offsetY)
844                 {
845                     fillPageBreak(
846                         false,
847                         isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD,
848                         JRExpression.EVALUATION_DEFAULT,
849                         true
850                         );
851
852                     currentDetailOffsetY = offsetY;
853                 }
854                 
855                 fillFixedBand(detailBand, JRExpression.EVALUATION_DEFAULT, false);
856
857                 if (detailElementRange == null)
858                 {
859                     // page break occurred so we give up keeping the detail element range altogether
860                     keepDetailElementRangeForOrphanFooter = false;
861                 }
862                 else
863                 {
864                     // there was no page break, otherwise this range would have been reset to null during page break
865                     
866                     ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
867
868                     ElementRangeUtil.expandOrIgnore(detailElementRange, newElementRange);
869                 }
870
871                 atLeastOneDetailBandPrinted = true;
872                 
873                 isFirstPageBand = false;
874                 isFirstColumnBand = false;
875             }
876         }
877
878         if (atLeastOneDetailBandPrinted)
879         {
880             if (groups != null)
881             {
882                 for (JRFillGroup group : groups)
883                 {
884                     group.incrementDetailsCount();
885                 }
886             }
887         }
888      
889         maxDetailOffsetY = maxDetailOffsetY < offsetY ? offsetY : maxDetailOffsetY;
890         offsetY = maxDetailOffsetY;
891
892         lastDetailOffsetX = offsetX;
893         lastDetailOffsetY = offsetY;
894         
895         isNewPage = false;
896         isNewColumn = false;
897     }
898
899
900     /**
901      *
902      */

903     @continuable
904     private void fillGroupFooters(boolean isFillAll) throws JRException
905     {
906         if (groups != null && groups.length > 0)
907         {
908             byte evaluation = (isFillAll)?JRExpression.EVALUATION_DEFAULT:JRExpression.EVALUATION_OLD;
909
910             preventOrphanFootersMinLevel = null;
911             for (int i = groups.length - 1; i >= 0; i--)
912             {
913                 JRFillGroup group = groups[i];
914                 
915                 if (
916                     (isFillAll || group.hasChanged())
917                     && group.isPreventOrphanFooter()
918                     )
919                 {
920                     // we need to decide up-front if during the current group footers printing,
921                     // there are any potential orphans to take care of
922                     preventOrphanFootersMinLevel = i;
923                     break;
924                 }
925             }
926             
927             for (int i = groups.length - 1; i >= 0; i--)
928             {
929                 JRFillGroup group = groups[i];
930                 
931                 crtGroupFootersLevel = i;
932                 if (
933                     preventOrphanFootersMinLevel != null
934                     && crtGroupFootersLevel < preventOrphanFootersMinLevel
935                     )
936                 {
937                     // reset the element ranges when we get to the group footers
938                     // that are outer to the ones for which we need to prevent orphans;
939                     // these ranges act like flags to signal we need to deal with orphans
940                     orphanGroupFooterDetailElementRange = null;
941                     orphanGroupFooterElementRange = null;
942                 }
943                 
944                 if (isFillAll || group.hasChanged())
945                 {
946                     fillGroupFooter(group, evaluation);
947                     
948                     // regardless of whether the fillGroupFooter was printed or not, 
949                     // we just need to mark the end of the group 
950                     group.setKeepTogetherElementRange(null);
951                 }
952             }
953             
954             // resetting orphan footer element ranges because all group footers have been rendered
955             orphanGroupFooterDetailElementRange = null;
956             orphanGroupFooterElementRange = null;
957             
958             // we need to take care of groupFooterPositionElementRange here because all groups footers have been 
959             // rendered and we need to consume remaining space before next groups start;
960             //
961             // but we don't process the last groupFooterPositionElementRange when the report ends (isFillAll true),
962             // because it will be dealt with during summary rendering, depending on whether a last page footer exists or not
963             if (
964                 !isFillAll
965                 && groupFooterPositionElementRange != null
966                 )
967             {
968                 ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
969                 groupFooterPositionElementRange = null;
970                 // update the offsetY to signal there is no more space left at the bottom after forcing the footer
971                 offsetY = columnFooterOffsetY;
972             }
973         }
974     }
975
976
977     /**
978      *
979      */

980     @continuable
981     private void fillGroupFooter(JRFillGroup group, byte evaluation) throws JRException
982     {
983         JRFillSection groupFooterSection = (JRFillSection)group.getGroupFooterSection();
984
985         if (log.isDebugEnabled() && !groupFooterSection.isEmpty())
986         {
987             log.debug("Fill " + fillerId + ": " + group.getName() + " footer at " + offsetY);
988         }
989         
990         JRFillBand[] groupFooterBands = groupFooterSection.getFillBands();
991         for (int i = 0; i < groupFooterBands.length; i++)
992         {
993             JRFillBand groupFooterBand = groupFooterBands[i];
994             
995             groupFooterBand.evaluatePrintWhenExpression(evaluation);
996
997             if (groupFooterBand.isToPrint())
998             {
999                 setFirstColumn();
1000
1001                 if (
1002                     preventOrphanFootersMinLevel != null
1003                     && crtGroupFootersLevel >= preventOrphanFootersMinLevel 
1004                     && orphanGroupFooterDetailElementRange == null
1005                     )
1006                 {
1007                     // the detail element range can't be null here, unless there is no detail printing;
1008                     // keeping the detail element range in this separate variable signals we are currently
1009                     // dealing with orphan group footers
1010                     orphanGroupFooterDetailElementRange = detailElementRange;
1011                 }
1012                 
1013                 if (
1014                     groupFooterBand.getBreakHeight() > columnFooterOffsetY - offsetY
1015                     )
1016                 {
1017                     fillPageBreak(false, evaluation, evaluation, true); // using same evaluation for both side of the break is ok here
1018                 }
1019
1020                 if (
1021                     groupFooterPositionElementRange == null 
1022                     && group.getFooterPositionValue() != FooterPositionEnum.NORMAL
1023                     )
1024                 {
1025                     groupFooterPositionElementRange = 
1026                         new SimpleGroupFooterElementRange(
1027                             new SimpleElementRange(getCurrentPage(), columnIndex, offsetY), 
1028                             group.getFooterPositionValue()
1029                             );
1030                 }
1031
1032                 if (groupFooterPositionElementRange != null)
1033                 {
1034                     // keep the current group footer position because it will be needed
1035                     // in case the band breaks and the group footer element range needs to
1036                     // be recreated on the new page
1037                     groupFooterPositionElementRange.setCurrentFooterPosition(group.getFooterPositionValue());
1038                 }
1039                 
1040                 if (orphanGroupFooterDetailElementRange != null)
1041                 {
1042                     ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
1043                     if (orphanGroupFooterElementRange == null)
1044                     {
1045                         orphanGroupFooterElementRange = newElementRange;
1046                     }
1047                     else
1048                     {
1049                         ElementRangeUtil.expandOrIgnore(orphanGroupFooterElementRange, newElementRange);
1050                     }
1051                 }
1052                 
1053                 fillColumnBand(groupFooterBand, evaluation);
1054                 
1055                 ElementRange newElementRange = new SimpleElementRange(getCurrentPage(), columnIndex, offsetY);
1056                     
1057                 if (groupFooterPositionElementRange != null)
1058                 {
1059                     ElementRangeUtil.expandOrIgnore(groupFooterPositionElementRange.getElementRange(), newElementRange);
1060
1061                     switch (group.getFooterPositionValue())
1062                     {
1063                         case STACK_AT_BOTTOM :
1064                         {
1065                             groupFooterPositionElementRange.setMasterFooterPosition(FooterPositionEnum.STACK_AT_BOTTOM);
1066                             break;
1067                         }
1068                         case FORCE_AT_BOTTOM :
1069                         {
1070                             groupFooterPositionElementRange.setMasterFooterPosition(FooterPositionEnum.FORCE_AT_BOTTOM);
1071                             break;
1072                         }
1073                         case COLLATE_AT_BOTTOM :
1074                         {
1075                             break;
1076                         }
1077                         case NORMAL :
1078                         default :
1079                         {
1080                             // only StackAtBottom and CollateAtBottom can get here
1081                             if (groupFooterPositionElementRange.getMasterFooterPosition() == FooterPositionEnum.COLLATE_AT_BOTTOM)
1082                             {
1083                                 groupFooterPositionElementRange = null;
1084                             }
1085                             break;
1086                         }
1087                     }
1088                 }
1089
1090                 isFirstPageBand = false;
1091                 isFirstColumnBand = true;
1092             }
1093         }
1094
1095         // we need to perform ForceAtBottom here because only the group footer as a whole should be forced to bottom, 
1096         // not the individual bands in this footer section;
1097         // also, when forcing a group footer to bottom, we consider the normal/current columnFooterOffsetY, because it is impossible
1098         // to tell at this point if this would be the last page or not (last page footer)
1099         if (
1100             groupFooterPositionElementRange != null
1101             && groupFooterPositionElementRange.getMasterFooterPosition() == FooterPositionEnum.FORCE_AT_BOTTOM
1102             )
1103         {
1104             ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1105             groupFooterPositionElementRange = null;
1106             // update the offsetY to signal there is no more space left at the bottom after forcing the footer
1107             offsetY = columnFooterOffsetY;
1108         }
1109
1110         isNewPage = false;
1111         isNewColumn = false;
1112
1113         group.setHeaderPrinted(false);
1114         group.setFooterPrinted(true);
1115     }
1116
1117
1118     /**
1119      *
1120      */

1121      private void fillColumnFooters(byte evaluation) throws JRException
1122      {
1123         if (log.isDebugEnabled() && !columnFooter.isEmpty())
1124         {
1125             log.debug("Fill " + fillerId + ": column footers at " + offsetY);
1126         }
1127
1128         /*
1129         if (!isSubreport)
1130         {
1131             offsetY = columnFooterOffsetY;
1132         }
1133         */

1134
1135         if (isSubreport() && !isSubreportRunToBottom())
1136         {
1137             columnFooterOffsetY = offsetY;
1138         }
1139
1140         int tmpColumnFooterOffsetY = columnFooterOffsetY;
1141
1142         if (isFloatColumnFooter || ignorePagination)
1143         {
1144             tmpColumnFooterOffsetY = offsetY;
1145         }
1146
1147         // we first let the column footer Y offset calculations to occur normally above, 
1148         // before attempting to deal with existing groupFooterPositionElementRange
1149         if (groupFooterPositionElementRange != null)
1150         {
1151             // all types of footer position can get here (StackAtBottom, CollapseAtBottom and ForceAtBottom);
1152             // ForceAtBottom group footer element ranges could reach this point in case multi-band group footer gets
1153             // split across a column/page break; remaining bands in such group footer would be dealt at the end 
1154             // of the group footer filling method (see fillGroupFooter() method above)
1155             ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1156             groupFooterPositionElementRange = null;
1157             // we do not need to set the offsetY because it has already been set properly earlier in this method;
1158         }
1159         
1160         if (isFloatColumnFooter && !ignorePagination)
1161         {
1162             floatColumnFooterElementRange = new SimpleElementRange(getCurrentPage(), 0, tmpColumnFooterOffsetY);
1163         }
1164         
1165         for (columnIndex = 0; columnIndex < columnCount; columnIndex++)
1166         {
1167             setColumnNumberVariable();
1168
1169             setOffsetX();
1170             offsetY = tmpColumnFooterOffsetY;
1171
1172             columnFooter.evaluatePrintWhenExpression(evaluation);
1173
1174             if (columnFooter.isToPrint())
1175             {
1176                 fillFixedBand(columnFooter, evaluation, false);
1177             }
1178         }
1179         
1180         if (floatColumnFooterElementRange != null)
1181         {
1182             floatColumnFooterElementRange.expand(offsetY);
1183         }
1184     }
1185
1186
1187     /**
1188      *
1189      */

1190     private void fillPageFooter(byte evaluation) throws JRException
1191     {
1192         JRFillBand crtPageFooter = getCurrentPageFooter();
1193
1194         if (log.isDebugEnabled() && !crtPageFooter.isEmpty())
1195         {
1196             log.debug("Fill " + fillerId + ": " + (isLastPageFooter ? "last " : "") + "page footer at " + offsetY);
1197         }
1198
1199         offsetX = leftMargin;
1200
1201         if ((!isSubreport() || isSubreportRunToBottom()) && !ignorePagination)
1202         {
1203             offsetY = pageHeight - crtPageFooter.getHeight() - bottomMargin;
1204         }
1205
1206         crtPageFooter.evaluatePrintWhenExpression(evaluation);
1207
1208         if (crtPageFooter.isToPrint())
1209         {
1210             fillFixedBand(crtPageFooter, evaluation);
1211         }
1212     }
1213
1214
1215     /**
1216      *
1217      */

1218     @continuable
1219     private void fillSummary() throws JRException
1220     {
1221         if (log.isDebugEnabled() && !summary.isEmpty())
1222         {
1223             log.debug("Fill " + fillerId + ": summary at " + offsetY);
1224         }
1225
1226         offsetX = leftMargin;
1227
1228         if (lastPageFooter == missingFillBand)
1229         {
1230             if (
1231                 !isSummaryNewPage
1232                 //&& columnIndex == 0
1233                 && summary.getBreakHeight() <= columnFooterOffsetY - offsetY
1234                 )
1235             {
1236                 fillSummaryNoLastFooterSamePage();
1237             }
1238             else
1239             {
1240                 fillSummaryNoLastFooterNewPage();
1241             }
1242         }
1243         else
1244         {
1245             if (isSummaryWithPageHeaderAndFooter)
1246             {
1247                 fillSummaryWithLastFooterAndPageBands();
1248             }
1249             else
1250             {
1251                 fillSummaryWithLastFooterNoPageBands();
1252             }
1253         }
1254
1255         resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1256         resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1257         resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1258         resolveReportBoundElements();
1259         if (isMasterReport())
1260         {
1261             resolveMasterBoundElements();
1262         }
1263     }
1264
1265
1266     /**
1267      *
1268      */

1269     @continuable
1270     private void fillSummaryNoLastFooterSamePage() throws JRException
1271     {
1272         summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1273
1274         if (summary.isToPrint())
1275         {
1276             // deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
1277             if (groupFooterPositionElementRange != null)
1278             {
1279                 ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1280                 offsetY = columnFooterOffsetY;
1281                 // reset the element range here although it will not be checked anymore as the report ends
1282                 groupFooterPositionElementRange = null;
1283             }
1284             
1285             summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1286
1287             JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);
1288
1289             if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1290             {
1291                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1292
1293                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1294
1295                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1296                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1297                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1298                 scriptlet.callBeforePageInit();
1299                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1300                 scriptlet.callAfterPageInit();
1301
1302                 addPage(false);
1303                 
1304                 if (isSummaryWithPageHeaderAndFooter)
1305                 {
1306                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1307                 }
1308
1309                 printBand = 
1310                     summary.refill(
1311                         JRExpression.EVALUATION_DEFAULT,
1312                         pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0)
1313                         );
1314
1315                 fillBand(printBand);
1316                 offsetY += printBand.getHeight();
1317                 isCrtRecordOnPage = true;
1318                 isCrtRecordOnColumn = true;
1319
1320                 /*   */
1321                 fillSummaryOverflow();
1322                 
1323                 //DONE
1324             }
1325             else
1326             {
1327                 fillBand(printBand);
1328                 offsetY += printBand.getHeight();
1329                 isCrtRecordOnPage = true;
1330                 isCrtRecordOnColumn = true;
1331
1332                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1333
1334                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1335                 
1336                 if (summary.willOverflow())
1337                 {
1338                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1339                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1340                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1341                     scriptlet.callBeforePageInit();
1342                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1343                     scriptlet.callAfterPageInit();
1344
1345                     addPage(false);
1346                     
1347                     if (isSummaryWithPageHeaderAndFooter)
1348                     {
1349                         fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1350                     }
1351
1352                     printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));
1353
1354                     fillBand(printBand);
1355                     offsetY += printBand.getHeight();
1356                     isCrtRecordOnPage = true;
1357                     isCrtRecordOnColumn = true;
1358
1359                     /*   */
1360                     fillSummaryOverflow();
1361                     
1362                     //DONE
1363                 }
1364                 else
1365                 {
1366                     resolveBandBoundElements(summary, JRExpression.EVALUATION_DEFAULT);
1367
1368                     //DONE
1369                 }
1370             }
1371         }
1372         else
1373         {
1374             // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1375             
1376             fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1377
1378             fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1379             
1380             //DONE
1381         }
1382     }
1383
1384
1385     /**
1386      *
1387      */

1388     @continuable
1389     private void fillSummaryNoLastFooterNewPage() throws JRException
1390     {
1391         // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1392         
1393         fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1394
1395         fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1396
1397         summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1398
1399         if (summary.isToPrint())
1400         {
1401             resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1402             resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1403             resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1404             scriptlet.callBeforePageInit();
1405             calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1406             scriptlet.callAfterPageInit();
1407
1408             addPage(false);
1409
1410             if (isSummaryWithPageHeaderAndFooter)
1411             {
1412                 fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1413             }
1414
1415             summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1416
1417             JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));
1418
1419             if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1420             {
1421                 if (isSummaryWithPageHeaderAndFooter)
1422                 {
1423                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1424                 }
1425
1426                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1427                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1428                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1429                 scriptlet.callBeforePageInit();
1430                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1431                 scriptlet.callAfterPageInit();
1432
1433                 addPage(false);
1434
1435                 if (isSummaryWithPageHeaderAndFooter)
1436                 {
1437                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1438                 }
1439
1440                 printBand = 
1441                     summary.refill(
1442                         JRExpression.EVALUATION_DEFAULT,
1443                         pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0)
1444                         );
1445             }
1446
1447             fillBand(printBand);
1448             offsetY += printBand.getHeight();
1449             isCrtRecordOnPage = true;
1450             isCrtRecordOnColumn = true;
1451
1452             /*   */
1453             fillSummaryOverflow();
1454         }
1455         
1456         //DONE
1457     }
1458
1459
1460     /**
1461      *
1462      */

1463     @continuable
1464     private void fillSummaryWithLastFooterAndPageBands() throws JRException
1465     {
1466         if (
1467             !isSummaryNewPage
1468             //&& columnIndex == 0
1469             && summary.getBreakHeight() <= columnFooterOffsetY - offsetY
1470             )
1471         {
1472             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1473
1474             if (summary.isToPrint())
1475             {
1476                 // deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
1477                 if (groupFooterPositionElementRange != null)
1478                 {
1479                     ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1480                     offsetY = columnFooterOffsetY;
1481                     // reset the element range here although it will not be checked anymore as the report ends
1482                     groupFooterPositionElementRange = null;
1483                 }
1484                 
1485                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1486
1487                 JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);
1488
1489                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1490                 {
1491                     fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1492
1493                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1494
1495                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1496                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1497                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1498                     scriptlet.callBeforePageInit();
1499                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1500                     scriptlet.callAfterPageInit();
1501
1502                     addPage(false);
1503                     
1504                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1505                     
1506                     printBand = 
1507                         summary.refill(
1508                             JRExpression.EVALUATION_DEFAULT,
1509                             pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
1510                             );
1511
1512                     fillBand(printBand);
1513                     offsetY += printBand.getHeight();
1514                     isCrtRecordOnPage = true;
1515                     isCrtRecordOnColumn = true;
1516                 }
1517                 else
1518                 {
1519                     //SummaryReport.17 test
1520
1521                     fillBand(printBand);
1522                     offsetY += printBand.getHeight();
1523                     isCrtRecordOnPage = true;
1524                     isCrtRecordOnColumn = true;
1525
1526                     if (
1527                         !summary.willOverflow()
1528                         && offsetY <= lastPageColumnFooterOffsetY
1529                         )
1530                     {
1531                         setLastPageFooter(true);
1532                     }
1533                     
1534                     fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1535                 }
1536                 
1537                 /*   */
1538                 fillSummaryOverflow();
1539
1540                 //DONE
1541             }
1542             else
1543             {
1544                 // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1545                 
1546                 setLastPageFooter(true);
1547
1548                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1549
1550                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1551                 
1552                 //DONE
1553             }
1554         }
1555         else if (
1556                 //columnIndex == 0 && 
1557                 offsetY <= lastPageColumnFooterOffsetY)
1558         {
1559             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1560
1561             if (summary.isToPrint())
1562             {
1563                 // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1564                 
1565                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1566
1567                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1568
1569                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1570                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1571                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1572                 scriptlet.callBeforePageInit();
1573                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1574                 scriptlet.callAfterPageInit();
1575
1576                 addPage(false);
1577                 
1578                 fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1579
1580                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1581
1582                 JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - pageFooter.getHeight());
1583
1584                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1585                 {
1586                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1587
1588                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1589                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1590                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1591                     scriptlet.callBeforePageInit();
1592                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1593                     scriptlet.callAfterPageInit();
1594
1595                     addPage(false);
1596                     
1597                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1598
1599                     printBand = 
1600                         summary.refill(
1601                             JRExpression.EVALUATION_DEFAULT,
1602                             pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
1603                             );
1604                 }
1605
1606                 fillBand(printBand);
1607                 offsetY += printBand.getHeight();
1608                 isCrtRecordOnPage = true;
1609                 isCrtRecordOnColumn = true;
1610
1611                 /*   */
1612                 fillSummaryOverflow();
1613                 
1614                 //DONE
1615             }
1616             else
1617             {
1618                 // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1619                 
1620                 setLastPageFooter(true);
1621
1622                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1623
1624                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1625                 
1626                 //DONE
1627             }
1628         }
1629         else
1630         {
1631             // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1632             
1633             fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1634
1635             fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1636
1637             resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
1638             resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1639             resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1640             scriptlet.callBeforePageInit();
1641             calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1642             scriptlet.callAfterPageInit();
1643
1644             addPage(false);
1645
1646             fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1647
1648             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1649
1650             if (summary.isToPrint())
1651             {
1652                 //SummaryReport.18 test
1653
1654                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1655
1656                 JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - pageFooter.getHeight());
1657
1658                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1659                 {
1660                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1661
1662                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1663                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1664                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1665                     scriptlet.callBeforePageInit();
1666                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1667                     scriptlet.callAfterPageInit();
1668
1669                     addPage(false);
1670                     
1671                     fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1672
1673                     printBand = 
1674                         summary.refill(
1675                             JRExpression.EVALUATION_DEFAULT,
1676                             pageHeight - bottomMargin - offsetY - pageFooter.getHeight()
1677                             );
1678                 }
1679
1680                 fillBand(printBand);
1681                 offsetY += printBand.getHeight();
1682                 isCrtRecordOnPage = true;
1683                 isCrtRecordOnColumn = true;
1684             }
1685             else
1686             {
1687                 //SummaryReport.19 test
1688             }
1689
1690             /*   */
1691             fillSummaryOverflow();
1692             
1693             //DONE
1694         }
1695     }
1696
1697
1698     /**
1699      *
1700      */

1701     @continuable
1702     private void fillSummaryWithLastFooterNoPageBands() throws JRException
1703     {
1704         if (
1705             !isSummaryNewPage
1706             //&& columnIndex == 0
1707             && summary.getBreakHeight() <= lastPageColumnFooterOffsetY - offsetY
1708             )
1709         {
1710             setLastPageFooter(true);
1711
1712             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1713
1714             if (summary.isToPrint())
1715             {
1716                 // deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
1717                 if (groupFooterPositionElementRange != null)
1718                 {
1719                     ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1720                     offsetY = columnFooterOffsetY;
1721                     // reset the element range here although it will not be checked anymore as the report ends
1722                     groupFooterPositionElementRange = null;
1723                 }
1724                 
1725                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1726
1727                 JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);
1728
1729                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1730                 {
1731                     fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1732
1733                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1734
1735                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1736                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1737                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1738                     scriptlet.callBeforePageInit();
1739                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1740                     scriptlet.callAfterPageInit();
1741
1742                     addPage(false);
1743
1744                     printBand = 
1745                         summary.refill(
1746                             JRExpression.EVALUATION_DEFAULT,
1747                             pageHeight - bottomMargin - offsetY
1748                             );
1749
1750                     fillBand(printBand);
1751                     offsetY += printBand.getHeight();
1752                     isCrtRecordOnPage = true;
1753                     isCrtRecordOnColumn = true;
1754                 }
1755                 else
1756                 {
1757                     fillBand(printBand);
1758                     offsetY += printBand.getHeight();
1759                     isCrtRecordOnPage = true;
1760                     isCrtRecordOnColumn = true;
1761
1762                     fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1763
1764                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1765                 }
1766
1767                 /*   */
1768                 fillSummaryOverflow();
1769                 
1770                 //DONE
1771             }
1772             else
1773             {
1774                 // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;
1775
1776                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1777
1778                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1779                 
1780                 //DONE
1781             }
1782         }
1783         else if (
1784             !isSummaryNewPage
1785             //&& columnIndex == 0
1786             && summary.getBreakHeight() <= columnFooterOffsetY - offsetY
1787             )
1788         {
1789             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1790
1791             if (summary.isToPrint())
1792             {
1793                 // deal with groupFooterPositionElementRange here because summary will attempt to use remaining space
1794                 if (groupFooterPositionElementRange != null)
1795                 {
1796                     ElementRangeUtil.moveContent(groupFooterPositionElementRange, columnFooterOffsetY);
1797                     offsetY = columnFooterOffsetY;
1798                     // reset the element range here although it will not be checked anymore as the report ends
1799                     groupFooterPositionElementRange = null;
1800                 }
1801                 
1802                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1803
1804                 JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);
1805
1806                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1807                 {
1808                     if (offsetY <= lastPageColumnFooterOffsetY)
1809                     {
1810                         setLastPageFooter(true);
1811
1812                         fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1813
1814                         fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1815
1816                         resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1817                         resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1818                         resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1819                         scriptlet.callBeforePageInit();
1820                         calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1821                         scriptlet.callAfterPageInit();
1822
1823                         addPage(false);
1824
1825                         printBand = 
1826                             summary.refill(
1827                                 JRExpression.EVALUATION_DEFAULT,
1828                                 pageHeight - bottomMargin - offsetY
1829                                 );
1830
1831                         fillBand(printBand);
1832                         offsetY += printBand.getHeight();
1833                         isCrtRecordOnPage = true;
1834                         isCrtRecordOnColumn = true;
1835                     }
1836                     else
1837                     {
1838                         fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);
1839
1840                         setLastPageFooter(true);
1841
1842                         printBand = 
1843                             summary.refill(
1844                                 JRExpression.EVALUATION_DEFAULT,
1845                                 lastPageColumnFooterOffsetY - offsetY
1846                                 );
1847
1848                         fillBand(printBand);
1849                         offsetY += printBand.getHeight();
1850                         isCrtRecordOnPage = true;
1851                         isCrtRecordOnColumn = true;
1852
1853                         fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1854
1855                         fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1856                     }
1857                 }
1858                 else
1859                 {
1860                     fillBand(printBand);
1861                     offsetY += printBand.getHeight();
1862                     isCrtRecordOnPage = true;
1863                     isCrtRecordOnColumn = true;
1864
1865                     fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);
1866
1867                     setLastPageFooter(true);
1868
1869                     if (summary.willOverflow())
1870                     {
1871                         printBand = summary.fill(lastPageColumnFooterOffsetY - offsetY);
1872
1873                         fillBand(printBand);
1874                         offsetY += printBand.getHeight();
1875                         isCrtRecordOnPage = true;
1876                         isCrtRecordOnColumn = true;
1877                     }
1878
1879                     fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1880
1881                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1882                 }
1883
1884                 /*   */
1885                 fillSummaryOverflow();
1886                 
1887                 //DONE
1888             }
1889             else
1890             {
1891                 // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;
1892                 // it will be either the one in fillPageBreak or the following
1893                 
1894                 if (offsetY > lastPageColumnFooterOffsetY)
1895                 {
1896                     fillPageBreak(false, JRExpression.EVALUATION_DEFAULT, JRExpression.EVALUATION_DEFAULT, false);
1897                 }
1898
1899                 setLastPageFooter(true);
1900
1901                 fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1902
1903                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1904                 
1905                 //DONE
1906             }
1907         }
1908         else if (
1909                 //columnIndex == 0 && 
1910                 offsetY <= lastPageColumnFooterOffsetY)
1911         {
1912             // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do
1913
1914             setLastPageFooter(true);
1915
1916             fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1917
1918             fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1919
1920             summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1921
1922             if (summary.isToPrint())
1923             {
1924                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1925                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1926                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1927                 scriptlet.callBeforePageInit();
1928                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1929                 scriptlet.callAfterPageInit();
1930
1931                 addPage(false);
1932
1933                 summary.evaluate(JRExpression.EVALUATION_DEFAULT);
1934
1935                 JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY);
1936
1937                 if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
1938                 {
1939                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1940                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1941                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1942                     scriptlet.callBeforePageInit();
1943                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1944                     scriptlet.callAfterPageInit();
1945
1946                     addPage(false);
1947
1948                     printBand = 
1949                         summary.refill(
1950                             JRExpression.EVALUATION_DEFAULT,
1951                             pageHeight - bottomMargin - offsetY
1952                             );
1953                 }
1954
1955                 fillBand(printBand);
1956                 offsetY += printBand.getHeight();
1957                 isCrtRecordOnPage = true;
1958                 isCrtRecordOnColumn = true;
1959
1960                 /*   */
1961                 fillSummaryOverflow();
1962             }
1963             
1964             //DONE
1965         }
1966         else
1967         {
1968             // do nothing about groupFooterPositionElementRange because the following fillColumnFooter will do;
1969
1970             fillColumnFooters(JRExpression.EVALUATION_DEFAULT);
1971
1972             fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1973
1974             resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
1975             resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1976             resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
1977             scriptlet.callBeforePageInit();
1978             calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
1979             scriptlet.callAfterPageInit();
1980
1981             addPage(false);
1982
1983             fillPageHeader(JRExpression.EVALUATION_DEFAULT);
1984
1985             //fillColumnHeader(JRExpression.EVALUATION_DEFAULT);
1986
1987             setLastPageFooter(true);
1988
1989             if (isSummaryNewPage)
1990             {
1991                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
1992
1993                 summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
1994
1995                 if (summary.isToPrint())
1996                 {
1997                     resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
1998                     resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
1999                     resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2000                     scriptlet.callBeforePageInit();
2001                     calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2002                     scriptlet.callAfterPageInit();
2003
2004                     addPage(false);
2005
2006                     summary.evaluate(JRExpression.EVALUATION_DEFAULT);
2007
2008                     JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY);
2009
2010                     if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
2011                     {
2012                         resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
2013                         resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2014                         resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2015                         scriptlet.callBeforePageInit();
2016                         calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2017                         scriptlet.callAfterPageInit();
2018
2019                         addPage(false);
2020
2021                         printBand = 
2022                             summary.refill(
2023                                 JRExpression.EVALUATION_DEFAULT,
2024                                 pageHeight - bottomMargin - offsetY
2025                                 );
2026                     }
2027
2028                     fillBand(printBand);
2029                     offsetY += printBand.getHeight();
2030                     isCrtRecordOnPage = true;
2031                     isCrtRecordOnColumn = true;
2032
2033                     /*   */
2034                     fillSummaryOverflow();
2035                 }
2036                 
2037                 //DONE
2038             }
2039             else
2040             {
2041                 summary.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
2042
2043                 if (summary.isToPrint())
2044                 {
2045                     summary.evaluate(JRExpression.EVALUATION_DEFAULT);
2046
2047                     JRPrintBand printBand = summary.fill(columnFooterOffsetY - offsetY);
2048
2049                     if (summary.willOverflow() && summary.isSplitPrevented() && !summary.isSplitTypePreventInhibited())
2050                     {
2051                         fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2052
2053                         resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
2054                         resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2055                         resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2056                         scriptlet.callBeforePageInit();
2057                         calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2058                         scriptlet.callAfterPageInit();
2059
2060                         addPage(false);
2061
2062                         printBand = 
2063                             summary.refill(
2064                                 JRExpression.EVALUATION_DEFAULT,
2065                                 pageHeight - bottomMargin - offsetY
2066                                 );
2067
2068                         fillBand(printBand);
2069                         offsetY += printBand.getHeight();
2070                         isCrtRecordOnPage = true;
2071                         isCrtRecordOnColumn = true;
2072                     }
2073                     else
2074                     {
2075                         fillBand(printBand);
2076                         offsetY += printBand.getHeight();
2077                         isCrtRecordOnPage = true;
2078                         isCrtRecordOnColumn = true;
2079
2080                         fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2081                     }
2082
2083                     /*   */
2084                     fillSummaryOverflow();
2085                 }
2086                 else
2087                 {
2088                     fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2089                 }
2090                 
2091                 //DONE
2092             }
2093         }
2094     }
2095
2096
2097     /**
2098      *
2099      */

2100     @continuable
2101     private void fillSummaryOverflow() throws JRException
2102     {
2103         while (summary.willOverflow())
2104         {
2105             if (isSummaryWithPageHeaderAndFooter)
2106             {
2107                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2108             }
2109             
2110             resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
2111             resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2112             resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2113             scriptlet.callBeforePageInit();
2114             calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2115             scriptlet.callAfterPageInit();
2116
2117             addPage(false);
2118
2119             if (isSummaryWithPageHeaderAndFooter)
2120             {
2121                 fillPageHeader(JRExpression.EVALUATION_DEFAULT);
2122             }
2123             
2124             JRPrintBand printBand = summary.fill(pageHeight - bottomMargin - offsetY - (isSummaryWithPageHeaderAndFooter?pageFooter.getHeight():0));
2125
2126             fillBand(printBand);
2127             offsetY += printBand.getHeight();
2128             isCrtRecordOnPage = true;
2129             isCrtRecordOnColumn = true;
2130         }
2131
2132         resolveBandBoundElements(summary, JRExpression.EVALUATION_DEFAULT);
2133
2134         if (isSummaryWithPageHeaderAndFooter)
2135         {
2136             if (offsetY > pageHeight - bottomMargin - lastPageFooter.getHeight())
2137             {
2138                 fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2139                 
2140                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, true);
2141                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2142                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2143                 scriptlet.callBeforePageInit();
2144                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2145                 scriptlet.callAfterPageInit();
2146
2147                 addPage(false);
2148
2149                 fillPageHeader(JRExpression.EVALUATION_DEFAULT);
2150             }
2151             
2152             if (lastPageFooter != missingFillBand)
2153             {
2154                 setLastPageFooter(true);
2155             }
2156             
2157             fillPageFooter(JRExpression.EVALUATION_DEFAULT);
2158         }
2159     }
2160
2161
2162     /**
2163      *
2164      */

2165     private void fillBackground() throws JRException
2166     {
2167         if (log.isDebugEnabled() && !background.isEmpty())
2168         {
2169             log.debug("Fill " + fillerId + ": background at " + offsetY);
2170         }
2171         
2172         //offsetX = leftMargin;
2173         
2174         //if (!isSubreport)
2175         //{
2176         //  offsetY = pageHeight - pageFooter.getHeight() - bottomMargin;
2177         //}
2178         
2179         if (background.getHeight() <= pageHeight - bottomMargin - offsetY)
2180         {
2181             background.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
2182             
2183             if (background.isToPrint())
2184             {
2185                 background.evaluate(JRExpression.EVALUATION_DEFAULT);
2186                 
2187                 JRPrintBand printBand = background.fill(pageHeight - bottomMargin - offsetY);
2188                 
2189                 fillBand(printBand);
2190                 //offsetY += printBand.getHeight();
2191                 isCrtRecordOnPage = true;
2192                 isCrtRecordOnColumn = true;
2193             }
2194         }
2195     }
2196
2197
2198     /**
2199      *
2200      */

2201     @continuable
2202     private void addPage(boolean isResetPageNumber) throws JRException
2203     {
2204         if (isSubreport())
2205         {
2206             addPageToParent(false);
2207         }
2208         
2209         if (printPage != null)
2210         {
2211             recordUsedPageHeight(offsetY + bottomMargin);
2212         }
2213
2214         printPage = newPage();
2215         printPageContentsWidth = 0;
2216
2217         JRFillVariable pageNumberVar = calculator.getPageNumber();
2218         if (isResetPageNumber)
2219         {
2220             pageNumberVar.setValue(1);
2221         }
2222         else
2223         {
2224             pageNumberVar.setValue(
2225                 ((Number)pageNumberVar.getValue()).intValue() + 1
2226                 );
2227         }
2228         pageNumberVar.setOldValue(pageNumberVar.getValue());
2229
2230         addPage(printPage);
2231         setFirstColumn();
2232         offsetY = topMargin;
2233         isFirstPageBand = true;
2234         isFirstColumnBand = true;
2235
2236         lastDetailOffsetX = -1;
2237         lastDetailOffsetY = -1;
2238         maxDetailOffsetY = 0;
2239
2240         fillBackground();
2241     }
2242
2243     /**
2244      * Sets the column number value computed based on {@link #columnIndex columnIndex}
2245      */

2246     private void setColumnNumberVariable()
2247     {
2248         JRFillVariable columnNumberVar = calculator.getColumnNumber();
2249         columnNumberVar.setValue(columnIndex + 1);
2250         columnNumberVar.setOldValue(columnNumberVar.getValue());
2251     }
2252
2253     /**
2254      *
2255      */

2256     @continuable
2257     private void fillPageBreak(
2258         boolean isResetPageNumber,
2259         byte evalPrevPage,
2260         byte evalNextPage,
2261         boolean isReprintGroupHeaders
2262         ) throws JRException
2263     {
2264         if (isCreatingNewPage)
2265         {
2266             throw 
2267                 new JRException(
2268                     EXCEPTION_MESSAGE_KEY_INFINITE_LOOP_CREATING_NEW_PAGE,  
2269                     (Object[])null 
2270                     );
2271         }
2272
2273         if (groups != null)
2274         {
2275             for (JRFillGroup group : groups)
2276             {
2277                 if (group.getKeepTogetherElementRange() != null)
2278                 {
2279                     group.getKeepTogetherElementRange().expand(offsetY);
2280                 }
2281             }
2282         }
2283         
2284         FooterPositionEnum groupFooterPositionForOverflow = null;
2285         if (groupFooterPositionElementRange != null)
2286         {
2287             groupFooterPositionForOverflow = groupFooterPositionElementRange.getCurrentFooterPosition();
2288             // we are during group footers filling, otherwise this element range would have been null;
2289             // adding the content of the group footer band that is currently breaking
2290             groupFooterPositionElementRange.getElementRange().expand(offsetY);
2291         }
2292
2293         if (orphanGroupFooterElementRange != null)
2294         {
2295             // we are during a group footer filling and footers already started to print,
2296             // so the current expansion applies to the group footer element range, not the detail element range
2297             orphanGroupFooterElementRange.expand(offsetY);
2298         }
2299         else if (orphanGroupFooterDetailElementRange != null)
2300         {
2301             // we are during a group footer filling, but footers did not yet start to print,
2302             // so the current expansion applies to the detail element range
2303             orphanGroupFooterDetailElementRange.expand(offsetY);
2304         }
2305         
2306         isCreatingNewPage = true;
2307
2308         fillColumnFooters(evalPrevPage);
2309
2310         fillPageFooter(evalPrevPage);
2311
2312         resolveGroupBoundElements(evalPrevPage, false);
2313         resolveColumnBoundElements(evalPrevPage);
2314         resolvePageBoundElements(evalPrevPage);
2315         scriptlet.callBeforePageInit();
2316         calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2317         scriptlet.callAfterPageInit();
2318
2319         JRFillGroup keepTogetherGroup = getKeepTogetherGroup();
2320
2321         ElementRange elementRangeToMove = null;
2322         ElementRange elementRangeToMove2 = null// we don't have more than two possible element ranges to move; at least for now
2323         if (keepTogetherGroup != null)
2324         {
2325             elementRangeToMove = keepTogetherGroup.getKeepTogetherElementRange();
2326         }
2327         else if (orphanGroupFooterDetailElementRange != null)
2328         {
2329             elementRangeToMove = orphanGroupFooterDetailElementRange;
2330             elementRangeToMove2 = orphanGroupFooterElementRange;
2331         }
2332
2333         if (
2334             floatColumnFooterElementRange != null 
2335             && elementRangeToMove != null
2336             && (elementRangeToMove.getColumnIndex() == 0 || elementRangeToMove2 != null
2337                 // either the moved detail is from first column, or there were some group footers moved too,
2338                 // otherwise the float column footer does not need to be moved
2339             )
2340         {
2341             ElementRangeUtil.moveContent(
2342                 floatColumnFooterElementRange, 
2343                 elementRangeToMove.getColumnIndex() == 0 ? elementRangeToMove.getTopY() : elementRangeToMove2.getTopY()
2344                 );
2345         }
2346
2347         // remove second range first, otherwise element indexes would become out of range
2348         ElementRangeContents elementsToMove2 = null;
2349         if (elementRangeToMove2 != null)
2350         {
2351             elementsToMove2 = ElementRangeUtil.removeContent(elementRangeToMove2, delayedActions);
2352         }
2353         ElementRangeContents elementsToMove = null;
2354         if (elementRangeToMove != null)
2355         {
2356             elementsToMove = ElementRangeUtil.removeContent(elementRangeToMove, delayedActions);
2357         }
2358
2359         addPage(isResetPageNumber);
2360
2361         fillPageHeader(evalNextPage);
2362
2363         fillColumnHeaders(evalNextPage);
2364
2365         if (isReprintGroupHeaders)
2366         {
2367             fillGroupHeadersReprint(evalNextPage);
2368             
2369             ElementRange keepTogetherElementRange = keepTogetherGroup == null ? null : keepTogetherGroup.getKeepTogetherElementRange();
2370
2371             if (
2372                 keepTogetherElementRange != null
2373                 && offsetY > keepTogetherElementRange.getBottomY()
2374                 )
2375             {
2376                 throw new JRException("Keep together moved content does not fit on the new page.");
2377             }
2378         }
2379
2380         // reseting all movable element ranges
2381         orphanGroupFooterDetailElementRange = null;
2382         orphanGroupFooterElementRange = null;
2383         if (keepTogetherGroup != null)
2384         {
2385             keepTogetherGroup.setKeepTogetherElementRange(null);
2386         }
2387
2388         if (elementRangeToMove != null)
2389         {
2390             ElementRangeUtil.addContent( 
2391                 printPage, 
2392                 currentPageIndex(),
2393                 elementsToMove,
2394                 //regardless whether there was page break or column  break, the X offset needs to account for columnIndex difference
2395                 (columnIndex - elementRangeToMove.getColumnIndex()) * (columnSpacing + columnWidth),
2396                 offsetY - elementRangeToMove.getTopY(),
2397                 delayedActions
2398                 );
2399
2400             offsetY = offsetY + elementRangeToMove.getBottomY() - elementRangeToMove.getTopY();
2401             
2402             if (elementRangeToMove2 != null)
2403             {
2404                 ElementRangeUtil.addContent( 
2405                     printPage, 
2406                     currentPageIndex(),
2407                     elementsToMove2,
2408                     //regardless whether there was page break or column  break, the X offset needs to account for columnIndex difference
2409                     (columnIndex - elementRangeToMove2.getColumnIndex()) * (columnSpacing + columnWidth),
2410                     offsetY - elementRangeToMove2.getTopY(),
2411                     delayedActions
2412                     );
2413
2414                 offsetY = offsetY + elementRangeToMove2.getBottomY() - elementRangeToMove2.getTopY();
2415             }
2416             
2417             isFirstPageBand = false;
2418             isFirstColumnBand = false;
2419         } 
2420         else if (
2421             groupFooterPositionForOverflow != null
2422             && groupFooterPositionForOverflow != FooterPositionEnum.NORMAL
2423             )
2424         {
2425             // here we are during a group footer filling that broke over onto a new page;
2426             // recreating the group footer element range for the overflow content of the band
2427             groupFooterPositionElementRange = 
2428                 new SimpleGroupFooterElementRange(
2429                     new SimpleElementRange(getCurrentPage(), columnIndex, offsetY), 
2430                     groupFooterPositionForOverflow
2431                     );
2432         }
2433
2434         isCreatingNewPage = false;
2435     }
2436
2437
2438     /**
2439      *
2440      */

2441     @continuable
2442     protected void fillColumnBand(JRFillBand band, byte evaluation) throws JRException
2443     {
2444         band.evaluate(evaluation);
2445
2446         JRPrintBand printBand = band.fill(columnFooterOffsetY - offsetY);
2447
2448         if (band.willOverflow())
2449         {
2450             boolean toRefill = band.isSplitPrevented() && !band.isSplitTypePreventInhibited();
2451             
2452             if (!toRefill)
2453             {
2454                 if (groups != null)
2455                 {
2456                     // outer groups keep together is honored, while for the
2457                     // inner keep together groups, the element range would be null 
2458                     // in-between parent group breaks
2459                     for (JRFillGroup group : groups)
2460                     {
2461                         if (
2462                             group.getKeepTogetherElementRange() != null
2463                             && (group.isKeepTogether() || !group.hasMinDetails())
2464                             )
2465                         {
2466                             toRefill = true;
2467                             break;
2468                         }
2469                     }
2470                 }
2471             }
2472             
2473             if (!toRefill)
2474             {
2475                 if (orphanGroupFooterDetailElementRange != null)
2476                 {
2477                     toRefill = true;
2478                 }
2479             }
2480             
2481             if (toRefill)
2482             {
2483                 fillPageBreak(
2484                     false
2485                     evaluation == JRExpression.EVALUATION_DEFAULT 
2486                         ? (isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD) 
2487                         : evaluation, 
2488                     evaluation,
2489                     true
2490                     );
2491
2492                 printBand = band.refill(evaluation, columnFooterOffsetY - offsetY);
2493             }
2494         }
2495
2496         fillBand(printBand);
2497         offsetY += printBand.getHeight();
2498         isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
2499         isCrtRecordOnColumn = isCrtRecordOnPage;
2500         
2501         while (band.willOverflow())
2502         {
2503             // this overflow here is special in the sense that it is the overflow of a detail band or group header or footer,
2504             // which are the only bands that are involved with movable element ranges such as keep together, footer position or orphan footer;
2505             // it is also special in the sense that it is an overflow after the band actually generated some content on the current page/column
2506             // and is not an early overflow like the one occurring when the band does not fit with its declared height or is non-splitting band;
2507             // having said that, it is OK to be more specific about the type of overflow here and only deal with non-white-space overflows of the band,
2508             // as they are the only ones which actually need to introduce a page/column break and continue rendering their remaining elements;
2509             // white space band overflows do not render anything on the next page/column and don't even preserve their remaining white space (historical behavior);
2510             // avoiding a page/column break here in case of white space overflows helps with preserving the detail element range, which would
2511             // thus be moved onto the new page/column as a non-breaking detail, if orphan footers follow; 
2512             // a page/column break here would cause the existing detail element range to be discarded (lost on subsequent element range expand),
2513             // and thus it would not be moved in case orphan footer follows, 
2514             // even if nothing gets rendered by this detail on the next page/column 
2515             if (band.willOverflowWithElements())
2516             {
2517                 fillPageBreak(
2518                     false
2519                     evaluation == JRExpression.EVALUATION_DEFAULT 
2520                         ? (isCrtRecordOnPage ? JRExpression.EVALUATION_DEFAULT : JRExpression.EVALUATION_OLD) 
2521                         : evaluation, 
2522                     evaluation,
2523                     true
2524                     );
2525             }
2526
2527             // we continue filling band overflow normally, because even in case of white space band overflow, nothing gets actually rendered
2528             // and the offsetY remains unchanged;
2529             // but we need to do this because the isOverflow flag would eventually be set to false and thus the current band rendering would end,
2530             // bringing the band into a state ready for the next filling
2531             printBand = band.fill(columnFooterOffsetY - offsetY);
2532
2533             fillBand(printBand);
2534             offsetY += printBand.getHeight();
2535             isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
2536             isCrtRecordOnColumn = isCrtRecordOnPage;
2537         }
2538
2539         resolveBandBoundElements(band, evaluation);
2540     }
2541
2542
2543     /**
2544      *
2545      */

2546     protected void fillFixedBand(JRFillBand band, byte evaluation) throws JRException
2547     {
2548         fillFixedBand(band, evaluation, true);
2549     }
2550
2551
2552     protected void fillFixedBand(JRFillBand band, byte evaluation, boolean allowShrinking) throws JRException
2553     {
2554         band.evaluate(evaluation);
2555
2556         JRPrintBand printBand = band.fill();
2557
2558         fillBand(printBand);
2559         offsetY += allowShrinking ? printBand.getHeight() : band.getHeight();
2560         isCrtRecordOnPage = evaluation == JRExpression.EVALUATION_DEFAULT;
2561         isCrtRecordOnColumn = isCrtRecordOnPage;
2562
2563         resolveBandBoundElements(band, evaluation);
2564     }
2565
2566
2567     /**
2568      *
2569      */

2570     private void setNewPageColumnInBands()
2571     {
2572         title.setNewPageColumn(true);
2573         pageHeader.setNewPageColumn(true);
2574         columnHeader.setNewPageColumn(true);
2575         detailSection.setNewPageColumn(true);
2576         columnFooter.setNewPageColumn(true);
2577         pageFooter.setNewPageColumn(true);
2578         lastPageFooter.setNewPageColumn(true);
2579         summary.setNewPageColumn(true);
2580         noData.setNewPageColumn(true);
2581
2582         if (groups != null && groups.length > 0)
2583         {
2584             for(int i = 0; i < groups.length; i++)
2585             {
2586                 ((JRFillSection)groups[i].getGroupHeaderSection()).setNewPageColumn(true);
2587                 ((JRFillSection)groups[i].getGroupFooterSection()).setNewPageColumn(true);
2588             }
2589         }
2590     }
2591
2592
2593     /**
2594      *
2595      */

2596     private void setNewGroupInBands(JRGroup group)
2597     {
2598         title.setNewGroup(group, true);
2599         pageHeader.setNewGroup(group, true);
2600         columnHeader.setNewGroup(group, true);
2601         detailSection.setNewGroup(group, true);
2602         columnFooter.setNewGroup(group, true);
2603         pageFooter.setNewGroup(group, true);
2604         lastPageFooter.setNewGroup(group, true);
2605         summary.setNewGroup(group, true);
2606
2607         if (groups != null && groups.length > 0)
2608         {
2609             for(int i = 0; i < groups.length; i++)
2610             {
2611                 ((JRFillSection)groups[i].getGroupHeaderSection()).setNewGroup(group, true);
2612                 ((JRFillSection)groups[i].getGroupFooterSection()).setNewGroup(group, true);
2613             }
2614         }
2615     }
2616
2617
2618     /**
2619      *
2620      */

2621     private JRFillBand getCurrentPageFooter()
2622     {
2623         return isLastPageFooter ? lastPageFooter : pageFooter;
2624     }
2625
2626
2627     /**
2628      *
2629      */

2630     private void setLastPageFooter(boolean isLastPageFooter)
2631     {
2632         this.isLastPageFooter = isLastPageFooter;
2633
2634         if (isLastPageFooter)
2635         {
2636             columnFooterOffsetY = lastPageColumnFooterOffsetY;
2637         }
2638     }
2639
2640     /**
2641      *
2642      */

2643     @continuable
2644     private void fillNoData() throws JRException
2645     {
2646         if (log.isDebugEnabled() && !noData.isEmpty())
2647         {
2648             log.debug("Fill " + fillerId + ": noData at " + offsetY);
2649         }
2650
2651         noData.evaluatePrintWhenExpression(JRExpression.EVALUATION_DEFAULT);
2652
2653         if (noData.isToPrint())
2654         {
2655             while (noData.getBreakHeight() > pageHeight - bottomMargin - offsetY)
2656             {
2657                 addPage(false);
2658             }
2659
2660             noData.evaluate(JRExpression.EVALUATION_DEFAULT);
2661
2662             JRPrintBand printBand = noData.fill(pageHeight - bottomMargin - offsetY);
2663
2664             if (noData.willOverflow() && noData.isSplitPrevented() && !noData.isSplitTypePreventInhibited())
2665             {
2666                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
2667                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2668                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2669                 scriptlet.callBeforePageInit();
2670                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2671                 scriptlet.callAfterPageInit();
2672
2673                 addPage(false);
2674
2675                 printBand = 
2676                     noData.refill(
2677                         JRExpression.EVALUATION_DEFAULT,
2678                         pageHeight - bottomMargin - offsetY
2679                         );
2680             }
2681
2682             fillBand(printBand);
2683             offsetY += printBand.getHeight();
2684             isCrtRecordOnPage = true;
2685             isCrtRecordOnColumn = true;
2686
2687             while (noData.willOverflow())
2688             {
2689                 resolveGroupBoundElements(JRExpression.EVALUATION_DEFAULT, false);
2690                 resolveColumnBoundElements(JRExpression.EVALUATION_DEFAULT);
2691                 resolvePageBoundElements(JRExpression.EVALUATION_DEFAULT);
2692                 scriptlet.callBeforePageInit();
2693                 calculator.initializeVariables(ResetTypeEnum.PAGE, IncrementTypeEnum.PAGE);
2694                 scriptlet.callAfterPageInit();
2695
2696                 addPage(false);
2697
2698                 printBand = noData.fill(pageHeight - bottomMargin - offsetY);
2699
2700                 fillBand(printBand);
2701                 offsetY += printBand.getHeight();
2702                 isCrtRecordOnPage = true;
2703                 isCrtRecordOnColumn = true;
2704             }
2705             resolveBandBoundElements(noData, JRExpression.EVALUATION_DEFAULT);
2706         }
2707     }
2708
2709     
2710     /**
2711      *
2712      */

2713     private void setOffsetX()
2714     {
2715         if (columnDirection == RunDirectionEnum.RTL)
2716         {
2717             offsetX = pageWidth - rightMargin - columnWidth - columnIndex * (columnSpacing + columnWidth);
2718         }
2719         else
2720         {
2721             offsetX = leftMargin + columnIndex * (columnSpacing + columnWidth);
2722         }
2723     }
2724
2725     
2726 }
2727