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.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36
37 import net.sf.jasperreports.engine.JRConditionalStyle;
38 import net.sf.jasperreports.engine.JRElement;
39 import net.sf.jasperreports.engine.JRElementGroup;
40 import net.sf.jasperreports.engine.JRException;
41 import net.sf.jasperreports.engine.JRFrame;
42 import net.sf.jasperreports.engine.JROrigin;
43 import net.sf.jasperreports.engine.JRPrintElement;
44 import net.sf.jasperreports.engine.JRPrintElementContainer;
45 import net.sf.jasperreports.engine.JRStyle;
46 import net.sf.jasperreports.engine.base.JRBaseStyle;
47 import net.sf.jasperreports.engine.type.PositionTypeEnum;
48 import net.sf.jasperreports.engine.type.StretchTypeEnum;
49 import net.sf.jasperreports.engine.util.StyleUtil;
50
51 /**
52  * Abstract implementation of an element container filler.
53  * <p>
54  * This is the base for band, frame and crosstab cell fillers.
55  * 
56  * @author Lucian Chirita (lucianc@users.sourceforge.net)
57  */

58 public abstract class JRFillElementContainer extends JRFillElementGroup implements FillContainerContext
59 {
60     protected JRBaseFiller filler;
61     
62     private JRFillElement[] ySortedElements;
63     private JRFillElement[] stretchElements;
64     private JRFillElement[] bandBottomElements;
65     private JRFillElement[] removableElements;
66     
67     protected boolean willOverflowWithElements;
68     protected boolean willOverflowWithWhiteSpace;
69     protected boolean isOverflow;
70     protected boolean currentOverflowWithElements;
71     protected boolean currentOverflowWithWhiteSpace;
72     private boolean currentOverflowAllowed;
73     
74     private int stretchHeight;
75     private int firstY;
76     protected boolean atLeastOneElementIsToPrint;
77     
78     protected final JRFillExpressionEvaluator expressionEvaluator;
79     
80     protected JRFillElement[] deepElements;
81
82     /**
83      *
84      */

85     protected Set<JRStyle> stylesToEvaluate = new HashSet<JRStyle>();
86     protected Map<JRStyle,JRStyle> evaluatedStyles = new HashMap<JRStyle,JRStyle>();
87     
88     protected boolean hasPrintWhenOverflowElement;
89     
90     private final boolean legacyElementStretchEnabled;
91
92     
93     protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory)
94     {
95         super(container, factory);
96         
97         expressionEvaluator = factory.getExpressionEvaluator();
98         initDeepElements();
99         
100         this.filler = filler;
101         
102         @SuppressWarnings("deprecation")
103         boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled();
104         legacyElementStretchEnabled = depFlag; 
105     }
106     
107     protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory)
108     {
109         super(container, factory);
110         
111         expressionEvaluator = container.expressionEvaluator;
112         initDeepElements();
113         
114         this.filler = container.filler;
115         
116         @SuppressWarnings("deprecation")
117         boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled();
118         legacyElementStretchEnabled = depFlag; 
119     }
120
121
122     protected void initDeepElements()
123     {
124         if (elements == null)
125         {
126             deepElements = new JRFillElement[0];
127         }
128         else
129         {
130             List<JRFillElement> deepElementsList = new ArrayList<JRFillElement>(elements.length);
131             collectDeepElements(elements, deepElementsList);
132             deepElements = new JRFillElement[deepElementsList.size()];
133             deepElementsList.toArray(deepElements);
134         }
135     }
136
137     private static void collectDeepElements(JRElement[] elements, List<JRFillElement> deepElementsList)
138     {
139         for (int i = 0; i < elements.length; i++)
140         {
141             JRElement element = elements[i];
142             deepElementsList.add((JRFillElement)element);
143             
144             if (element instanceof JRFillFrame)
145             {
146                 JRFrame frame = (JRFrame) element;
147                 collectDeepElements(frame.getElements(), deepElementsList);
148             }
149         }
150     }
151
152     /**
153      * @deprecated To be removed.
154      */

