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.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedHashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import net.sf.jasperreports.engine.CommonReturnValue;
38 import net.sf.jasperreports.engine.ExpressionReturnValue;
39 import net.sf.jasperreports.engine.JRBand;
40 import net.sf.jasperreports.engine.JRElement;
41 import net.sf.jasperreports.engine.JRException;
42 import net.sf.jasperreports.engine.JRExpression;
43 import net.sf.jasperreports.engine.JRGroup;
44 import net.sf.jasperreports.engine.JROrigin;
45 import net.sf.jasperreports.engine.JRPropertiesHolder;
46 import net.sf.jasperreports.engine.JRPropertiesMap;
47 import net.sf.jasperreports.engine.JRRuntimeException;
48 import net.sf.jasperreports.engine.type.BandTypeEnum;
49 import net.sf.jasperreports.engine.type.PrintOrderEnum;
50 import net.sf.jasperreports.engine.type.SplitTypeEnum;
51
52
53 /**
54  * @author Teodor Danciu (teodord@users.sourceforge.net)
55  */

56 public class JRFillBand extends JRFillElementContainer implements JRBand, JROriginProvider
57 {
58
59     private static final Log log = LogFactory.getLog(JRFillBand.class);
60
61     /**
62      *
63      */

64     private JRBand parent;
65
66     private boolean isPrintWhenTrue = true;
67
68     /**
69      *
70      */

71     private boolean isNewPageColumn;
72     private boolean isFirstWholeOnPageColumn;
73     private Map<JRGroup,Boolean> isNewGroupMap = new HashMap<JRGroup,Boolean>();
74
75     private Set<JREvaluationTime> nowEvaluationTimes;
76     
77     // used by subreports to save values of variables used as return receptacles
78     // so that the values can be restored when the bands gets rewound
79     private Map<String,Object> savedVariableValues = new HashMap<String,Object>();
80
81     protected JROrigin origin;
82     
83     private SplitTypeEnum splitType;
84     private Integer breakHeight;
85
86     private FillReturnValues returnValues;
87     private FillReturnValues.SourceContext returnValuesContext = new FillReturnValues.SourceContext() 
88     {
89         @Override
90         public Object getValue(CommonReturnValue returnValue) 
91         {
92             ExpressionReturnValue expressionReturnValue = (ExpressionReturnValue)returnValue;
93             Object value = null;
94             try
95             {
96                 value = filler.evaluateExpression(expressionReturnValue.getExpression(), JRExpression.EVALUATION_DEFAULT);
97             }
98             catch (JRException e)
99             {
100                 throw new JRRuntimeException(e);
101             }
102             return value;
103         }
104         
105         @Override
106         public void check(CommonReturnValue returnValue) throws JRException 
107         {
108             //FIXMERETURN check something
109         }
110
111         @Override
112         public JRFillVariable getToVariable(String name)
113         {
114             return filler.getVariable(name);
115         }
116     };
117
118     private Set<FillReturnValues> returnValuesSet;
119     
120     /**
121      *
122      */

123     protected JRFillBand(
124         JRBaseFiller filler,
125         JRBand band,
126         JRFillObjectFactory factory
127         )
128     {
129         super(filler, band, factory);
130
131         parent = band;
132         
133         // we need to do this before setBand()
134         returnValuesSet = new LinkedHashSet<FillReturnValues>();
135
136         if (deepElements.length > 0)
137         {
138             for(int i = 0; i < deepElements.length; i++)
139             {
140                 deepElements[i].setBand(this);
141             }
142         }
143
144         List<ExpressionReturnValue> expRetValues = getReturnValues();
145         returnValues = 
146             new FillReturnValues(
147                 expRetValues == null ? null : (ExpressionReturnValue[]) expRetValues.toArray(new ExpressionReturnValue[expRetValues.size()]), //FIXMERETURN make special class for constructor differentiation
148                 factory, 
149                 filler
150                 );
151         registerReturnValues(returnValues);
152         
153         initElements();
154
155         initConditionalStyles();
156
157         nowEvaluationTimes = new HashSet<JREvaluationTime>();
158     }
159
160
161     @Override
162     public JROrigin getOrigin()
163     {
164         return origin;
165     }
166
167     
168     /**
169      *
170      */

171     protected void setOrigin(JROrigin origin)
172     {
173         if (log.isDebugEnabled())
174         {
175             log.debug("Origin " + origin + for band " + getId());
176         }
177         
178         this.origin = origin;
179         this.filler.getJasperPrint().addOrigin(origin);
180     }
181
182     
183     /**
184      *
185      */

186     protected void setNewPageColumn(boolean isNew)
187     {
188         this.isNewPageColumn = isNew;
189     }
190
191
192     /**
193      *
194      */

195     protected boolean isNewPageColumn()
196     {
197         return isNewPageColumn;
198     }
199
200
201     /**
202      * Decides whether this band is the for whole band on the page/column.
203      *
204      * @return whether this band is the for whole band on the page/column
205      */

206     protected boolean isFirstWholeOnPageColumn()
207     {
208         return isFirstWholeOnPageColumn;
209     }
210
211
212     /**
213      *
214      */

215     protected void setNewGroup(JRGroup group, boolean isNew)
216     {
217         isNewGroupMap.put(group, isNew);
218     }
219
220
221     /**
222      *
223      */

224     protected boolean isNewGroup(JRGroup group)
225     {
226         Boolean value = isNewGroupMap.get(group);
227
228         if (value == null)
229         {
230             value = Boolean.FALSE;
231         }
232
233         return value;
234     }
235
236
237     @Override
238     public int getHeight()
239     {
240         return (parent == null ? 0 : parent.getHeight());
241     }
242
243     /**
244      *
245      */

246     public int getBreakHeight()
247     {
248         // needs to be lazy calculated because it depends on splitType, which is itself lazy loaded
249         if (breakHeight == null)
250         {
251             breakHeight = getHeight();
252             if (
253                 SplitTypeEnum.IMMEDIATE == getSplitTypeValue()
254                 && elements != null && elements.length > 0
255                 )
256             {
257                 for(int i = 0; i < elements.length; i++)
258                 {
259                     JRElement element = elements[i];
260                     int bottom = element.getY() + element.getHeight();
261                     breakHeight = bottom < breakHeight ? bottom : breakHeight;
262                 }
263             }
264         }
265
266         return breakHeight;
267     }
268
269     @Override
270     public SplitTypeEnum getSplitTypeValue()
271     {
272         // needs to be lazy loaded because in JRFillBand constructor above, the filler.getMainDataset() is not yet set, 
273         // when the band is a group band
274         if (splitType == null)
275         {
276             splitType = (parent == null ? null : parent.getSplitTypeValue());
277             if (splitType == null)
278             {
279                 splitType = 
280                     SplitTypeEnum.getByName(
281                         filler.getPropertiesUtil().getProperty(filler.getMainDataset(), JRBand.PROPERTY_SPLIT_TYPE)
282                         );
283             }
284         }
285         
286         return splitType;
287     }
288
289     @Override
290     public void setSplitType(SplitTypeEnum splitType)
291     {
292         throw new UnsupportedOperationException();
293     }
294
295     @Override
296     public JRExpression getPrintWhenExpression()
297     {
298         return (parent == null ? null : parent.getPrintWhenExpression());
299     }
300
301     /**
302      *
303      */

304     protected boolean isSplitPrevented()
305     {
306         return SplitTypeEnum.PREVENT == getSplitTypeValue();
307     }
308
309     /**
310      *
311      */

312     protected boolean isPrintWhenExpressionNull()
313     {
314         return (getPrintWhenExpression() == null);
315     }
316
317     /**
318      *
319      */

320     protected boolean isPrintWhenTrue()
321     {
322         return isPrintWhenTrue;
323     }
324
325     /**
326      *
327      */

328     protected void setPrintWhenTrue(boolean isPrintWhenTrue)
329     {
330         this.isPrintWhenTrue = isPrintWhenTrue;
331     }
332
333     /**
334      *
335      */

336     protected boolean isToPrint()
337     {
338         return
339             this != filler.missingFillBand
340             && (isPrintWhenExpressionNull() 
341             || (!isPrintWhenExpressionNull() && isPrintWhenTrue()));
342     }
343
344
345     /**
346      *
347      */

348     protected void evaluatePrintWhenExpression(
349         byte evaluation
350         ) throws JRException
351     {
352         boolean isPrintTrue = false;
353
354         JRExpression expression = getPrintWhenExpression();
355         if (expression != null)
356         {
357             Boolean printWhenExpressionValue = (Boolean)filler.evaluateExpression(expression, evaluation);
358             if (printWhenExpressionValue == null)
359             {
360                 isPrintTrue = false;
361             }
362             else
363             {
364                 isPrintTrue = printWhenExpressionValue;
365             }
366         }
367
368         setPrintWhenTrue(isPrintTrue);
369     }
370
371
372
373     /**
374      *
375      */

376     protected JRPrintBand refill(
377         byte evaluation,
378         int availableHeight
379         ) throws JRException
380     {
381         rewind();
382         restoreSavedVariables();
383
384         JRPrintBand printBand = null;
385         
386         @SuppressWarnings("deprecation")
387         boolean isLegacyBandEvaluationEnabled = filler.getFillContext().isLegacyBandEvaluationEnabled(); 
388         if (isLegacyBandEvaluationEnabled)
389         {
390             printBand = fill(availableHeight);
391         }
392         else
393         {
394             evaluatePrintWhenExpression(evaluation);
395             
396             if (isToPrint())
397             {
398                 evaluate(evaluation);
399                 
400                 printBand = fill(availableHeight);
401             }
402             else
403             {
404                 printBand = new JRPrintBand();
405             }
406         }
407         
408         return printBand;
409     }
410
411
412     /**
413      *
414      */

415     protected JRPrintBand fill() throws JRException
416     {
417         return fill(getHeight(), false);
418     }
419
420
421     /**
422      *
423      */

424     protected JRPrintBand fill(
425         int availableHeight
426         ) throws JRException
427     {
428         return fill(availableHeight, true);
429     }
430
431
432     /**
433      *
434      */

435     protected JRPrintBand fill(
436         int availableHeight,
437         boolean isOverflowAllowed
438         ) throws JRException
439     {
440         filler.checkInterrupted();
441
442         filler.setBandOverFlowAllowed(isOverflowAllowed);
443
444         initFill();
445
446         if (isNewPageColumn && !isOverflow)
447         {
448             isFirstWholeOnPageColumn = true;
449         }
450         
451         resetElements();
452
453         prepareElements(availableHeight, isOverflowAllowed);
454
455         if (isLegacyElementStretchEnabled())
456         {
457             stretchElements();
458
459             moveBandBottomElements();
460
461             removeBlankElements();
462         }
463
464         isFirstWholeOnPageColumn = isNewPageColumn && isOverflow;
465         isNewPageColumn = false;
466         isNewGroupMap = new HashMap<JRGroup,Boolean>();
467
468         JRPrintBand printBand = new JRPrintBand();
469         fillElements(printBand);
470         
471         if (!willOverflow())
472         {
473             returnValues.copyValues(returnValuesContext);
474         }
475
476         return printBand;
477     }
478
479
480     protected boolean willOverflowWithElements()
481     {
482         return willOverflowWithElements;
483     }
484
485
486     @Override
487     protected int getContainerHeight()
488     {
489         return getHeight();
490     }
491
492
493     @Override
494     protected int getActualContainerHeight()
495     {
496         return getContainerHeight(); 
497     }
498
499
500     protected boolean isVariableUsedInReturns(String variableName)
501     {
502         boolean used = false;
503         for (FillReturnValues returnValues : returnValuesSet)
504         {
505             if (returnValues.usesForReturnValue(variableName))
506             {
507                 used = true;
508                 break;
509             }
510         }
511         return used;
512     }
513
514
515     protected void addNowEvaluationTime(JREvaluationTime evaluationTime)
516     {
517         nowEvaluationTimes.add(evaluationTime);
518     }
519
520
521     protected void addNowEvaluationTimes(JREvaluationTime[] evaluationTimes)
522     {
523         for (int i = 0; i < evaluationTimes.length; i++)
524         {
525             nowEvaluationTimes.add(evaluationTimes[i]);
526         }
527     }
528
529
530     protected boolean isNowEvaluationTime(JREvaluationTime evaluationTime)
531     {
532         return nowEvaluationTimes.contains(evaluationTime);
533     }
534
535
536     protected int getId()
537     {
538         //FIXME this is not necessarily unique
539         return System.identityHashCode(this);
540     }
541
542
543     @Override
544     protected void evaluate(byte evaluation) throws JRException
545     {
546         resetSavedVariables();
547         evaluateConditionalStyles(evaluation);
548         super.evaluate(evaluation);
549     }
550     
551     protected void resetSavedVariables()
552     {
553         savedVariableValues.clear();
554     }
555     
556     protected void saveVariable(String variableName)
557     {
558         if (!savedVariableValues.containsKey(variableName))
559         {
560             Object value = filler.getVariableValue(variableName);
561             savedVariableValues.put(variableName, value);
562         }
563     }
564     
565     protected void restoreSavedVariables()
566     {
567         for (Iterator<Map.Entry<String,Object>> it = savedVariableValues.entrySet().iterator(); it.hasNext();)
568         {
569             Map.Entry<String,Object> entry = it.next();
570             String variableName = entry.getKey();
571             Object value = entry.getValue();
572             JRFillVariable variable = filler.getVariable(variableName);
573             variable.setOldValue(value);
574             variable.setValue(value);
575             variable.setIncrementedValue(value);
576         }
577     }
578
579
580     protected boolean isEmpty()
581     {
582         return this == filler.missingFillBand
583             || (getHeight() == 0
584                     && (getElements() == null || getElements().length == 0)
585                     && getPrintWhenExpression() == null);
586     }
587
588     protected boolean isColumnBand()
589     {
590         BandTypeEnum bandType = origin.getBandTypeValue();
591         
592         return
593             bandType == BandTypeEnum.GROUP_HEADER
594             || bandType == BandTypeEnum.GROUP_FOOTER
595             || bandType == BandTypeEnum.DETAIL;
596     }
597
598     protected boolean isPageBreakInhibited()
599     {
600         boolean isPageBreakInhibited = filler.isFirstPageBand && !atLeastOneElementIsToPrint;
601         
602         if (isPageBreakInhibited && filler.isSubreport())
603         {
604             isPageBreakInhibited = filler.getBandReportParent().isPageBreakInhibited();
605         }
606         
607         return isPageBreakInhibited;
608     }
609     
610     protected boolean isSplitTypePreventInhibited()
611     {
612         return isSplitTypePreventInhibited(true);
613     }
614     
615     @Override
616     public boolean isSplitTypePreventInhibited(boolean isTopLevelCall)
617     {
618         boolean isSplitTypePreventInhibited = false;
619         
620         if (
621             ((filler.printOrder == PrintOrderEnum.VERTICAL && filler.isFirstColumnBand)
622             || (filler.printOrder == PrintOrderEnum.HORIZONTAL && filler.isFirstPageBand))
623             && (isTopLevelCall || !atLeastOneElementIsToPrint)
624             )
625         {
626             if (isColumnBand() && filler.columnIndex < filler.columnCount - 1)
627             {
628                 isSplitTypePreventInhibited = true;
629             }
630             else
631             {
632                 if (filler.isSubreport())
633                 {
634                     isSplitTypePreventInhibited = filler.getBandReportParent().isSplitTypePreventInhibited(false);
635                 }
636                 else
637                 {
638                     isSplitTypePreventInhibited = true;
639                 }
640             }
641         }
642         
643         return isSplitTypePreventInhibited;
644     }
645     
646     @Override
647     public boolean hasProperties()
648     {
649         return parent.hasProperties();
650     }
651
652     // not doing anything with the properties at fill time
653     @Override
654     public JRPropertiesMap getPropertiesMap()
655     {
656         return parent.getPropertiesMap();
657     }
658     
659     @Override
660     public JRPropertiesHolder getParentProperties()
661     {
662         return null;
663     }
664
665     @Override
666     public List<ExpressionReturnValue> getReturnValues()
667     {
668         return parent == null ? null : parent.getReturnValues();
669     }
670
671     public void registerReturnValues(FillReturnValues fillReturnValues)
672     {
673         returnValuesSet.add(fillReturnValues);
674     }
675
676 }
677