How to populate chart data with JavaBeans collection dataSet? - charts

I have already created a working jrxml report presenting a table populated by a dataset of a collection (List) of Java beans.
Now I would like to use that same dataset to create Chart (basic bar chart for starters). Each bean contains 4 values that I would like to show on bar chart: month, normal hours, travel hours, and overtime hours. I was hoping that each bean would generate a group of 3-bars for each month so in the end the chart would contain 12x3 bars growing from bottom to up and the name of the month would act as a label under the 3-bar groups, each group positioned next to another starting from left to right.
Unfortunately creating this chart seems much harder than I thought. At least it seems to be totally different compared to creating the table. I'm not sure if the Jasper Studio's Chart wizard is even working. At least it doesn't allow me to add any series in the chart data series dialog: if I press Add absolutely nothing happens - no dialog opens, no error message, nothing, nothing to hint me what is wrong.
Main problem is that I don't see a way to connect the dataset data to the Chart.
After trying embedding chart to my main report, I tried to add it also to a new subreport created only for to act as chart container. I passed the main report dataset as a datasource to subreport and tried to use it as main data set in subreport's chart. Still no luck with the dataset/chart connection, f.e. Still nothing happens if I press the Add-button.
Below you can see the simple beans I'm using. First one, the WorkingHoursReport is the bean I'm passing to report as JRBeanCollectionDataSource. I believe the most interesting field of that bean is the list of WorkingHours-beans. There will be always 12 items on that list: one for each month. That is the list I'm currently passing to my table element using datasource expression: new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{workingHours}).
WorkingHoursReport.java:
public class WorkingHoursReport extends CommonReport {
private int year;
private List<WorkingHours> workingHours;
}
WorkingHours.java:
public class WorkingHours {
private int month = 0;
private double hoursNormal = 0;
private double hoursTravel = 0;
private double hoursOvertime = 0;
private double hoursTotal = 0;
private double hoursTotalCumulative = 0;
}
While trying to create my first chart ever, I was naturally trying to populate the data to chart using the very same command for defining the datasource as I was already using successfully with my table:
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{workingHours}).
Unfortunately it seemed that at least the jasper studio chart creation wizard did not get any connection to the data (no dialog was opening where, according to the docs, I should have been able to select data fields to chart ).

This is how I would solve your problem considering that you are using java beans, need to sum up data and then have both series (hours) and categories (months). Don't connect it to your table datasource but create a specific datasource for it.
Create a specific chart bean
public class ChartData {
private String serie;
private String category;
private double value;
public ChartData(String serie, String category, double value) {
super();
this.serie = serie;
this.category = category;
this.value = value;
}
.... getter and setters
}
Fill the chart bean's with data
Loop your dataset (List) and fill the ChartData list, you will probably need a map to find same month and add to the hours. I will not show you this but create them statically to show an example
List<ChartData> cList = new ArrayList<ChartData>();
cList.add(new ChartData("hoursNormal","month1", 12.3)); //The use of resources or static text is beyond this example
cList.add(new ChartData("hoursTravel","month1", 3.2));
cList.add(new ChartData("hoursOvertime","month1", 1.3));
cList.add(new ChartData("hoursNormal","month2", 16.4));
cList.add(new ChartData("hoursTravel","month2", 5.2));
cList.add(new ChartData("hoursOvertime","month2", 4.1));
Pass the List as a datasource through the parameter map
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("CHART_DATASET", new JRBeanCollectionDataSource(cList));
Display the chart
Now we can display the chart in the title or summary band using a subDataset passed on the parameter $P{CHART_DATASET}
<?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="working_hours" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="1a12c021-57e2-4482-a273-56cbd3f78a17">
<subDataset name="chartDataSet" uuid="119b7f0e-01ef-4e2b-b628-d76f51e83768">
<field name="serie" class="java.lang.String"/>
<field name="category" class="java.lang.String"/>
<field name="value" class="java.lang.Double"/>
</subDataset>
<parameter name="CHART_DATASET" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource" isForPrompting="false"/>
<summary>
<band height="142" splitType="Stretch">
<barChart>
<chart>
<reportElement x="80" y="0" width="337" height="142" uuid="c8f4dc5d-47e7-489b-b27e-09976d90994a"/>
<chartTitle/>
<chartSubtitle/>
<chartLegend/>
</chart>
<categoryDataset>
<dataset>
<datasetRun subDataset="chartDataSet" uuid="abec2dce-b670-4e84-b71f-469d954dbcb5">
<dataSourceExpression><![CDATA[$P{CHART_DATASET}]]></dataSourceExpression>
</datasetRun>
</dataset>
<categorySeries>
<seriesExpression><![CDATA[$F{serie}]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$F{value}]]></valueExpression>
</categorySeries>
</categoryDataset>
<barPlot>
<plot/>
<itemLabel/>
<categoryAxisFormat>
<axisFormat/>
</categoryAxisFormat>
<valueAxisFormat>
<axisFormat/>
</valueAxisFormat>
</barPlot>
</barChart>
</band>
</summary>
</jasperReport>
Settings in JasperSoft Studio
Enjoy the result