155     protected final void _initElements()
156     {
157         hasPrintWhenOverflowElement = false;
158         
159         if (elements != null && elements.length > 0)
160         {
161             List<JRFillElement> sortedElemsList = new ArrayList<JRFillElement>();
162             List<JRFillElement> stretchElemsList = new ArrayList<JRFillElement>();
163             List<JRFillElement> bandBottomElemsList = new ArrayList<JRFillElement>();
164             List<JRFillElement> removableElemsList = new ArrayList<JRFillElement>();
165             
166             topElementInGroup = null;
167             bottomElementInGroup = null;
168
169             for (JRFillElement element : elements)
170             {
171                 sortedElemsList.add(element);
172                 
173                 if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM)
174                 {
175                     bandBottomElemsList.add(element);
176                 }
177
178                 if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH)
179                 {
180                     stretchElemsList.add(element);
181                 }
182                 
183                 if (element.isRemoveLineWhenBlank())
184                 {
185                     removableElemsList.add(element);
186                 }
187                 
188                 if (element.isPrintWhenDetailOverflows())
189                 {
190                     hasPrintWhenOverflowElement = true;
191                 }
192
193                 if (
194                     topElementInGroup == null ||
195                     (
196                     element.getY() + element.getHeight() <
197                     topElementInGroup.getY() + topElementInGroup.getHeight())
198                     )
199                 {
200                     topElementInGroup = element;
201                 }
202
203                 if (
204                     bottomElementInGroup == null ||
205                     (
206                     element.getY() + element.getHeight() >
207                     bottomElementInGroup.getY() + bottomElementInGroup.getHeight())
208                     )
209                 {
210                     bottomElementInGroup = element;
211                 }
212             }
213
214             /*   */
215             Collections.sort(sortedElemsList, new JRYComparator());
216             ySortedElements = new JRFillElement[elements.length];
217             sortedElemsList.toArray(ySortedElements);
218
219             /*   */
220             stretchElements = new JRFillElement[stretchElemsList.size()];
221             stretchElemsList.toArray(stretchElements);
222
223             /*   */
224             bandBottomElements = new JRFillElement[bandBottomElemsList.size()];
225             bandBottomElemsList.toArray(bandBottomElements);
226
227             /*   */
228             removableElements = new JRFillElement[removableElemsList.size()];
229             removableElemsList.toArray(removableElements);
230         }
231         
232         /*   */
233         setDependentElements();
234     }
235
236     protected final void initElements()
237     {
238         if (isLegacyElementStretchEnabled())
239         {
240             _initElements();
241             return;
242         }
243         
244         hasPrintWhenOverflowElement = false;
245         
246         if (elements != null && elements.length > 0)
247         {
248             List<JRFillElement> stretchElemsList = new ArrayList<JRFillElement>();
249             List<JRFillElement> bandBottomElemsList = new ArrayList<JRFillElement>();
250             List<JRFillElement> removableElemsList = new ArrayList<JRFillElement>();
251             
252             JRYComparator yComparator = new JRYComparator();
253
254             /*   */
255             ySortedElements = Arrays.copyOf(elements, elements.length);
256             Arrays.sort(ySortedElements, yComparator);
257
258             topElementInGroup = null;
259             bottomElementInGroup = null;
260
261             for (JRFillElement element : ySortedElements)
262             {
263                 if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM)
264                 {
265                     bandBottomElemsList.add(element);
266                 }
267
268                 if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH)
269                 {
270                     stretchElemsList.add(element);
271                 }
272                 
273                 if (element.isRemoveLineWhenBlank())
274                 {
275                     removableElemsList.add(element);
276                 }
277                 
278                 if (element.isPrintWhenDetailOverflows())
279                 {
280                     hasPrintWhenOverflowElement = true;
281                 }
282
283                 if (
284                     topElementInGroup == null ||
285                     (
286                     element.getY() + element.getHeight() <
287                     topElementInGroup.getY() + topElementInGroup.getHeight())
288                     )
289                 {
290                     topElementInGroup = element;
291                 }
292
293                 if (
294                     bottomElementInGroup == null ||
295                     (
296                     element.getY() + element.getHeight() >
297                     bottomElementInGroup.getY() + bottomElementInGroup.getHeight())
298                     )
299                 {
300                     bottomElementInGroup = element;
301                 }
302             }
303
304             /*   */
305             stretchElements = new JRFillElement[stretchElemsList.size()];
306             stretchElemsList.toArray(stretchElements);
307
308             /*   */
309             bandBottomElements = new JRFillElement[bandBottomElemsList.size()];
310             bandBottomElemsList.toArray(bandBottomElements);
311
312             /*   */
313             removableElements = new JRFillElement[removableElemsList.size()];
314             removableElemsList.toArray(removableElements);
315         }
316         
317         /*   */
318         setDependentElements();
319     }
320
321     /**
322      *
323      */

