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.awt.Color;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import net.sf.jasperreports.engine.JRChild;
32 import net.sf.jasperreports.engine.JRElement;
33 import net.sf.jasperreports.engine.JRException;
34 import net.sf.jasperreports.engine.JRExpressionCollector;
35 import net.sf.jasperreports.engine.JRFrame;
36 import net.sf.jasperreports.engine.JRLineBox;
37 import net.sf.jasperreports.engine.JRPrintElement;
38 import net.sf.jasperreports.engine.JRStyle;
39 import net.sf.jasperreports.engine.JRVisitor;
40 import net.sf.jasperreports.engine.base.JRBaseElementGroup;
41 import net.sf.jasperreports.engine.type.BorderSplitType;
42 import net.sf.jasperreports.engine.type.ModeEnum;
43 import net.sf.jasperreports.engine.util.ElementsVisitorUtils;
44 import net.sf.jasperreports.engine.util.JRBoxUtil;
45
46 /**
47  * Fill time implementation of a frame element.
48  * 
49  * @author Lucian Chirita (lucianc@users.sourceforge.net)
50  */

51 public class JRFillFrame extends JRFillElement implements JRFrame
52 {
53     protected final JRFrame parentFrame;
54     
55     protected final JRLineBox lineBox;
56     
57     protected final BorderSplitType borderSplitType;
58     
59     protected final boolean widthStretchEnabled;
60     
61     /**
62      * Element container used for filling.
63      */

64     private JRFillFrameElements frameContainer;
65     
66     /**
67      * Template frame without the bottom border.
68      */

69     private Map<JRStyle,JRTemplateElement> bottomTemplateFrames;
70     
71     /**
72      * Template frame without the top border
73      */

74     private Map<JRStyle,JRTemplateElement> topTemplateFrames;
75     
76     /**
77      * Template frame without the top and bottom borders
78      */

79     private Map<JRStyle,JRTemplateElement> topBottomTemplateFrames;
80     
81     private boolean fillTopBorder;
82     private boolean fillBottomBorder;
83     
84     /**
85      * Whether the frame has started filling and not ended.
86      */

87     private boolean filling;
88
89     public JRFillFrame(JRBaseFiller filler, JRFrame frame, JRFillObjectFactory factory)
90     {
91         super(filler, frame, factory);
92         
93         parentFrame = frame;
94         
95         lineBox = frame.getLineBox().clone(this);
96         borderSplitType = initBorderSplitType(filler, frame);
97         widthStretchEnabled = initWidthStretchEnabled(filler, frame);
98         
99         frameContainer = new JRFillFrameElements(factory);
100         
101         bottomTemplateFrames = new HashMap<JRStyle,JRTemplateElement>();
102         topTemplateFrames = new HashMap<JRStyle,JRTemplateElement>();
103         topBottomTemplateFrames = new HashMap<JRStyle,JRTemplateElement>();
104         
105         setShrinkable(true);
106     }
107
108     protected JRFillFrame(JRFillFrame frame, JRFillCloneFactory factory)
109     {
110         super(frame, factory);
111         
112         parentFrame = frame.parentFrame;
113         
114         lineBox = frame.getLineBox().clone(this);
115         borderSplitType = frame.borderSplitType;
116         widthStretchEnabled = frame.widthStretchEnabled;
117         
118         frameContainer = new JRFillFrameElements(frame.frameContainer, factory);
119         
120         bottomTemplateFrames = frame.bottomTemplateFrames;
121         topTemplateFrames = frame.topTemplateFrames;
122         topBottomTemplateFrames = frame.topBottomTemplateFrames;
123     }
124     
125     private BorderSplitType initBorderSplitType(JRBaseFiller filler, JRFrame frame)
126     {
127         BorderSplitType splitType = frame.getBorderSplitType();
128         if (splitType == null)
129         {
130             String splitTypeProp = filler.getPropertiesUtil().getProperty(filler.getJasperReport(), PROPERTY_BORDER_SPLIT_TYPE); // property expression does not work, 
131             // but even if we would call filler.getMainDataset(), it would be too early as it is null here for frame elements placed in group bands
132             if (splitTypeProp != null)
133             {
134                 splitType = BorderSplitType.byName(splitTypeProp);
135             }
136         }
137         return splitType;
138     }
139     
140     private boolean initWidthStretchEnabled(JRBaseFiller filler, JRFrame frame)
141     {
142         boolean stretchDisabled = filler.getPropertiesUtil().getBooleanProperty(
143                 PROPERTY_FRAME_WIDTH_STRETCH_DISABLED, false
144                 frame, filler.getJasperReport());
145         return !stretchDisabled;
146     }
147
148     @Override
149     public ModeEnum getModeValue()
150     {
151         return getStyleResolver().getMode(this, ModeEnum.TRANSPARENT);
152     }
153
154     @Override
155     public Color getDefaultLineColor() 
156     {
157         return getForecolor();
158     }
159
160     
161     @Override
162     protected void evaluate(byte evaluation) throws JRException
163     {
164         reset();
165
166         evaluatePrintWhenExpression(evaluation);
167         if (isPrintWhenExpressionNull() || isPrintWhenTrue())
168         {
169             evaluateProperties(evaluation);
170             evaluateStyle(evaluation);
171
172             frameContainer.evaluate(evaluation);
173             
174             boolean repeating = true;
175             JRFillElement[] elements = (JRFillElement[]) getElements();
176             for (int i = 0; repeating && i < elements.length; i++)
177             {
178                 repeating &= elements[i].isValueRepeating();
179             }
180             setValueRepeating(repeating);
181         }
182         
183         filling = false;
184     }
185
186     @Override
187     protected void rewind() throws JRException
188     {
189         frameContainer.rewind();
190         
191         filling = false;
192     }
193
194     protected boolean drawTopBorderOnSplit()
195     {
196         return borderSplitType == BorderSplitType.DRAW_BORDERS;
197     }
198
199     protected boolean drawBotomBorderOnSplit()
200     {
201         return borderSplitType == BorderSplitType.DRAW_BORDERS;
202     }
203     
204     @Override
205     protected boolean prepare(int availableHeight, boolean isOverflow) throws JRException
206     {
207         super.prepare(availableHeight, isOverflow);
208
209         if (!isToPrint())
210         {
211             return false;
212         }
213         
214         // whether the current frame chunk is the first one.
215         boolean first = !isOverflow || !filling;
216         
217         int topPadding = getLineBox().getTopPadding();
218         int bottomPadding = getLineBox().getBottomPadding();        
219         
220         if (availableHeight < getRelativeY() + getHeight() - topPadding - bottomPadding)
221         {
222             setToPrint(false);
223             return true;
224         }
225         
226         if (!filling && !isPrintRepeatedValues() && isValueRepeating() &&
227                 (!isPrintInFirstWholeBand() || !getBand().isFirstWholeOnPageColumn()) &&
228                 (getPrintWhenGroupChanges() == null || !getBand().isNewGroup(getPrintWhenGroupChanges())) &&
229                 (!isOverflow || !isPrintWhenDetailOverflows())
230             )
231         {
232             setToPrint(false);
233             return false;
234         }
235
236         // FIXME reprinted when isAlreadyPrinted() || !isPrintRepeatedValues()?
237         if (!filling && isOverflow && isAlreadyPrinted())
238         {
239             if (isPrintWhenDetailOverflows())
240             {
241                 rewind();
242                 setReprinted(true);
243             }
244             else
245             {
246                 setToPrint(false);
247                 return false;
248             }
249         }
250         
251         frameContainer.initFill();
252         frameContainer.resetElements();
253         
254         frameContainer.prepareElements(availableHeight - getRelativeY() - topPadding - bottomPadding, true);
255         
256         boolean willOverflow = frameContainer.willOverflow();
257         fillTopBorder = first || drawTopBorderOnSplit();
258         fillBottomBorder = !willOverflow || drawBotomBorderOnSplit();
259         
260         if (willOverflow)
261         {
262             setPrepareHeight(availableHeight - getRelativeY());
263         }
264         else
265         {
266             int neededStretch = frameContainer.getStretchHeight() - frameContainer.getFirstY() + topPadding + bottomPadding;
267             if (neededStretch <= availableHeight - getRelativeY()) 
268             {
269                 setPrepareHeight(neededStretch);
270             }
271             else
272             {
273                 //FIXME is this case possible?
274                 setPrepareHeight(availableHeight - getRelativeY());
275             }
276         }
277
278         filling = willOverflow;
279
280         return willOverflow;
281     }
282
283     @Override
284     protected void setStretchHeight(int stretchHeight)
285     {
286         super.setStretchHeight(stretchHeight);
287         
288         int topPadding = getLineBox().getTopPadding();
289         int bottomPadding = getLineBox().getBottomPadding();        
290         frameContainer.setStretchHeight(stretchHeight + frameContainer.getFirstY() - topPadding - bottomPadding);
291     }
292     
293     
294     /**
295      * @deprecated To be removed.
296      */

297     @Override
298     protected void stretchHeightFinal()
299     {
300         // only do this if the frame is printing
301         if (isToPrint())
302         {
303             frameContainer.stretchElements();
304             frameContainer.moveBandBottomElements();
305             frameContainer.removeBlankElements();
306
307             int topPadding = getLineBox().getTopPadding();
308             int bottomPadding = getLineBox().getBottomPadding();
309             super.setStretchHeight(frameContainer.getStretchHeight() - frameContainer.getFirstY() + topPadding + bottomPadding);
310         }
311     }
312
313
314     @Override
315     protected boolean stretchElementToHeight(int stretchHeight)
316     {
317         boolean applied = super.stretchElementToHeight(stretchHeight); 
318         if (applied)
319         {
320             frameContainer.stretchElementsToContainer();
321             frameContainer.moveBandBottomElements();
322         }
323         return applied;
324     }
325
326
327     @Override
328     protected JRPrintElement fill() throws JRException
329     {        
330         JRTemplatePrintFrame printFrame = new JRTemplatePrintFrame(getTemplate(), printElementOriginator);
331         printFrame.setUUID(getUUID());
332         printFrame.setX(getX());
333         printFrame.setY(getRelativeY());
334         
335         VirtualizableFrame virtualizableFrame = new VirtualizableFrame(printFrame, 
336                 filler.getVirtualizationContext(), filler.getCurrentPage());
337         frameContainer.fillElements(virtualizableFrame);
338         virtualizableFrame.fill();
339         
340         int width = getWidth();
341         if (widthStretchEnabled)
342         {
343             JRLineBox printBox = printFrame.getLineBox();
344             int padding = (printBox.getLeftPadding() == null ? 0 : printBox.getLeftPadding())
345                     + (printBox.getRightPadding() == null ? 0 : printBox.getRightPadding());
346             if (virtualizableFrame.getContentsWidth() + padding > width)
347             {
348                 width = virtualizableFrame.getContentsWidth() + padding;
349             }
350         }
351         printFrame.setWidth(width);
352         
353         printFrame.setHeight(getStretchHeight());
354         transferProperties(printFrame);
355         
356         return printFrame;
357     }
358
359     protected JRTemplateFrame getTemplate()
360     {
361         JRStyle style = getStyle();
362
363         Map<JRStyle,JRTemplateElement> templatesMap;
364         if (fillTopBorder)
365         {
366             if (fillBottomBorder)
367             {
368                 templatesMap = templates;
369             }
370             else //remove the bottom border
371             {
372                 templatesMap = bottomTemplateFrames;
373             }
374         }
375         else
376         {
377             if (fillBottomBorder) //remove the top border
378             {
379                 templatesMap = topTemplateFrames;
380             }
381             else //remove the top and bottom borders
382             {
383                 templatesMap = topBottomTemplateFrames;
384             }
385         }
386         
387         JRTemplateFrame boxTemplate = (JRTemplateFrame) templatesMap.get(style);
388         if (boxTemplate == null)
389         {
390             boxTemplate = createFrameTemplate();
391             transferProperties(boxTemplate);
392             
393             //FIXME up to revision 2006 (Dec 5 2007) we were resetting both the border and the padding.
394             // now we are only resetting the border and not the padding, prepare() assumes that the top and bottom paddings are always used.
395             if (fillTopBorder)
396             {
397                 if (!fillBottomBorder) //remove the bottom border
398                 {                
399                     boxTemplate.copyBox(getLineBox());
400                     JRBoxUtil.reset(boxTemplate.getLineBox(), falsefalsefalsetrue);
401                 }
402             }
403             else
404             {
405                 if (fillBottomBorder) //remove the top border
406                 {
407                     boxTemplate.copyBox(getLineBox());
408                     JRBoxUtil.reset(boxTemplate.getLineBox(), falsefalsetruefalse);
409                 }
410                 else //remove the top and bottom borders
411                 {
412                     boxTemplate.copyBox(getLineBox());
413                     JRBoxUtil.reset(boxTemplate.getLineBox(), falsefalsetruetrue);                    
414                 }
415             }
416             
417             boxTemplate = filler.fillContext.deduplicate(boxTemplate);
418             templatesMap.put(style, boxTemplate);
419         }
420         
421         return boxTemplate;
422     }
423
424     protected JRTemplateFrame createFrameTemplate()
425     {
426         return new JRTemplateFrame(getElementOrigin(), 
427                 filler.getJasperPrint().getDefaultStyleProvider(), this);
428     }
429
430     @Override
431     protected JRTemplateElement createElementTemplate()
432     {
433         return createFrameTemplate();
434     }
435
436     @Override
437     protected void resolveElement(JRPrintElement element, byte evaluation)
438     {
439         // nothing
440     }
441
442     @Override
443     public JRElement[] getElements()
444     {
445         return frameContainer.getElements();
446     }
447     
448     @Override
449     public List<JRChild> getChildren()
450     {
451         return frameContainer.getChildren();
452     }
453
454     @Override
455     public void collectExpressions(JRExpressionCollector collector)
456     {
457         collector.collect(this);
458     }
459
460     @Override
461     public JRLineBox getLineBox()
462     {
463         return lineBox;
464     }
465
466
467     @Override
468     public BorderSplitType getBorderSplitType()
469     {
470         return borderSplitType;
471     }
472     
473     @Override
474     public void visit(JRVisitor visitor)
475     {
476         visitor.visitFrame(this);
477         
478         if (ElementsVisitorUtils.visitDeepElements(visitor))
479         {
480             ElementsVisitorUtils.visitElements(visitor, getChildren());
481         }
482     }
483     
484     
485     @Override
486     public JRElement getElementByKey(String key)
487     {
488         return JRBaseElementGroup.getElementByKey(getElements(), key);
489     }
490
491     @Override
492     public JRFillCloneable createClone(JRFillCloneFactory factory)
493     {
494         return new JRFillFrame(this, factory);
495     }
496     
497
498     /**
499      * Frame element container filler.
500      */

501     protected class JRFillFrameElements extends JRFillElementContainer
502     {
503         JRFillFrameElements(JRFillObjectFactory factory)
504         {
505             super(JRFillFrame.this.filler, parentFrame, factory);
506             initElements();
507         }
508
509         JRFillFrameElements(JRFillFrameElements frameElements, JRFillCloneFactory factory)
510         {
511             super(frameElements, factory);
512             initElements();
513         }
514
515         @Override
516         protected int getContainerHeight()
517         {
518             return JRFillFrame.this.getHeight() - getLineBox().getTopPadding() - getLineBox().getBottomPadding(); 
519         }
520
521         @Override
522         protected int getActualContainerHeight()
523         {
524             int containerHeight = JRFillFrame.this.getHeight() - getLineBox().getTopPadding() - getLineBox().getBottomPadding(); 
525             
526             if (JRFillFrame.this.frameContainer.bottomElementInGroup != null)
527             {
528                 if (
529                     getLineBox().getTopPadding() 
530                     + JRFillFrame.this.frameContainer.bottomElementInGroup.getY() 
531                     + JRFillFrame.this.frameContainer.bottomElementInGroup.getHeight() > JRFillFrame.this.getHeight()
532                     )
533                 {
534                     containerHeight = 
535                         JRFillFrame.this.frameContainer.bottomElementInGroup.getY() 
536                         + JRFillFrame.this.frameContainer.bottomElementInGroup.getHeight();
537                 }
538             }
539
540             return containerHeight; 
541         }
542
543         @Override
544         public boolean isSplitTypePreventInhibited(boolean isTopLevelCall)
545         {
546             //not actually called because fillContainerContext in the children is actually the band
547             return JRFillFrame.this.fillContainerContext.isSplitTypePreventInhibited(isTopLevelCall);
548         }
549     }
550     
551 }
552