If-else in Jasper Reports expressions

Introduction

Jasper Reports do not support if-else statements when defining variable expressions. Instead it relies on the ternary operators {cond} ? {statement 1} : {statement 2} which means you have to nest the operators if your expression depends on more than one condition. This blog demonstrates how you can (somewhat) get around this by implementing your own class to handle multiple if-else conditions and use it in Jasper Reports.

If-else in Jasper Reports

Suppose you want to define a text expression to render different text based on the value of a field or variable. In Jasper Reports, you have to nest the ternary operator as follows:

$F{cond}.equalsIgnoreCase("NSW") ? "Go Sydney!" :
( $F{cond}.equalsIgnoreCase("QLD") ? "Go Brisbane!" :
    ($F{cond}.equalsIgnoreCase("VIC") ? "Go Melbourne!" :
      "Go Aussie!")
  )

As the number of conditions increase, you will have to nest the ternary operators which will make the expression harder to read if you have more than 4 or more conditions to test.

Extending Jasper Reports for multiple if-elses

The solution described here involves implementing a new class to emulate if-else statements in Java which can then be used in defining expressions in Jasper Reports. Instead of using nested ternary operators illustrated in previous section, the expression will look like:

new JasperIf().
jif($F{cond}.equalsIgnoreCase("NSW")).jthen("Go Sydney!")
.jelseif($F{cond}.equalsIgnoreCase("QLD"), "Go Brisbane!")
.jelseif($F{cond}.equalsIgnoreCase("VIC"), "Go Melbourne!")
.jelse("Go Aussie!")

1. Implement the JasperIf class

I include the java class I implemented below. The codes are fairly self explanatory. Note the implementations of methods jif, jelseif, jelse to emulate the corresponding Java keywords.

package com.rlee.jasperext;

import java.util.ArrayList;
import java.util.List;

public class JasperIf {

    private Boolean ifCondition;
    private List<ElseIf> elseifConditions;
    private String thenStatement;
    private String elseStatement;

    public JasperIf() {
        this.elseifConditions = new ArrayList<ElseIf>();
    }

    public JasperIf jif(Boolean ifCond) {
        this.ifCondition = ifCond;
        return this;
    }

    public JasperIf jthen(String then) {
        this.thenStatement = then;
        return this;
    }

    public JasperIf jelse(String elseStatement) {
        this.elseStatement = elseStatement;
        return this;
    }

    public JasperIf jelseif(Boolean elseif, String elseifStatement) {
        this.elseifConditions.add(new ElseIf(elseif, elseifStatement));
        return this;
    }

    public String toString() {
        return evaluate();
    }

    private String evaluate() {
        if (ifCondition) {
            return thenStatement;
        } else {
            for (ElseIf elseif : elseifConditions) {
                if (elseif.cond) {
                    return elseif.statement;
                }
            }
        }
        return elseStatement;
    }

    private class ElseIf {
        private Boolean cond;
        private String statement;

        ElseIf(Boolean cond, String statement) {
            this.cond = cond;
            this.statement = statement;
        }
    }
}

2. Add class to iReport

To use the above class in your Jasper Reports template, you will first have to package it in a jar file and then add the jar file in your iReport installation. You do this by adding the jar file to directory <Your JasperReport dir>/ireport/modules/ext. For example, C:\Program Files (x86)\Jaspersoft\iReport-4.6.0\ireport\modules\ext.

In iReport,

  1. Go to menu Tools->Options
  2. Go to Classpath tab under iReport icon, add the path to jar file (see screenshot below)

3. Restart iReport

3. Use JasperIf class in report templates

Now you can use the new class in the report’s expression. For example,

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" 
   ...
    <import value="com.rlee.jasperext.JasperIf"/>
        <variableExpression><![CDATA[new JasperIf().
jif($F{cond}.equalsIgnoreCase("NSW")).jthen("Go Sydney!")
.jelseif($F{cond}.equalsIgnoreCase("QLD"), "Go Brisbane!")
.jelseif($F{cond}.equalsIgnoreCase("VIC"), "Go Melbourne!")
.jelse("Go Aussie!")]]></variableExpression>
    </variable>

That’s it. The solution is a bit of work around to avoid deep nesting of ternary operators for expressions that require multiple conditions check. It’s still a bit wordy. Note also the same approach can be used to extend Jasper Reports if you have complex rendering logic based on data values in the reports.

Using various Java exporters provided by Jasper Reports

In my previous blog, I demonstrate how to export a Jasper report with the Java class JRHtmlExporter to an HTML email. In this blog, I will include the codes I use to export a Jasper report to other commonly used file formats. In all cases, I start with a JasperPrint object from a given Jasper Reports template and create the resulting file as an attachment in a HTTP response.

PDF file

For PDF file, you may just use the static method exportReportToPdf in class net.sf.jasperreports.engine.JasperExportManager as shown below:

byte[] pdfBytes = JasperExportManager.exportReportToPdf(print);
response.setContentType(“application/pdf”);
response.setHeader(“Content-Disposition”, “attachment;filename=example.pdf”);
response.getOutputStream().write(pdfBytes);
response.flushBuffer();

MS Excel xlsx file

For MS Excel 2007 or 2010 file, use class net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter:

JRXlsxExporter exporter = new JRXlsxExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
ByteArrayOutputStream os = new ByteArrayOutputStream();
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
exporter.exportReport();