324     private void setDependentElements()
325     {
326         if (ySortedElements != null && ySortedElements.length > 0)
327         {
328             for(int i = 0; i < ySortedElements.length - 1; i++)
329             {
330                 JRFillElement iElem = ySortedElements[i];
331                 boolean isBreakElem = iElem instanceof JRFillBreak;
332
333                 for(int j = i + 1; j < ySortedElements.length; j++)
334                 {
335                     JRFillElement jElem = ySortedElements[j];
336                     
337                     int left = Math.min(iElem.getX(), jElem.getX());
338                     int right = Math.max(iElem.getX() + iElem.getWidth(), jElem.getX() + jElem.getWidth());
339                     
340                     if (
341                         ((isBreakElem && jElem.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_TOP) || jElem.getPositionTypeValue() == PositionTypeEnum.FLOAT) &&
342                         iElem.getY() + iElem.getHeight() <= jElem.getY() &&
343                         iElem.getWidth() + jElem.getWidth() > right - left // FIXME band bottom elements should not have dependent elements
344                         )
345                     {
346                         iElem.addDependantElement(jElem);
347                     }
348                 }
349
350                 /*
351                 if (iElem.getParent().getElementGroup() != null//parent might be null
352                 {
353                     iElem.setGroupElements(
354                         iElem.getParent().getElementGroup().getElements()
355                         );
356                 }
357                 */

358             }
359         }
360     }
361
362     
363     /**
364      *
365      */

366     protected void evaluate(byte evaluation) throws JRException
367     {
368         //evaluatePrintWhenExpression(evaluation);
369
370         //if (
371         //    (isPrintWhenExpressionNull() ||
372         //    (!isPrintWhenExpressionNull() && 
373         //    isPrintWhenTrue()))
374         //    )
375         //{
376             JRElement[] allElements = getElements();
377             if (allElements != null && allElements.length > 0)
378             {
379                 for(int i = 0; i < allElements.length; i++)
380                 {
381                     JRFillElement element = (JRFillElement)allElements[i];
382                     element.setCurrentEvaluation(evaluation);
383                     element.evaluate(evaluation);
384                 }
385             }
386         //}
387     }
388
389
390     /**
391      *
392      */

393     protected void resetElements()
394     {
395         if (ySortedElements != null && ySortedElements.length > 0)
396         {
397             for(int i = 0; i < ySortedElements.length; i++)
398             {
399                 JRFillElement element = ySortedElements[i];
400
401                 element.reset();
402                 
403                 if (!isOverflow)
404                 {
405                     element.setAlreadyPrinted(false);
406                 }
407             }
408         }
409     }
410     
411
412     /**
413      * Indicates whether the elements in this container will overflow.
414      * 
415      * @return whether this container will overflow
416      */

417     public boolean willOverflow()
418     {
419         return willOverflowWithElements || willOverflowWithWhiteSpace;
420     }
421
422
423     protected void initFill()
424     {
425         isOverflow = willOverflow();
426         firstY = 0;
427         atLeastOneElementIsToPrint = false;
428     }
429
430
431     /**
432      * @deprecated To be removed.
433      */

434     protected void _prepareElements(
435         int availableHeight,
436         boolean isOverflowAllowed
437         ) throws JRException
438     {
439         currentOverflowWithElements = false;
440         currentOverflowWithWhiteSpace = false;
441         currentOverflowAllowed = isOverflowAllowed;
442
443         int calculatedStretchHeight = getContainerHeight();
444
445         firstY = isOverflow ? getActualContainerHeight() : 0;
446         atLeastOneElementIsToPrint = false;
447         boolean isFirstYFound = false;
448
449         if (ySortedElements != null && ySortedElements.length > 0)
450         {
451             for(int i = 0; i < ySortedElements.length; i++)
452             {
453                 JRFillElement element = ySortedElements[i];
454
455                 currentOverflowWithElements = 
456                     element.prepare(
457                         availableHeight + getElementFirstY(element),
458                         isOverflow
459                         ) 
460                     || currentOverflowWithElements;
461
462                 element._moveDependantElements();
463
464                 if (element.isToPrint())
465                 {
466                     if (isOverflow)
467                     {
468                         if (element.isReprinted())
469                         {
470                             firstY = 0;
471                         }
472                         else if (!isFirstYFound)
473                         {
474                             firstY = element.getY();
475                         }
476                         isFirstYFound = true;
477                     }
478
479                     atLeastOneElementIsToPrint = true;
480
481                     int spaceToBottom = getContainerHeight() - element.getY() - element.getHeight();
482                     if (spaceToBottom < 0)
483                     {
484                         spaceToBottom = 0;
485                     }
486                     
487                     if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom)
488                     {
489                         calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom;
490                     }
491                 }
492             }
493         }
494         
495         if (calculatedStretchHeight > availableHeight + firstY)
496         {
497             currentOverflowWithWhiteSpace = true;
498         }
499         
500         // stretchHeight includes firstY, which is subtracted in fillElements
501         if (currentOverflowWithElements || currentOverflowWithWhiteSpace)
502         {
503             stretchHeight = availableHeight + firstY;
504         }
505         else
506         {
507             stretchHeight = calculatedStretchHeight;
508         }
509
510         willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed;
511         willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed;
512     }
513
514     
515     /**
516      *
517      */

