JasperReports: Append an In-Report ToC without Scriptlets

Whenever you want to add a Table of Contents to your Jasper Report you have a problem, because there is no build-in function to do this. Most solutions suggest to derive an own Scriptlet class, use subreports or provide a custom data source.

Let me add another quick & dirty solution that requires no external resources, not even the Groovy library. It works with pure embedded Java code, at least as long you're ok with the very simplified layout of this solution, and do not require to have the ToC at the beginning of your report. And furthermore, it is pretty simple.

First, create a new parameter of type java.lang.StringBuilder:

<parameter name="toc" class="java.lang.StringBuilder" isForPrompting="false">
	<defaultValueExpression><![CDATA[new StringBuilder()]]></defaultValueExpression>
</parameter>

This parameter called toc will be filled with the table of content during the rendering of the detail sections. You may add markup if you want to.

Next, on the summary page, print the content of this parameter. I'll use HTML formated strings, here:

<statictext>
	<reportElement x="0" y="0" width="540" height="20"/>
	<textElement verticalAlignment="Middle">
		<font size="14" isBold="true" isItalic="true"/>
	</textElement>
	<text><![CDATA[Table of Contents]]>
</staticText>
<textField isStretchWithOverflow="true">
	<reportElement x="28" y="30" width="471" height="20"/>
	<textElement markup="html">
		<font fontName="Monospaced" size="8"/>
	</textElement>
	<textFieldExpression><![CDATA[$P{toc}.toString()]]></textFieldExpression>
</textField>

Maybe you noticed that I use a monospace font here. This will be explained later.

Whereever you want to have a new entry (e.g. in the detail band or within a group header) add a new text field and put the following expression into it:

$P{toc}.append(
	$F{section}.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")
		+ "<font color='#999999'>"
			/* Fill the string with up to 80 dots */
			+ new String(new char[80 - Math.min($F{section}.length(), 80)]).replaceAll(".", ".")
		+ "</font>"
		+ $V{PAGE_NUMBER}
		+ "<br>"
) == null ? "" : ""

This works since StringBuilder.append returns an object (itself). It won't be visible since the expression will result in an empty string in any case.

In this example, I want to use the field "section" as an index. Because the resulting string will be formatted using html markup we have to escape "<" as "&lt;", ">" as "&gt;" and "&" as "&amp;". In the next lines, you see the reason why I decided to use a monospace font for the ToC: the section name should be followed by up to 80 grey dots and the page number.

Since you cannot use loops within JasperReport's expressions (at least not without Groovy) I used the trick to create a new String containing the required number of 0x0 characters and replace all characters (the first parameter of String.replaceAll is a regular expression) with dots.

Comments

Addind a Table of contents to a jasper report is very simple in DynamicReports. This library is open source and it is based on JasperReports.
http://www.dynamicreports.org/
See the following toc examples:
http://www.dynamicreports.org/examples/examples-overview#tableofcontents

Clever Solution !

Thanks, short, sweet and it works perfectly :)

Here is how you can put it on your FIRST PAGE - put "EVALUATION TIME" - "REPORT"!

By the way - thanks a lot for this solution! Worked perfect! (after fixing few typos ;))

Nice! After 10 minutes i've got a great TOC. After 60 minutes it's now full working with my right alignment and some tabs for better structure.
Thanks! :)

Hi, I'm new to Jasper.
Can you please explain more at the 3rd step?

$P{toc}.append(
$F{section}.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")
+ ""
/* Fill the string with up to 80 dots */
+ new String(new char[80 - Math.min($F{section}.length(), 80)]).replaceAll(".", ".")
+ ""
+ $V{PAGE_NUMBER}
+ ""
) == null ? "" : ""

1st, I think the syntax has a typo as Aliaksandr described above
2nd, I try to add a text field and copy the expression to the summary band but it didn't work.
Thank you

Aliaksandr, Can you please what typos you fix? Thanks