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
25 /*
26  * Contributors:
27  * John Bindel - jbindel@users.sourceforge.net
28  */

29
30 package net.sf.jasperreports.engine.fill;
31
32 import java.text.Format;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.ResourceBundle;
44 import java.util.Set;
45 import java.util.TimeZone;
46 import java.util.UUID;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.stream.Collectors;
49
50 import org.apache.commons.javaflow.api.continuable;
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53
54 import net.sf.jasperreports.engine.JRBand;
55 import net.sf.jasperreports.engine.JRDefaultStyleProvider;
56 import net.sf.jasperreports.engine.JRException;
57 import net.sf.jasperreports.engine.JRExpression;
58 import net.sf.jasperreports.engine.JRGroup;
59 import net.sf.jasperreports.engine.JROrigin;
60 import net.sf.jasperreports.engine.JRParameter;
61 import net.sf.jasperreports.engine.JRPrintElement;
62 import net.sf.jasperreports.engine.JRPrintPage;
63 import net.sf.jasperreports.engine.JRReport;
64 import net.sf.jasperreports.engine.JRReportTemplate;
65 import net.sf.jasperreports.engine.JRRuntimeException;
66 import net.sf.jasperreports.engine.JRStyle;
67 import net.sf.jasperreports.engine.JRStyleSetter;
68 import net.sf.jasperreports.engine.JRTemplate;
69 import net.sf.jasperreports.engine.JRTemplateReference;
70 import net.sf.jasperreports.engine.JRVariable;
71 import net.sf.jasperreports.engine.JasperPrint;
72 import net.sf.jasperreports.engine.JasperReportsContext;
73 import net.sf.jasperreports.engine.base.JRBasePrintPage;
74 import net.sf.jasperreports.engine.base.JRVirtualPrintPage;
75 import net.sf.jasperreports.engine.type.BandTypeEnum;
76 import net.sf.jasperreports.engine.type.EvaluationTimeEnum;
77 import net.sf.jasperreports.engine.type.OrientationEnum;
78 import net.sf.jasperreports.engine.type.PrintOrderEnum;
79 import net.sf.jasperreports.engine.type.PropertyEvaluationTimeEnum;
80 import net.sf.jasperreports.engine.type.RunDirectionEnum;
81 import net.sf.jasperreports.engine.type.SectionTypeEnum;
82 import net.sf.jasperreports.engine.type.WhenNoDataTypeEnum;
83 import net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum;
84 import net.sf.jasperreports.engine.util.JRDataUtils;
85 import net.sf.jasperreports.engine.util.JRStyledTextParser;
86 import net.sf.jasperreports.engine.util.JRStyledTextUtil;
87 import net.sf.jasperreports.engine.util.StyleResolver;
88 import net.sf.jasperreports.repo.RepositoryContext;
89 import net.sf.jasperreports.repo.RepositoryResourceContext;
90 import net.sf.jasperreports.repo.SimpleRepositoryContext;
91 import net.sf.jasperreports.repo.SimpleRepositoryResourceContext;
92
93
94 /**
95  * @author Teodor Danciu (teodord@users.sourceforge.net)
96  */