518     protected void prepareElements(
519         int availableHeight,
520         boolean isOverflowAllowed
521         ) throws JRException
522     {
523         if (isLegacyElementStretchEnabled())
524         {
525             _prepareElements(availableHeight, isOverflowAllowed);
526             return;
527         }
528         
529         currentOverflowWithElements = false;
530         currentOverflowWithWhiteSpace = false;
531         currentOverflowAllowed = isOverflowAllowed;
532
533         firstY = isOverflow ? getActualContainerHeight() : 0;
534         atLeastOneElementIsToPrint = false;
535         boolean isFirstYFound = false;
536
537         if (ySortedElements != null && ySortedElements.length > 0)
538         {
539             for (JRFillElement element : ySortedElements)
540             {
541                 currentOverflowWithElements = 
542                     element.prepare(
543                         availableHeight + getElementFirstY(element),
544                         isOverflow
545                         ) 
546                     || currentOverflowWithElements;
547                 
548                 // it does not seem to make sense for elements that do not print because of their isToPrint() returning false,
549                 // to push other dependent elements, but it was always like that; furthermore, such disappearing elements are pushed by 
550                 // other elements and also participate in white space collapse later on, so it is somewhat fair to also allow them 
551                 // to push others 
552                 element.moveDependantElements();
553
554                 if (element.isToPrint())
555                 {
556                     if (isOverflow)
557                     {
558                         if (element.isReprinted())
559                         {
560                             firstY = 0;
561                         }
562                         else if (!isFirstYFound)
563                         {
564                             firstY = element.getY();
565                         }
566                         isFirstYFound = true;
567                     }
568
569                     atLeastOneElementIsToPrint = true;
570                 }
571             }
572         }
573         
574         // normally, we should add firstY here, because it says below stretchHeight contains firstY; 
575         // this initialization matters when band overflows with white space and no element is rendered on the next page;
576         // but we don't add it because, historically, bands did not preserve the white space when overflowing, unlike frames for example,
577         // which re-render at their design height even when overflowing with white space
578         stretchHeight = getContainerHeight();// + firstY;
579
580         // certain elements have stretched to their natural height, while others have been moved in the process;
581         // we are now ready to calculate the stretch height of the current container, so that we can use that for
582         // moving elements to bottom, before attempting to remove blank elements;
583         // ATTENTION: this calculation needed to be in a separate ySortedElement loop as the above one, because
584         // it needs to take into consideration the displacement of dependent elements made after each element prepare
585         prepareStretchHeight(availableHeight, isOverflowAllowed);
586         
587         moveBandBottomElements();
588         
589         // removing blank elements and thus collapsing white space performs both
590         // element height shrinking and relative Y repositioning, also changing the current
591         // container stretch height
592         removeBlankElements();
593
594         // we are first stretching elements relative to group, because they do not need container height and might
595         // actually cause the container to stretch further
596         stretchElementsToElementGroup();
597
598         // recalculating container stretch height to account for element group stretching, just before triggering
599         // container related stretch
600         prepareStretchHeight(availableHeight, isOverflowAllowed);
601
602         moveBandBottomElements();
603
604         // container based element stretching is the last one to be performed
605         stretchElementsToContainer();
606     }
607
608     /**
609      *
610      */

611     protected void prepareStretchHeight(
612         int availableHeight,
613         boolean isOverflowAllowed
614         ) throws JRException
615     {
616         int calculatedStretchHeight = calculateStretchHeight();
617         
618         if (calculatedStretchHeight > availableHeight + firstY)
619         {
620             currentOverflowWithWhiteSpace = true;
621         }
622         
623         // stretchHeight includes firstY, which is subtracted in fillElements
624         if (currentOverflowWithElements || currentOverflowWithWhiteSpace)
625         {
626             stretchHeight = availableHeight + firstY;
627         }
628         else
629         {
630             stretchHeight = calculatedStretchHeight;
631         }
632
633         willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed;
634         willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed;
635     }
636
637     /**
638      *
639      */

640     protected int calculateStretchHeight() throws JRException
641     {
642         int calculatedStretchHeight = -1;
643
644         if (ySortedElements != null && ySortedElements.length > 0)
645         {
646             int containerHeight = getContainerHeight();
647
648             for (JRFillElement element : ySortedElements)
649             {
650                 if (element.isToPrint())
651                 {
652                     int spaceToBottom = containerHeight - (element.getY() + element.getHeight()) - element.getCollapsedHeightBelow();
653                     if (spaceToBottom < 0)
654                     {
655                         spaceToBottom = 0;
656                     }
657                     
658                     if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom)
659                     {
660                         calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom;
661                     }
662                 }
663             }
664         }
665         
666         if (calculatedStretchHeight < 0)
667         {
668             // there was no element printing; so trying to preserve stretchHeight
669             calculatedStretchHeight = stretchHeight;
670         }
671         
672         return calculatedStretchHeight;
673     }
674
675     /**
676      *
677      */