Related

how to write fixedlength file to CSV file with bean io with all values come in diffent columns of a record

This code is able to write data to csv file but the only problem is the data is getting written in single column only.
I want the data to come in different column. I am new to bean io and not able to figure it out.
I have tried below given code and not able get output in proper format:
public class XlsWriter {
public static void main(String[] args) throws Exception {
StreamFactory factory = StreamFactory.newInstance();
factory.load("C:\\Users\\PV5057094\\Demo_workspace\\XlsxMapper\\src\\main\\resources\\Employee.xml");
Field[] fields = Employee.class.getDeclaredFields();
System.out.println("fileds" + fields.length);
List<Object> list = new ArrayList<Object>();
for (Field field : fields) {
list.add(field.getName());
}
BeanReader in = factory.createReader("EmployeeInfo", new File("C:\\Temp\\Soc\\textInput.txt"));
BeanWriter out = factory.createWriter("EmployeeInfo", new File("C:\\Temp\\Soc\\output.csv"));
Object record;
while ((record = in.read()) != null) {
System.out.println(record.toString().length());
out.write(record);
System.out.println("Record Written:" + record.toString());
}
in.close();
out.flush();
out.close();
}
}
textInput.txt
AAAAABBBBBCCCCC
AAAAABBBBBCCCCC
AAAAABBBBBCCCCC
AAAAABBBBBCCCCC
AAAAABBBBBCCCCC
<?xml version="1.0" encoding="UTF-8"?>
<beanio xmlns="http://www.beanio.org/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.beanio.org/2012/03 http://www.beanio.org/2012/03/mapping.xsd">
<stream name="EmployeeInfo" format="fixedlength">
<record name="employee"
class="com.aexp.gmnt.imc.record.submission.Employee" minOccurs="0"
maxOccurs="unbounded" order="1">
<field name="firstName" length="5" padding="0" justify="right" />
<field name="lastName" length="5" padding="0" justify="right"/>
<field name="title" length="5" padding="0" justify="right"/>
</record>
</stream>
I want every record value in different column of a CSV file, but currently it is comming in a single column only, please help.
You need to have a different stream definition in your mapping file for writing to the CSV file. The EmployeeInfo stream can only deal with fixed length content because that is how it is configured.
You need to add a second <stream> definition to handle the CSV file you want to generate and your BeanWriter need to reference the new CSV stream instead of the fixed length one.
Add a new <stream> definition to your existing mapping.xml file:
<stream name="EmployeeInfoCSV" format="csv">
<record name="employee" class="com.aexp.gmnt.imc.record.submission.Employee" minOccurs="0" maxOccurs="unbounded">
<field name="firstName" />
<field name="lastName" />
<field name="title" />
</record>
</stream>
Note the change in the name of the <stream> and the format set to csv. In this <stream> definition you can then also change the order in which the data is written to the csv file if you want to without affecting your BeanReader's order in which it expects to read the data. The length,padding and justify attributes are not required for a csv file.
Now you only need to change how you configure your BeanWriter from:
BeanWriter out = factory.createWriter("EmployeeInfo", new File("C:\\Temp\\Soc\\output.csv"));
to
BeanWriter out = factory.createWriter("EmployeeInfoCSV", new File("C:\\Temp\\Soc\\output.csv"));
Note the change to use the csv stream name in the createWriter method parameters.
Edit to answer this question from the comments:
just a added question of I need to add the as first line with header
values as field values without writing them as header record type in
bean io then is it possible through reflection or something?
No need for reflection or jumping through hoops to get it done. You can create a Writer that you can use to write out the header (column) names to the file first before passing the writer to the BeanWriter for appending the rest of the output.
Instead of using the BeanWriter like above:
BeanWriter out = factory.createWriter("EmployeeInfoCSV", new File("C:\\Temp\\Soc\\output.csv"));
You would now do something like:
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("C:\\Temp\\Soc\\output.csv")));
writer.write("First Name,Last Name,Title");
writer.newLine();
BeanWriter out = factory.createWriter("EmployeeInfoCSV", writer);
BeanIO would then carry on writing its output to the writer which will append the data to the existing file. Remember to close() the writer as well when you are done.

No markup processor factory specified for "HTML" markup error on JaspeReports Server

