Skip to content

Commit 4e73b87

Browse files
committed
Added ability to fill a vertically-filled page with blank data rows.
1 parent 357cdf1 commit 4e73b87

File tree

8 files changed

+1834
-1
lines changed

8 files changed

+1834
-1
lines changed

jasperreports/src/net/sf/jasperreports/engine/JRParameter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,11 @@ public interface JRParameter extends JRPropertiesHolder, JRCloneable
262262
*/
263263
public static final String REPORT_FORMAT_FACTORY = "REPORT_FORMAT_FACTORY";
264264

265+
/**
266+
* An option allowing the remainder of a vertically-filled report's detail section to be filled with blank rows of
267+
* data when the main data set is exhausted.
268+
*/
269+
public static final String REPORT_FILL_DETAIL_WITH_BLANK_ROWS = "REPORT_FILL_DETAIL_WITH_BLANK_ROWS";
265270

266271
/**
267272
* This built-in flag parameter specifies whether to ignore pagination.

jasperreports/src/net/sf/jasperreports/engine/fill/JRBaseFiller.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,13 @@ protected boolean next() throws JRException
11941194
return mainDataset.next();
11951195
}
11961196

1197+
/**
1198+
* @return whether elements should evaluate as blank
1199+
*/
1200+
protected boolean evaluateElementToBlank() {
1201+
return false;
1202+
}
1203+
11971204
/**
11981205
* Resolves elements which are to be evaluated at report level.
11991206
*/

jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,9 @@ protected boolean delayedEvaluationUpdatesTemplate()
11661166
*/
11671167
public final Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException
11681168
{
1169+
if (filler.evaluateElementToBlank()) {
1170+
return null;
1171+
}
11691172
return expressionEvaluator.evaluate(expression, evaluation);
11701173
}
11711174

jasperreports/src/net/sf/jasperreports/engine/fill/JRVerticalFiller.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@
2323
*/
2424
package net.sf.jasperreports.engine.fill;
2525

26+
import java.util.Map;
27+
2628
import org.apache.commons.javaflow.api.continuable;
2729
import org.apache.commons.logging.Log;
2830
import org.apache.commons.logging.LogFactory;
2931

3032
import net.sf.jasperreports.engine.JRException;
3133
import net.sf.jasperreports.engine.JRExpression;
3234
import net.sf.jasperreports.engine.JRGroup;
35+
import net.sf.jasperreports.engine.JRParameter;
3336
import net.sf.jasperreports.engine.JRRuntimeException;
37+
import net.sf.jasperreports.engine.JasperPrint;
3438
import net.sf.jasperreports.engine.JasperReport;
3539
import net.sf.jasperreports.engine.JasperReportsContext;
3640
import net.sf.jasperreports.engine.type.FooterPositionEnum;
@@ -47,6 +51,16 @@ public class JRVerticalFiller extends JRBaseFiller
4751

4852
private static final Log log = LogFactory.getLog(JRVerticalFiller.class);
4953

54+
// Whether we are configured to fill the detail section with blank rows of data.
55+
// Is configured to not fill by default.
56+
private boolean fillDetailWithBlankRows = false;
57+
58+
// Flag to indicate whether we've reached the end of our main data set.
59+
private boolean isEndOfMainDataset = false;
60+
61+
// Flag to indicate when we're writing the detail section.
62+
private boolean writingDetailSection = false;
63+
5064
/**
5165
*
5266
*/
@@ -81,6 +95,26 @@ public JRVerticalFiller(
8195
setPageHeight(pageHeight);
8296
}
8397

98+
@Override
99+
protected void setParameters(Map<String, Object> parameterValues) throws JRException
100+
{
101+
super.setParameters(parameterValues);
102+
103+
setFillDetailWithBlankRows(parameterValues);
104+
}
105+
106+
protected void setFillDetailWithBlankRows(Map<String,Object> parameterValues)
107+
{
108+
Object value = parameterValues.getOrDefault(JRParameter.REPORT_FILL_DETAIL_WITH_BLANK_ROWS, Boolean.FALSE);
109+
if (value instanceof String)
110+
{
111+
fillDetailWithBlankRows = Boolean.parseBoolean((String)value);
112+
} else if (value instanceof Boolean)
113+
{
114+
fillDetailWithBlankRows = ((Boolean)value).booleanValue();
115+
}
116+
}
117+
84118

85119
@Override
86120
protected void setPageHeight(int pageHeight)
@@ -98,6 +132,15 @@ protected void setPageHeight(int pageHeight)
98132
}
99133
}
100134

135+
/**
136+
* {@inheritDoc}
137+
*/
138+
@Override
139+
public JasperPrint fill(Map<String, Object> parameterValues) throws JRException
140+
{
141+
isEndOfMainDataset = false;
142+
return super.fill(parameterValues);
143+
}
101144

102145
@Override
103146
@continuable
@@ -231,7 +274,42 @@ protected synchronized void fillReport() throws JRException
231274
}
232275
}
233276

277+
/**
278+
* {@inheritDoc}
279+
*/
280+
@Override
281+
protected boolean next() throws JRException {
282+
if (super.next()) {
283+
return true;
284+
}
285+
if (fillDetailWithBlankRows) {
286+
// If we have no more data in our main data set and we are configured to fill will blank rows,
287+
// move the cursor forward until we have reached the end of the current page's detail section.
288+
isEndOfMainDataset = true;
289+
return !endOfPageDetail();
290+
}
291+
return false;
292+
}
293+
294+
private boolean endOfPageDetail() {
295+
JRFillBand[] detailBands = detailSection.getFillBands();
296+
for (int i = 0; i < detailBands.length; i++) {
297+
JRFillBand detailBand = detailBands[i];
298+
if (detailBand.isToPrint() && (detailBand.getBreakHeight() > columnFooterOffsetY - offsetY)) {
299+
return true;
300+
}
301+
}
302+
return false;
303+
}
234304

305+
/**
306+
* {@inheritDoc}
307+
*/
308+
@Override
309+
protected boolean evaluateElementToBlank() {
310+
return isEndOfMainDataset && writingDetailSection;
311+
}
312+
235313
/**
236314
*
237315
*/
@@ -752,6 +830,8 @@ private void fillGroupHeaderReprint(JRFillGroup group, byte evaluation) throws J
752830
@continuable
753831
private void fillDetail() throws JRException
754832
{
833+
writingDetailSection = true;
834+
755835
if (log.isDebugEnabled() && !detailSection.isEmpty())
756836
{
757837
log.debug("Fill " + fillerId + ": detail at " + offsetY);
@@ -851,6 +931,7 @@ private void fillDetail() throws JRException
851931

852932
isNewPage = false;
853933
isNewColumn = false;
934+
writingDetailSection = false;
854935
}
855936

856937

jasperreports/tests/net/sf/jasperreports/AbstractTest.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
import java.util.HashMap;
3939
import java.util.List;
4040
import java.util.Locale;
41+
import java.util.Map;
4142
import java.util.TimeZone;
4243

4344
import org.apache.commons.logging.Log;
4445
import org.apache.commons.logging.LogFactory;
4546
import org.testng.annotations.BeforeClass;
4647

48+
import net.sf.jasperreports.engine.JRDataSource;
4749
import net.sf.jasperreports.engine.JRException;
4850
import net.sf.jasperreports.engine.JRParameter;
4951
import net.sf.jasperreports.engine.JasperCompileManager;
@@ -103,16 +105,26 @@ protected void runReport(String jrxmlFileName, String referenceFileNamePrefix)
103105
HashMap<String, Object> params = new HashMap<String, Object>();
104106
params.put(JRParameter.REPORT_LOCALE, Locale.US);
105107
params.put(JRParameter.REPORT_TIME_ZONE, TimeZone.getTimeZone("GMT"));
108+
params.putAll(additionalReportParams());
106109
params.put(TEST, this);
107110

111+
JRDataSource dataSource = createDataSource();
112+
108113
log.debug("Running report " + jrxmlFileName);
109114

110115
try
111116
{
112117
JasperReport report = compileReport(jrxmlFileName);
113118
if (report != null)
114119
{
115-
JasperPrint print = fillManager.fill(report, params);
120+
JasperPrint print;
121+
if (dataSource == null)
122+
{
123+
print = fillManager.fill(report, params);
124+
} else
125+
{
126+
print = fillManager.fill(report, params, dataSource);
127+
}
116128

117129
assert !print.getPages().isEmpty();
118130

@@ -154,6 +166,14 @@ protected void runReport(String jrxmlFileName, String referenceFileNamePrefix)
154166
}
155167
}
156168

169+
protected Map<String, Object> additionalReportParams() {
170+
return new HashMap<>();
171+
}
172+
173+
protected JRDataSource createDataSource() {
174+
return null;
175+
}
176+
157177
protected JasperReportsContext getJasperReportsContext()
158178
{
159179
return jasperReportsContext;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.bands.fill;
25+
26+
import java.io.IOException;
27+
import java.security.NoSuchAlgorithmException;
28+
import java.util.HashMap;
29+
import java.util.Map;
30+
import java.util.stream.IntStream;
31+
32+
import org.testng.annotations.DataProvider;
33+
import org.testng.annotations.Test;
34+
35+
import net.sf.jasperreports.AbstractTest;
36+
import net.sf.jasperreports.engine.JRDataSource;
37+
import net.sf.jasperreports.engine.JRException;
38+
import net.sf.jasperreports.engine.JRParameter;
39+
import net.sf.jasperreports.engine.data.JRBeanArrayDataSource;
40+
41+
/** @author Tim Hewson */
42+
public class BandFillTest extends AbstractTest
43+
{
44+
45+
@Override
46+
protected Map<String, Object> additionalReportParams()
47+
{
48+
Map<String, Object> params = new HashMap<String, Object>();
49+
params.put(JRParameter.REPORT_FILL_DETAIL_WITH_BLANK_ROWS, Boolean.TRUE);
50+
return params;
51+
}
52+
53+
54+
@Override
55+
protected JRDataSource createDataSource()
56+
{
57+
return new JRBeanArrayDataSource(
58+
IntStream.range(0, 90)
59+
.mapToObj(i -> new Ascii(33 + i, String.valueOf((char) (33 + i))))
60+
.toArray());
61+
}
62+
63+
@Test(dataProvider = "testArgs")
64+
public void testReport(String jrxmlFileName, String referenceFileNamePrefix)
65+
throws JRException, NoSuchAlgorithmException, IOException
66+
{
67+
runReport(jrxmlFileName, referenceFileNamePrefix);
68+
}
69+
70+
@DataProvider
71+
public Object[][] testArgs()
72+
{
73+
return runReportArgs("net/sf/jasperreports/bands/fill/repo", "BandFillReport", 40);
74+
}
75+
76+
public static class Ascii
77+
{
78+
long record_num;
79+
String record_val;
80+
81+
Ascii(long number, String value) {
82+
this.record_num = number;
83+
this.record_val = value;
84+
}
85+
86+
public long getRecord_num() {
87+
return record_num;
88+
}
89+
90+
public String getRecord_val() {
91+
return record_val;
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)