678     public boolean isLegacyElementStretchEnabled()
679     {
680         return legacyElementStretchEnabled;
681     }
682
683     @Override
684     public boolean isCurrentOverflow()
685     {
686         return currentOverflowWithElements || currentOverflowWithWhiteSpace;
687     }
688
689     @Override
690     public boolean isCurrentOverflowAllowed()
691     {
692         return currentOverflowAllowed;
693     }
694     
695     private int getElementFirstY(JRFillElement element)
696     {
697         int elemFirstY;
698         if (!isOverflow || hasPrintWhenOverflowElement)
699         {
700             elemFirstY = 0;
701         }
702         else if (element.getY() >= firstY)
703         {
704             elemFirstY = firstY;
705         }
706         else
707         {
708             elemFirstY = element.getY();
709         }
710         return elemFirstY;
711     }
712
713     /**
714      * @deprecated To be removed.
715      */

716     protected void _setStretchHeight(int stretchHeight)
717     {
718         if (stretchHeight > this.stretchHeight)
719         {
720             this.stretchHeight = stretchHeight;
721         }
722     }
723
724     /**
725      * This method is deprecated and is going to be removed. 
726      * Not marked as deprecated to avoid deprecation warnings.
727      */

728     @SuppressWarnings("deprecation")
729     protected void stretchElements()
730     {
731         if (stretchElements != null && stretchElements.length > 0)
732         {
733             for(int i = 0; i < stretchElements.length; i++)
734             {
735                 JRFillElement element = stretchElements[i];
736                 
737                 element._stretchElement(stretchHeight - getContainerHeight());//TODO subtract firstY?
738                 
739                 element._moveDependantElements();
740             }
741         }
742         
743         if (ySortedElements != null && ySortedElements.length > 0)
744         {
745             for(int i = 0; i < ySortedElements.length; i++)
746             {
747                 JRFillElement element = ySortedElements[i];
748
749                 element.stretchHeightFinal();
750             }
751         }
752     }
753
754     protected void setStretchHeight(int stretchHeight)
755     {
756         if (isLegacyElementStretchEnabled())
757         {
758             _setStretchHeight(stretchHeight);
759             return;
760         }
761         
762         this.stretchHeight = stretchHeight;
763     }
764
765     /**
766      *
767      */

768     protected void stretchElementsToElementGroup()
769     {
770         if (stretchElements != null && stretchElements.length > 0)
771         {
772             for (int i = 0; i < stretchElements.length; i++)
773             {
774                 JRFillElement element = stretchElements[i];
775
776                 if (element.isToPrint())
777                 {
778                     boolean applied = element.stretchElementToElementGroup();
779                     
780                     if (applied)
781                     {
782                         element.moveDependantElements();
783                     }
784                 }
785             }
786         }
787     }
788
789     /**
790      *
791      */

792     protected void stretchElementsToContainer()
793     {
794         if (stretchElements != null && stretchElements.length > 0)
795         {
796             int containerStretch = stretchHeight - getContainerHeight();
797             
798             for (int i = 0; i < stretchElements.length; i++)
799             {
800                 JRFillElement element = stretchElements[i];
801
802                 if (element.isToPrint())
803                 {
804                     boolean applied = element.stretchElementToContainer(containerStretch);
805                     
806                     if (applied)
807                     {
808                         element.moveDependantElements();
809                     }
810                 }
811             }
812         }
813     }
814
815     
816     protected int getStretchHeight()
817     {
818         return stretchHeight;
819     }
820
821     
822     /**
823      *
824      */

825     protected void moveBandBottomElements()
826     {
827         //if (!willOverflow)
828         //{
829             if (bandBottomElements != null && bandBottomElements.length > 0)
830             {
831                 for (int i = 0; i < bandBottomElements.length; i++)
832                 {
833                     JRFillElement element = bandBottomElements[i];
834
835                     if (element.isToPrint())
836                     {
837                         // band bottom elements do not print if there will be an overflow
838                         if (currentOverflowWithElements || currentOverflowWithWhiteSpace)
839                         {
840                             currentOverflowWithElements = true;
841                         }
842                         
843                         element.setToPrint(!((currentOverflowWithElements || willOverflowWithWhiteSpace) && currentOverflowAllowed));// don't use willOverflow() method as it is overridden at least in bands
844                     }
845                     
846                     if (element.isToPrint())
847                     {
848                         element.setRelativeY(
849                             element.getY() + stretchHeight - getActualContainerHeight()
850                             );
851                     }
852                 }
853             }
854         //}
855     }
856
857
858     /**
859      * @deprecated To be removed.
860      */

861     protected void _removeBlankElements()
862     {
863         JRElement[] remElems = removableElements;
864         if (remElems != null && remElems.length > 0)
865         {
866             JRElement[] elems = ySortedElements;
867             
868             for(int i = 0; i < remElems.length; i++)
869             {
870                 JRFillElement iElem = (JRFillElement)remElems[i];
871
872                 int blankHeight;
873                 if (iElem.isToPrint())
874                 {
875                     blankHeight = iElem.getHeight() - iElem.getStretchHeight();
876                 }
877                 else
878                 {
879                     blankHeight = iElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space
880                 }
881                 
882                 if (
883                     blankHeight > 0 && 
884                     iElem.getRelativeY() + iElem.getStretchHeight() <= stretchHeight &&
885                     iElem.getRelativeY() >= firstY
886                     )
887                 {
888                     int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight;
889                     boolean isToRemove = true;
890                     
891                     for(int j = 0; j < elems.length; j++)
892                     {
893                         JRFillElement jElem = (JRFillElement)elems[j];
894                         
895                         if (iElem != jElem && jElem.isToPrint())
896                         {
897                             int top = 
898                                 Math.min(blankY, jElem.getRelativeY());
899                             int bottom = 
900                                 Math.max(
901                                     blankY + blankHeight, 
902                                     jElem.getRelativeY() + jElem.getStretchHeight()
903                                     );
904                             
905                             if (blankHeight + jElem.getStretchHeight() > bottom - top)
906                             {
907                                 isToRemove = false;
908                                 break;
909                             }
910                         }
911                     }
912                     
913                     if (isToRemove)
914                     {
915                         for(int j = 0; j < elems.length; j++)
916                         {
917                             JRFillElement jElem = (JRFillElement)elems[j];
918                             
919                             if (jElem.getRelativeY() >= blankY + blankHeight)
920                             {
921                                 jElem.setRelativeY(jElem.getRelativeY() - blankHeight);
922                             }
923                         }
924                         
925                         stretchHeight = stretchHeight - blankHeight;
926                     }
927                 }
928             }
929         }
930     }
931
932
933     /**
934      *
935      */

936     protected void removeBlankElements()
937     {
938         if (isLegacyElementStretchEnabled())
939         {
940             _removeBlankElements();
941             return;
942         }
943         
944         if (removableElements != null && removableElements.length > 0)
945         {
946             for (JRFillElement remElem : removableElements)
947             {
948                 int blankHeight;
949                 if (remElem.isToPrint())
950                 {
951                     blankHeight = remElem.getHeight() - remElem.getStretchHeight();
952                 }
953                 else
954                 {
955                     blankHeight = remElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space
956                 }
957                 
958                 if (
959                     blankHeight > 0 && 
960                     remElem.getRelativeY() + remElem.getStretchHeight() <= stretchHeight &&
961                     remElem.getRelativeY() >= firstY
962                     )
963                 {
964                     int blankY = remElem.getRelativeY() + remElem.getHeight() - blankHeight;
965                     boolean isToRemove = true;
966                     
967                     for (JRFillElement jElem : ySortedElements)
968                     {
969                         if (remElem != jElem && jElem.isToPrint())
970                         {
971                             int top = 
972                                 Math.min(blankY, jElem.getRelativeY());
973                             int bottom = 
974                                 Math.max(
975                                     blankY + blankHeight, 
976                                     jElem.getRelativeY() + jElem.getStretchHeight()
977                                     );
978                             
979                             if (blankHeight + jElem.getStretchHeight() > bottom - top)
980                             {
981                                 isToRemove = false;
982                                 break;
983                             }
984                         }
985                     }
986                     
987                     if (isToRemove)
988                     {
989                         for (JRFillElement jElem : ySortedElements)
990                         {
991                             if (jElem.getRelativeY() + jElem.getStretchHeight() <= blankY)
992                             {
993                                 jElem.setCollapsedHeightBelow(jElem.getCollapsedHeightBelow() + blankHeight);
994                             }
995
996                             if (jElem.getRelativeY() >= blankY + blankHeight)
997                             {
998                                 jElem.setCollapsedHeightAbove(jElem.getCollapsedHeightAbove() + blankHeight);
999                                 jElem.setRelativeY(jElem.getRelativeY() - blankHeight);
1000                             }
1001                         }
1002                         
1003                         stretchHeight = stretchHeight - blankHeight;
1004                     }
1005                 }
1006             }
1007         }
1008     }
1009
1010
1011     /**
1012      * Fills the elements from this container into a print element container.
1013      * 
1014      * @param printContainer the print element container
1015      * @throws JRException
1016      */

