Conditional Style Based on TextField Value [duplicate] - jasper-reports

I want to make text in the textValue to be red when the numeric value of the cell is less than zero. Currently my configuration is like this:
<style name="RedText">
<conditionalStyle>
<conditionExpression><![CDATA[$F{col1}.compareTo(BigDecimal.ZERO) == -1]]></conditionExpression>
<style forecolor="#FF0000"/>
</conditionalStyle>
</style>
I have more cells (col2, col3, col4 ...) that I would need to apply this red text style. Is it possible to make it universal for current value, etc. col*?

The standard/classical way without using Java code
It is impossible without using Java code. In your case you need to create the new style for each field and apply this concrete style for textField element with conditionExpression contains the value of concrete field.
In common case the expression of textField can be complex, the same is true for the conditionExpression. The style does not know to which control it belongs. We can't operate with value of abstract field (parameter/variable), the engine need the concrete names to calculate expression.
Using Java API
Is it possible to solve the task applying style with JasperReports Java API. For example:
JRDesignStyle boldStyle = new JRDesignStyle();
boldStyle.setName("Sans_Bold");
boldStyle.setFontName("DejaVu Sans");
boldStyle.setFontSize(12);
boldStyle.setBold(true);
// ...
textField.setStyle(boldStyle);
JRDesignExpression expression = new JRDesignExpression();
expression.setText("$F{col1}");
textField.setExpression(expression);
Using backdoor or "yes we like to hack"
Look at the beatiful class in Java is Map, it so invaluable in JasperReports. It can help us to crack this task.
We need parameter of Map type for storing the value of field (it will be abstract field - "coln")
We need to enable applying style during resolving expression of a textField. The propery
net.sf.jasperreports.style.evaluation.time.enabled will help to enable this feature.
We need some Java method for putting value in to the Map (as a storage of a "coln") which returns the value we just put. The Map.put(K key, V value) is not good for this purpose. I wrote the small Utility class with wrapper over Map.put method.
The datasouce
The using of simple csv datasource is enough for this example. I like this kind of datasource for testing and debugging.
col1,col2,col3,col4,col5
1,2,3,9,-5
-2,6,-3,4,1
2,-2,-2,7,-3
8,3,4,-5,6
The name of data adapter for this datasource in the example below is numbers.csv. The first line from the file is skipped - it is contains the column's name.
Java code
It is a very simple utility class
public class Storage {
public static Integer add(Map<String, Integer> map, String key, Integer value) {
map.put(key, value);
return value;
}
}
Report template
The import for our utitlity class should be added for using it.
For correct resolving expression of conditional style we need to use in expression some parameter or some variable (you can check the source code of JasperReports). I'll add some fake variable for using in conditionExpression
We need to put the value of "coln" to the Map and then use this value in conditional style. In this case we don't need to know the name of field we are checking in conditional style. The parameter will be used for storing Map.
We need place and draw the value of "coln" in two runs. We can use fake invisible textField to put the field's value to the Map and another textField to show value with applying conditional style. The positions and sizes of this textFields are same - one above the other.
Our condition will be like this:
<style name="ColoredText">
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) < 0)]]></conditionExpression>
<style forecolor="blue"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) > 0)]]></conditionExpression>
<style forecolor="green"/>
</conditionalStyle>
</style>
There is no field in conditionExpression and it works :)
The jrxml file:
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Apply style withou name" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="numbers.csv"/>
<property name="net.sf.jasperreports.style.evaluation.time.enabled" value="true"/>
<import value="some.package.Storage"/>
<style name="ColoredText">
<conditionalStyle>
<conditionExpression><![CDATA[$P{STORAGE}.get($P{KEY}) == null && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#CC0000"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) < 0) && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#0037FF"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) > 0) && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#00BA00"/>
</conditionalStyle>
</style>
<style name="Fake">
<conditionalStyle>
<conditionExpression><![CDATA[!"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style mode="Opaque" forecolor="#FFFFFF" backcolor="#FFFFFF"/>
</conditionalStyle>
</style>
<parameter name="STORAGE" class="java.util.Map">
<defaultValueExpression><![CDATA[new java.util.HashMap()]]></defaultValueExpression>
</parameter>
<parameter name="KEY" class="java.lang.String">
<defaultValueExpression><![CDATA["key"]]></defaultValueExpression>
</parameter>
<field name="col1" class="java.lang.Integer"/>
<field name="col2" class="java.lang.Integer"/>
<field name="col3" class="java.lang.Integer"/>
<field name="col4" class="java.lang.Integer"/>
<field name="col5" class="java.lang.Integer"/>
<variable name="FAKE" class="java.lang.String">
<variableExpression><![CDATA["FAKE"]]></variableExpression>
</variable>
<detail>
<band height="20">
<textField evaluationTime="Auto">
<reportElement style="Fake" x="0" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col1})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="0" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="100" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col2})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="100" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="200" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col3})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="200" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="300" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col4})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="300" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="400" y="0" width="140" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col5})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="400" y="0" width="140" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
The green color is used for positive numbers and blue - for negative. The red color is using to indicate algorithm's problems.
Output result
The generated pdf with help of JRPdfExporter:

Related

Conditional Formatting according to value in Jasper Reports

I would like to know if there's a way to re-use conditional formatting for several textFields each one associated to different parameters.
Right now I´ve manage to do it by writing a custom style for each one, but it´s a nightmare to mantain.
Here´s what I´ve got:
<style name="style_door" mode="Transparent" forecolor="#000000">
<conditionalStyle>
<conditionExpression><![CDATA[$P{door}.equals("REPLACE")]]></conditionExpression>
<style mode="Transparent" forecolor="#CC0000"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[$P{door}.equals("REPAIR")]]></conditionExpression>
<style mode="Transparent" forecolor="#FF9900"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[$P{door}.equals("OK")]]></conditionExpression>
<style mode="Transparent" forecolor="#009900"/>
</conditionalStyle>
</style>
<style name="style_window" mode="Transparent" forecolor="#000000">
<conditionalStyle>
<conditionExpression><![CDATA[$P{window}.equals("REPLACE")]]></conditionExpression>
<style mode="Transparent" forecolor="#CC0000"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[$P{window}.equals("REPAIR")]]></conditionExpression>
<style mode="Transparent" forecolor="#FF9900"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[$P{window}.equals("OK")]]></conditionExpression>
<style mode="Transparent" forecolor="#009900"/>
</conditionalStyle>
</style>
<textField>
<reportElement style="style_door" x="112" y="26" width="53" height="13" uuid="07eacbda-9ed8-418e-9f97-937bb44ca976"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font size="7"/>
</textElement>
<textFieldExpression><![CDATA[$P{door}]]></textFieldExpression>
</textField>
<textField>
<reportElement style="style_window" x="112" y="26" width="53" height="13" uuid="07eacbda-9ed8-418e-9f97-937bb44ca976"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font size="7"/>
</textElement>
<textFieldExpression><![CDATA[$P{window}]]></textFieldExpression>
</textField>
Notice that the 2 styles are the same, it just change the condition, because it should match a different parameter. And then I apply the style to each textField.
Is there a way I can write a single style and the condition be matched with the value of the element associated to? Or could I pass the element's value as an argument to the style and be used in the condition?
Thank you!
Here is a answer to a similar question from a while back.
I've done some testing and contradictory to the answer linked above I managed to do it without a additional class. All other statements, especially regarding evaluation time and style.evaluation.time still apply. Please read the explanations linked in the answer above.
Example for your application:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.20.0.final using JasperReports Library version 6.1.1 -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Apply style withou name" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="c7e9565b-28fb-4eeb-b6e0-cd9bd8460722">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="New Data Adapter"/>
<property name="net.sf.jasperreports.style.evaluation.time.enabled" value="true"/>
<style name="ColoredText">
<conditionalStyle>
<conditionExpression><![CDATA[((String)$V{STORAGE}.get("key")).equals("OK")]]></conditionExpression>
<style forecolor="#009900"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[((String)$V{STORAGE}.get("key")).equals("REPAIR")]]></conditionExpression>
<style forecolor="#FF9900"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[((String)$V{STORAGE}.get("key")).equals("REPLACE")]]></conditionExpression>
<style forecolor="#CC0000"/>
</conditionalStyle>
</style>
<parameter name="door" class="java.lang.String"/>
<parameter name="window" class="java.lang.String"/>
<variable name="STORAGE" class="java.util.Map" calculation="System">
<initialValueExpression><![CDATA[new java.util.HashMap()]]></initialValueExpression>
</variable>
<detail>
<band height="21">
<textField evaluationTime="Auto" isBlankWhenNull="true">
<reportElement x="0" y="0" width="100" height="20" uuid="d9392134-e246-41c7-8c66-9d21f06e0465"/>
<textFieldExpression><![CDATA[$V{STORAGE}.put("key", $P{window}) == null ? null : null]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto" isBlankWhenNull="true">
<reportElement style="ColoredText" x="0" y="0" width="100" height="20" uuid="fda2f3f5-fe34-424e-8af9-b4f0111936b2"/>
<textFieldExpression><![CDATA[$V{STORAGE}.get("key")]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto" isBlankWhenNull="true">
<reportElement x="100" y="0" width="100" height="20" uuid="6dcf014e-ffc9-47be-81ee-04f1912d3be2"/>
<textFieldExpression><![CDATA[$V{STORAGE}.put("key", $P{door}) == null ? null : null]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto" isBlankWhenNull="true">
<reportElement style="ColoredText" x="100" y="0" width="100" height="20" uuid="54cc1367-8387-4fc0-be00-5ba789cce7e9"/>
<textFieldExpression><![CDATA[$V{STORAGE}.get("key")]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>

The net.sf.jasperreports.style.backcolor attribute is not working for text fields at Frame

In my database I have the hex colour codes for various scenarios and I want to highlight the rows using these variable colours. I have used net.sf.jasperreports.style.backcolor, but all rows have the same background colour which is that of the first row.
<frame>
<reportElement stretchType="ContainerHeight" mode="Opaque" x="0" y="0" width="802" height="12" isRemoveLineWhenBlank="true">
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{sp_colour}]]></propertyExpression>
</reportElement>
Looks like the engine is initializing the frame's attribute using the first record from the datasource and using this initial value for all other records (2nd iteration from the result set, 3rd iteration and so on). Probably this is a bug of JasperReports library.
The issue can be solved using several workarounds.
Workaround 1. Using conditional style
The introduction of conditional style is resolving the issue. If we have N unique values of colors we have to add at least N - 1 conditions for style - one condition for one color.
Working example. Datasource
I used csv based datasource for testing report:
team1,team2,color1,color2,duration_color,date
BEL,POR,#0000FF,#7F8C8D,#FFFFFF,"27 June"
ITA,AUT,#0000FF,#7F8C8D,#F2F3F4,"26 June"
FRA,SUI,#7F8C8D,#0000FF,#F6DDCC,"28 June"
CRO,ESP,#7F8C8D,#0000FF,#F2F3F4,"28 June"
SWE,UKR,#7F8C8D,#0000FF,#F2F3F4,"29 June"
ENG,GER,#0000FF,#7F8C8D,#FFFFFF,"29 June"
NED,CZE,#7F8C8D,#0000FF,#FFFFFF,"27 June"
WAL,DEN,#7F8C8D,#0000FF,#FFFFFF,"26 June"
The first row at the file contains the column names and should be skipped (for example using settings of data adapter).
This file contains 3 unique colors (the field duration_color) that will be used as background color.
Working example. Report template
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Backcolor, W1" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="595" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="colors"/>
<style name="frameStyle">
<conditionalStyle>
<conditionExpression><![CDATA["#FFFFFF".equals($F{duration_color})]]></conditionExpression>
<style/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA["#F2F3F4".equals($F{duration_color})]]></conditionExpression>
<style/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA["#F6DDCC".equals($F{duration_color})]]></conditionExpression>
<style/>
</conditionalStyle>
</style>
<field name="team1" class="java.lang.String"/>
<field name="team2" class="java.lang.String"/>
<field name="color1" class="java.lang.String"/>
<field name="color2" class="java.lang.String"/>
<field name="duration_color" class="java.lang.String"/>
<field name="date" class="java.lang.String"/>
<detail>
<band height="20">
<frame>
<reportElement style="frameStyle" mode="Opaque" x="172" y="0" width="250" height="20">
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{duration_color}]]></propertyExpression>
</reportElement>
<textField>
<reportElement x="0" y="0" width="100" height="20"/>
<textElement textAlignment="Left" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[String.format("Match date: %s", $F{date})]]></textFieldExpression>
</textField>
<textField>
<reportElement x="100" y="0" width="60" height="20">
<propertyExpression name="net.sf.jasperreports.style.forecolor"><![CDATA[$F{color1}]]></propertyExpression>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{team1}]]></textFieldExpression>
</textField>
<staticText>
<reportElement x="160" y="0" width="30" height="20"/>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[vs]]></text>
</staticText>
<textField>
<reportElement x="190" y="0" width="60" height="20">
<propertyExpression name="net.sf.jasperreports.style.forecolor"><![CDATA[$F{color2}]]></propertyExpression>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{team2}]]></textFieldExpression>
</textField>
</frame>
</band>
</detail>
<summary>
<band height="50">
<staticText>
<reportElement mode="Opaque" x="20" y="0" width="100" height="20" backcolor="#F2F3F4"/>
<text><![CDATA[Extra time]]></text>
</staticText>
<staticText>
<reportElement mode="Opaque" x="20" y="20" width="100" height="20" backcolor="#F6DDCC"/>
<text><![CDATA[Penalty shootout]]></text>
</staticText>
</band>
</summary>
</jasperReport>
The 3 conditional styles were introduced at frameStyle and this style is applied for the whole frame.
Working example. The output result
I used Jaspersoft Studio (JSS) to generate the output result.
Workaround 2. Applying attributes at elements, not at frame
Another workaround is to apply the net.sf.jasperreports.style.backcolor attribute at all elements (textField and staticText) instead of using the frame's attribute.
Working example. Report template
No need to introduce conditional style in this case.
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Backcolor, W2" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="595" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="colors"/>
<field name="team1" class="java.lang.String"/>
<field name="team2" class="java.lang.String"/>
<field name="color1" class="java.lang.String"/>
<field name="color2" class="java.lang.String"/>
<field name="duration_color" class="java.lang.String"/>
<field name="date" class="java.lang.String"/>
<detail>
<band height="20">
<frame>
<reportElement x="172" y="0" width="250" height="20"/>
<textField>
<reportElement mode="Opaque" x="0" y="0" width="100" height="20"/>
<textElement textAlignment="Left" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[String.format("Match date: %s", $F{date})]]></textFieldExpression>
</textField>
<textField>
<reportElement mode="Opaque" x="100" y="0" width="60" height="20">
<propertyExpression name="net.sf.jasperreports.style.forecolor"><![CDATA[$F{color1}]]></propertyExpression>
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{duration_color}]]></propertyExpression>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{team1}]]></textFieldExpression>
</textField>
<staticText>
<reportElement mode="Opaque" x="160" y="0" width="30" height="20">
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{duration_color}]]></propertyExpression>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[vs]]></text>
</staticText>
<textField>
<reportElement mode="Opaque" x="190" y="0" width="60" height="20">
<propertyExpression name="net.sf.jasperreports.style.forecolor"><![CDATA[$F{color2}]]></propertyExpression>
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{duration_color}]]></propertyExpression>
</reportElement>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{team2}]]></textFieldExpression>
</textField>
</frame>
</band>
</detail>
<summary>
<band height="50">
<staticText>
<reportElement mode="Opaque" x="20" y="0" width="100" height="20" backcolor="#F2F3F4"/>
<text><![CDATA[Extra time]]></text>
</staticText>
<staticText>
<reportElement mode="Opaque" x="20" y="20" width="100" height="20" backcolor="#F6DDCC"/>
<text><![CDATA[Penalty shootout]]></text>
</staticText>
</band>
</summary>
</jasperReport>
The frame even can be removed.
Working example. The output result
The result generated at JSS is absolutely the same:
Thanks you Alex K.
The first option of using conditional formatting was not going to work as the users can pick a large number of colours
The second option of applying the net.sf.jasperreports.style.backcolor attribute to all elements was where I was thinking of heading, but with 2 details lines with 12+ elements in each was going to be time consuming.
Instead I added a textbox element the same size as the frame, as the first element in the frame and applied the net.sf.jasperreports.style.backcolor attribute to it. I used a printWhenExpression as the colouring was only required for special make and therefore retained alternate row highlighting for the other rows in the report
<frame>
<reportElement style="Row" stretchType="ContainerHeight" mode="Opaque" x="0" y="0" width="802" height="12" isRemoveLineWhenBlank="true" uuid="b9bde183-f2ec-4051-8c7e-22072071357d"/>
<textField isStretchWithOverflow="true" isBlankWhenNull="true">
<reportElement mode="Opaque" x="0" y="0" width="798" height="12" uuid="d110cab3-6534-4c2a-9304-74a11c2a7cb8">
<propertyExpression name="net.sf.jasperreports.style.backcolor"><![CDATA[$F{sp_colour}]]></propertyExpression>
<printWhenExpression><![CDATA["Y".equals($F{is_special_make})]]>/printWhenExpression>
</reportElement>
<textElement verticalAlignment="Middle">
<font fontName="Lucida Sans" size="6"/>
</textElement>
</textField>