97 public abstract class JRBaseFiller extends BaseReportFiller implements JRDefaultStyleProvider
98 {
99
100     private static final Log log = LogFactory.getLog(JRBaseFiller.class);
101     
102     public static final String EXCEPTION_MESSAGE_KEY_INFINITE_LOOP_CREATING_NEW_PAGE = "fill.common.filler.infinite.loop.creating.new.page";
103     public static final String EXCEPTION_MESSAGE_KEY_COLUMN_HEADER_OVERFLOW_INFINITE_LOOP = "fill.common.filler.column.header.overflow.infinite.loop";
104     public static final String EXCEPTION_MESSAGE_KEY_CIRCULAR_DEPENDENCY_FOUND = "fill.base.filler.circular.dependency.found";
105     public static final String EXCEPTION_MESSAGE_KEY_EXTERNAL_STYLE_NAME_NOT_SET = "fill.base.filler.external.style.name.not.set";
106     public static final String EXCEPTION_MESSAGE_KEY_NO_SUCH_GROUP = "fill.base.filler.no.such.group";
107     public static final String EXCEPTION_MESSAGE_KEY_PAGE_HEADER_OVERFLOW_INFINITE_LOOP = "fill.common.filler.page.header.overflow.infinite.loop";
108     public static final String EXCEPTION_MESSAGE_KEY_UNSUPPORTED_REPORT_SECTION_TYPE = "fill.base.filler.unsupported.report.section.type";
109     public static final String EXCEPTION_MESSAGE_KEY_KEEP_TOGETHER_CONTENT_DOES_NOT_FIT = "fill.common.filler.keep.together.content.does.not.fit";
110     
111     private static final int PAGE_HEIGHT_PAGINATION_IGNORED = 0x7d000000;//less than Integer.MAX_VALUE to avoid 
112     private static final int PAGE_WIDTH_IGNORED = 0x7d000000;
113     
114     private JRStyledTextParser styledTextParser = JRStyledTextParser.getInstance();
115
116     /**
117      *
118      */

119     protected String name;
120
121     protected int columnCount;
122
123     protected PrintOrderEnum printOrder;
124
125     protected RunDirectionEnum columnDirection;
126
127     protected int pageWidth;
128     protected int maxPageWidth;
129
130     protected int pageHeight;
131
132     protected OrientationEnum orientation;
133
134     private WhenNoDataTypeEnum whenNoDataType;
135
136     protected int columnWidth;
137
138     protected int columnSpacing;
139
140     protected int leftMargin;
141
142     protected int rightMargin;
143
144     protected int topMargin;
145
146     protected int bottomMargin;
147
148     protected boolean isTitleNewPage;
149
150     protected boolean isSummaryNewPage;
151
152     protected boolean isSummaryWithPageHeaderAndFooter;
153
154     protected boolean isFloatColumnFooter;
155
156     /**
157      * the resource missing handling type
158      */

159     protected WhenResourceMissingTypeEnum whenResourceMissingType;
160
161     protected JRFillReportTemplate[] reportTemplates;
162     
163     protected List<ReportTemplateSource> templates;
164
165     protected JRStyle defaultStyle;
166     
167     protected StyleResolver styleResolver;
168
169     protected JRStyle[] styles;
170
171     protected JRFillGroup[] groups;
172
173     protected JRFillSection missingFillSection;
174     protected JRFillBand missingFillBand;
175
176     protected JRFillBand background;
177
178     protected JRFillBand title;
179
180     protected JRFillBand pageHeader;
181
182     protected JRFillBand columnHeader;
183
184     protected JRFillSection detailSection;
185
186     protected JRFillBand columnFooter;
187
188     protected JRFillBand pageFooter;
189
190     protected JRFillBand lastPageFooter;
191
192     protected JRFillBand summary;
193
194     protected JRFillBand noData;
195
196     protected JRPrintPage printPage;
197     protected int printPageContentsWidth;
198
199     /**
200      * List of {@link JRFillBand JRFillBand} objects containing all bands of the
201      * report.
202      */

203     protected List<JRBand> bands;
204
205     /**
206      * Collection of subfillers
207      */

208     protected Map<Integer, JRBaseFiller> subfillers;
209
210     private boolean bandOverFlowAllowed;
211
212     /**
213      *
214      */

215     protected Map<String,Format> dateFormatCache = new HashMap<String,Format>();
216     protected Map<String,Format> numberFormatCache = new HashMap<String,Format>();
217
218     protected GroupFooterElementRange groupFooterPositionElementRange;
219     // we need to keep detail element range separate from orphan group footer element range
220     // because it is created in advance, as by the time we need to create the element range 
221     // for the current detail, we do not know if any group will break and footers would be filled
222     protected ElementRange detailElementRange;
223     // we use this element range to keep the detail element range once orphan footers start to render;
224     // this is more like a flag to signal that we are currently dealing with orphan group footers
225     protected ElementRange orphanGroupFooterDetailElementRange;
226     // we keep the content of orphan group footers bands in separate element range because in horizontal
227     // filler, the detail element range can have a different columnIndex and thus be moved with a different X offset
228     protected ElementRange orphanGroupFooterElementRange;
229     // keep the content of floating column footer so that it can be moved up in case content is moved
230     // from one page/column to the next due to keep together, min details or orphan footer groups
231     protected ElementRange floatColumnFooterElementRange;
232     
233
234     /**
235      *
236      */

237     protected boolean isCreatingNewPage;
238     protected boolean isNewPage;
239     protected boolean isNewColumn;
240     protected boolean isFirstPageBand;
241     protected boolean isFirstColumnBand;
242     // indicates if values from current record have already appeared on current page through a band being evaluated with JRExpression.EVALUATION_DEFAULT
243     protected boolean isCrtRecordOnPage;
244     protected boolean isCrtRecordOnColumn;
245     // we call it min level because footers print in reverse order and lower level means outer footer
246     protected Integer preventOrphanFootersMinLevel;
247     protected int crtGroupFootersLevel;
248
249     protected int columnIndex;
250
251     protected int offsetX;
252     protected int offsetY;
253     protected int columnHeaderOffsetY;
254     protected int columnFooterOffsetY;
255     protected int lastPageColumnFooterOffsetY;
256
257     protected boolean isLastPageFooter;
258
259     protected boolean isReorderBandElements;
260     
261     protected int usedPageHeight = 0;
262
263     /**
264      *
265      */

266     protected JRBaseFiller(
267         JasperReportsContext jasperReportsContext, 
268         JasperReportSource reportSource,  
269         BandReportFillerParent parent 
270         ) throws JRException
271     {
272         super(jasperReportsContext, reportSource, parent);
273         
274         groups = mainDataset.groups;
275
276         createReportTemplates(factory);
277
278         String reportName = getBandReportParent() == null ? null : getBandReportParent().getReportName();
279         
280         background = createFillBand(jasperReport.getBackground(), reportName, BandTypeEnum.BACKGROUND);
281         title = createFillBand(jasperReport.getTitle(), reportName, BandTypeEnum.TITLE);
282         pageHeader = createFillBand(jasperReport.getPageHeader(), reportName, BandTypeEnum.PAGE_HEADER);
283         columnHeader = createFillBand(jasperReport.getColumnHeader(), reportName, BandTypeEnum.COLUMN_HEADER);
284         
285         detailSection = factory.getSection(jasperReport.getDetailSection());
286         if (detailSection != missingFillSection)
287         {
288             detailSection.setOrigin(
289                 new JROrigin(
290                     reportName,
291                     BandTypeEnum.DETAIL
292                     )
293                 );
294         }
295         
296         columnFooter = createFillBand(jasperReport.getColumnFooter(), reportName, BandTypeEnum.COLUMN_FOOTER);
297         pageFooter = createFillBand(jasperReport.getPageFooter(), reportName, BandTypeEnum.PAGE_FOOTER);
298         lastPageFooter = createFillBand(jasperReport.getLastPageFooter(), reportName, BandTypeEnum.LAST_PAGE_FOOTER);
299         
300         summary = createFillBand(jasperReport.getSummary(), reportName, BandTypeEnum.SUMMARY);
301         if (summary != missingFillBand && summary.isEmpty())
302         {
303             summary = missingFillBand;
304         }
305         
306         noData = createFillBand(jasperReport.getNoData(), reportName, BandTypeEnum.NO_DATA);
307
308         initDatasets();
309         initBands();
310     }
311
312     @Override
313     protected void jasperReportSet()
314     {
315         SectionTypeEnum sectionType = jasperReport.getSectionType();
316         if (sectionType != null && sectionType != SectionTypeEnum.BAND)
317         {
318             throw 
319                 new JRRuntimeException(
320                     EXCEPTION_MESSAGE_KEY_UNSUPPORTED_REPORT_SECTION_TYPE,  
321                     new Object[]{jasperReport.getSectionType()} 
322                     );
323         }
324
325         /*   */
326         name = jasperReport.getName();
327         columnCount = jasperReport.getColumnCount();
328         printOrder = jasperReport.getPrintOrderValue();
329         columnDirection = jasperReport.getColumnDirection();
330         pageWidth = jasperReport.getPageWidth();
331         pageHeight = jasperReport.getPageHeight();
332         orientation = jasperReport.getOrientationValue();
333         whenNoDataType = jasperReport.getWhenNoDataTypeValue();
334         columnWidth = jasperReport.getColumnWidth();
335         columnSpacing = jasperReport.getColumnSpacing();
336         leftMargin = jasperReport.getLeftMargin();
337         rightMargin = jasperReport.getRightMargin();
338         topMargin = jasperReport.getTopMargin();
339         bottomMargin = jasperReport.getBottomMargin();
340         isTitleNewPage = jasperReport.isTitleNewPage();
341         isSummaryNewPage = jasperReport.isSummaryNewPage();
342         isSummaryWithPageHeaderAndFooter = jasperReport.isSummaryWithPageHeaderAndFooter();
343         isFloatColumnFooter = jasperReport.isFloatColumnFooter();
344         whenResourceMissingType = jasperReport.getWhenResourceMissingTypeValue();
345     }
346
347     @Override
348     protected JRFillObjectFactory initFillFactory()
349     {
350         JRFillObjectFactory fillFactory = new JRFillObjectFactory(this);
351         
352         // needed when creating group bands
353         defaultStyleListeners = new ArrayList<DefaultStyleListener>();
354         
355         missingFillBand = new JRFillBand(thisnull, fillFactory);
356         missingFillSection = new JRFillSection(thisnull, fillFactory);
357
358         return fillFactory;
359     }
360     
361     private JRFillBand createFillBand(JRBand reportBand, String reportName, BandTypeEnum bandType)
362     {
363         JRFillBand fillBand = factory.getBand(reportBand);
364         if (fillBand != missingFillBand)
365         {
366             JROrigin origin = new JROrigin(reportName, bandType);
367             fillBand.setOrigin(origin);
368         }
369         return fillBand;
370     }
371
372     @Override
373     protected void setJasperReportsContext(JasperReportsContext jasperReportsContext)
374     {
375         super.setJasperReportsContext(jasperReportsContext);
376
377         this.styleResolver = new StyleResolver(jasperReportsContext);
378     }
379
380     /**
381      * Returns the report fields indexed by name.
382      *
383      * @return the report fields map
384      */

385     protected Map<String,JRFillField> getFieldsMap()
386     {
387         return mainDataset.fieldsMap;
388     }
389
390
391     /**
392      * Returns the report variables indexed by name.
393      *
394      * @return the report variables map
395      */

396     protected Map<String,JRFillVariable> getVariablesMap()
397     {
398         return mainDataset.variablesMap;
399     }
400
401
402     /**
403      * Returns a report field.
404      *
405      * @param fieldName the field name
406      * @return the field
407      */

408     protected JRFillField getField(String fieldName)
409     {
410         return mainDataset.getFillField(fieldName);
411     }
412
413     private void initBands()
414     {
415         bands = new ArrayList<JRBand>(8 + (groups == null ? 0 : (2 * groups.length)));
416         bands.add(title);
417         bands.add(summary);
418         bands.add(pageHeader);
419         bands.add(pageFooter);
420         bands.add(lastPageFooter);
421         bands.add(columnHeader);
422         bands.add(columnFooter);
423         if (detailSection.getBands() != null)
424         {
425             bands.addAll(Arrays.asList(detailSection.getBands()));
426         }
427         bands.add(noData);
428
429         if (groups != null && groups.length > 0)
430         {
431             for (int i = 0; i < groups.length; i++)
432             {
433                 JRFillGroup group = groups[i];
434                 if (group.getGroupHeaderSection().getBands() != null)
435                 {
436                     bands.addAll(Arrays.asList(group.getGroupHeaderSection().getBands()));
437                 }
438                 if (group.getGroupFooterSection().getBands() != null)
439                 {
440                     bands.addAll(Arrays.asList(group.getGroupFooterSection().getBands()));
441                 }
442             }
443         }
444
445         initBandsNowEvaluationTimes();
446     }
447
448
449     private void initBandsNowEvaluationTimes()
450     {
451         JREvaluationTime[] groupEvaluationTimes;
452         if (groups == null)
453         {
454             groupEvaluationTimes = new JREvaluationTime[0];
455         }
456         else
457         {
458             groupEvaluationTimes = new JREvaluationTime[groups.length];
459             for (int i = 0; i < groups.length; i++)
460             {
461                 groupEvaluationTimes[i] = JREvaluationTime.getGroupEvaluationTime(groups[i].getName());
462             }
463
464             for (int i = 0; i < groups.length; i++)
465             {
466                 JRGroup group = groups[i];
467                 JRFillSection footer = (JRFillSection) group.getGroupFooterSection();
468
469                 for (int j = i; j < groupEvaluationTimes.length; ++j)
470                 {
471                     footer.addNowEvaluationTime(groupEvaluationTimes[j]);
472                 }
473             }
474         }
475
476         columnFooter.addNowEvaluationTime(JREvaluationTime.EVALUATION_TIME_COLUMN);
477
478         pageFooter.addNowEvaluationTime(JREvaluationTime.EVALUATION_TIME_COLUMN);
479         pageFooter.addNowEvaluationTime(JREvaluationTime.EVALUATION_TIME_PAGE);
480
481         summary.addNowEvaluationTimes(groupEvaluationTimes);
482         noData.addNowEvaluationTimes(groupEvaluationTimes);
483     }
484
485     protected BandReportFillerParent getBandReportParent()
486     {
487         return (BandReportFillerParent)parent;
488     }
489
490     protected List<String> getPrintTransferPropertyPrefixes()
491     {
492         return printTransferPropertyPrefixes;
493     }
494
495     /**
496      *
497      */

498     public JRStyledTextParser getStyledTextParser()
499     {
500         return styledTextParser;
501     }
502     
503     protected JRStyledTextUtil getStyledTextUtil()
504     {
505         return fillContext.getStyledTextUtil();
506     }
507
508     /**
509      * Returns the number of generated master print pages.
510      * 
511      * @return the number of generated master print pages
512      */

513     public int getCurrentPageCount()
514     {
515         return getMasterFiller().jasperPrint.getPages().size();
516     }
517     
518     @Override
519     public JRStyle getDefaultStyle()
520     {
521         return defaultStyle;
522     }
523
524     @Override
525     public StyleResolver getStyleResolver()
526     {
527         return styleResolver;
528     }
529
530     protected boolean isSubreportRunToBottom()
531     {
532         return getBandReportParent() != null && getBandReportParent().isRunToBottom();
533     }
534     
535     /**
536      *
537      */

538     public JRPrintPage getCurrentPage()
539     {
540         return printPage;
541     }
542     
543     protected int getCurrentPageContentsWidth()
544     {
545         return printPageContentsWidth;
546     }
547
548     /**
549      *
550      */

551     protected abstract void setPageHeight(int pageHeight);
552
553
554     @Override
555     @continuable
556     public JasperPrint fill(Map<String,Object> parameterValues) throws JRException
557     {
558         if (parameterValues == null)
559         {
560             parameterValues = new HashMap<String,Object>();
561         }
562
563         if (log.isDebugEnabled())
564         {
565             log.debug("Fill " + fillerId + ": filling report");
566         }
567
568         setParametersToContext(parameterValues);
569
570         fillingThread = Thread.currentThread();
571         
572         JRResourcesFillUtil.ResourcesFillContext resourcesContext = 
573             JRResourcesFillUtil.setResourcesFillContext(parameterValues);
574         
575         boolean success = false;
576         try
577         {
578             createBoundElemementMaps();
579
580             if (parent != null)
581             {
582                 getBandReportParent().registerSubfiller(this);
583             }
584
585             setParameters(parameterValues);
586
587             setBookmarkHelper();
588             
589             loadStyles();
590
591             jasperPrint.setName(name);
592             jasperPrint.setPageWidth(pageWidth);
593             jasperPrint.setPageHeight(pageHeight);
594             jasperPrint.setTopMargin(topMargin);
595             jasperPrint.setLeftMargin(leftMargin);
596             jasperPrint.setBottomMargin(bottomMargin);
597             jasperPrint.setRightMargin(rightMargin);
598             jasperPrint.setOrientation(orientation);
599
600             jasperPrint.setFormatFactoryClass(jasperReport.getFormatFactoryClass());
601             jasperPrint.setLocaleCode(JRDataUtils.getLocaleCode(getLocale()));
602             jasperPrint.setTimeZoneId(JRDataUtils.getTimeZoneId(getTimeZone()));
603
604             propertiesUtil.transferProperties(mainDataset, jasperPrint, 
605                 JasperPrint.PROPERTIES_PRINT_TRANSFER_PREFIX);
606
607             jasperPrint.setDefaultStyle(defaultStyle);
608
609             /*   */
610             if (styles != null && styles.length > 0)
611             {
612                 for (int i = 0; i < styles.length; i++)
613                 {
614                     addPrintStyle(styles[i]);
615                 }
616             }
617
618             /*   */
619             mainDataset.start();
620
621             /*   */
622             fillReport();
623             
624             mainDataset.evaluateProperties(PropertyEvaluationTimeEnum.REPORT);
625             
626             propertiesUtil.transferProperties(
627                 mainDataset, 
628                 jasperPrint, 
629                 JasperPrint.PROPERTIES_PRINT_TRANSFER_PREFIX
630                 );
631
632             // add consolidates styles as normal styles in the print object
633 //            for (Iterator it = consolidatedStyles.values().iterator(); it.hasNext();)
634 //            {
635 //                jasperPrint.addStyle((JRStyle) it.next());
636 //            }
637
638             if (log.isDebugEnabled())
639             {
640                 log.debug("Fill " + fillerId + ": ended");
641             }
642
643             success = true;
644             return jasperPrint;
645         }
646         finally
647         {
648             mainDataset.closeDatasource();
649             mainDataset.disposeParameterContributors();
650             
651             if (success && parent == null)
652             {
653                 // commit the cached data
654                 fillContext.cacheDone();
655             }
656
657             if (parent != null)
658             {
659                 getBandReportParent().unregisterSubfiller(this);
660             }
661             
662             delayedActions.dispose();
663
664             fillingThread = null;
665
666             //kill the subreport filler threads
667             abortSubfillers();
668             
669             if (parent == null)
670             {
671                 fillContext.dispose();
672             }
673
674             JRResourcesFillUtil.revertResourcesFillContext(resourcesContext);
675         }
676     }
677         
678     public void addPrintStyle(JRStyle style) throws JRException
679     {
680         jasperPrint.addStyle(style, true);
681     }
682     
683     protected static interface DefaultStyleListener
684     {
685         void defaultStyleSet(JRStyle style);
686     }
687
688     private List<DefaultStyleListener> defaultStyleListeners;
689
690     protected void addDefaultStyleListener(DefaultStyleListener listener)
691     {
692         defaultStyleListeners.add(listener);
693     }
694
695     protected void setDefaultStyle(JRStyle style)
696     {
697         defaultStyle = style;
698
699         for (Iterator<DefaultStyleListener> it = defaultStyleListeners.iterator(); it.hasNext();)
700         {
701             DefaultStyleListener listener = it.next();
702             listener.defaultStyleSet(style);
703         }
704     }
705
706     protected void loadStyles() throws JRException
707     {
708         List<JRStyle> styleList = collectStyles();
709         JRStyle reportDefaultStyle = jasperReport.getDefaultStyle();
710         if (reportDefaultStyle == null)
711         {
712             lookupExternalDefaultStyle(styleList);
713         }
714
715         List<JRStyle> includedStyles = factory.setStyles(styleList);
716         if (getBandReportParent() != null)
717         {
718             getBandReportParent().registerReportStyles(includedStyles);
719         }
720
721         styles = includedStyles.toArray(new JRStyle[includedStyles.size()]);
722
723         if (reportDefaultStyle != null)
724         {
725             setDefaultStyle(factory.getStyle(reportDefaultStyle));
726         }
727     }
728
729     public void registerReportStyles(UUID id, List<JRStyle> styles)
730     {
731         if (getBandReportParent() == null)
732         {
733             fillContext.registerReportStyles(jasperReport, id, styles);
734         }
735         else
736         {
737             String reportLocation = getBandReportParent().getReportLocation();
738             if (reportLocation != null)
739             {
740                 fillContext.registerReportStyles(reportLocation, id, styles);
741             }
742         }
743         
744     }
745
746     private static final JRStyleSetter DUMMY_STYLE_SETTER = new JRStyleSetter()
747     {
748         @Override
749         public void setStyle(JRStyle style)
750         {
751         }
752
753         @Override
754         public void setStyleNameReference(String name)
755         {
756         }
757     };
758
759     protected List<JRStyle> collectStyles() throws JRException
760     {
761         List<JRStyle> styleList = collectTemplateStyles();
762
763         JRStyle[] reportStyles = jasperReport.getStyles();
764         if (reportStyles != null)
765         {
766             styles = new JRStyle[reportStyles.length];//FIXME remove this
767
768             for (int i = 0; i < reportStyles.length; i++)
769             {
770                 JRStyle style = reportStyles[i];
771                 styleList.add(style);
772
773                 //add dummy style requester so that report styles are always included
774                 //in the final list
775                 factory.registerDelayedStyleSetter(DUMMY_STYLE_SETTER, style.getName());
776             }
777         }
778
779         return styleList;
780     }
781
782     protected void collectTemplates() throws JRException
783     {
784         templates = new ArrayList<>();
785
786         if (reportTemplates != null)
787         {
788             for (JRFillReportTemplate reportTemplate : reportTemplates)
789             {
790                 ReportTemplateSource template = reportTemplate.evaluate();
791                 if (template != null)
792                 {
793                     templates.add(template);
794                 }
795             }
796         }
797
798         Collection<JRTemplate> paramTemplates = (Collection<JRTemplate>) mainDataset.getParameterValue(
799                 JRParameter.REPORT_TEMPLATES, true);
800         if (paramTemplates != null)
801         {
802             for (JRTemplate template : paramTemplates)
803             {
804                 templates.add(ReportTemplateSource.of(template));
805             }
806         }
807     }
808
809     public List<JRTemplate> getTemplates()
810     {
811         return templates.stream().map(source -> source.getTemplate()).collect(Collectors.toList());
812     }
813     
814     protected List<JRStyle> collectTemplateStyles() throws JRException
815     {
816         collectTemplates();
817         
818         List<JRStyle> externalStyles = new ArrayList<JRStyle>();
819         HashSet<String> loadedLocations = new HashSet<String>();
820         for (ReportTemplateSource template : templates)
821         {
822             collectStyles(template, externalStyles, loadedLocations);
823         }
824         return externalStyles;
825     }
826
827     protected void collectStyles(ReportTemplateSource template, List<JRStyle> externalStyles, Set<String> loadedLocations) throws JRException
828     {
829         HashSet<String> parentLocations = new HashSet<String>();
830         collectStyles(template, externalStyles, loadedLocations, parentLocations);
831     }
832     
833     protected void collectStyles(ReportTemplateSource templateSource, List<JRStyle> externalStyles, 
834             Set<String> loadedLocations, Set<String> templateParentLocations) throws JRException
835     {
836         String templateLocation = templateSource.getTemplateResourceInfo() == null ? null
837                 : templateSource.getTemplateResourceInfo().getRepositoryResourceLocation();
838         if (templateLocation != null && !templateParentLocations.add(templateLocation))
839         {
840             throw 
841                 new JRRuntimeException(
842                     EXCEPTION_MESSAGE_KEY_CIRCULAR_DEPENDENCY_FOUND,  
843                     new Object[]{templateLocation} 
844                     );
845         }
846         
847         if (templateLocation != null && !loadedLocations.add(templateLocation))
848         {
849             //already loaded
850             return;
851         }
852         
853         collectIncludedTemplates(templateSource, externalStyles, 
854                 loadedLocations, templateParentLocations);
855
856         JRStyle[] templateStyles = templateSource.getTemplate().getStyles();
857         if (templateStyles != null)
858         {
859             for (int i = 0; i < templateStyles.length; i++)
860             {
861                 JRStyle style = templateStyles[i];
862                 String styleName = style.getName();
863                 if (styleName == null)
864                 {
865                     throw 
866                         new JRRuntimeException(
867                             EXCEPTION_MESSAGE_KEY_EXTERNAL_STYLE_NAME_NOT_SET,  
868                             (Object[])null 
869                             );
870                 }
871
872                 externalStyles.add(style);
873             }
874         }
875     }
876
877     protected void collectIncludedTemplates(ReportTemplateSource templateSource, List<JRStyle> externalStyles, 
878             Set<String> loadedLocations, Set<String> templateParentLocations) throws JRException
879     {
880         JRTemplateReference[] includedTemplates = templateSource.getTemplate().getIncludedTemplates();
881         if (includedTemplates != null && includedTemplates.length > 0)
882         {
883             RepositoryResourceContext currentContext = repositoryContext.getResourceContext();
884             String contextLocation = templateSource.getTemplateResourceInfo() == null ? null 
885                     : templateSource.getTemplateResourceInfo().getRepositoryContextLocation();
886             RepositoryResourceContext templateResourceContext = SimpleRepositoryResourceContext.of(contextLocation,
887                     currentContext == null ? null : currentContext.getDerivedContextFallback());
888             RepositoryContext templateRepositoryContext = SimpleRepositoryContext.of(repositoryContext.getJasperReportsContext(), 
889                     templateResourceContext);
890             
891             for (int i = 0; i < includedTemplates.length; i++)
892             {
893                 JRTemplateReference reference = includedTemplates[i];
894                 String location = reference.getLocation();
895                 
896                 ReportTemplateSource includedTemplate = JRFillReportTemplate.loadTemplate(
897                         location, this, templateRepositoryContext);
898                 collectStyles(includedTemplate, externalStyles, 
899                         loadedLocations, templateParentLocations);
900             }
901         }
902     }
903
904     protected void lookupExternalDefaultStyle(Collection<JRStyle> styleList)
905     {
906         JRStyle defStyle = null;
907         for (Iterator<JRStyle> it = styleList.iterator(); it.hasNext();)
908         {
909             JRStyle style = it.next();
910             if (style.isDefault())
911             {
912                 defStyle = style;
913             }
914         }
915
916         if (defStyle != null)
917         {
918             factory.registerDelayedStyleSetter(new JRStyleSetter()
919             {
920                 @Override
921                 public void setStyle(JRStyle style)
922                 {
923                     if (style.isDefault())
924                     {
925                         setDefaultStyle(style);
926                     }
927                 }
928
929                 @Override
930                 public void setStyleNameReference(String name)
931                 {
932                 }
933             }, defStyle.getName());
934         }
935     }
936
937
938     private void createBoundElemementMaps()
939     {
940         createBoundElementMaps(JREvaluationTime.EVALUATION_TIME_MASTER);
941         createBoundElementMaps(JREvaluationTime.EVALUATION_TIME_REPORT);
942         createBoundElementMaps(JREvaluationTime.EVALUATION_TIME_PAGE);
943         createBoundElementMaps(JREvaluationTime.EVALUATION_TIME_COLUMN);
944
945         if (groups != null)
946         {
947             for (int i = 0; i < groups.length; i++)
948             {
949                 createBoundElementMaps(JREvaluationTime.getGroupEvaluationTime(groups[i].getName()));
950             }
951         }
952
953         for (Iterator<JRBand> i = bands.iterator(); i.hasNext();)
954         {
955             JRFillBand band = (JRFillBand) i.next();
956             createBoundElementMaps(JREvaluationTime.getBandEvaluationTime(band));
957         }
958     }
959
960
961     private void abortSubfillers()
962     {
963         if (subfillers != null && !subfillers.isEmpty())
964         {
965             for (JRBaseFiller subfiller : subfillers.values())
966             {
967                 if (subfiller.getBandReportParent() != null)
968                 {
969                     if (log.isDebugEnabled())
970                     {
971                         log.debug("Fill " + fillerId + ": Aborting subfiller " + subfiller.fillerId);
972                     }
973                     
974                     subfiller.getBandReportParent().abortSubfiller(subfiller);
975                 }
976             }
977         }
978     }
979
980
981     /**
982      *
983      */

984     @continuable
985     protected abstract void fillReport() throws JRException;
986
987     @Override
988     protected void ignorePaginationSet(Map<String, Object> parameterValues)
989     {
990         if (ignorePagination)
991         {
992             isTitleNewPage = false;
993             isSummaryNewPage = false;
994             if (groups != null)
995             {
996                 for (int i = 0; i < groups.length; i++)
997                 {
998                     groups[i].setStartNewPage(false);
999                     groups[i].setResetPageNumber(false);
1000                     groups[i].setStartNewColumn(false);
1001                 }
1002             }
1003             
1004             if (isMasterReport() || !getBandReportParent().isParentPagination())//subreport page height is already set by band master
1005             {
1006                 int maxPageHeight = getMaxPageHeight(parameterValues);
1007                 setPageHeight(maxPageHeight);
1008             }
1009         }
1010         
1011         maxPageWidth = getMaxPageWidth(parameterValues);
1012     }
1013     
1014     protected int getMaxPageHeight(Map<String,Object> parameterValues)
1015     {
1016         Integer maxPageHeightParam = (Integer) parameterValues.get(JRParameter.MAX_PAGE_HEIGHT);
1017         int maxPageHeight = maxPageHeightParam != null ? maxPageHeightParam : PAGE_HEIGHT_PAGINATION_IGNORED;
1018         if (maxPageHeight < pageHeight)
1019         {
1020             if (log.isDebugEnabled())
1021             {
1022                 log.debug("max page height " + maxPageHeight + " smaller than report page height " + pageHeight);
1023             }
1024             
1025             //use the report page height
1026             maxPageHeight = pageHeight;
1027         }
1028         
1029         if (log.isDebugEnabled())
1030         {
1031             log.debug("max page height is " + maxPageHeight);
1032         }
1033         
1034         return maxPageHeight;
1035     }
1036     
1037     protected int getMaxPageWidth(Map<String,Object> parameterValues)
1038     {
1039         Integer maxPageWidthParam = (Integer) parameterValues.get(JRParameter.MAX_PAGE_WIDTH);
1040         int maxPageWidth = maxPageWidthParam != null ?  maxPageWidthParam : PAGE_WIDTH_IGNORED;
1041         
1042         if (maxPageWidth < pageWidth)
1043         {
1044             if (log.isDebugEnabled())
1045             {
1046                 log.debug("max page width " + maxPageWidth + " smaller than report page width " + pageWidth);
1047             }
1048             
1049             //use the report page width
1050             maxPageWidth = pageWidth;
1051         }
1052         
1053         if (log.isDebugEnabled())
1054         {
1055             log.debug("max page width is " + maxPageWidth);
1056         }
1057         
1058         return maxPageWidth;
1059     }
1060     
1061     protected void recordUsedPageHeight(int pageHeight)
1062     {
1063         if (pageHeight > usedPageHeight)
1064         {
1065             usedPageHeight = pageHeight;
1066         }
1067     }
1068
1069
1070     /**
1071      * Returns the report resource bundle.
1072      *
1073      * @return the report resource bundle
1074      */

1075     protected ResourceBundle getResourceBundle()
1076     {
1077         return mainDataset.resourceBundle;
1078     }
1079
1080
1081     /**
1082      *
1083      */

1084     protected WhenNoDataTypeEnum getWhenNoDataType()
1085     {
1086         WhenNoDataTypeEnum result = whenNoDataType;
1087         
1088         if (result == null)
1089         {
1090             result = 
1091                 WhenNoDataTypeEnum.getByName(
1092                     propertiesUtil.getProperty(mainDataset, JRReport.CONFIG_PROPERTY_WHEN_NO_DATA_TYPE)
1093                     );
1094         }
1095         
1096         return result;
1097     }
1098
1099
1100     /**
1101      *
1102      */

1103     public Format getDateFormat(String pattern)
1104     {
1105         return getDateFormat(pattern, null);
1106     }
1107     
1108     protected Format getDateFormat(String pattern, TimeZone timeZone)
1109     {
1110         Locale lc = getLocale();
1111         TimeZone tz = timeZone == null ? getTimeZone() : timeZone;// default to filler timezone
1112         String key = pattern + "|" + JRDataUtils.getLocaleCode(lc) + "|" + JRDataUtils.getTimeZoneId(tz);
1113         Format format = dateFormatCache.get(key);
1114         if (format == null)
1115         {
1116             format = getFormatFactory().createDateFormat(pattern, lc, tz);
1117             if (format != null)
1118             {
1119                 dateFormatCache.put(key, format);
1120             }
1121         }
1122         return format;
1123     }
1124
1125
1126     /**
1127      *
1128      */

1129     public Format getNumberFormat(String pattern)
1130     {
1131         Locale lc = getLocale();
1132         String key = pattern + "|" + JRDataUtils.getLocaleCode(lc);
1133         Format format = numberFormatCache.get(key);
1134         if (format == null)
1135         {
1136             format = getFormatFactory().createNumberFormat(pattern, lc);
1137             if (format != null)
1138             {
1139                 numberFormatCache.put(key, format);
1140             }
1141         }
1142         return format;
1143     }
1144
1145
1146     protected boolean hasMasterFormatFactory()
1147     {
1148         return
1149             !isSubreport()
1150             || getFormatFactory().getClass().getName().equals(
1151                 fillContext.getMasterFormatFactory().getClass().getName()
1152                 );
1153     }
1154
1155
1156     protected boolean hasMasterLocale()
1157     {
1158         return !isSubreport() || getLocale().equals(fillContext.getMasterLocale());
1159     }
1160
1161
1162     protected boolean hasMasterTimeZone()
1163     {
1164         return !isSubreport() || getTimeZone().equals(fillContext.getMasterTimeZone());
1165     }
1166
1167
1168     /**
1169      * Sets a parameter's value.
1170      *
1171      * @param parameterName the parameter name
1172      * @param value the value
1173      * @throws JRException
1174      */

1175     protected void setParameter(String parameterName, Object value) throws JRException
1176     {
1177         mainDataset.setParameter(parameterName, value);
1178     }
1179
1180
1181     /**
1182      * Sets a parameter's value.
1183      *
1184      * @param parameter the parameter
1185      * @param value the value
1186      * @throws JRException
1187      */

1188     protected void setParameter(JRFillParameter parameter, Object value) throws JRException
1189     {
1190         mainDataset.setParameter(parameter, value);
1191     }
1192
1193     /**
1194      *
1195      */

1196     protected boolean next() throws JRException
1197     {
1198         isCrtRecordOnPage = false;
1199         isCrtRecordOnColumn = false;
1200         
1201         return mainDataset.next();
1202     }
1203
1204     /**
1205      * Resolves elements which are to be evaluated at report level.
1206      */

1207     protected void resolveReportBoundElements() throws JRException
1208     {
1209         resolveBoundElements(JREvaluationTime.EVALUATION_TIME_REPORT, JRExpression.EVALUATION_DEFAULT);
1210     }
1211
1212     /**
1213      * Resolves elements which are to be evaluated at page level.
1214      *
1215      * @param evaluation
1216      *            the evaluation type
1217      */

1218     protected void resolvePageBoundElements(byte evaluation) throws JRException
1219     {
1220         resolveBoundElements(JREvaluationTime.EVALUATION_TIME_PAGE, evaluation);
1221     }
1222
1223     /**
1224      * Resolves elements which are to be evaluated at column level.
1225      *
1226      * @param evaluation
1227      *            the evaluation type
1228      */

1229     protected void resolveColumnBoundElements(byte evaluation) throws JRException
1230     {
1231         resolveBoundElements(JREvaluationTime.EVALUATION_TIME_COLUMN, evaluation);
1232     }
1233
1234     /**
1235      * Resolves elements which are to be evaluated at group level.
1236      *
1237      * @param evaluation
1238      *            the evaluation type
1239      * @param isFinal
1240      */

1241     protected void resolveGroupBoundElements(byte evaluation, boolean isFinal) throws JRException
1242     {
1243         if (groups != null && groups.length > 0)
1244         {
1245             for (int i = 0; i < groups.length; i++)
1246             {
1247                 JRFillGroup group = groups[i];
1248
1249                 if ((group.hasChanged() && group.isFooterPrinted()) || isFinal)
1250                 {
1251                     String groupName = group.getName();
1252
1253                     resolveBoundElements(JREvaluationTime.getGroupEvaluationTime(groupName), evaluation);
1254                 }
1255             }
1256         }
1257     }
1258
1259     protected JRPrintPage newPage()
1260     {
1261         JRPrintPage page;
1262
1263         if (fillContext.isUsingVirtualizer())
1264         {
1265             JRVirtualPrintPage virtualPage = new JRVirtualPrintPage(virtualizationContext);
1266             page = virtualPage;
1267         }
1268         else
1269         {
1270             page = new JRBasePrintPage();
1271         }
1272
1273         return page;
1274     }
1275
1276     /**
1277      * Resloves elements which are to be evaluated at band level.
1278      *
1279      * @param band
1280      *            the band
1281      * @param evaluation
1282      *            the evaluation type
1283      * @throws JRException
1284      */

1285     protected void resolveBandBoundElements(JRFillBand band, byte evaluation) throws JRException
1286     {
1287         resolveBoundElements(JREvaluationTime.getBandEvaluationTime(band), evaluation);
1288     }
1289
1290
1291     protected void registerSubfiller(JRBaseFiller subfiller)
1292     {
1293         if (subfillers == null)
1294         {
1295             subfillers = new ConcurrentHashMap<Integer, JRBaseFiller>(16, 0.75f, 1);
1296         }
1297
1298         subfillers.put(subfiller.fillerId, subfiller);
1299     }
1300
1301     protected void unregisterSubfiller(JRBaseFiller subfiller)
1302     {
1303         if (subfillers != null)
1304         {
1305             subfillers.remove(subfiller.fillerId);
1306         }
1307     }
1308
1309
1310     /**
1311      *
1312      */

1313     protected void fillBand(JRPrintBand band)
1314     {
1315         if (isReorderBandElements())
1316         {
1317             List<JRPrintElement> elements = new ArrayList<JRPrintElement>();
1318             for(Iterator<JRPrintElement> it = band.iterateElements(); it.hasNext();)
1319             {
1320                 JRPrintElement element = it.next();
1321                 element.setX(element.getX() + offsetX);
1322                 element.setY(element.getY() + offsetY);
1323                 elements.add(element);
1324             }
1325             Collections.sort(elements, new JRYXComparator());//FIXME make singleton comparator; same for older comparator
1326             for (JRPrintElement element : elements)
1327             {
1328                 printPage.addElement(element);
1329                 recordUsedWidth(element);
1330             }
1331         }
1332         else
1333         {
1334             for(Iterator<JRPrintElement> it = band.iterateElements(); it.hasNext();)
1335             {
1336                 JRPrintElement element = it.next();
1337                 element.setX(element.getX() + offsetX);
1338                 element.setY(element.getY() + offsetY);
1339                 printPage.addElement(element);
1340                 recordUsedWidth(element);
1341             }
1342         }
1343         
1344         int contentsWidth = band.getContentsWidth();
1345         if (offsetX + contentsWidth + rightMargin > printPageContentsWidth)
1346         {
1347             printPageContentsWidth = offsetX + contentsWidth + rightMargin;
1348         }
1349     }
1350     
1351     protected void recordUsedWidth(JRPrintElement element)
1352     {
1353         recordUsedPageWidth(element.getX() + element.getWidth() + rightMargin);
1354     }
1355
1356
1357     protected void addPage(JRPrintPage page)
1358     {
1359         if (!isSubreport())
1360         {
1361             if (log.isDebugEnabled())
1362             {
1363                 log.debug("Fill " + fillerId + ": adding page " + (jasperPrint.getPages().size() + 1));
1364             }
1365
1366             addLastPageBookmarks();
1367             
1368             // notify that the previous page was generated
1369             int pageCount = jasperPrint.getPages().size();
1370             if (pageCount > 0 && fillListener != null)
1371             {
1372                 fillListener.pageGenerated(jasperPrint, pageCount - 1);
1373             }
1374
1375             jasperPrint.addPage(page);
1376             fillContext.setPrintPage(page);
1377         }
1378     }
1379     
1380     @continuable
1381     protected void addPageToParent(final boolean ended) throws JRException
1382     {
1383         if (printPage == null)
1384         {
1385             return;
1386         }
1387         
1388         FillerPageAddedEvent pageAdded = new FillerPageAddedEvent()
1389         {
1390             @Override
1391             public JasperPrint getJasperPrint()
1392             {
1393                 return jasperPrint;
1394             }
1395             
1396             @Override
1397             public JRPrintPage getPage()
1398             {
1399                 return printPage;
1400             }
1401
1402             @Override
1403             public boolean hasReportEnded()
1404             {
1405                 return ended;
1406             }
1407
1408             @Override
1409             public int getPageStretchHeight()
1410             {
1411                 return offsetY + bottomMargin;
1412             }
1413
1414             @Override
1415             public int getPageIndex()
1416             {
1417                 Number pageNumber = (Number) calculator.getPageNumber().getValue();
1418                 if (pageNumber == null)//this happens when whenNoDataType="BlankPage" //FIXMEBOOK maybe we should set the variable?
1419                 {
1420                     return 0;
1421                 }
1422                 return pageNumber.intValue() - 1;
1423             }
1424
1425             @Override
1426             public JRBaseFiller getFiller()
1427             {
1428                 return JRBaseFiller.this;
1429             }
1430
1431             @Override
1432             public DelayedFillActions getDelayedActions()
1433             {
1434                 return delayedActions;
1435             }
1436         };
1437         
1438         //FIXMEBOOK use a fill listener instead of this?
1439         getBandReportParent().addPage(pageAdded);
1440     }
1441
1442     protected void setMasterPageVariables(int currentPageIndex, int totalPages)
1443     {
1444         JRFillVariable masterCurrentPage = getVariable(JRVariable.MASTER_CURRENT_PAGE);
1445         if (masterCurrentPage != null)
1446         {
1447             masterCurrentPage.setValue(currentPageIndex + 1);
1448         }
1449         
1450         JRFillVariable masterTotalPages = getVariable(JRVariable.MASTER_TOTAL_PAGES);
1451         if (masterTotalPages != null)
1452         {
1453             masterTotalPages.setValue(totalPages);
1454         }
1455     }
1456
1457     protected WhenResourceMissingTypeEnum getWhenResourceMissingType()
1458     {
1459         return mainDataset.whenResourceMissingType;
1460     }
1461
1462
1463     protected boolean isBandOverFlowAllowed()
1464     {
1465         return bandOverFlowAllowed;
1466     }
1467
1468
1469     protected void setBandOverFlowAllowed(boolean splittableBand)
1470     {
1471         this.bandOverFlowAllowed = splittableBand;
1472     }
1473
1474
1475     protected boolean isReorderBandElements()
1476     {
1477         return isReorderBandElements;
1478     }
1479
1480
1481     protected void setReorderBandElements(boolean isReorderBandElements)
1482     {
1483         this.isReorderBandElements = isReorderBandElements;
1484     }
1485
1486
1487     protected int getMasterColumnCount()
1488     {
1489         FillerParent fillerParent = parent;
1490         int colCount = 1;
1491
1492         while (fillerParent != null)
1493         {
1494             BaseReportFiller filler = fillerParent.getFiller();
1495             if (filler instanceof JRBaseFiller)//FIXMEBOOK
1496             {
1497                 colCount *= ((JRBaseFiller) filler).columnCount;
1498             }
1499             fillerParent = filler.parent;
1500         }
1501
1502         return colCount;
1503     }
1504
1505     /**
1506      * Returns the top-level (master) filler object.
1507      * 
1508      * @return the master filler object
1509      */

1510     public BaseReportFiller getMasterFiller()
1511     {
1512         BaseReportFiller filler = this;
1513         while (filler.parent != null)
1514         {
1515             filler = filler.parent.getFiller();
1516         }
1517         return filler;
1518     }
1519
1520
1521     protected void addBoundElement(JRFillElement element, JRPrintElement printElement, 
1522             EvaluationTimeEnum evaluationType, String groupName, JRFillBand band)
1523     {
1524         JRFillGroup group = groupName == null ? null : getGroup(groupName);
1525         addBoundElement(element, printElement, evaluationType, group, band);
1526     }
1527
1528     protected void addBoundElement(JRFillElement element, JRPrintElement printElement, EvaluationTimeEnum evaluationType, JRGroup group, JRFillBand band)
1529     {
1530         JREvaluationTime evaluationTime = JREvaluationTime.getEvaluationTime(evaluationType, group, band);
1531         addBoundElement(element, printElement, evaluationTime);
1532     }
1533
1534     protected void addBoundElement(JRFillElement element, JRPrintElement printElement, JREvaluationTime evaluationTime)
1535     {
1536         // the key contains the page and its index; the index is only stored so that we have it in resolveBoundElements
1537         int pageIndex = currentPageIndex();
1538         FillPageKey pageKey = new FillPageKey(printPage, pageIndex);
1539         
1540         addBoundElement(element, printElement, evaluationTime, pageKey);
1541     }
1542
1543     protected int currentPageIndex()
1544     {
1545         int pageIndex = ((Number) calculator.getPageNumber().getValue()).intValue() - 1;
1546         return pageIndex;
1547     }
1548
1549     protected void subreportPageFilled(JRPrintPage subreportPage)
1550     {
1551         FillPageKey subreportKey = new FillPageKey(subreportPage);
1552         
1553         // this method is only called when the parent is a band report
1554         JRBaseFiller parentFiller = (JRBaseFiller) parent.getFiller();
1555         //FIXMEBOOK the index is only correct when the parent is the master, see fillListener.pageUpdated
1556         int parentPageIndex = parentFiller.getJasperPrint().getPages().size() - 1;
1557         FillPageKey parentKey = new FillPageKey(parentFiller.printPage, parentPageIndex);
1558         
1559         // move all delayed elements from the subreport page to the master page
1560         moveBoundActions(subreportKey, parentKey);
1561         // move all master evaluations to the parent
1562         parent.getFiller().delayedActions.moveMasterEvaluations(delayedActions, parentKey);
1563     }
1564
1565     protected void moveBoundActions(FillPageKey subreportKey, FillPageKey parentKey)
1566     {
1567         delayedActions.moveActions(subreportKey, parentKey);
1568         
1569         if (subfillers != null)//recursive
1570         {
1571             for (JRBaseFiller subfiller : subfillers.values())
1572             {
1573                 subfiller.moveBoundActions(subreportKey, parentKey);
1574             }
1575         }
1576     }
1577
1578     @Override
1579     public boolean isPageFinal(int pageIdx)
1580     {
1581         JRPrintPage page = jasperPrint.getPages().get(pageIdx);
1582         return !hasBoundActions(page);
1583     }
1584
1585     public boolean isPageFinal(JRPrintPage page)
1586     {
1587         return !hasBoundActions(page);
1588     }
1589     
1590     protected boolean hasBoundActions(JRPrintPage page)
1591     {
1592         boolean hasActions = delayedActions.hasDelayedActions(page);
1593         if (hasActions)
1594         {
1595             return true;
1596         }
1597         
1598         if (subfillers != null)
1599         {
1600             for (JRBaseFiller subfiller : subfillers.values())
1601             {
1602                 // recursive
1603                 if (subfiller.hasBoundActions(page))
1604                 {
1605                     return true;
1606                 }
1607             }
1608         }
1609
1610         return false;
1611     }
1612
1613     protected JRFillGroup getGroup(String groupName)
1614     {
1615         JRFillGroup group = null;
1616         if (groups != null)
1617         {
1618             for (int i = 0; i < groups.length; i++)
1619             {
1620                 if (groups[i].getName().equals(groupName))
1621                 {
1622                     group = groups[i];
1623                     break;
1624                 }
1625             }
1626         }
1627         
1628         if (group == null)
1629         {
1630             throw 
1631             new JRRuntimeException(
1632                 EXCEPTION_MESSAGE_KEY_NO_SUCH_GROUP,  
1633                 new Object[]{groupName} 
1634                 );
1635         }
1636         return group;
1637     }
1638
1639
1640     /**
1641      *
1642      */

1643     protected JRFillGroup getKeepTogetherGroup()
1644     {
1645         Integer keepTogetherGroupLevel = null;
1646
1647         if (groups != null)
1648         {
1649             // check to see if there is any group that needs to be kept together or does not have the required min details
1650             for (int i = 0; i <  groups.length; i++)
1651             {
1652                 JRFillGroup group = groups[i];
1653                 if (
1654                     group.getKeepTogetherElementRange() != null
1655                     && (group.isKeepTogether() || !group.hasMinDetails())
1656                     )
1657                 {
1658                     keepTogetherGroupLevel = i;
1659                     break;
1660                 }
1661             }
1662         }
1663         
1664         if (
1665             keepTogetherGroupLevel != null
1666             || orphanGroupFooterDetailElementRange != null
1667             )
1668         {
1669             // if there was a group that was going to be moved, either because it had keepTogether or less than required min details,
1670             // or if there is an orphan to be moved, we need to check if the outer groups still meet their required min details or need 
1671             // to be moved themselves, possibly instead of the inner group
1672             
1673             int detailsToMove = Math.max(
1674                 keepTogetherGroupLevel == null ? 0 : groups[keepTogetherGroupLevel].getDetailsCount(),
1675                 orphanGroupFooterDetailElementRange == null ? 0 : 1
1676                 );
1677             
1678             int lcMaxGrpIdx = keepTogetherGroupLevel == null ? groups.length : keepTogetherGroupLevel;
1679             
1680             for (int i = 0; i <  lcMaxGrpIdx; i++)
1681             {
1682                 JRFillGroup group = groups[i];
1683                 if (
1684                     group.getKeepTogetherElementRange() != null
1685                     && !group.hasMinDetails(detailsToMove)
1686                     )
1687                 {
1688                     keepTogetherGroupLevel = i;
1689                     break;
1690                 }
1691             }
1692         }
1693         
1694         return keepTogetherGroupLevel == null ? null : groups[keepTogetherGroupLevel];
1695     }
1696
1697
1698 //    protected JRStyle getConsolidatedStyle(String consolidatedStyleName)
1699 //    {
1700 //        return (JRStyle) consolidatedStyles.get(consolidatedStyleName);
1701 //    }
1702 //
1703 //
1704 //    protected void putConsolidatedStyle(JRStyle consolidatedStyle)
1705 //    {
1706 //        consolidatedStyles.put(consolidatedStyle.getName(), consolidatedStyle);
1707 //    }
1708
1709
1710     protected void createReportTemplates(JRFillObjectFactory factory)
1711     {
1712         JRReportTemplate[] templates = jasperReport.getTemplates();
1713         if (templates != null)
1714         {
1715             reportTemplates = new JRFillReportTemplate[templates.length];
1716
1717             for (int i = 0; i < templates.length; i++)
1718             {
1719                 JRReportTemplate template = templates[i];
1720                 reportTemplates[i] = factory.getReportTemplate(template);
1721             }
1722         }
1723     }
1724
1725
1726     protected int getFillerId()
1727     {
1728         return fillerId;
1729     }
1730
1731     protected PrintElementOriginator assignElementId(JRFillElement fillElement)
1732     {
1733         int id = getFillContext().generateFillElementId();
1734         DefaultPrintElementOriginator originator = new DefaultPrintElementOriginator(id);
1735         return originator;
1736     }
1737 }
1738