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 java.io.File;
27 import java.io.InputStream;
28 import java.net.URL;
29 import java.sql.Connection;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38 import org.apache.commons.javaflow.api.continuable;
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 import net.sf.jasperreports.annotations.properties.Property;
43 import net.sf.jasperreports.annotations.properties.PropertyScope;
44 import net.sf.jasperreports.data.cache.DataCacheHandler;
45 import net.sf.jasperreports.engine.CommonReturnValue;
46 import net.sf.jasperreports.engine.JRDataSource;
47 import net.sf.jasperreports.engine.JRDatasetParameter;
48 import net.sf.jasperreports.engine.JRException;
49 import net.sf.jasperreports.engine.JRExpression;
50 import net.sf.jasperreports.engine.JRExpressionCollector;
51 import net.sf.jasperreports.engine.JRParameter;
52 import net.sf.jasperreports.engine.JRPrintElement;
53 import net.sf.jasperreports.engine.JRPrintPage;
54 import net.sf.jasperreports.engine.JRPrintRectangle;
55 import net.sf.jasperreports.engine.JRPropertiesUtil;
56 import net.sf.jasperreports.engine.JRRewindableDataSource;
57 import net.sf.jasperreports.engine.JRRuntimeException;
58 import net.sf.jasperreports.engine.JRScriptlet;
59 import net.sf.jasperreports.engine.JRStyle;
60 import net.sf.jasperreports.engine.JRSubreport;
61 import net.sf.jasperreports.engine.JRSubreportParameter;
62 import net.sf.jasperreports.engine.JRSubreportReturnValue;
63 import net.sf.jasperreports.engine.JRVariable;
64 import net.sf.jasperreports.engine.JRVisitor;
65 import net.sf.jasperreports.engine.JasperCompileManager;
66 import net.sf.jasperreports.engine.JasperReport;
67 import net.sf.jasperreports.engine.ReportContext;
68 import net.sf.jasperreports.engine.VariableReturnValue;
69 import net.sf.jasperreports.engine.base.JRVirtualPrintPage;
70 import net.sf.jasperreports.engine.design.JRValidationException;
71 import net.sf.jasperreports.engine.design.JRValidationFault;
72 import net.sf.jasperreports.engine.design.JRVerifier;
73 import net.sf.jasperreports.engine.type.ModeEnum;
74 import net.sf.jasperreports.engine.type.OverflowType;
75 import net.sf.jasperreports.engine.type.SectionTypeEnum;
76 import net.sf.jasperreports.engine.util.JRLoader;
77 import net.sf.jasperreports.engine.util.JRSingletonCache;
78 import net.sf.jasperreports.properties.PropertyConstants;
79 import net.sf.jasperreports.repo.RepositoryResourceContext;
80 import net.sf.jasperreports.repo.RepositoryUtil;
81 import net.sf.jasperreports.repo.ResourceInfo;
82 import net.sf.jasperreports.repo.ResourcePathKey;
83 import net.sf.jasperreports.repo.SimpleRepositoryResourceContext;
84
85
86 /**
87  * @author Teodor Danciu (teodord@users.sourceforge.net)
88  */