I am working on a JasperReports report that works totally fine when I compile it on the Jaspersoft Studio but when I deploy it on the JR Server 6.3.0 it gives me an error that no markup processor specified for the the HTML markup:
No markup processor factory specified for "HTML" markup
I used the html markup to add few font effects to some text parts in the report. I am attaching a screenshot of the error.
How I can fix this error?
The Java code that looking for a processor is a case sensitive.
package net.sf.jasperreports.engine.fill;
// ...
public abstract class JRFillTextElement extends JRFillElement implements JRTextElement {
// ...
private static final Map<String, MarkupProcessor> markupProcessors = new HashMap<String, MarkupProcessor>();
// ...
protected MarkupProcessor getMarkupProcessor(String markup) {
MarkupProcessor markupProcessor = markupProcessors.get(markup); // it is just a Map. The key is case sensetive
In default.jasperreports.properties file the processor for html markup is registered with lower case. Look at the end of name (factory.html):
net.sf.jasperreports.markup.processor.factory.html=net.sf.jasperreports.engine.util.JEditorPaneMarkupProcessor.HtmlFactory
You should use the name at lower case, like this:
<textField>
<reportElement x="0" y="0" width="100" height="10"/>
<textElement markup="html">

Data will not shown at Jaspersoft Studio 6.2.2 data set dialog

I have faced suddenly the following issues with Jaspersoft Studio data set dialog:
First: After running the first query select KN_Perzentile from "Kennzahl" where KN_ID = 215 and KN_RefWert1 = 8.3 and clicking the refresh preview data the Studio shows that 2 fields are read (which should be one in fact as I tested the query against DB), but the data will not shown at all!
Second: Running any other query there encounters an error, saying that the field KN_Perzentile is not found! This field is not asked in the second query at all!
Both queries are tested against Database and correct. I have these problems only on one project.
The variable vVisual should read the value from the data base.And the java class which is in build path should read his variable and use it for creating the chart. But the data will not be added to the chart. I guess that is because jasper does not read the KN_Perzentile.
This is the chart with Visual Basic = 0 after genarating:
Here is the jrxml:
<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="JFreeReport" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="1edc4d15-06d0-46cf-8d57-179fd0bed566">
<scriptlet name="BoxPlotScript" class="testProjektIman.toc.JFreeChartScriptlet"/>
<queryString>
<![CDATA[select KN_Perzentile from "Kennzahl" where KN_ID = 215 and KN_RefWert1 = 8.3
]]>
</queryString>
<field name="KN_Perzentile" class="java.lang.Double"/>
<variable name="Pie" class="net.sf.jasperreports.engine.JRRenderable" calculation="System">
<variableExpression><![CDATA[$V{Pie}]]></variableExpression>
<initialValueExpression><![CDATA[$V{Pie}]]></initialValueExpression>
</variable>
<variable name="vVisual" class="java.lang.Double" calculation="First">
<variableExpression><![CDATA[$F{KN_Perzentile}]]></variableExpression>
<initialValueExpression><![CDATA[$F{KN_Perzentile}]]> </initialValueExpression>
</variable>
<detail>
<band height="290" splitType="Stretch">
<image scaleImage="Clip" hAlign="Center">
<reportElement x="60" y="50" width="440" height="215" uuid="60c9de91-e7ad-4ffb-81fd-109a381532b6"/>
<imageExpression><![CDATA[$V{Pie}]]></imageExpression>
</image>
</band>
</detail>
</jasperReport>
Java Class:
public class JFreeChartScriptlet extends JRDefaultScriptlet
{
#Override
public void afterReportInit() throws JRScriptletException {
Double testVb = 0.0;
DefaultPieDataset dataset = new DefaultPieDataset();
testVb = (Double) this.getVariableValue("vVisual");
dataset.setValue("Java", new Double(43.2));
dataset.setValue("Visual Basic", testVb);
dataset.setValue("C/C++", new Double(17.5));
dataset.setValue("PHP", new Double(32.5));
dataset.setValue("Perl", new Double(1.0));
JFreeChart chart =
ChartFactory.createPieChart3D(
"Pie Chart 3D Demo 1",
dataset,
true,
true,
false);
PiePlot3D plot = (PiePlot3D) chart.getPlot();
plot.setStartAngle(290);
plot.setDirection(Rotation.CLOCKWISE);
plot.setForegroundAlpha(0.5f);
plot.setNoDataMessage("No data to display");
this.setVariableValue("Pie", new JCommonDrawableRenderer(chart));
}
}
First
I don't see any use of any field out of the result. $V{Pie} just refers to itself and is being used in the reports Detail band, $V{vVisual} is being calculated but not being used anywhere.
Because of this structure, no fields are being shown.
Second
There is a field defined called KN_Perzentile. If you change your query and your query does not return this field (with the correct data type) anymore you get the error you are seeing.
EDIT
According to the documentation for scriptlets, there are two rules to comply with:
The important part when ensuring a variable in your report template is filled by a Scriptlet (or subreport) is to ensure the Variable has a calculation type of 'System' in the report design:
<variable name="AllCities" class="java.lang.String" calculation="System"/>
Also notice that there is no Variable Expression. Make sure you remember these two points when creating Variables in your own report with values supplied by Scriptlets.
Following this, the variableExpression of the system variable should be removed.

Creating Subreport with list of list

I have a list of goals, each of which have another ArrayList in it. I want to have the details in the child ArrayList to be displayed using a subreport. I would like to have a subreport for each of the objects in the child ArrayList.
The issue I am facing is that, I can't seem to find a way to specify the ArrayListas data source for the subreport. When trying to create datasource, I can't select fields of the dataset on which the list is built, only the fields of the main report can be selected.
Is it possible to do this in jasper report? I am stuck with this for quite some time now.
With information given this would be the answer.
Main bean (your goals?), containing List of other bean (SubBean).
public class Bean {
private String var1;
private List<SubBean> subBeans;
public String getVar1() {
return var1;
}
public void setVar1(String var1) {
this.var1 = var1;
}
public List<SubBean> getSubBeans() {
return subBeans;
}
public void setSubBeans(List<SubBean> subBean) {
this.subBeans = subBean;
}
}
Sub bean
public class SubBean {
private String var2;
public SubBean(String var2){
this.var2 = var2;
}
public String getVar2() {
return var2;
}
public void setVar2(String var2) {
this.var2 = var2;
}
}
How to pass the SubBean List to a subreport.
You need the field subBeans in you main report.
<field name="subBeans" class="java.util.List"/>
I suggest that you pass the location of the .jasper file as a parameter (Note jasper report needs absolute path) es. in main report
<parameter name="SUBREPORT_DIR" class="java.lang.String" isForPrompting="false"/>
and in java pass it (in example sub folder "jasper" in working directory is the location of the subreport.jasper)
paramMap.put("SUBREPORT_DIR", new File("jasper").getAbsolutePath() + File.separator);
Now just call your subreport (needs to be complied into .jasper) from main report like this.
<subreport>
<reportElement x="105" y="4" width="400" height="100"/>
<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{subBeans})]]></dataSourceExpression>
<subreportExpression class="java.lang.String"><![CDATA[$P{SUBREPORT_DIR} + "subreport.jasper"]]></subreportExpression>
</subreport>
Hence:
I'm creating a new JRDataSource for the sub report passing the List of SubBean in the current Bean
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{subBeans})
indicating the absolute location of the compiled subreport.jasper
$P{SUBREPORT_DIR} + "subreport.jasper"
so now in your subreport you can use the field: var2 of the SubBean, just define it like this in the subreport.jrxml
<field name="var2" class="java.lang.String"/>