How to get sum per page of column in table component and return to main report to display value?

Recently I started working with jasper report and faced some problem. I have table with records and also a have label with text field where I should put sum of column per page. (I added table footer for debugging). Here is a picture:
I did that in following way: in the table I add variable "amount" with following properties:
Notice: reset type is page, because I should display sum per page.
In the main report I added variable "sumPerList" according to How to return value from table's DataSource to main report in iReport? and tried to play with evaluation time but it didn't help (it doesn't matter what evaluation time I set, the value is always wrong), I couldn't display in field "total sum Main" the same value like in field "total sum" (table scope).
The reason why I decided display total sum separately from table, because I have empty space between them, but I cann't add empty row in table.
Also I tried to set summ function here:
But it doesn't help either. May be there is another way to do it?
(I use TIBCO Jaspersoft® Studio 6.8.0 - Visual Designer for JasperReports 6.8.0.)
A dataset return value will not help because the value is returned only when the table is complete, not on every page break.
One (hacky) way to do it is to pass a value holder object to the table subdataset, and write an expression in the table that sets the value in the object as a side effect.
You can use for instance an AtomicInteger as a value holder, note that we don't need atomicity and we're only using it as a mutable integer.
The solution would look something like this:
The report has a variable TableTotal initialized to an AtomicInteger
TableTotal is passed to the table subdataset as a parameter called PageTotalHolder
The table subdataset has a variable called PageTotal (whose value we want to show in the page footer)
In the table column footer we use a print when expression that sets the current PageTotal value to PageTotalHolder (and always returns true): $P{PageTotalHolder}.getAndSet($V{PageTotal}) >= Integer.MIN_VALUE
The report page footer shows $V{TableTotal}.get()
Pasting here the full JRXML for convenience. You can run it in Jaspersoft Studio with One Empty Record.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.8.0.final using JasperReports Library version 6.8.0-2ed8dfabb690ff337a5797129f2cd92902b0c87b -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4_10" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f750af4c-d61b-4bdb-b537-6d71b6754832">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
<style name="Table_TH" mode="Opaque" backcolor="#F0F8FF">
<box>
<pen lineWidth="0.5" lineColor="#000000"/>
</box>
</style>
<style name="Table_CH" mode="Opaque" backcolor="#BFE1FF">
<box>
<pen lineWidth="0.5" lineColor="#000000"/>
</box>
</style>
<style name="Table_TD" mode="Opaque" backcolor="#FFFFFF">
<box>
<pen lineWidth="0.5" lineColor="#000000"/>
</box>
</style>
<subDataset name="Dataset1" uuid="8668793a-f800-4489-9589-c9b6d2ee97a0">
<parameter name="PageTotalHolder" class="java.util.concurrent.atomic.AtomicInteger"/>
<variable name="Value" class="java.lang.Integer">
<variableExpression><![CDATA[(int) (10 + 10 * Math.sin($V{REPORT_COUNT}))]]></variableExpression>
</variable>
<variable name="PageTotal" class="java.lang.Integer" resetType="Page" calculation="Sum">
<variableExpression><![CDATA[$V{Value}]]></variableExpression>
</variable>
</subDataset>
<variable name="TableTotal" class="java.util.concurrent.atomic.AtomicInteger" calculation="System">
<initialValueExpression><![CDATA[new java.util.concurrent.atomic.AtomicInteger()]]></initialValueExpression>
</variable>
<detail>
<band height="203" splitType="Stretch">
<componentElement>
<reportElement x="0" y="0" width="200" height="200" uuid="657ddbb8-e98b-43de-be5e-10645069dc44"/>
<jr:table xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd">
<datasetRun subDataset="Dataset1" uuid="2eb1dde6-8cf9-45c1-94ce-0bdcb26e120a">
<datasetParameter name="PageTotalHolder">
<datasetParameterExpression><![CDATA[$V{TableTotal}]]></datasetParameterExpression>
</datasetParameter>
<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.JREmptyDataSource(200)]]></dataSourceExpression>
</datasetRun>
<jr:column width="120" uuid="51f2b0c5-0655-4cc3-8d57-75149a7514f6">
<property name="com.jaspersoft.studio.components.table.model.column.name" value="Column1"/>
<jr:columnHeader style="Table_CH" height="30"/>
<jr:columnFooter style="Table_CH" height="30">
<textField>
<reportElement x="0" y="0" width="120" height="30" uuid="95a0ff6d-cab6-44df-9aa0-3e41774194e2">
<printWhenExpression><![CDATA[$P{PageTotalHolder}.getAndSet($V{PageTotal}) >= Integer.MIN_VALUE]]></printWhenExpression>
</reportElement>
<textElement textAlignment="Right"/>
<textFieldExpression><![CDATA[$V{PageTotal}]]></textFieldExpression>
</textField>
</jr:columnFooter>
<jr:detailCell style="Table_TD" height="30">
<textField>
<reportElement x="0" y="0" width="120" height="30" uuid="c3ee39f0-59fd-47a3-8cba-90f009dc42ca"/>
<textElement textAlignment="Right"/>
<textFieldExpression><![CDATA[$V{Value}]]></textFieldExpression>
</textField>
</jr:detailCell>
</jr:column>
</jr:table>
</componentElement>
</band>
</detail>
<pageFooter>
<band height="54" splitType="Stretch">
<textField>
<reportElement x="200" y="0" width="100" height="30" uuid="eb1905ee-50d5-4601-b8ed-62864e6caea5"/>
<textFieldExpression><![CDATA[$V{TableTotal}.get()]]></textFieldExpression>
</textField>
</band>
</pageFooter>
</jasperReport>

