/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.components.reports.builder;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.sf.jasperreports.engine.JRDefaultStyleProvider;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignElement;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignField;
import net.sf.jasperreports.engine.design.JRDesignImage;
import net.sf.jasperreports.engine.design.JRDesignLine;
import net.sf.jasperreports.engine.design.JRDesignSection;
import net.sf.jasperreports.engine.design.JRDesignStaticText;
import net.sf.jasperreports.engine.design.JRDesignSubreport;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.type.EvaluationTimeEnum;
import net.sf.jasperreports.engine.type.HorizontalAlignEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.engine.type.PositionTypeEnum;
import net.sf.jasperreports.engine.type.ScaleImageEnum;
import net.sf.jasperreports.engine.type.StretchTypeEnum;
import net.sf.jasperreports.engine.type.VerticalAlignEnum;
import net.sf.jasperreports.engine.type.WhenNoDataTypeEnum;
import net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.components.reports.ReportConstants;
import com.opendoorlogistics.core.AppConstants;
import com.opendoorlogistics.core.tables.ColumnValueProcessor;
import com.opendoorlogistics.core.utils.strings.Strings;
final public class ReportBuilderUtils {
private ReportBuilderUtils() {
}
// static void addColumnHeaderSection(ODLTableDefinition table, JasperDesign ret) {
// addColumnHeaderSection(table, getAvailableWidth(ret), ret);
// }
static void addColumnHeaderSection(ODLTableDefinition table, int elementWidth, JasperDesign ret) {
// Add column header
JRDesignBand chBand = new JRDesignBand();
chBand.setHeight(20);
addColumnHeaderToBand(table, elementWidth, chBand);
ret.setColumnHeader(chBand);
}
private static void addColumnHeaderToBand(ODLTableDefinition table, int elementWidth, JRDesignBand chBand) {
JRDesignStaticText back = new JRDesignStaticText();
back.setBackcolor(new Color(230, 230, 230));
back.setWidth(elementWidth);
back.setHeight(20);
back.setMode(ModeEnum.OPAQUE);
chBand.addElement(back);
List<Double> colWidths = getColumnWidths(table, elementWidth);
int nc = table.getColumnCount();
if (nc > 0) {
double dx=0;
for (int i = 0; i < nc; i++) {
JRDesignStaticText text = new JRDesignStaticText();
int x = (int) Math.round(dx);
text.setX(x);
text.setY(4);
text.setWidth((int) Math.floor(colWidths.get(i)));
text.setHeight(15);
text.setText(table.getColumnName(i));
text.setFontSize(11);
// int fs = text.getFontSize();
text.setForecolor(new Color(0, 0, 80));
text.setBold(true);
chBand.addElement(text);
dx += colWidths.get(i);
}
}
JRDesignLine line = new JRDesignLine();
// line.setX(-ret.getLeftMargin());
line.setY(19);
line.setWidth(elementWidth);
line.setHeight(0);
line.setPositionType(PositionTypeEnum.FLOAT);
chBand.addElement(line);
}
static int getAvailableWidth(JasperDesign design) {
return design.getPageWidth() - design.getLeftMargin() - design.getRightMargin();
}
static int getAvailablePageHeight(JasperDesign design) {
return design.getPageHeight() - design.getTopMargin() - design.getBottomMargin();
}
static JasperDesign createEmptyA4(String tablename, OrientationEnum orientation, boolean margins, int horizontalReduction) {
switch (orientation) {
case LANDSCAPE:
return createEmpty(tablename, 842 - horizontalReduction, 595, margins);
case PORTRAIT:
return createEmpty(tablename, 595 - horizontalReduction, 842, margins);
default:
throw new RuntimeException();
}
}
static JasperDesign createEmpty(String tablename, int pageWidth, int pageHeight, boolean margins) {
JasperDesign ret = new JasperDesign();
ret.setName(Strings.removeExportIllegalChars(AppConstants.ORG_NAME + " - " + tablename));
ret.setWhenNoDataType(WhenNoDataTypeEnum.ALL_SECTIONS_NO_DETAIL);
ret.setWhenResourceMissingType(WhenResourceMissingTypeEnum.EMPTY);
ret.setOrientation(OrientationEnum.LANDSCAPE);
ret.setPageWidth(pageWidth);
ret.setPageHeight(pageHeight);
if (!margins) {
ret.setLeftMargin(0);
ret.setRightMargin(0);
ret.setTopMargin(0);
ret.setBottomMargin(0);
}
ret.setColumnWidth(Math.min(getAvailableWidth(ret), ret.getColumnWidth()));
return ret;
}
static int addDetailBand(ODLTableDefinition table, int elementWidth, boolean isHeaderRowForSubreport, JasperDesign ret) {
// add details
JRDesignSection detailSection = (JRDesignSection) ret.getDetailSection();
JRDesignBand band = new JRDesignBand();
List<Double> colWidths = getColumnWidths(table, elementWidth);
// decide on the row height.. set differently if have images; assume images are square
int headerHeight=0;
int rowHeight = 18;
if (isHeaderRowForSubreport) {
headerHeight = 22;
rowHeight = 24;
// repeat header for each master report element
addColumnHeaderToBand(table, elementWidth, band);
}
// make row taller if we have an image, based on making the image square
int nc = table.getColumnCount();
for (int i = 0; i < nc; i++) {
if (table.getColumnType(i) == ODLColumnType.IMAGE) {
rowHeight = Math.max(rowHeight, (int)Math.ceil(colWidths.get(i)));
}
}
// Add alternating row background BEFORE column data (so drawn behind)
if (!isHeaderRowForSubreport) {
addAlternativeRowBackground(elementWidth, rowHeight, band);
}
// Add column data
if (nc > 0) {
double dx=0;
for (int i = 0; i < nc; i++) {
int x = (int) Math.round(dx);
JRDesignElement element;
if (table.getColumnType(i) == ODLColumnType.IMAGE) {
element = createImageField(table, i);
} else {
JRDesignTextField textField = createTextField(table, i);
// make bigger if this is the title row for a subreport
if (isHeaderRowForSubreport) {
textField.setFontSize(16);
textField.setBold(true);
}
element = textField;
}
element.setX(x);
element.setY(headerHeight);
element.setWidth((int) Math.floor(colWidths.get(i)));
element.setHeight(rowHeight);
if (isHeaderRowForSubreport) {
//element.setY(0);
element.setStretchType(StretchTypeEnum.NO_STRETCH);
} else {
element.setStretchType(StretchTypeEnum.RELATIVE_TO_BAND_HEIGHT);
}
band.addElement(element);
dx += colWidths.get(i);
}
}
band.setHeight(headerHeight + rowHeight);
detailSection.addBand(band);
return headerHeight + rowHeight;
}
private static List<Double> getColumnWidths(ODLTableDefinition table, int elementWidth) {
// decide on column width; give more weight for images
ArrayList<Double> colWidths =new ArrayList<>();
double sum=0;
for (int i = 0; i < table.getColumnCount(); i++) {
double val = 1;
if (table.getColumnType(i) == ODLColumnType.IMAGE) {
val = 2;
}
colWidths.add(val);
sum += val;
}
for (int i = 0; i < table.getColumnCount(); i++) {
colWidths.set(i, elementWidth * colWidths.get(i) / sum);
}
return colWidths;
}
private static JRDesignImage createImageField(ODLTableDefinition table, int i) {
JRDesignImage image = new JRDesignImage(null);
setImageBorder(image);
image.setScaleImage(ScaleImageEnum.RETAIN_SHAPE);
String fld = "$F{" + table.getColumnName(i) + "}";
JRDesignExpression expression = new JRDesignExpression();
expression.setText(fld);
image.setExpression(expression);
image.setPrintWhenDetailOverflows(false);
return image;
}
private static JRDesignTextField createTextField(ODLTableDefinition table, int columnIndex) {
JRDesignTextField textField = new JRDesignTextField();
textField.setVerticalAlignment(VerticalAlignEnum.MIDDLE);
JRDesignExpression expression = new JRDesignExpression();
String fld = "$F{" + table.getColumnName(columnIndex) + "}";
if (table.getColumnType(columnIndex) == ODLColumnType.DOUBLE) {
String combined = fld + "!=null ? new DecimalFormat(\"###,###.###\").format(" + fld + "):\"\"";
expression.setText(combined);
} else {
expression.setText(fld);
}
textField.setExpression(expression);
textField.setPrintWhenDetailOverflows(false);
return textField;
}
private static void addAlternativeRowBackground(int width, int height, JRDesignBand band) {
JRDesignStaticText alt = new JRDesignStaticText();
alt.setBackcolor(new Color(240, 240, 250));
alt.setPrintWhenExpression(new JRDesignExpression("new java.lang.Boolean(($V{REPORT_COUNT}.intValue() % 2)==0)"));
alt.setWidth(width);
alt.setHeight(height);
alt.setMode(ModeEnum.OPAQUE);
alt.setStretchType(StretchTypeEnum.RELATIVE_TO_BAND_HEIGHT);
band.addElement(alt);
}
static JRDesignSubreport createSubreport(int x, int y, int subheight, String subreportDesignExpression, String subreportDataExpression,
JRDefaultStyleProvider defaultStyleProvider) {
JRDesignSubreport sub = new JRDesignSubreport(defaultStyleProvider);
sub.setHeight(subheight);
sub.setX(x);
sub.setY(y);
sub.setPrintRepeatedValues(true);
sub.setRemoveLineWhenBlank(true);
//sub.setPrintInFirstWholeBand(true);
if (subreportDataExpression != null) {
sub.setDataSourceExpression(new JRDesignExpression(subreportDataExpression));
}
// expect the subreport template as a parameter to the main report - see
// http://stackoverflow.com/questions/9785451/generate-jasper-report-with-subreport-from-java
if (subreportDesignExpression != null) {
JRDesignExpression subexpression = new JRDesignExpression(subreportDesignExpression);
sub.setExpression(subexpression);
}
return sub;
}
static void addPageFooter( JasperDesign ret) {
addPageFooter( getAvailableWidth(ret), ret);
}
static void addPageFooter( int elementWidth, JasperDesign ret) {
JRDesignBand band = new JRDesignBand();
band.setHeight(14);
int height = 13;
int joinPoint = 40;
Color backCol = new Color(230, 230, 230);
JRDesignTextField pageXOf = new JRDesignTextField();
pageXOf.setWidth(elementWidth - joinPoint);
pageXOf.setHeight(height);
pageXOf.setBackcolor(backCol);
pageXOf.setExpression(new JRDesignExpression("\"Page \"+$V{PAGE_NUMBER}+\" of\""));
pageXOf.setMode(ModeEnum.OPAQUE);
pageXOf.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
band.addElement(pageXOf);
JRDesignTextField pageN = new JRDesignTextField();
pageN.setWidth(joinPoint);
pageN.setX(elementWidth - joinPoint);
pageN.setHeight(height);
pageN.setMode(ModeEnum.OPAQUE);
pageN.setEvaluationTime(EvaluationTimeEnum.REPORT);
pageN.setBackcolor(backCol);
pageN.setHorizontalAlignment(HorizontalAlignEnum.LEFT);
pageN.setExpression(new JRDesignExpression("\" \" +$V{PAGE_NUMBER}"));
band.addElement(pageN);
JRDesignTextField date = new JRDesignTextField();
date.setHeight(height);
date.setWidth(200);
date.setPattern("EEEEE dd MMMMM yyyy");
date.setExpression(new JRDesignExpression("new java.util.Date()"));
band.addElement(date);
JRDesignTextField odl = new JRDesignTextField();
odl.setWidth(elementWidth);
odl.setHeight(height);
odl.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
odl.setExpression(new JRDesignExpression("\"Created by ODL Studio with maps � OpenStreetMap\""));
band.addElement(odl);
ret.setPageFooter(band);
}
static void addTitle(String title,boolean hasHeaderMap,boolean hasDetails, JasperDesign ret) {
addTitle(title, getAvailableWidth(ret),hasHeaderMap,hasDetails, ret);
}
static void addTitle(String title, int elementWidth,boolean hasHeaderMap, boolean hasDetails,JasperDesign ret) {
JRDesignBand band = new JRDesignBand();
int titleHeight = 50;
band.setHeight(titleHeight);
JRDesignTextField textField = new JRDesignTextField();
textField.setBlankWhenNull(true);
textField.setX(0);
textField.setY(10);
textField.setWidth(elementWidth);
textField.setHeight(38);
textField.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
textField.setFontSize(26);
textField.setBold(true);
JRDesignExpression expression = new JRDesignExpression();
expression.setText("\"" + title + "\"");
textField.setExpression(expression);
band.addElement(textField);
if(hasHeaderMap){
double pictureWidthPoints = elementWidth;// / 10.0;
double pictureXOffset = (elementWidth - pictureWidthPoints)/2.0;
double pictureWidthCM = pictureWidthPoints * ReportConstants.POINT_SIZE_IN_CM;
// get picture height
double pictureHeightCM = 10;
if(hasDetails==false){
// take whole page except for title
double points = getAvailablePageHeight(ret) - titleHeight -40;
pictureHeightCM = points * ReportConstants.POINT_SIZE_IN_CM;
}
else if (getAvailablePageHeight(ret) < getAvailableWidth(ret)){
// landscape; make shorter
pictureHeightCM = 6;
}
double pictureHeightPoints = pictureHeightCM / ReportConstants.POINT_SIZE_IN_CM;
String imgExpression = "((" + ReportConstants.IMAGE_PROVIDER_INTERFACE + ")" + "$P{" + ReportConstants.HEADER_MAP_PROVIDER_PARAMETER + "})."
+ ReportConstants.IMAGE_PROVIDER_INTERFACE_METHOD + "(" +pictureWidthCM +"," + pictureHeightCM + ",200)";
JRDesignImage img = new JRDesignImage(null);
img.setScaleImage(ScaleImageEnum.RETAIN_SHAPE);
img.setExpression(new JRDesignExpression(imgExpression));
img.setX((int)Math.round(pictureXOffset));
img.setY(titleHeight);
img.setWidth((int)Math.round(pictureWidthPoints));
img.setHeight((int)Math.round(pictureHeightPoints));
setImageBorder(img);
// img.getLineBox().s
band.setHeight(band.getHeight() + 10 +img.getHeight());
band.addElement(img);
//JRdesign
}
ret.setTitle(band);
}
static void setImageBorder(JRDesignImage img) {
float bw = .5f;
Color c = Color.BLACK;
JRLineBox box = img.getLineBox();
box.getLeftPen().setLineWidth(bw);
box.getLeftPen().setLineColor(c);
box.getRightPen().setLineWidth(bw);
box.getRightPen().setLineColor(c);
box.getBottomPen().setLineWidth(bw);
box.getBottomPen().setLineColor(c);
box.getTopPen().setLineWidth(bw);
box.getTopPen().setLineColor(c);
}
static JRDesignField[] createFields(ODLTableDefinition table, boolean includeArtificialSubreportField) {
int nc = table.getColumnCount();
JRDesignField[] ret = new JRDesignField[nc + (includeArtificialSubreportField ? 1 : 0)];
for (int i = 0; i < nc; i++) {
ret[i] = new JRDesignField();
ret[i].setName(table.getColumnName(i));
ret[i].setValueClass(ColumnValueProcessor.getJavaClass(table.getColumnType(i)));
}
if (includeArtificialSubreportField) {
ret[nc] = new JRDesignField();
ret[nc].setName(ReportConstants.SUBREPORT_DATASTORE_FIELDNAME);
ret[nc].setValueClass(net.sf.jasperreports.engine.JRRewindableDataSource.class);
}
return ret;
}
static void addFields(ODLTableDefinition table, boolean includeArtificialSubreportField, JasperDesign out) {
for (JRDesignField field : createFields(table, includeArtificialSubreportField)) {
try {
out.addField(field);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
}