response.setContentType(“application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”);
response.setHeader(“Content-Disposition”, “attachment;filename=example.xlsx”);
response.getOutputStream().write(os.toByteArray());
response.flushBuffer();

Note the content type set in the response header.

MS Excel xls file

If you need to export your report in older Excel file format, use class net.sf.jasperreports.engine.export.JRXlsExporter similar to codes above and replace the content type in the response header with “application/vnd.ms-excel” and filename suffix to xls:

JRXlsExporter exporter = new JRXlsExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
ByteArrayOutputStream os = new ByteArrayOutputStream();
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
exporter.exportReport();

response.setContentType(“application/vnd.ms-excel”);
response.setHeader(“Content-Disposition”, “attachment;filename=example.xls”);
response.getOutputStream().write(os.toByteArray());
response.flushBuffer();

CSV file

For csv files, use class net.sf.jasperreports.engine.export.JRCsvExporter

JRCsvExporter exporter = new JRCsvExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
StringBuffer buffer = new StringBuffer();
exporter.setParameter(JRExporterParameter.OUTPUT_STRING_BUFFER, buffer);
exporter.exportReport();

response.setContentType(“text/csv”);
response.setHeader(“Content-Disposition”, “attachment;filename=” + getFileName(name) + “.csv”);
response.getOutputStream().write(buffer.toString().getBytes());
response.flushBuffer();

Other file formats

The Jasper Reports API provides quite a few other exporters. Look at the Java Doc here .

Creat rich text (HTML) emails with inline images from Japser Report

This blog demonstrates how to create and export a HTML file with inline images with Jasper Report for use as an email message in Java Mail.

First, create the report from Jasper Report template in JasperPrint object:

JasperPrint jasperPrint = … // Run report

Now setup HTML exporter:

JRHtmlExporter exporter = new JRHtmlExporter();

Map imageData = new HashMap();

exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STRING_BUFFER, buffer);   exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, “cid:”);      exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesData);
exporter.exportReport();

Of particular interest is the parameters IMAGES_URI and IMAGES_MAP of the HTML exporter. You provide the URI for inlining and an empty hashmap for the exporter to store the images rendered in the  output html string.

Next add inline images to the Java Mail message by processing the imageData map populated by the exporter as follows using the addInLine method of Java Mail class MimeMessageHelper:

MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED);
// attach the html data from htmlOutputData to inline images
for (Iterator<Entry> it = imagesData.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = it.next();
String imageName = (String) entry.getKey();
byte[] imageData = (byte[]) entry.getValue();
// attach imageData using imageName as Content-ID
helper.addInline(imageName, new ByteArrayResource(imageData), “image/jpeg”);
}

JasperReports Server – Fixing the missing menu for adding file resources

There is an apparent bug in JasperServer Community Edition v4.2.1 where users would not be able to add images and other file resources because the context menu does not appear (see screenshot below). I notice this in both Linux and Windows versions of the server.

To fix this, you have to modify the file actionModel-search.xml in the jasperserver webapp directory. Assuming you are using the embedded Tomcat web server, it will be under Jasper <jasperserver>/apache-tomcat/webapps/jasperserver/WEB-INF. Open the file and find the element with the labelKey RM_NEW_RESOURCE_FILE and comment out the enclosing conditon elements as shown below:

<!–
                        <condition test=”isProVersion”>
                            <condition test=”isSupportedDevice”>
                                <option labelKey=”RM_NEW_DOMAIN” action=”invokeCreate” actionArgs=”SemanticLayerDataSource”
                                        clientTest=”canResourceBeCreated” clientTestArgs=”SemanticLayerDataSource” className=”up”/>
                            </condition>
 –>
<selectAction labelKey=”RM_NEW_RESOURCE_FILE” className=”flyout”
clientTest=”canResourceBeCreated” clientTestArgs=”FileResource” >
<condition test=”isProVersion”>
<option labelKey=”resourceTypes.access.grant.schema” action=”invokeCreate” actionArgs=”FileResource@@accessGrantSchema”
className=”up”/>
</condition>
<option labelKey=”resourceTypes.font” action=”invokeCreate” actionArgs=”FileResource@@font”
className=”up”/>
<option labelKey=”resourceTypes.image” action=”invokeCreate” actionArgs=”FileResource@@img”
className=”up”/>
<option labelKey=”resourceTypes.css.file” action=”invokeCreate” actionArgs=”FileResource@@css”
className=”up”/>
<option labelKey=”resourceTypes.jar” action=”invokeCreate” actionArgs=”FileResource@@jar”
className=”up”/>
<option labelKey=”resourceTypes.jrxml” action=”invokeCreate” actionArgs=”FileResource@@jrxml”
className=”up”/>
<option labelKey=”resourceTypes.olap.schema” action=”invokeCreate” actionArgs=”FileResource@@olapMondrianSchema”
className=”up”/>
<option labelKey=”resourceTypes.resource.bundle” action=”invokeCreate” actionArgs=”FileResource@@prop”
className=”up”/>
<option labelKey=”resourceTypes.style.template” action=”invokeCreate” actionArgs=”FileResource@@jrtx”
className=”up”/>
<option labelKey=”resourceTypes.xml.file” action=”invokeCreate” actionArgs=”FileResource@@xml”
className=”up”/>
</selectAction>
<!–
                        </condition>
 –>

Restart the server and the File context menu should now appear:

Screenshot showing Add Resource->File menu

Hope the above helps.