How to insert empty row at the table based on condition in case using JRBeanCollectionDataSource?

There is a list and I use this mechanism to pass it to my jasper report:
JRBeanCollectionDataSource jrBeanCollectionDataSource = new JRBeanCollectionDataSource(list);
The Object of that list is as follows:
public class Test{
private int id;
//other properties + getters & setter
}
Now Assume I have 10 elements within my list (id= 1 to 10) and I want to separate them in one table using an empty row, meaning 5 rows top, one empty row, 5 rows down.
How to do that ?
The subreport element can helps with solving this problem - we can show or hide subreport based on condition using printWhenExpression.
In your case we need to show subreport with one empty line.
Example
The subreport for showing empty line is very simple. It does not need datasource for showing data - we can put staticText right at Title band. We don't need also margins.
The jrxml will be:
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Empty Line" pageWidth="572" pageHeight="30" whenNoDataType="AllSectionsNoDetail" columnWidth="572" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0">
<style name="bordered">
<box>
<pen lineWidth="0.25"/>
</box>
</style>
<title>
<band height="30" splitType="Stretch">
<staticText>
<reportElement style="bordered" x="0" y="0" width="572" height="30"/>
</staticText>
</band>
</title>
</jasperReport>
I used simple csv datasource for example - file films.csv contains data for report:
id,name,year,rating
1,The Shawshank Redemption,1994,9.3
2,The Godfather,1972,9.2
3,The Dark Knight,2008,9.0
4,The Godfather: Part II,1974,9.0
5,The Lord of the Rings: The Return of the King,2003,8.9
6,Pulp Fiction,1994,8.9
7,Schindler's List,1993,8.9
8,"The Good, the Bad and the Ugly",1966,8.9
9,12 Angry Men,1957,8.9
10,Avengers: Endgame,2019,8.8
The main (master) report is using CSV File Data Adapter for building report.
The jrxml of master report:
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Insert blank row on condition" pageWidth="612" pageHeight="792" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" >
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="films.csv"/>
<style name="bordered">
<box>
<pen lineWidth="0.25"/>
</box>
</style>
<field name="id" class="java.lang.Integer"/>
<field name="name" class="java.lang.String"/>
<field name="year" class="java.lang.String"/>
<field name="rating" class="java.math.BigDecimal"/>
<title>
<band height="30" splitType="Stretch">
<staticText>
<reportElement x="158" y="0" width="256" height="30"/>
<text><![CDATA[Films. Showing empty line for Id == 5]]></text>
</staticText>
</band>
</title>
<columnHeader>
<band height="30">
<staticText>
<reportElement x="0" y="0" width="70" height="30"/>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[Id]]></text>
</staticText>
<staticText>
<reportElement x="70" y="0" width="290" height="30"/>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[Name]]></text>
</staticText>
<staticText>
<reportElement x="360" y="0" width="69" height="30"/>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[Year]]></text>
</staticText>
<staticText>
<reportElement x="429" y="0" width="143" height="30"/>
<textElement textAlignment="Center" verticalAlignment="Middle"/>
<text><![CDATA[Rating]]></text>
</staticText>
</band>
</columnHeader>
<detail>
<band height="60">
<subreport>
<reportElement x="0" y="30" width="572" height="30" isRemoveLineWhenBlank="true" >
<printWhenExpression><![CDATA[$F{id} == 5]]></printWhenExpression>
</reportElement>
<subreportExpression><![CDATA["empty_row.jasper"]]></subreportExpression>
</subreport>
<textField>
<reportElement style="bordered" x="0" y="0" width="70" height="30"/>
<textElement textAlignment="Center"/>
<textFieldExpression><![CDATA[$F{id}]]></textFieldExpression>
</textField>
<textField>
<reportElement style="bordered" x="70" y="0" width="290" height="30"/>
<textElement textAlignment="Center"/>
<textFieldExpression><![CDATA[$F{name}]]></textFieldExpression>
</textField>
<textField>
<reportElement style="bordered" x="360" y="0" width="69" height="30"/>
<textElement textAlignment="Center"/>
<textFieldExpression><![CDATA[$F{year}]]></textFieldExpression>
</textField>
<textField>
<reportElement style="bordered" x="429" y="0" width="143" height="30"/>
<textElement textAlignment="Center"/>
<textFieldExpression><![CDATA[$F{rating}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
I used <printWhenExpression><![CDATA[$F{id} == 5]]></printWhenExpression> for hiding subreport with empty line, it means that empty line will show only of row with id == 5. You can use any expression you want.
The output at JSS will be:
Alternative solutions
You can insert element during building collection before passing this collection at JRBeanCollectionDataSource.
You can implement custom DataSource based on JRBeanCollectionDataSource with some logic.

How to get current element in JasperReports when using conditionalExpression with styles?

I want to make text in the textValue to be red when the numeric value of the cell is less than zero. Currently my configuration is like this:
<style name="RedText">
<conditionalStyle>
<conditionExpression><![CDATA[$F{col1}.compareTo(BigDecimal.ZERO) == -1]]></conditionExpression>
<style forecolor="#FF0000"/>
</conditionalStyle>
</style>
I have more cells (col2, col3, col4 ...) that I would need to apply this red text style. Is it possible to make it universal for current value, etc. col*?
The standard/classical way without using Java code
It is impossible without using Java code. In your case you need to create the new style for each field and apply this concrete style for textField element with conditionExpression contains the value of concrete field.
In common case the expression of textField can be complex, the same is true for the conditionExpression. The style does not know to which control it belongs. We can't operate with value of abstract field (parameter/variable), the engine need the concrete names to calculate expression.
Using Java API
Is it possible to solve the task applying style with JasperReports Java API. For example:
JRDesignStyle boldStyle = new JRDesignStyle();
boldStyle.setName("Sans_Bold");
boldStyle.setFontName("DejaVu Sans");
boldStyle.setFontSize(12);
boldStyle.setBold(true);
// ...
textField.setStyle(boldStyle);
JRDesignExpression expression = new JRDesignExpression();
expression.setText("$F{col1}");
textField.setExpression(expression);
Using backdoor or "yes we like to hack"
Look at the beatiful class in Java is Map, it so invaluable in JasperReports. It can help us to crack this task.
We need parameter of Map type for storing the value of field (it will be abstract field - "coln")
We need to enable applying style during resolving expression of a textField. The propery
net.sf.jasperreports.style.evaluation.time.enabled will help to enable this feature.
We need some Java method for putting value in to the Map (as a storage of a "coln") which returns the value we just put. The Map.put(K key, V value) is not good for this purpose. I wrote the small Utility class with wrapper over Map.put method.
The datasouce
The using of simple csv datasource is enough for this example. I like this kind of datasource for testing and debugging.
col1,col2,col3,col4,col5
1,2,3,9,-5
-2,6,-3,4,1
2,-2,-2,7,-3
8,3,4,-5,6
The name of data adapter for this datasource in the example below is numbers.csv. The first line from the file is skipped - it is contains the column's name.
Java code
It is a very simple utility class
public class Storage {
public static Integer add(Map<String, Integer> map, String key, Integer value) {
map.put(key, value);
return value;
}
}
Report template
The import for our utitlity class should be added for using it.
For correct resolving expression of conditional style we need to use in expression some parameter or some variable (you can check the source code of JasperReports). I'll add some fake variable for using in conditionExpression
We need to put the value of "coln" to the Map and then use this value in conditional style. In this case we don't need to know the name of field we are checking in conditional style. The parameter will be used for storing Map.
We need place and draw the value of "coln" in two runs. We can use fake invisible textField to put the field's value to the Map and another textField to show value with applying conditional style. The positions and sizes of this textFields are same - one above the other.
Our condition will be like this:
<style name="ColoredText">
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) < 0)]]></conditionExpression>
<style forecolor="blue"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) > 0)]]></conditionExpression>
<style forecolor="green"/>
</conditionalStyle>
</style>
There is no field in conditionExpression and it works :)
The jrxml file:
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Apply style withou name" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="numbers.csv"/>
<property name="net.sf.jasperreports.style.evaluation.time.enabled" value="true"/>
<import value="some.package.Storage"/>
<style name="ColoredText">
<conditionalStyle>
<conditionExpression><![CDATA[$P{STORAGE}.get($P{KEY}) == null && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#CC0000"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) < 0) && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#0037FF"/>
</conditionalStyle>
<conditionalStyle>
<conditionExpression><![CDATA[(((Integer)$P{STORAGE}.get($P{KEY})) > 0) && !"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style forecolor="#00BA00"/>
</conditionalStyle>
</style>
<style name="Fake">
<conditionalStyle>
<conditionExpression><![CDATA[!"null".equalsIgnoreCase($V{FAKE})]]></conditionExpression>
<style mode="Opaque" forecolor="#FFFFFF" backcolor="#FFFFFF"/>
</conditionalStyle>
</style>
<parameter name="STORAGE" class="java.util.Map">
<defaultValueExpression><![CDATA[new java.util.HashMap()]]></defaultValueExpression>
</parameter>
<parameter name="KEY" class="java.lang.String">
<defaultValueExpression><![CDATA["key"]]></defaultValueExpression>
</parameter>
<field name="col1" class="java.lang.Integer"/>
<field name="col2" class="java.lang.Integer"/>
<field name="col3" class="java.lang.Integer"/>
<field name="col4" class="java.lang.Integer"/>
<field name="col5" class="java.lang.Integer"/>
<variable name="FAKE" class="java.lang.String">
<variableExpression><![CDATA["FAKE"]]></variableExpression>
</variable>
<detail>
<band height="20">
<textField evaluationTime="Auto">
<reportElement style="Fake" x="0" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col1})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="0" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="100" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col2})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="100" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="200" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col3})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="200" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="300" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col4})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="300" y="0" width="100" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="Fake" x="400" y="0" width="140" height="20"/>
<textFieldExpression><![CDATA[some.package.Storage.add($P{STORAGE}, $P{KEY}, $F{col5})]]></textFieldExpression>
</textField>
<textField evaluationTime="Auto">
<reportElement style="ColoredText" x="400" y="0" width="140" height="20"/>
<textFieldExpression><![CDATA[$P{STORAGE}.get($P{KEY})]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
The green color is used for positive numbers and blue - for negative. The red color is using to indicate algorithm's problems.
Output result
The generated pdf with help of JRPdfExporter: