Archive for February, 2007

Java Injection in Jasper Reports

Wednesday, February 14th, 2007

I needed to create a JasperReports variable of type NumberFormat to format dollars without cents.

Try to stay calm while reading this.

JasperReports allows the creation of variables that can be evaluated at report run time. The variables can be of any Java object type. You can set the value of the variable to be the result of any Java expression that evaluates to the same type as the object variable (which cannot be void).

I created a JasperReports variable named “nfc” for (number format currency) and set it up to be of type java.text.NumberFormat. Then I set its value to be NumberFormat.getCurrencyInstance()

java-injection-1

It’s convenient to use in fields throughout my report:

$V{nfc}.format($F{assets})

That worked, but to my chagrin, getCurrencyInstance() returns a formatter that includes two decimal places for cents. In free-form Java of course, we’d just call setMaximumFractionDigits(0) on the formatter, and be rid of the wretched pennies. But in JasperReports we can only evaluate Java “expressions.” Because setMaxiumFractionDigits returns void, it can’t be used as an expression.

Luckily I figured out a way to inject arbitrary Java into the JasperReports expression evaluator. You can do this in iReport or straight in the jrxml file.

Here’s how:
1. Create a variable: (View->Variables->New)
2. Type in the type: java.text.NumberFormat
3. Set the value to:

java-injection-2

NumberFormat.getCurrencyInstance()
);
System.out.println( "Hello World! Java Injection!" );
((NumberFormat)value).setMaximumFractionDigits(0

There are two things to notice:

A. The fun begins with ); That is what JasperReports expression evaluator sticks at the end of your expression when it compiles. After you provide your own ); to complete that expression evaluation, you can add any Java statements you like. Just remember when you’re done, leave off the trailing ); because the expression evaluator will tack that onto the end.

B. “value” is the name of the temporary variable that will contain the result of your expression. In order to call setMaximumFractionDigits on my NumberFormat object, I first have to cast “value” to NumberFormat.

Here’s how that variable looks in the JasperReports xml (jrxml) file:

<variable name=”nfc” class=”java.text.NumberFormat” resetType=”Report” calculation=”Nothing”>
<variableExpression> <![CDATA[
NumberFormat.getCurrencyInstance()
);
System.out.println( “Hello World! Java Injection!” );
((NumberFormat)value).setMaximumFractionDigits(0
]]>
</variableExpression>
</variable>

Note: JasperReports also allows the use of Java ‘Scriptlets’ that would solve this problem, but I didn’t want to use that feature because it would force other users of my report to get the pre-compiled external class file into their classpath, yadda blah blah ClassNotFoundException blah half-the-day-wasted yadda yadda… My point is that I wanted the JasperReports XML to stand on its own three feet, shielding everyone else from external dependencies.

UPDATE: I have now learned that JasperReports has formatting built in and while this Java injection technique is “cool”, it is not necessary for number formatting. Here’s what I eventually figured out: In iReport, right click on the number field, choose Properties, click on the “Text Field” tab, and then enter #,##0.0 in the Pattern box. This example will round to one decimal digit. You can use any pattern supported by java.text.NumberFormat.