Creating Subreport with list of list - jasper-reports

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"/>

Related

Xstream attribute and implict collection ambiguity

Hope you are fine.
Can you please help me to resolve my below problem. I have an xml element with attribute named as “value” which can have one value E.g. (1) below , but there can be another case which i can have it as a child element named as “value” with multiple values E.g. (2) below.
E.g. 1
<variable id="123" value="Adam"/>
E.g. 2
<variable id="123">
<value>Adam</value>
<value>Philip</value>
... ...
</variable>
Can you please tell me how would i map this in Xstream serializer in same java class?
if i configure as below it gives me duplicate error
#XStreamAlias(“variable”)
public class Variable {
#XStreamAsAttribute
private String id;
#XStreamAsAttribute
private String value;
#XStreamImplicit
private List<String> value = new ArrayList<String>();

Can JRXML extend data source before using it?

I use the same data source for generating several reports. One of the reports needs to be printed with a few empty lines ("reserve") at the bottom, so that users can manually write missing data by hand if needed.
E.g.:
+---------+-------------+
| item 1 | bla bla |
+---------+-------------+
| item 2 | foo |
+---------+-------------+
| | | <--- here user can just add forgotten
+---------+-------------+ items with a pen
| | |
+---------+-------------+
The easiest for the JRXML would be if there were several records of nulls at the end of the data source. Then it would just print its "details" band a few times with no text, just as required. However, the data source is reused for other reports which certainly don't want this.
Can I somehow inject such empty lines into the data source in JRXML itself, just before report filling?
You can modify your CustomDataSource and set it from jrxml to generate extra records when needed:
Example
public class JRExtraEmptyRecordsDataSource extends JRBeanCollectionDataSource {
private int nrOfEmptyRecords=0;
private int currentExtraRecord = 0;
public JRExtraEmptyRecordsDataSource(Collection<?> beanCollection) {
super(beanCollection);
}
#Override
public Object getFieldValue(JRField field) throws JRException {
if (currentExtraRecord==0){
return super.getFieldValue(field);
}
Class<?> theCorrectClass = field.getValueClass();
//Implement your logic to return correct class (reflection or switch) if you need speciall values
//or just return null
return null;
}
#Override
public boolean next() {
boolean next = super.next();
if (next){
return true;
}
currentExtraRecord++;
return currentExtraRecord<=nrOfEmptyRecords;
}
#Override
public void moveFirst() {
super.moveFirst();
currentExtraRecord=0;
}
public int getNrOfEmptyRecords() {
return nrOfEmptyRecords;
}
public int setNrOfEmptyRecords(int nrOfEmptyRecords) {
this.nrOfEmptyRecords = nrOfEmptyRecords;
return this.nrOfEmptyRecords;//Lets return something for variable
}
}
When you need extra empty records add this variable definition in your jrxml
<variable name="extraRecords" class="java.lang.Integer">
<initialValueExpression><![CDATA[((my.package.JRExtraEmptyRecordsDataSource)$P{REPORT_DATA_SOURCE}).setNrOfEmptyRecords(2)]]></initialValueExpression>
</variable>
I do not have custom datasource what can I do?
Normally without custom datasource, you can use the columnFooter with the attribute isFloatColumnFooter="true" (or a dummy group footer band) to display extra info at the end of your detail band. Probably I would choose this method anyway since it helps to customize the extra rows and avoid checking for null in the normal detail band
If you need dynamically to indicate the number of empty records, include a subreport with an empty record and pass as datasource JREmptyDataSource
new net.sf.jasperreports.engine.JREmptyDataSource(P{nrOfEmptyRecords})

How to populate chart data with JavaBeans collection dataSet?

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

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>

How to: Call Model-Defined Functions in Queries

I am trying to do the following:
http://msdn.microsoft.com/en-us/library/dd456857.aspx
I created the function in the edmx file here just before the schema element:
<Function Name="YearsSince" ReturnType="Edm.Int32">
<Parameter Name="date" Type="Edm.DateTime" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(AppliedDate)
</DefiningExpression>
</Function>
</Schema>
Now, I want to be able to use that in a query.
I created the following code in the ApplicantPosition partial class
[EdmFunction("HRModel", "YearsSince")]
public static int YearsSince(DateTime date)
{
throw new NotSupportedException("Direct calls are not supported.");
}
And I am trying to do the following query
public class Class1
{
public void question()
{
using (HREntities context = new HREntities())
{
// Retrieve instructors hired more than 10 years ago.
var applicantPositions = from p in context.ApplicantPositions
where YearsSince((DateTime)p.AppliedDate) > 10
select p;
foreach (var applicantPosition in applicantPositions)
{
Console.WriteLine(applicantPosition.);
}
}
}
}
The YearsSince is not recognized, the MSDN tutorial does not show exactly where I need to put the functio, so that might be my problem.
Your static YearsSince function must be defined in some class so if it is not Class1 you must use full identification with class name to call it. Check also this answer.