1017     public void fillElements(JRPrintElementContainer printContainer) throws JRException
1018     {
1019         //int maxStretch = 0;
1020         //int stretch = 0;
1021         int maxWidth = 0;
1022         JRElement[] allElements = getElements();
1023         if (allElements != null && allElements.length > 0)
1024         {
1025             for(int i = 0; i < allElements.length; i++)
1026             {
1027                 JRFillElement element = (JRFillElement)allElements[i];
1028                 
1029                 element.setRelativeY(element.getRelativeY() - firstY);
1030
1031                 if (element.getRelativeY() + element.getStretchHeight() > stretchHeight - firstY)
1032                 {
1033                     element.setToPrint(false);
1034                 }
1035                 
1036                 element.setAlreadyPrinted(element.isToPrint() || element.isAlreadyPrinted());
1037                 
1038                 if (element.isToPrint())
1039                 {
1040                     JRPrintElement printElement = element.fill();
1041                     //printElement.setY(printElement.getY() - firstY);
1042
1043                     if (printElement != null)
1044                     {
1045                         //FIXME not all elements affect height
1046                         //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight();
1047                         //if (stretch > maxStretch)
1048                         //{
1049                         //    maxStretch = stretch;
1050                         //}
1051                         printContainer.addElement(printElement);
1052                         if (printElement.getX() + printElement.getWidth() > maxWidth)
1053                         {
1054                             maxWidth = printElement.getX() + printElement.getWidth();
1055                         }
1056                     }
1057                     
1058                     if (element instanceof JRFillSubreport)
1059                     {
1060                         JRFillSubreport subreport = (JRFillSubreport)element;
1061                         
1062                         List<JRStyle> styles = subreport.subreportFiller.getJasperPrint().getStylesList();
1063                         for(int j = 0; j < styles.size(); j++)
1064                         {
1065                             filler.addPrintStyle(styles.get(j));
1066                         }
1067                         
1068                         List<JROrigin> origins = subreport.subreportFiller.getJasperPrint().getOriginsList();
1069                         for(int j = 0; j < origins.size(); j++)
1070                         {
1071                             filler.getJasperPrint().addOrigin(origins.get(j));
1072                         }
1073                         
1074                         Collection<JRPrintElement> printElements = subreport.getPrintElements();
1075                         addSubElements(printContainer, element, printElements);
1076                         if (subreport.getX() + subreport.getPrintContentsWidth() > maxWidth)
1077                         {
1078                             maxWidth = subreport.getX() + subreport.getPrintContentsWidth();
1079                         }
1080                         
1081                         subreport.subreportPageFilled();
1082                     }
1083                     
1084                     // crosstabs do not return a fill() element
1085                     if (element instanceof JRFillCrosstab)
1086                     {
1087                         JRFillCrosstab crosstab = (JRFillCrosstab) element;
1088                         List<? extends JRPrintElement> printElements = crosstab.getPrintElements();
1089                         addSubElements(printContainer, element, printElements);
1090                         if (crosstab.getX() + crosstab.getPrintElementsWidth() > maxWidth)
1091                         {
1092                             maxWidth = crosstab.getX() + crosstab.getPrintElementsWidth();
1093                         }
1094                     }
1095                 }
1096             }
1097         }
1098         
1099         //printBand.setHeight(getHeight() + maxStretch - firstY);
1100         printContainer.setHeight(stretchHeight - firstY);
1101         printContainer.setContentsWidth(maxWidth);
1102     }
1103
1104
1105     protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, 
1106             Collection<? extends JRPrintElement> printElements)
1107     {
1108         if (printContainer instanceof OffsetElementsContainer)
1109         {
1110             // adding the subelements as whole lists to bands so that we don't need
1111             // another virtualized list at print band level
1112             ((OffsetElementsContainer) printContainer).addOffsetElements(printElements, 
1113                     element.getX(), element.getRelativeY());
1114         }
1115         else
1116         {
1117             if (printElements != null && printElements.size() > 0)
1118             {
1119                 for(Iterator<? extends JRPrintElement> it = printElements.iterator(); it.hasNext();)
1120                 {
1121                     JRPrintElement printElement =it.next();
1122                     printElement.setX(element.getX() + printElement.getX());
1123                     printElement.setY(element.getRelativeY() + printElement.getY());
1124                     printContainer.addElement(printElement);
1125                 }
1126             }
1127         }
1128     }
1129
1130     
1131     /**
1132      *
1133      */

1134     protected void rewind() throws JRException
1135     {
1136         if (ySortedElements != null && ySortedElements.length > 0)
1137         {
1138             for(int i = 0; i < ySortedElements.length; i++)
1139             {
1140                 JRFillElement element = ySortedElements[i];
1141
1142                 element.rewind();
1143
1144                 element.setAlreadyPrinted(false);
1145             }
1146         }
1147         
1148         willOverflowWithElements = false;
1149         willOverflowWithWhiteSpace = false;
1150     }
1151     
1152     protected int getFirstY()
1153     {
1154         return firstY;
1155     }
1156
1157     
1158     /**
1159      * Returns the actual height of the element container.
1160      * Some element containers such as frames have a larger calculated container height, resulting from content being placed beyond container declared height.
1161      * 
1162      * @return the height of the element container
1163      */