Chart localization in JasperReports

I'm using JasperReports 5.5.1 to generate reports from Java. I designed the reports using Jaspersoft Studio. The report has several charts of different types and I have some problems when trying to localize the report using a ResourceBundle passed in the REPORT_RESOURCE_BUNDLE parameter. All the texts in the report are translated correctly except for the ones in the charts. I filled the keyExpression, labelExpression and seriesExpression with $R{STRING_KEY}, but the report is filled with the STRING_KEY instead of its value in the properties file. The title of the charts though is correctly translated.
Can anyone help me with this issue?
I found a solution.
First create a class responsible for getting the localized string from a specific resource bundle.
public class ReportLocalizer {
private static String resourceBundleBaseName = "com.company.package.boundle_name";
public static String getLocalizedString(Locale locale, String key) {
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleBaseName, locale);
return resourceBundle.getString(key);
}
}
Set the desired locale using JasperReports' parameters:
Map<String, Object> parameters = new HashMap<String, Object>();
...
parameters.put(JRParameter.REPORT_LOCALE, locale);
...
final JasperPrint print = JasperFillManager.fillReport(report, parameters, datasource);
To translate a string in a chart, pass it together with the report's locale to ReportLocalizer's getLocalizedString method.
[CDATA[com.company.package.ReportLocalizer.getLocalizedString($P{REPORT_LOCALE}, "string_key")]]>
For example, we can localize a pie chart showing the amount of men and women this way:
<pieChart>
<chart>
...
</chart>
<pieDataset>
<dataset>
...
</dataset>
<keyExpression><![CDATA[com.company.package.ReportLocalizer.getLocalizedString($P{REPORT_LOCALE}, $F{gender}.toString())]]></keyExpression>
<valueExpression><![CDATA[$F{amount}]]></valueExpression>
<labelExpression><![CDATA[String.valueOf($F{amount})]]></labelExpression>
</pieDataset>
<piePlot>
...
</piePlot>
</pieChart>