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  * Peter Severin - peter_p_s@users.sourceforge.net 
28  */

29 package net.sf.jasperreports.engine.fill;
30
31 import java.util.Map;
32
33 import net.sf.jasperreports.annotations.properties.Property;
34 import net.sf.jasperreports.annotations.properties.PropertyScope;
35 import net.sf.jasperreports.engine.JRException;
36 import net.sf.jasperreports.engine.JRExpression;
37 import net.sf.jasperreports.engine.JRPropertiesUtil;
38 import net.sf.jasperreports.engine.JRVariable;
39 import net.sf.jasperreports.engine.type.DatasetResetTypeEnum;
40 import net.sf.jasperreports.engine.type.IncrementTypeEnum;
41 import net.sf.jasperreports.engine.type.ResetTypeEnum;
42 import net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum;
43 import net.sf.jasperreports.properties.PropertyConstants;
44
45
46 /**
47  * Evaluates JasperReports expressions.
48  * <p>
49  * The expressions calculator is the entity inside JasperReports that evaluates
50  * expressions and increments variables or datasets at report-filling time. When a report
51  * template is compiled, the report compiler produces and stores in the compiled report
52  * template ({@link net.sf.jasperreports.engine.JasperReport} object) information that it 
53  * will use at report-filling time to build an instance of the 
54  * {@link net.sf.jasperreports.engine.fill.JRCalculator} class.
55  * </p><p>
56  * The Java-based report compilers generate a Java source file and compile it on the fly.
57  * This generated class is a subclass of the {@link net.sf.jasperreports.engine.fill.JRCalculator}, 
58  * and the bytecode produced by compiling it is stored inside the 
59  * {@link net.sf.jasperreports.engine.JasperReport} object. At report-filling time, this
60  * bytecode is loaded and the resulting class is instantiated to obtain the calculator object
61  * needed for expression evaluation.
62  * </p><p>
63  * Only the report compiler creates the calculator instance because only the report compiler
64  * can make sense of the information it stored in the compiled report template at report compilation
65  * time.
66  * </p>
67  * @author Teodor Danciu (teodord@users.sourceforge.net)
68  */

69 public class JRCalculator implements JRFillExpressionEvaluator
70 {
71
72     /**
73      * 
74      */

75     @Property(
76             category = PropertyConstants.CATEGORY_FILL,
77             defaultValue = PropertyConstants.BOOLEAN_FALSE,
78             scopes = {PropertyScope.CONTEXT},
79             sinceVersion = PropertyConstants.VERSION_6_5_0,
80             valueType = Boolean.class
81             )
82     public static final String PROPERTY_LEGACY_BAND_EVALUATION_ENABLED = 
83         JRPropertiesUtil.PROPERTY_PREFIX + "legacy.band.evaluation.enabled";
84
85     /**
86      *
87      */

88     protected JRFillDataset dataset;
89     protected Map<String, JRFillParameter> parsm;
90     protected Map<String, JRFillField> fldsm;
91     protected Map<String, JRFillVariable> varsm;
92     protected JRFillVariable[] variables;
93     protected JRFillGroup[] groups;
94     protected JRFillElementDataset[] datasets;
95
96     private JRFillVariable pageNumber;
97     private JRFillVariable columnNumber;
98     
99     /**
100      * @deprecated To be removed.
101      */

102     private boolean legacyBandEvaluationEnabled;
103     
104     /**
105      * The expression evaluator
106      */

107     private final DatasetExpressionEvaluator evaluator;
108
109
110     /**
111      * Creates a calculator using an expression evaluator.
112      * 
113      * @param evaluator the expression evaluator
114      */

115     protected JRCalculator(DatasetExpressionEvaluator evaluator)
116     {
117         this.evaluator = evaluator;
118     }
119
120
121     /**
122      * Creates a calculator using an expression evaluator.
123      * 
124      * @param evaluator the expression evaluator
125      */

126     protected JRCalculator(JREvaluator evaluator)
127     {
128         this((DatasetExpressionEvaluator) evaluator);
129     }
130
131
132     /**
133      * Initializes the calculator.
134      * 
135      * @param dataset the dataset this calculator is used for
136      * @throws JRException
137      */

138     protected void init(JRFillDataset dataset) throws JRException
139     {
140         this.dataset = dataset;
141         parsm = dataset.parametersMap;
142         fldsm = dataset.fieldsMap;
143         varsm = dataset.variablesMap;
144         variables = dataset.variables;
145         groups = dataset.groups;
146         datasets = dataset.elementDatasets;
147
148         pageNumber = varsm.get(JRVariable.PAGE_NUMBER);
149         columnNumber = varsm.get(JRVariable.COLUMN_NUMBER);
150         
151         WhenResourceMissingTypeEnum whenResourceMissingType = dataset.getWhenResourceMissingTypeValue();
152         boolean ignoreNPE = 
153             JRPropertiesUtil.getInstance(getFillDataset().getJasperReportsContext())
154                 .getBooleanProperty(
155                     getFillDataset(), 
156                     JREvaluator.PROPERTY_IGNORE_NPE, 
157                     true
158                     );
159         evaluator.init(parsm, fldsm,varsm, whenResourceMissingType, ignoreNPE);
160         
161         legacyBandEvaluationEnabled = 
162             JRPropertiesUtil.getInstance(getFillDataset().getJasperReportsContext())
163                 .getBooleanProperty(
164                     PROPERTY_LEGACY_BAND_EVALUATION_ENABLED
165                     );
166     }
167
168
169     /**
170      *
171      */

172     public JRFillVariable getPageNumber()
173     {
174         return pageNumber;
175     }
176     
177
178     /**
179      *
180      */

181     public JRFillVariable getColumnNumber()
182     {
183         return columnNumber;
184     }
185     
186
187     /**
188      *
189      */

190     public void calculateVariables(boolean incrementDatasets) throws JRException
191     {
192         if (variables != null && variables.length > 0)
193         {
194             for (int i = 0; i < variables.length; i++)
195             {
196                 JRFillVariable variable = variables[i];
197                 Object expressionValue = evaluate(variable.getExpression());
198                 Object newValue = variable.getIncrementer().increment(variable, expressionValue, AbstractValueProvider.getCurrentValueProvider());
199                 variable.setValue(newValue);
200                 variable.setInitialized(false);
201                 variable.setPreviousIncrementedValue(variable.getIncrementedValue());
202
203                 if (variable.getIncrementTypeValue() == IncrementTypeEnum.NONE)
204                 {
205                     variable.setIncrementedValue(variable.getValue());
206                 }
207             }
208         }
209
210         if (incrementDatasets && datasets != null && datasets.length > 0)
211         {
212             for (int i = 0; i < datasets.length; i++)
213             {
214                 JRFillElementDataset elementDataset = datasets[i];
215                 elementDataset.evaluate(this);
216
217                 if (elementDataset.getIncrementTypeValue() == IncrementTypeEnum.NONE)
218                 {
219                     elementDataset.increment();
220                 }
221             }
222         }
223     }
224
225
226     protected void recalculateVariables() throws JRException
227     {
228         if (variables != null)
229         {
230             for (JRFillVariable variable : variables)
231             {
232                 variable.setIncrementedValue(variable.getPreviousIncrementedValue());
233             }
234         }
235         
236         calculateVariables(false);
237     }
238
239
240     /**
241      *
242      */

243     public void estimateVariables() throws JRException
244     {
245         if (variables != null && variables.length > 0)
246         {
247             for(int i = 0; i < variables.length; i++)
248             {
249                 JRFillVariable variable = variables[i];
250                 Object expressionValue = evaluateEstimated(variable.getExpression());
251                 Object newValue = variable.getIncrementer().increment(variable, expressionValue,  AbstractValueProvider.getEstimatedValueProvider());
252                 variable.setEstimatedValue(newValue);
253                 //variable.setInitialized(false);
254             }
255         }
256     }
257
258
259     /**
260      * Determines group breaks based on estimated report values. 
261      * <p>
262      * {@link #estimateVariables() estimateVariables()} needs to be called prior to this method.
263      * </p>
264      * 
265      * @throws JRException
266      */

267     public void estimateGroupRuptures() throws JRException
268     {
269         if (groups != null && groups.length > 0)
270         {
271             // we are making a first group break estimation pass just so that we give inner group level 
272             // increment variables the chance to increment themselves, just in case they are participating 
273             // into the group expression of outer groups 
274             for(int i = groups.length - 1; i >= 0; i--)
275             {
276                 JRFillGroup group = groups[i];
277                 
278                 Object oldValue = evaluateOld(group.getExpression());
279                 Object estimatedValue = evaluateEstimated(group.getExpression());
280
281                 if ( 
282                     (oldValue == null && estimatedValue != null) ||
283                     (oldValue != null && !oldValue.equals(estimatedValue))
284                     )
285                 {
286                     group.setHasChanged(true);
287                 }
288                 else
289                 {
290                     group.setHasChanged(false);
291                 }
292             }
293
294             // incrementing inner group level increment variables, just in case they are participating 
295             // into the group expression of outer groups
296             if (variables != null && variables.length > 0)
297             {
298                 for(int i = 0; i < variables.length; i++)
299                 {
300                     JRFillVariable variable = variables[i];
301                     if (variable.getIncrementTypeValue() == IncrementTypeEnum.GROUP)
302                     {
303                         JRFillGroup group = (JRFillGroup)variable.getIncrementGroup();
304                         if (group.hasChanged())
305                         {
306                             variable.setIncrementedValue(variable.getValue());
307                         }
308                     }
309                 }
310             }
311             
312             // estimate variables again so that group level increment variables that might have been 
313             // incremented above, are taken into consideration
314             estimateVariables();
315
316             boolean groupHasChanged = false;
317             for(int i = 0; i < groups.length; i++)
318             {
319                 JRFillGroup group = groups[i];
320                 
321                 boolean isTopLevelChange = false;
322
323                 if (!groupHasChanged)
324                 {
325                     Object oldValue = evaluateOld(group.getExpression());
326                     Object estimatedValue = evaluateEstimated(group.getExpression());
327
328                     if ( 
329                         (oldValue == null && estimatedValue != null) ||
330                         (oldValue != null && !oldValue.equals(estimatedValue))
331                         )
332                     {
333                         groupHasChanged = true;
334                         isTopLevelChange = true;
335                     }
336                 }
337
338                 group.setHasChanged(groupHasChanged);
339                 group.setTopLevelChange(isTopLevelChange);
340             }
341         }
342     }
343
344
345     /**
346      *
347      */

348     public void initializeVariables(ResetTypeEnum resetType, IncrementTypeEnum incrementType) throws JRException
349     {
350         if (variables != null && variables.length > 0)
351         {
352             for(int i = 0; i < variables.length; i++)
353             {
354                 incrementVariable(variables[i], incrementType);
355                 initializeVariable(variables[i], resetType);
356             }
357         }
358
359         if (datasets != null && datasets.length > 0)
360         {
361             for(int i = 0; i < datasets.length; i++)
362             {
363                 incrementDataset(datasets[i], incrementType);
364                 initializeDataset(datasets[i], resetType);
365             }
366         }
367     }
368
369
370     /**
371      *
372      */

373     private void incrementVariable(JRFillVariable variable, IncrementTypeEnum incrementType)
374     {
375         if (variable.getIncrementTypeValue() != IncrementTypeEnum.NONE)
376         {
377             boolean toIncrement = false;
378             boolean toSetPreviousValue = false;
379             switch (incrementType)
380             {
381                 case REPORT :
382                 {
383                     toIncrement = true;
384                     break;
385                 }
386                 case PAGE :
387                 {
388                     toIncrement = 
389                         (
390                         variable.getIncrementTypeValue() == IncrementTypeEnum.PAGE || 
391                         variable.getIncrementTypeValue() == IncrementTypeEnum.COLUMN
392                         );
393                     toSetPreviousValue = toIncrement;
394                     break;
395                 }
396                 case COLUMN :
397                 {
398                     toIncrement = (variable.getIncrementTypeValue() == IncrementTypeEnum.COLUMN);
399                     toSetPreviousValue = toIncrement;
400                     break;
401                 }
402                 case GROUP :
403                 {
404                     if (variable.getIncrementTypeValue() == IncrementTypeEnum.GROUP)
405                     {
406                         JRFillGroup group = (JRFillGroup)variable.getIncrementGroup();
407                         toIncrement = group.hasChanged();
408                     }
409                     break;
410                 }
411                 case NONE :
412                 default :
413                 {
414                 }
415             }
416
417             if (toIncrement)
418             {
419                 variable.setIncrementedValue(variable.getValue());
420                 if (toSetPreviousValue && !legacyBandEvaluationEnabled)
421                 {
422                     variable.setPreviousIncrementedValue(variable.getValue());
423                 }
424 //                variable.setValue(
425 //                    evaluate(variable.getInitialValueExpression())
426 //                    );
427 //                variable.setInitialized(true);
428             }
429         }
430         else
431         {
432             variable.setIncrementedValue(variable.getValue());
433 //            variable.setValue(
434 //                evaluate(variable.getExpression())
435 //                );
436         }
437     }
438
439
440     /**
441      *
442      */

443     private void incrementDataset(JRFillElementDataset elementDataset, IncrementTypeEnum incrementType)
444     {
445         if (elementDataset.getIncrementTypeValue() != IncrementTypeEnum.NONE)
446         {
447             boolean toIncrement = false;
448             switch (incrementType)
449             {
450                 case REPORT :
451                 {
452                     toIncrement = true;
453                     break;
454                 }
455                 case PAGE :
456                 {
457                     toIncrement = 
458                         (
459                         elementDataset.getIncrementTypeValue() == IncrementTypeEnum.PAGE || 
460                         elementDataset.getIncrementTypeValue() == IncrementTypeEnum.COLUMN
461                         );
462                     break;
463                 }
464                 case COLUMN :
465                 {
466                     toIncrement = (elementDataset.getIncrementTypeValue() == IncrementTypeEnum.COLUMN);
467                     break;
468                 }
469                 case GROUP :
470                 {
471                     if (elementDataset.getIncrementTypeValue() == IncrementTypeEnum.GROUP)
472                     {
473                         JRFillGroup group = (JRFillGroup)elementDataset.getIncrementGroup();
474                         toIncrement = group.hasChanged();
475                     }
476                     break;
477                 }
478                 case NONE :
479                 default :
480                 {
481                 }
482             }
483
484             if (toIncrement)
485             {
486                 elementDataset.increment();
487             }
488         }
489     }
490
491
492     /**
493      *
494      */

495     private void initializeVariable(JRFillVariable variable, ResetTypeEnum resetType) throws JRException
496     {
497         //if (jrVariable.getCalculation() != JRVariable.CALCULATION_NOTHING)
498         if (variable.getResetTypeValue() != ResetTypeEnum.NONE)
499         {
500             boolean toInitialize = false;
501             boolean toSetOldValue = false;
502             switch (resetType)
503             {
504                 case REPORT :
505                 {
506                     toInitialize = true;
507                     break;
508                 }
509                 case PAGE :
510                 {
511                     toInitialize = 
512                         (
513                         variable.getResetTypeValue() == ResetTypeEnum.PAGE || 
514                         variable.getResetTypeValue() == ResetTypeEnum.COLUMN
515                         );
516                     toSetOldValue = toInitialize;
517                     break;
518                 }
519                 case COLUMN :
520                 {
521                     toInitialize = (variable.getResetTypeValue() == ResetTypeEnum.COLUMN);
522                     toSetOldValue = toInitialize;
523                     break;
524                 }
525                 case GROUP :
526                 {
527                     if (variable.getResetTypeValue() == ResetTypeEnum.GROUP)
528                     {
529                         JRFillGroup group = (JRFillGroup)variable.getResetGroup();
530                         toInitialize = group.hasChanged();
531                     }
532                     break;
533                 }
534                 case NONE :
535                 default :
536                 {
537                 }
538             }
539
540             if (toInitialize)
541             {
542                 variable.setValue(
543                     evaluate(variable.getInitialValueExpression())
544                     );
545                 variable.setInitialized(true);
546                 variable.setIncrementedValue(null);
547                 if (toSetOldValue && !legacyBandEvaluationEnabled)
548                 {
549                     variable.setOldValue(variable.getValue());
550                     variable.setPreviousIncrementedValue(null);
551                 }
552             }
553         }
554         else
555         {
556             variable.setValue(
557                 evaluate(variable.getExpression())
558                 );
559             variable.setIncrementedValue(variable.getValue());
560         }
561     }
562
563
564     /**
565      *
566      */

567     private void initializeDataset(JRFillElementDataset elementDataset, ResetTypeEnum resetType)
568     {
569         boolean toInitialize = false;
570         switch (resetType)
571         {
572             case REPORT :
573             {
574                 toInitialize = true;
575                 break;
576             }
577             case PAGE :
578             {
579                 toInitialize = 
580                     (
581                     elementDataset.getDatasetResetType() == DatasetResetTypeEnum.PAGE || 
582                     elementDataset.getDatasetResetType() == DatasetResetTypeEnum.COLUMN
583                     );
584                 break;
585             }
586             case COLUMN :
587             {
588                 toInitialize = (elementDataset.getDatasetResetType() == DatasetResetTypeEnum.COLUMN);
589                 break;
590             }
591             case GROUP :
592             {
593                 if (elementDataset.getDatasetResetType() == DatasetResetTypeEnum.GROUP)
594                 {
595                     JRFillGroup group = (JRFillGroup)elementDataset.getResetGroup();
596                     toInitialize = group.hasChanged();
597                 }
598                 else if (elementDataset.getDatasetResetType() == DatasetResetTypeEnum.NONE)
599                 {
600                     // None reset type for datasets means resetting at each record
601                     toInitialize = true;
602                 }
603                 break;
604             }
605             case NONE :
606             default :
607             {
608             }
609         }
610
611         if (toInitialize)
612         {
613             elementDataset.initialize();
614         }
615     }
616
617
618     @Override
619     public Object evaluate(
620         JRExpression expression,
621         byte evaluationType
622         ) throws JRException
623     {
624         Object value = null;
625         
626         switch (evaluationType)
627         {
628             case JRExpression.EVALUATION_OLD :
629             {
630                 value = evaluateOld(expression);
631                 break;
632             }
633             case JRExpression.EVALUATION_ESTIMATED :
634             {
635                 value = evaluateEstimated(expression);
636                 break;
637             }
638             case JRExpression.EVALUATION_DEFAULT :
639             default :
640             {
641                 value = evaluate(expression);
642                 break;
643             }
644         }
645
646         return value;
647     }
648     
649
650     /**
651      *
652      */

653     public Object evaluateOld(JRExpression expression) throws JRExpressionEvalException
654     {
655         return evaluator.evaluateOld(expression);
656     }
657
658
659     /**
660      *
661      */

662     public Object evaluateEstimated(JRExpression expression) throws JRExpressionEvalException
663     {
664         return evaluator.evaluateEstimated(expression);
665     }
666
667
668     /**
669      *
670      */

671     public Object evaluate(JRExpression expression) throws JRExpressionEvalException
672     {
673         return evaluator.evaluate(expression);
674     }
675
676
677     @Override
678     public JRFillDataset getFillDataset()
679     {
680         return dataset;
681     }
682 }
683