1164     protected abstract int getActualContainerHeight();
1165
1166
1167     /**
1168      * Returns the height of the element container.
1169      * 
1170      * @return the height of the element container
1171      */

1172     protected abstract int getContainerHeight();
1173
1174
1175     /**
1176      * Find all styles containing conditional styles which are referenced by elements in this band.
1177      */

1178     protected void initConditionalStyles()
1179     {
1180         filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener(){
1181             @Override
1182             public void defaultStyleSet(JRStyle style)
1183             {
1184                 collectConditionalStyle(style);
1185             }
1186         });
1187         
1188         for (int i = 0; i < deepElements.length; i++)
1189         {
1190             JRStyle style = deepElements[i].initStyle;
1191             collectConditionalStyle(style);
1192         }
1193         
1194         if (deepElements.length > 0)
1195         {
1196             for(int i = 0; i < deepElements.length; i++)
1197             {
1198                 deepElements[i].setConditionalStylesContainer(this);
1199             }
1200         }
1201     }
1202
1203     protected void collectConditionalStyle(JRStyle style)
1204     {
1205         if (style != null)// && style.getConditionalStyles() != null)
1206         {
1207             stylesToEvaluate.add(style);
1208         }
1209     }
1210
1211
1212     protected void evaluateConditionalStyles(byte evaluation) throws JRException
1213     {
1214         for (Iterator<JRStyle> it = stylesToEvaluate.iterator(); it.hasNext();) 
1215         {
1216             evaluateConditionalStyle(it.next(), evaluation);
1217         }
1218     }
1219
1220
1221     protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException
1222     {
1223         JRStyle consolidatedStyle = initialStyle;
1224
1225         StringBuilder code = new StringBuilder();
1226         List<JRStyle> condStylesToApply = new ArrayList<JRStyle>();
1227         
1228         boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply);
1229         
1230         if (anyTrue)
1231         {
1232             String consolidatedStyleName = initialStyle.getName() + "|" + code.toString();
1233             consolidatedStyle = filler.getJasperPrint().getStylesMap().get(consolidatedStyleName);
1234             if (consolidatedStyle == null)
1235             {
1236                 JRBaseStyle style = new JRBaseStyle(consolidatedStyleName);
1237                 for (int j = condStylesToApply.size() - 1; j >= 0; j--)
1238                 {
1239                     StyleUtil.appendStyle(style, condStylesToApply.get(j));
1240                 }
1241
1242                 // deduplicate to previously created identical instances
1243                 style = filler.fillContext.deduplicate(style);
1244                 filler.addPrintStyle(style);
1245                 
1246                 consolidatedStyle = style;
1247             }
1248         }
1249
1250         evaluatedStyles.put(initialStyle, consolidatedStyle);
1251         
1252         return consolidatedStyle;
1253     }
1254
1255
1256     protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuilder code, List<JRStyle> condStylesToApply) throws JRException
1257     {
1258         boolean anyTrue = false;
1259         
1260         JRConditionalStyle[] conditionalStyles = style.getConditionalStyles();
1261         if (conditionalStyles != null && conditionalStyles.length > 0)
1262         {
1263             for (int j = 0; j < conditionalStyles.length; j++) 
1264             {
1265                 JRConditionalStyle conditionalStyle = conditionalStyles[j];
1266                 Boolean expressionValue = 
1267                     (Boolean) expressionEvaluator.evaluate(
1268                         conditionalStyle.getConditionExpression(),
1269                         evaluation
1270                         );
1271                 
1272                 boolean condition;
1273                 if (expressionValue == null)
1274                 {
1275                     condition = false;
1276                 }
1277                 else
1278                 {
1279                     condition = expressionValue;
1280                 }
1281                 
1282                 code.append(condition ? '1' : '0');
1283                 anyTrue = anyTrue | condition;
1284
1285                 if (condition)
1286                 {
1287                     condStylesToApply.add(conditionalStyle);
1288                 }
1289             }
1290         }
1291
1292         condStylesToApply.add(style);
1293         
1294         if (style.getStyle() != null)
1295         {
1296             anyTrue = anyTrue | buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply);
1297         }
1298         return anyTrue;
1299     }
1300
1301
1302     public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle)
1303     {
1304         return evaluatedStyles.get(parentStyle);
1305     }
1306     
1307     protected final void setElementOriginProvider(JROriginProvider originProvider)
1308     {
1309         if (originProvider != null)
1310         {
1311             for (int i = 0; i < deepElements.length; i++)
1312             {
1313                 deepElements[i].setOriginProvider(originProvider);
1314             }
1315         }
1316     }
1317 }
1318