How to build a table of contents in iReport/JasperReports


We use iReport to enable our customers to generate catalogs for print for their products. This process works GREAT. One of the things we've struggled with is how to create a table of contents for these product catalogs.

I decided to use the Scriptlet functionality to give this a shot. Note that our reports are all master-detail in nature, in that there is a master report and a subreport.

At first, I attached the scriptlet to the master report, and tried to find a way to collect all of the product numbers in the detail report, then pass that value back to the master report. I can't figure out how to get the detail information back to the master report, as it appears that you can only pass default variables from the subreport back to the master (eg PAGE_NUMBER, COLUMN_NUMBER, PAGE_COUNT, COLUMN_COUNT).

So then I tried attaching the Scriptlet to the detail report. Well, the detail report doesn't know what page the master is on, so I started passing the master's page_number into a variable in the detail report. That worked great, except that sometimes the detail can overflow to the point that page numbers from the master are skipped. In other words, if a subreport returns more than two pages worth of data, you are going to miss a page number when the subreport is called the next time.

So how to do this?

Best Solution

I found an approach that worked, similar to one of my ideas above. First of all, I could NOT get a subreport to return a value to the calling report, and then retrieve that value using getVariableValue from inside a scriptlet. I tried forever, and I just could not get that to work.

Instead, I went with the approach where I attached the same scriptlet to both the master and subreports. The scriptlet has a class variable where I can keep a running tally of all the product numbers I encounter in the detail, and this code goes inside the afterDetailEval() method of the scriptlet. Note that I only want this code to run for the subreport, so I do a check to see if a particular detail field, which will never be null, is present. If it's not, I don't run the code. So when that event is called by the master report, the code doesn't run.

Now for the part that I learned from the example. The admirable side-effect programming in the example is that there is a line of dimensions 1x1 that contains a call to a custom scriptlet method in the "Print When Expression" property. That's a great idea! So this custom method returns false no matter what, and the line never prints. I did the same thing in the Page Footer band, and this method collects the values from the class variable, determines the page, and stores the results in a hash. Then it resets the class variable.

In the Report Summary band, I have another line that will never print, that calls another custom method. All I do here is iterate through a map of product numbers and show all the pages each one appears on. Done!

Related Question