89 public class JRFillSubreport extends JRFillElement implements JRSubreport
90 {
91
92     private static final Log log = LogFactory.getLog(JRFillSubreport.class);
93     
94     public static final String EXCEPTION_MESSAGE_KEY_PROPERTY_NOT_SET = "fill.subreport.property.not.set";
95     public static final String EXCEPTION_MESSAGE_KEY_NO_REWINDABLE_DATA_SOURCE = "fill.subreport.no.rewindable.data.source";
96     public static final String EXCEPTION_MESSAGE_KEY_UNSUPPORTED_SECTION_TYPE = "fill.subreport.unsupported.section.type";
97     public static final String EXCEPTION_MESSAGE_KEY_UNKNOWN_SOURCE_CLASS = "fill.subreport.unknown.source.class";
98             
99     /**
100      * Property used to specify when rectangle elements should be generated  for subreports during the report filling. 
101      * If the property value is <code>always</code>, rectangle elements will be always generated, otherwise they will 
102      * be created only if the subreport element is not transparent or it has properties
103      */

104     @Property(
105             category = PropertyConstants.CATEGORY_FILL,
106             scopes = {PropertyScope.CONTEXT, PropertyScope.DATASET, PropertyScope.ELEMENT},
107             sinceVersion = PropertyConstants.VERSION_6_0_3
108             )
109     public static final String PROPERTY_SUBREPORT_GENERATE_RECTANGLE = 
110             JRPropertiesUtil.PROPERTY_PREFIX + "subreport.generate.rectangle";
111     
112     public static final String SUBREPORT_GENERATE_RECTANGLE_ALWAYS = "always";
113     
114     private static final JRSingletonCache<JRSubreportRunnerFactory> runnerFactoryCache = 
115             new JRSingletonCache<JRSubreportRunnerFactory>(JRSubreportRunnerFactory.class);
116
117     /**
118      *
119      */

120     private Map<String, Object> parameterValues;
121     private JRSubreportParameter[] parameters;
122     private FillDatasetPosition datasetPosition;
123     private boolean cacheIncluded;
124     private Connection connection;
125     private JRDataSource dataSource;
126     private JasperReportSource jasperReportSource;
127     private Object source;
128
129     private Map<JasperReport,JREvaluator> loadedEvaluators;
130     
131     /**
132      * Values to be copied from the subreport.
133      */

134     private FillReturnValues returnValues;
135
136     private FillReturnValues.SourceContext returnValuesContext = new AbstractVariableReturnValueSourceContext() 
137     {
138         @Override
139         public Object getValue(CommonReturnValue returnValue) {
140             return subreportFiller.getVariableValue(((VariableReturnValue)returnValue).getFromVariable());
141         }
142
143         @Override
144         public JRFillVariable getToVariable(String name) {
145             return expressionEvaluator.getFillDataset().getVariable(name);
146         }
147
148         @Override
149         public JRVariable getFromVariable(String name) {
150             return subreportFiller.getVariable(name);
151         }
152     };
153     
154     /**
155      *
156      */

157     protected JRBaseFiller subreportFiller;
158     protected FillerSubreportParent subFillerParent;
159     protected JRPrintPage printPage;
160     private int printPageContentsWidth;
161
162     private JRSubreportRunner runner;
163     
164     /**
165      * Set of checked reports.
166      */

167     private Set<JasperReport> checkedReports;
168
169     private final String defaultGenerateRectangle;
170     private final boolean dynamicGenerateRectangle;
171
172
173     /**
174      *
175      */

176     protected JRFillSubreport(
177         JRBaseFiller filler,
178         JRSubreport subreport, 
179         JRFillObjectFactory factory
180         )
181     {
182         super(filler, subreport, factory);
183
184         parameters = subreport.getParameters();
185         returnValues = new FillReturnValues(subreport.getReturnValues(), factory, filler);
186         
187         loadedEvaluators = new HashMap<JasperReport,JREvaluator>();
188         checkedReports = new HashSet<JasperReport>();
189         
190         this.defaultGenerateRectangle = filler.getPropertiesUtil().getProperty( 
191             PROPERTY_SUBREPORT_GENERATE_RECTANGLE, subreport, filler.getJasperReport()); // property expression does not work, 
192             // but even if we would call filler.getMainDataset(), it would be too early as it is null here for subreport elements placed in group bands
193         this.dynamicGenerateRectangle = hasDynamicProperty(PROPERTY_SUBREPORT_GENERATE_RECTANGLE);
194     }
195
196     protected JRFillSubreport(JRFillSubreport subreport, JRFillCloneFactory factory)
197     {
198         super(subreport, factory);
199         
200         parameters = subreport.parameters;
201         returnValues = new FillReturnValues(subreport.returnValues, factory);
202         returnValuesContext = subreport.returnValuesContext;//FIXMERETURN this was missing; really need it?
203         
204         loadedEvaluators = new HashMap<JasperReport,JREvaluator>();// not sharing evaluators between clones
205         checkedReports = subreport.checkedReports;
206         
207         defaultGenerateRectangle = subreport.defaultGenerateRectangle;
208         dynamicGenerateRectangle = subreport.dynamicGenerateRectangle;
209     }
210
211     @Override
212     protected void setBand(JRFillBand band)
213     {
214         super.setBand(band);
215         
216         returnValues.setBand(band);
217     }
218
219
220     @Override
221     public ModeEnum getModeValue()
222     {
223         return getStyleResolver().getMode(this, ModeEnum.TRANSPARENT);
224     }
225
226     /**
227      *
228      */

229     public boolean usingCache()
230     {
231         Boolean isUsingCache = getUsingCache();
232         if (isUsingCache == null)
233         {
234             return source instanceof String;
235         }
236         return isUsingCache;
237     }
238         
239     @Override
240     public Boolean isRunToBottom()
241     {
242         return ((JRSubreport) parent).isRunToBottom();
243     }
244
245     @Override
246     public void setRunToBottom(Boolean runToBottom)
247     {
248         throw new UnsupportedOperationException();
249     }
250
251     @Override
252     public OverflowType getOverflowType()
253     {
254         return ((JRSubreport) parent).getOverflowType();
255     }
256
257     @Override
258     public void setOverflowType(OverflowType overflowType)
259     {
260         throw new UnsupportedOperationException();
261     }
262         
263     @Override
264     public JRExpression getParametersMapExpression()
265     {
266         return ((JRSubreport)parent).getParametersMapExpression();
267     }
268
269     @Override
270     public JRSubreportParameter[] getParameters()
271     {
272         return parameters;
273     }
274
275     @Override
276     public JRExpression getConnectionExpression()
277     {
278         return ((JRSubreport)parent).getConnectionExpression();
279     }
280
281     @Override
282     public JRExpression getDataSourceExpression()
283     {
284         return ((JRSubreport)parent).getDataSourceExpression();
285     }
286
287     @Override
288     public JRExpression getExpression()
289     {
290         return ((JRSubreport)parent).getExpression();
291     }
292
293     /**
294      *
295      */

296     protected JRTemplateRectangle getJRTemplateRectangle()
297     {
298         return (JRTemplateRectangle) getElementTemplate();
299     }
300
301
302     @Override
303     protected JRTemplateElement createElementTemplate()
304     {
305         return new JRTemplateRectangle(getElementOrigin(), 
306                 filler.getJasperPrint().getDefaultStyleProvider(), this);
307     }
308
309
310     /**
311      *
312      */

313     protected Collection<JRPrintElement> getPrintElements()
314     {
315         Collection<JRPrintElement> printElements = null;
316         
317         if (printPage != null)
318         {
319             printElements = printPage.getElements();
320             //FIXME lucianc immediately dispose the page if virtualized
321         }
322         
323         return printElements;
324     }
325
326     protected int getPrintContentsWidth()
327     {
328         return printPageContentsWidth;
329     }
330     
331     public void subreportPageFilled()
332     {
333         if (printPage != null)
334         {
335             if (subreportFiller.delayedActions.hasMasterDelayedActions(printPage))
336             {
337                 // if there are master delayed evaluations, the evaluator needs to keep the current variables and cannot be reused
338                 evictReportEvaluator();
339             }
340
341             subreportFiller.subreportPageFilled(printPage);
342         }
343     }
344
345
346     @Override
347     protected void evaluate(
348         byte evaluation
349         ) throws JRException
350     {
351         reset();
352         
353         evaluatePrintWhenExpression(evaluation);
354
355         if (isPrintWhenExpressionNull() || isPrintWhenTrue())
356         {
357             evaluateSubreport(evaluation);
358         }
359     }
360
361     protected JasperReportSource evaluateReportSource(byte evaluation) throws JRException
362     {
363         JRExpression expression = getExpression();
364         source = evaluateExpression(expression, evaluation);
365         return getReportSource(source, getUsingCache(), filler);
366     }
367     
368     public static JasperReportSource getReportSource(Object source, Boolean isUsingCache,
369             BaseReportFiller filler) throws JRException
370     {
371         JasperReportSource report = null;
372         if (source != null// FIXME put some default broken image like in browsers
373         {
374             if (isUsingCache == null)
375             {
376                 isUsingCache = source instanceof String;
377             }
378             
379             Object cacheKey = source;
380             if (source instanceof String)
381             {
382                 cacheKey = ResourcePathKey.inContext(filler.getRepositoryContext(), (String) source);
383             }
384             
385             if (isUsingCache && filler.fillContext.hasLoadedSubreport(cacheKey))
386             {
387                 report = filler.fillContext.getLoadedSubreport(cacheKey);
388             }
389             else
390             {
391                 if (source instanceof String)
392                 {
393                     RepositoryUtil repository = RepositoryUtil.getInstance(filler.getRepositoryContext());
394                     ResourceInfo resourceInfo = repository.getResourceInfo((String) source);
395                     if (resourceInfo == null)
396                     {
397                         report = loadReportSource(source, null, filler);
398                     }
399                     else
400                     {
401                         String reportLocation = resourceInfo.getRepositoryResourceLocation();
402                         String contextLocation = resourceInfo.getRepositoryContextLocation();
403                         if (log.isDebugEnabled())
404                         {
405                             log.debug("subreport source " + source + " resolved to " + reportLocation
406                                     + ", context " + contextLocation);
407                         }
408                         
409                         ResourcePathKey absolutePathKey = ResourcePathKey.absolute(reportLocation);
410                         if (isUsingCache && filler.fillContext.hasLoadedSubreport(absolutePathKey))
411                         {
412                             report = filler.fillContext.getLoadedSubreport(absolutePathKey);
413                         }
414                         else
415                         {
416                             report = loadReportSource(reportLocation, contextLocation, filler);
417                             if (isUsingCache)
418                             {
419                                 filler.fillContext.registerLoadedSubreport(absolutePathKey, report);
420                             }
421                         }
422                     }                    
423                 }
424                 else
425                 {
426                     report = loadReportSource(source, null, filler);
427                 }
428                 
429                 if (isUsingCache)
430                 {
431                     filler.fillContext.registerLoadedSubreport(cacheKey, report);
432                 }
433             }
434         }
435         
436         return report;
437     }
438     
439     protected static JasperReportSource loadReportSource(Object reportSource, String contextLocation, 
440             BaseReportFiller filler) throws JRException
441     {
442         JasperReport jasperReport = loadReport(reportSource, filler);
443         JasperReportSource report = null;
444         if (jasperReport != null)
445         {
446             RepositoryResourceContext currentContext = filler.getRepositoryContext().getResourceContext();
447             RepositoryResourceContext reportContext = SimpleRepositoryResourceContext.of(contextLocation,
448                     currentContext == null ? null : currentContext.getDerivedContextFallback());
449             report = SimpleJasperReportSource.from(jasperReport, 
450                     reportSource instanceof String ? (String) reportSource : null
451                     reportContext);
452         }
453         return report;
454     }
455
456     public static JasperReport loadReport(Object source, BaseReportFiller filler) throws JRException
457     {
458         JasperReport report;
459         if (source instanceof net.sf.jasperreports.engine.JasperReport)
460         {
461             report = (JasperReport)source;
462         }
463         else if (source instanceof java.io.InputStream)
464         {
465             report = (JasperReport)JRLoader.loadObject((InputStream)source);
466         }
467         else if (source instanceof java.net.URL)
468         {
469             report = (JasperReport)JRLoader.loadObject((URL)source);
470         }
471         else if (source instanceof java.io.File)
472         {
473             report = (JasperReport)JRLoader.loadObject((File)source);
474         }
475         else if (source instanceof java.lang.String)
476         {
477             report = RepositoryUtil.getInstance(filler.getRepositoryContext()).getReport(filler.getFillContext().getReportContext(), (String)source);
478 //                        (JasperReport)JRLoader.loadObjectFromLocation(
479 //                            (String)source, 
480 //                            filler.reportClassLoader,
481 //                            filler.urlHandlerFactory,
482 //                            filler.fileResolver
483 //                            );
484         }
485         else
486         {
487             throw 
488             new JRRuntimeException(
489                 EXCEPTION_MESSAGE_KEY_UNSUPPORTED_SECTION_TYPE,  
490                 new Object[]{source.getClass().getName()} 
491                 );
492         }
493         return report;
494     }
495
496     /**
497      *
498      */

499     protected void evaluateSubreport(
500         byte evaluation
501         ) throws JRException
502     {
503         evaluateProperties(evaluation);
504         evaluateStyle(evaluation);
505
506         jasperReportSource = evaluateReportSource(evaluation);
507         
508         if (jasperReportSource != null)
509         {
510             JRFillDataset parentDataset = expressionEvaluator.getFillDataset();
511             datasetPosition = new FillDatasetPosition(parentDataset.fillPosition);
512             datasetPosition.addAttribute("subreportUUID", getUUID());
513             parentDataset.setCacheRecordIndex(datasetPosition, evaluation);
514             
515             /*   */
516             connection = (Connection) evaluateExpression(
517                     getConnectionExpression(), evaluation);
518     
519             String cacheIncludedProp = JRPropertiesUtil.getOwnProperty(this, DataCacheHandler.PROPERTY_INCLUDED); 
520             cacheIncluded = JRPropertiesUtil.asBoolean(cacheIncludedProp, true);// default to true
521
522             if (filler.fillContext.hasDataSnapshot() && cacheIncluded)
523             {
524                 // TODO lucianc put something here so that data adapters know not to create a data source
525                 dataSource = null;
526             }
527             else
528             {
529                 dataSource = (JRDataSource) evaluateExpression(
530                         getDataSourceExpression(), evaluation);
531             }
532             
533             parameterValues = 
534                 evaluateParameterValues(evaluation);
535
536             if (subreportFiller != null)
537             {
538                 filler.unregisterSubfiller(subreportFiller);
539             }
540
541             /*   */
542             DatasetExpressionEvaluator evaluator = loadReportEvaluator();
543             initSubreportFiller(evaluator);
544             
545             validateReport();
546             
547             returnValues.saveReturnVariables();
548         }
549     }
550
551     protected JasperReport getReport()
552     {
553         return jasperReportSource == null ? null : jasperReportSource.getReport();
554     }
555
556     protected Map<String, Object> evaluateParameterValues(byte evaluation) throws JRException
557     {
558         JasperReport jasperReport = getReport();
559         return getParameterValues(
560             filler, 
561             expressionEvaluator,
562             getParametersMapExpression(), 
563             getParameters(), 
564             evaluation, 
565             false
566             jasperReport.getResourceBundle() != null,//hasResourceBundle 
567             jasperReport.getFormatFactoryClass() != null//hasFormatFactory
568             );
569     }
570
571     protected DatasetExpressionEvaluator loadReportEvaluator() throws JRException
572     {
573         JasperReport jasperReport = getReport();
574         DatasetExpressionEvaluator evaluator = null;
575         boolean usingCache = usingCache();
576         if (usingCache)
577         {
578             evaluator = loadedEvaluators.get(jasperReport);
579         }
580         if (evaluator == null)
581         {
582             evaluator = createEvaluator();
583             if (usingCache)
584             {
585                 loadedEvaluators.put(jasperReport, (JREvaluator)evaluator);
586             }
587         }
588         return evaluator;
589     }
590     
591     protected void evictReportEvaluator()
592     {
593         loadedEvaluators.remove(getReport());
594     }
595
596
597     protected DatasetExpressionEvaluator createEvaluator() throws JRException
598     {
599         return JasperCompileManager.getInstance(filler.getJasperReportsContext()).getEvaluator(
600                 getReport());
601     }
602
603
604     protected boolean isReorderBandElements()
605     {
606         return false;
607     }
608
609     
610     protected void initSubreportFiller(DatasetExpressionEvaluator evaluator) throws JRException
611     {
612         JasperReport jasperReport = getReport();
613         if (log.isDebugEnabled())
614         {
615             log.debug("Fill " + filler.fillerId + ": creating subreport filler for " + jasperReport.getName());
616         }
617         
618         SectionTypeEnum subreportSectionType = jasperReport.getSectionType();
619         if (subreportSectionType != null && subreportSectionType != SectionTypeEnum.BAND)
620         {
621             throw 
622                 new JRRuntimeException(
623                     EXCEPTION_MESSAGE_KEY_UNSUPPORTED_SECTION_TYPE,  
624                     new Object[]{subreportSectionType} 
625                     );
626         }
627         
628         subFillerParent = createFillerParent(evaluator);
629
630         switch (jasperReport.getPrintOrderValue())
631         {
632             case HORIZONTAL :
633             {
634                 subreportFiller = new JRHorizontalFiller(filler.getJasperReportsContext(), jasperReportSource, subFillerParent);
635                 break;
636             }
637             case VERTICAL :
638             default :
639             {
640                 subreportFiller = new JRVerticalFiller(filler.getJasperReportsContext(), jasperReportSource, subFillerParent);
641                 break;
642             }
643         }
644         
645         subreportFiller.setReorderBandElements(isReorderBandElements());
646
647         runner = getRunnerFactory().createSubreportRunner(this, subreportFiller);
648         subFillerParent.setSubreportRunner(runner);
649         
650         subreportFiller.mainDataset.setFillPosition(datasetPosition);
651         subreportFiller.mainDataset.setCacheSkipped(!cacheIncluded);
652     }
653
654     protected FillerSubreportParent createFillerParent(DatasetExpressionEvaluator evaluator) throws JRException
655     {
656         return new FillerSubreportParent(this, evaluator);
657     }
658     
659     /**
660      * Utility method used for constructing a parameter values map for subreports, sub datasets and crosstabs.
661      * 
662      * @param filler report filler
663      * @param parametersMapExpression expression that yields bulk parameter values map
664      * @param subreportParameters list of individual parameter values
665      * @param evaluation evaluation type
666      * @param ignoreNullExpressions whether to ignore individual parameter value expressions
667      * @param removeResourceBundle whether to remove the {@link JRParameter#REPORT_RESOURCE_BUNDLE REPORT_RESOURCE_BUNDLE}
668      *     value from the bulk values map
669      * @return the parameter values map
670      * @throws JRException
671      */

672     public static Map<String, Object> getParameterValues(
673             BaseReportFiller filler, 
674             JRExpression parametersMapExpression, 
675             JRDatasetParameter[] subreportParameters, 
676             byte evaluation, 
677             boolean ignoreNullExpressions, 
678             boolean removeResourceBundle,
679             boolean removeFormatFactory
680             ) throws JRException
681     {
682         return getParameterValues(filler, filler.getExpressionEvaluator(), 
683                 parametersMapExpression, subreportParameters, evaluation, 
684                 ignoreNullExpressions, removeResourceBundle, removeFormatFactory);
685     }
686
687     /**
688      * Utility method used for constructing a parameter values map for subreports, sub datasets and crosstabs.
689      * 
690      * @param filler report filler
691      * @param expressionEvaluator expression evaluator
692      * @param parametersMapExpression expression that yields bulk parameter values map
693      * @param subreportParameters list of individual parameter values
694      * @param evaluation evaluation type
695      * @param ignoreNullExpressions whether to ignore individual parameter value expressions
696      * @param removeResourceBundle whether to remove the {@link JRParameter#REPORT_RESOURCE_BUNDLE REPORT_RESOURCE_BUNDLE}
697      *     value from the bulk values map
698      * @return the parameter values map
699      * @throws JRException
700      */

701     public static Map<String, Object> getParameterValues(
702             //TODO using the filler or current dataset?
703             BaseReportFiller filler, 
704             JRFillExpressionEvaluator expressionEvaluator,
705             JRExpression parametersMapExpression, 
706             JRDatasetParameter[] subreportParameters, 
707             byte evaluation, 
708             boolean ignoreNullExpressions, 
709             boolean removeResourceBundle,
710             boolean removeFormatFactory
711             ) throws JRException
712     {
713         Map<String, Object> parameterValues = null;
714         if (parametersMapExpression != null)
715         {
716             parameterValues = (Map<String, Object>) expressionEvaluator.evaluate(parametersMapExpression, evaluation);
717         }        
718         
719         if (parameterValues != null)
720         {
721             //if the expression evaluates to the master parameters map
722             if (parameterValues == filler.getParameterValuesMap())
723             {
724                 //create a clone of the map so that the master map is not altered
725                 parameterValues = new HashMap<String, Object>(parameterValues);
726             }
727             
728             //parameterValues.remove(JRParameter.REPORT_LOCALE);
729             if (removeResourceBundle)
730             {
731                 parameterValues.remove(JRParameter.REPORT_RESOURCE_BUNDLE);
732             }
733             if (removeFormatFactory)
734             {
735                 parameterValues.remove(JRParameter.REPORT_FORMAT_FACTORY);
736             }
737             //parameterValues.remove(JRParameter.REPORT_TIME_ZONE);
738             parameterValues.remove(JRParameter.JASPER_REPORTS_CONTEXT);//FIXMENOW this is probably not necessary. other too
739             parameterValues.remove(JRParameter.JASPER_REPORT);
740             parameterValues.remove(JRParameter.REPORT_CONNECTION);
741             parameterValues.remove(JRParameter.REPORT_MAX_COUNT);
742             parameterValues.remove(JRParameter.REPORT_DATA_SOURCE);
743             parameterValues.remove(JRParameter.REPORT_SCRIPTLET);
744             // should we give access to scriplet extensions so that they can remove their parameters here?
745             // yes, but then you should also give them access to create built-in parameters... too much trouble.
746             JRScriptlet[] scriptlets = filler.getJasperReport().getScriptlets();
747             if (scriptlets != null)
748             {
749                 for(int i = 0; i < scriptlets.length; i++)
750                 {
751                     parameterValues.remove(scriptlets[i].getName() 
752                             + JRScriptlet.SCRIPTLET_PARAMETER_NAME_SUFFIX);
753                 }
754             }
755             parameterValues.remove(JRParameter.REPORT_VIRTUALIZER);
756             //parameterValues.remove(JRParameter.REPORT_CLASS_LOADER);
757             parameterValues.remove(JRParameter.IS_IGNORE_PAGINATION);
758             parameterValues.remove(JRParameter.SORT_FIELDS);
759             parameterValues.remove(JRParameter.FILTER);
760             parameterValues.remove(JRParameter.REPORT_PARAMETERS_MAP);
761         }
762         
763         if (parameterValues == null)
764         {
765             parameterValues = new HashMap<String, Object>();
766         }
767         
768         /*   */
769         if (subreportParameters != null && subreportParameters.length > 0)
770         {
771             Object parameterValue = null;
772             for(int i = 0; i < subreportParameters.length; i++)
773             {
774                 JRExpression expression = subreportParameters[i].getExpression();
775                 if (expression != null || !ignoreNullExpressions)
776                 {
777                     parameterValue = expressionEvaluator.evaluate(expression, evaluation);
778                     if (parameterValue == null)
779                     {
780                         parameterValues.remove(subreportParameters[i].getName());
781                     }
782                     else
783                     {
784                         parameterValues.put(subreportParameters[i].getName(), parameterValue);
785                     }
786                 }
787             }
788         }
789         
790         if (!parameterValues.containsKey(JRParameter.REPORT_LOCALE))
791         {
792             parameterValues.put(JRParameter.REPORT_LOCALE, filler.getLocale());
793         }
794
795         if (!parameterValues.containsKey(JRParameter.REPORT_TIME_ZONE))
796         {
797             parameterValues.put(JRParameter.REPORT_TIME_ZONE, filler.getTimeZone());
798         }
799
800         if (
801             !parameterValues.containsKey(JRParameter.REPORT_FORMAT_FACTORY)
802             && !removeFormatFactory
803             )
804         {
805             parameterValues.put(JRParameter.REPORT_FORMAT_FACTORY, filler.getFormatFactory());
806         }
807
808         if (!parameterValues.containsKey(JRParameter.REPORT_CONTEXT))
809         {
810             ReportContext context = (ReportContext) filler.getMainDataset().getParameterValue(
811                     JRParameter.REPORT_CONTEXT, true);
812             if (context != null)
813             {
814                 parameterValues.put(JRParameter.REPORT_CONTEXT, context);
815             }
816         }
817         
818         return parameterValues;
819     }
820
821     @continuable
822     protected void fillSubreport() throws JRException
823     {
824         if (getConnectionExpression() != null)
825         {
826             subreportFiller.fill(parameterValues, connection);
827         }
828         else if (getDataSourceExpression() != null)
829         {
830             subreportFiller.fill(parameterValues, dataSource);
831         }
832         else
833         {
834             subreportFiller.fill(parameterValues);
835         }
836     }
837     
838
839     @Override
840     protected boolean prepare(
841         int availableHeight,
842         boolean isOverflow
843         ) throws JRException
844     {
845         boolean willOverflow = false;
846
847         super.prepare(availableHeight, isOverflow);
848         
849         if (subreportFiller == null)
850         {
851             setToPrint(false);
852         }
853
854         if (!isToPrint())
855         {
856             return willOverflow;
857         }
858
859         int elementHeight = getHeight();
860         if (availableHeight < getRelativeY() + elementHeight)
861         {
862             setToPrint(false);
863             return true;//willOverflow;
864         }
865             
866         //willOverflow = prepareTextField((JRFillTextField)fillElement, availableStretchHeight);
867         
868         //subreportFiller.setPageHeight(getHeight() + availableStretchHeight);
869         
870         boolean filling = runner.isFilling();
871         boolean toPrint = !isOverflow || isPrintWhenDetailOverflows() || !isAlreadyPrinted();
872         boolean reprinted = isOverflow && isPrintWhenDetailOverflows();
873
874         // for zero height subreports, check if we are at the bottom of the available space
875         // and if the container is already marked to overflow.  in that casedo not
876         // start the subreport here as the column header infinite loop test could throw
877         // a false positive.
878         if (elementHeight == 0 && availableHeight == getRelativeY()
879                 // test whether the report is starting now
880                 && !filling && toPrint
881                 && fillContainerContext != null// not sure if we need this
882                 && fillContainerContext.isCurrentOverflow()
883                 && fillContainerContext.isCurrentOverflowAllowed())
884         {
885             if (log.isDebugEnabled())
886             {
887                 log.debug("zero height subreport at the bottom, not starting");
888             }
889             
890             setToPrint(false);
891             return true;//willOverflow;
892         }
893         
894         if (!filling && toPrint && reprinted)
895         {
896             rewind();
897         }
898         
899         if (printPage instanceof JRVirtualPrintPage)
900         {
901             // if the previous page was virtualized, dispose it as soon as possible.
902             // this normally already happened when we added the elements to the master page,
903             // but there are cases (e.g. overflow) when a page is not added to the master.
904             ((JRVirtualPrintPage) printPage).dispose();
905         }
906         
907         int pageHeight;
908         OverflowType overflowType = getOverflowType();
909         if (overflowType == OverflowType.NO_STRETCH && !filler.isIgnorePagination())
910         {
911             // not allowed to stretch beyond the element height
912             // note that we always have elementHeight <= availableHeight - getRelativeY(), it's tested above
913             pageHeight = elementHeight;
914         }
915         else
916         {
917             // stretching by default
918             pageHeight = availableHeight - getRelativeY();
919         }
920         subreportFiller.setPageHeight(pageHeight);
921
922         synchronized (subreportFiller)
923         {
924             JRSubreportRunResult result;
925             if (filling)
926             {
927                 if (log.isDebugEnabled())
928                 {
929                     log.debug("Fill " + filler.fillerId + ": resuming " + subreportFiller.fillerId);
930                 }
931
932                 result = runner.resume();
933             }
934             else if (toPrint)
935             {
936                 setReprinted(reprinted);
937
938                 if (log.isDebugEnabled())
939                 {
940                     log.debug("Fill " + filler.fillerId + ": starting " + subreportFiller.fillerId);
941                 }
942
943                 result = runner.start();
944             }
945             else
946             {
947                 printPage = null;
948                 printPageContentsWidth = 0;
949                 setPrepareHeight(getHeight());
950                 setToPrint(false);
951
952                 return willOverflow;
953             }
954             
955             if (result.getException() != null)
956             {
957                 Throwable error = result.getException();
958                 
959                 if (log.isErrorEnabled())
960                 {
961                     log.error("Fill " + filler.fillerId + ": exception", error);
962                 }
963                 
964                 if (error instanceof RuntimeException)
965                 {
966                     throw (RuntimeException) error;
967                 }
968
969                 throw new JRRuntimeException(error);
970             }
971
972             if (result.hasFinished())
973             {
974                 if (log.isDebugEnabled())
975                 {
976                     log.debug("Fill " + filler.fillerId + ": subreport " + subreportFiller.fillerId + " finished");
977                 }
978                 
979                 returnValues.copyValues(returnValuesContext);
980             }
981             else
982             {
983                 if (log.isDebugEnabled())
984                 {
985                     log.debug("Fill " + filler.fillerId + ": subreport " + subreportFiller.fillerId + " to continue");
986                 }
987             }
988
989             printPage = subreportFiller.getCurrentPage();
990             printPageContentsWidth = subreportFiller.getCurrentPageContentsWidth();
991             setPrepareHeight(result.hasFinished() ? subFillerParent.getCurrentPageStretchHeight() : pageHeight);
992
993             //if the subreport fill thread has not finished, 
994             // it means that the subreport will overflow on the next page
995             willOverflow = !result.hasFinished();
996             
997             if (!willOverflow)
998             {
999                 //the subreport fill thread has finished and the next time we shall create a new one
1000                 runner.reset();
1001             }
1002         }// synchronized
1003         
1004         Collection<JRPrintElement> printElements = getPrintElements();
1005         if (
1006             (printElements == null || printElements.size() == 0) &&
1007             isRemoveLineWhenBlank() //FIXME if the line won't be removed, the background does not appear
1008             )
1009         {
1010             setToPrint(false);
1011         }
1012
1013         return willOverflow;
1014     }
1015
1016
1017     @Override
1018     public void rewind() throws JRException
1019     {
1020         if (subreportFiller == null)
1021         {
1022             return;
1023         }
1024         
1025         cancelSubreportFill();
1026         
1027         initSubreportFiller(null);//FIXME used cached evaluator
1028
1029         if (getConnectionExpression() == null && dataSource != null)
1030         {
1031             if(dataSource instanceof JRRewindableDataSource)
1032             {
1033                 ((JRRewindableDataSource) dataSource).moveFirst();
1034             }
1035             else
1036             {
1037 //                if (log.isWarnEnabled())
1038 //                    log.warn("The subreport is placed on a non-splitting band, but it does not have a rewindable data source.");
1039                 throw 
1040                     new JRException(
1041                         EXCEPTION_MESSAGE_KEY_NO_REWINDABLE_DATA_SOURCE,  
1042                         (Object[])null 
1043                         );
1044             }
1045         }
1046     }
1047
1048
1049     protected void cancelSubreportFill() throws JRException
1050     {
1051         if (log.isDebugEnabled())
1052         {
1053             log.debug("Fill " + filler.fillerId + ": cancelling " + subreportFiller.fillerId);
1054         }
1055         
1056         // marking the subreport filler for interruption
1057         subreportFiller.setInterrupted(true);
1058         
1059         synchronized (subreportFiller)
1060         {
1061             // forcing the creation of a new thread and a new subreport filler
1062             runner.cancel();
1063             runner.reset();
1064         }
1065
1066         filler.unregisterSubfiller(subreportFiller);
1067     }
1068
1069
1070     @Override
1071     protected JRPrintElement fill()
1072     {
1073         //FIXME lucianc create a frame instead to avoid HTML layers
1074         JRPrintRectangle printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle(), printElementOriginator);
1075
1076         if (printRectangle.getModeValue() == ModeEnum.TRANSPARENT && !printRectangle.hasProperties())
1077         {
1078             String generateRectangle = generateRectangleOption();
1079             if (log.isDebugEnabled())
1080             {
1081                 log.debug("empty rectangle, generate option: " + generateRectangle);
1082             }
1083             
1084             if (generateRectangle == null || !generateRectangle.equals(SUBREPORT_GENERATE_RECTANGLE_ALWAYS))
1085             {
1086                 // skipping empty rectangle
1087                 return null;
1088             }
1089         }
1090         
1091         printRectangle.setUUID(getUUID());
1092         printRectangle.setX(getX());
1093         printRectangle.setY(getRelativeY());
1094         printRectangle.setWidth(getWidth());
1095         printRectangle.setHeight(getStretchHeight());
1096         
1097         return printRectangle;
1098     }
1099     
1100     protected String generateRectangleOption()
1101     {
1102         String generateRectangle = defaultGenerateRectangle;
1103         if (dynamicGenerateRectangle)
1104         {
1105             String generateRectangleProp = getDynamicProperties().getProperty(PROPERTY_SUBREPORT_GENERATE_RECTANGLE);
1106             if (generateRectangleProp != null)
1107             {
1108                 generateRectangle = generateRectangleProp;
1109             }
1110         }
1111         return generateRectangle;
1112     }
1113
1114
1115     @Override
1116     public void collectExpressions(JRExpressionCollector collector)
1117     {
1118         collector.collect(this);
1119     }
1120
1121     @Override
1122     public void visit(JRVisitor visitor)
1123     {
1124         visitor.visitSubreport(this);
1125     }
1126     
1127
1128     @Override
1129     public JRSubreportReturnValue[] getReturnValues()
1130     {
1131         return ((JRSubreport) parent).getReturnValues();
1132     }
1133
1134     protected void validateReport() throws JRException
1135     {
1136         JasperReport jasperReport = getReport();
1137         if (!checkedReports.contains(jasperReport))
1138         {
1139             verifyBandHeights();
1140             returnValues.checkReturnValues(returnValuesContext);
1141             
1142             if (usingCache())
1143             {
1144                 checkedReports.add(jasperReport);
1145             }
1146         }
1147     }
1148     
1149     protected void verifyBandHeights() throws JRException
1150     {
1151         if (!filler.isIgnorePagination())
1152         {
1153             JasperReport jasperReport = getReport();
1154             
1155             int pageHeight;
1156             int topMargin = jasperReport.getTopMargin();
1157             int bottomMargin = jasperReport.getBottomMargin();
1158             
1159             JRBaseFiller parentFiller = filler;
1160             do
1161             {
1162                 // set every time, so at the end it will be the master page height
1163                 pageHeight = parentFiller.jasperReport.getPageHeight();
1164                 
1165                 // sum parent page margins
1166                 topMargin += parentFiller.jasperReport.getTopMargin();
1167                 bottomMargin += parentFiller.jasperReport.getBottomMargin();
1168                 
1169                 parentFiller = 
1170                     parentFiller.parent != null && parentFiller.parent.getFiller() instanceof JRBaseFiller 
1171                         ? (JRBaseFiller) parentFiller.parent.getFiller() 
1172                         : null;//FIXMEBOOK
1173             }
1174             while (parentFiller != null);
1175             
1176             List<JRValidationFault> brokenRules = new ArrayList<JRValidationFault>();
1177             JRVerifier.verifyBandHeights(brokenRules, 
1178                     jasperReport, pageHeight, topMargin, bottomMargin);
1179             
1180             if (!brokenRules.isEmpty())
1181             {
1182                 throw new JRValidationException("Band height validation for subreport \""
1183                         + jasperReport.getName() + "\" failed in the current page context "
1184                         + "(height = " + pageHeight + ", top margin = " + topMargin
1185                         + ", bottom margin = " + bottomMargin + ") : ",
1186                         brokenRules);
1187             }
1188             else if (log.isDebugEnabled())
1189             {
1190                 log.debug("Band height validation for subreport \""
1191                         + jasperReport.getName() + "\" succeeded in the current page context "
1192                         + "(height = " + pageHeight + ", top margin = " + topMargin
1193                         + ", bottom margin = " + bottomMargin + ")");
1194             }
1195         }
1196     }
1197     
1198     
1199     @Override
1200     protected void resolveElement (JRPrintElement element, byte evaluation)
1201     {
1202         // nothing
1203     }
1204
1205
1206     @Override
1207     public Boolean getUsingCache()
1208     {
1209         return ((JRSubreport)parent).getUsingCache();
1210     }
1211
1212
1213     @Override
1214     public void setUsingCache(Boolean isUsingCache)
1215     {
1216     }
1217
1218
1219     @Override
1220     public JRFillCloneable createClone(JRFillCloneFactory factory)
1221     {
1222         return new JRFillSubreport(this, factory);
1223     }
1224     
1225     protected JRSubreportRunnerFactory getRunnerFactory() throws JRException
1226     {
1227         String factoryClassName = filler.getPropertiesUtil().getProperty(JRSubreportRunnerFactory.SUBREPORT_RUNNER_FACTORY);
1228         if (factoryClassName == null)
1229         {
1230             throw 
1231                 new JRException(
1232                     EXCEPTION_MESSAGE_KEY_PROPERTY_NOT_SET,  
1233                     new Object[]{JRSubreportRunnerFactory.SUBREPORT_RUNNER_FACTORY} 
1234                     );
1235         }
1236         return runnerFactoryCache.getCachedInstance(factoryClassName);
1237     }
1238
1239     protected int getContentsStretchHeight()
1240     {
1241         return subFillerParent.getCurrentPageStretchHeight();
1242     }
1243
1244     protected String getReportLocation()
1245     {
1246         return jasperReportSource == null ? null : jasperReportSource.getReportLocation();
1247     }
1248
1249     protected void registerReportStyles(List<JRStyle> styles)
1250     {
1251         //NOP
1252     }
1253     
1254     protected String getReportName()
1255     {
1256         return getReport().getName();
1257     }
1258
1259     protected boolean isSplitTypePreventInhibited(boolean isTopLevelCall)
1260     {
1261         return fillContainerContext.isSplitTypePreventInhibited(isTopLevelCall);
1262     }
1263 }
1264