JasperReports Ultimate Guide - Sample Reference - Schema Reference - Configuration Reference - API (Javadoc)
JasperReports - Table of Contents Sample (version 5.6.0) |

Shows how table-of-contents structures could be created for the generated reports.
Download All Sample Source Files
Browse Sample Source Files on SVN

Main Features in This Sample
| Creating Table-Of-Contents Structures
| File Resolver

top
| Creating Table-Of-Contents Structures | Documented by Sanda Zaharia
| Description / Goal |  | How to create a table of contents with hyperlinks at the beginning of a document.
| Since |  | 0.4.2
- | Table Of Contents - Overview A table of content becomes very necessary when complex documents with huge number of pages are generated. But, being almost impossible to determine the total number of pages, or the current content of generated pages at report design time, there is no built-in report element in JasperReports to handle the table of contents. The document structure unveils itself step by step at report filling time. Therefore this is the proper moment for building a table of content. Once a print element is generated, its place within document is completely determined, so from now on we are able to find and reference that element. If the element is starting a new section, we can add a new entry in the table of contents, pointing here. The table of content cannot be finalized before the report is completely filled, so it can be very easily placed on the last page of the document in the `summary` section. But how about documents that require to start with a table of contents? With JasperReports this document layout can be also realized, as shown in this sample. Table Of Contents Sample There are various ways to build tables of contents, depending on everyone's free imagination. The way imagined here is the following: The table of content structure was configured in a subreport. See the `reports/HeadingsReport.jrxml` file for layout details. It is based on local anchor hyperlinks with different levels of indentation, depending on the heading type. Notice the presence of the invisible element labeled `"HIDDEN TEXT TO MARK THE BEGINNING OF THE TABLE OF CONTENTS"` in the title section. It will be used to identify the first page of the table of contents in the generated document. A `HeadingBean` class (see the `src/HeadingBean.java` file) was created in order to store the meaningful information of individual entries in the table of contents. A `HeadingBean` object exposes the following bean properties: `headingType` - an integer that indicates the heading type of the entry. `headingText` - the text to be written in the table of contents for this entry. `reference` - the hyperlink reference associated with the entry. `pageIndex` - the page index to be associated with this entry in the table of contents. During the report filling a JRBeanCollectionDataSource will be populated with `HeadingBean` object. The mechanism used to populate it is an inventive combination between report scriptlets and `printWhenExpression`s, as we'll see in a while. The bean collection will be passed as data source parameter to the `HeadingsReport` subreport placed in the summary section. At that moment the collection data source will be entirely complete and ready to be used. Every time the `addHeading(String )` method in the `HeadingsScriptlet` class is called, a new member is added to the bean collection data source described above. So, we have to ensure that the method is called any time a heading element is met in the document. This is done using an interesting condition in `printWhenExpression`s in the master report (see the `reports/TableOfContentsReport.jrxml` file):
      <group name="FirstLetterGroup" minHeightToStartNewPage="60">
        ...
        <groupHeader>
        <band height="25">
          <line>
            <reportElement x="0" y="0" width="1" height="1">
              <printWhenExpression>
                <![CDATA[((HeadingsScriptlet)$P{REPORT_SCRIPTLET}).addHeading("FirstLetterGroup")]] ></printWhenExpression>
            </reportElement>
            <graphicElement/>
          </line>
          ...
          <textField>
            <reportElement mode="Opaque" x="190" y="10" width="325" height="15" backcolor="#c0c0c0" style="Sans_Bold"/>
            <textFieldExpression class="java.lang.String"><![CDATA[$V{FirstLetter}]] ></textFieldExpression>
            <anchorNameExpression><![CDATA["FirstLetterGroup_" + $V{FirstLetter}]] ></anchorNameExpression>
          </textField>
        </band>
        </groupHeader>
      </group>
      <group name="ShipCountryGroup" minHeightToStartNewPage="60">
        ...
        <groupHeader>
        <band height="20">
          <line>
            <reportElement x="0" y="0" width="1" height="1">
              <printWhenExpression>
                <![CDATA[((HeadingsScriptlet)$P{REPORT_SCRIPTLET}).addHeading("ShipCountryGroup")]] >
              </printWhenExpression>
            </reportElement>
            <graphicElement/>
          </line>
          ...
          <textField>
            <reportElement x="0" y="4" width="515" height="15" style="Sans_Bold"/>
            <textFieldExpression class="java.lang.String">
              <![CDATA["  " + String.valueOf($V{ShipCountryNumber}) + ". " + String.valueOf($F{ShipCountry})]] >
            </textFieldExpression>
            <anchorNameExpression><![CDATA["ShipCountryGroup_" + $V{ShipCountryNumber}]] ></anchorNameExpression>
          </textField>
        </band>
        </groupHeader>
        ...
      </group>
One can see a similar construction present in both group headers of the report: a `line` with a `printWhenExpression` and a textfield with an `anchorNameExpression`. The `addHeading(...)` method called in the `printWhenExpression` populates the data source and always returns `false`. That means the `line` element never gets printed, being used only for calling the scriptlet method. But the related textfield will be printed and will be marked with the same anchor name as that one built in the `reference` field of the corresponding `HeadingBean` object. In the final document, clicking on any entry in the table of contents will send to the bookmarked element in the document. If the document would require a table of contents placed at the end, all the work would stop here. There would be nothing else to do. But in this sample is assumed that the table of contents is needed at the beginning of the document, hence an additional step is required. After filling the `JasperPrint` object, one can iterate through its pages and elements. We have to identify the pages containing the table of contents, to cut and move them at the beginning of the document. That's what the `moveTableOfContents(JasperPrint)` method in the `src/TableOfContentsApp.java` is doing. The method is called at fill time after the JasperPrint object was generated. The final result of the mechanism described above can be observed when running the sample. Running the Sample Running the sample requires the Apache Ant library. Make sure that `ant` is already installed on your system (version 1.5 or later). In a command prompt/terminal window set the current folder to `demo/hsqldb` within the JasperReports source project and run the `> ant runServer` command. It will start the HSQLDB server shipped with the JasperReports distribution package. Let this terminal running the HSQLDB server. Open a new command prompt/terminal window and set the current folder to `demo/samples/tableofcontents` within the JasperReports source project and run the `> ant test view` command. It will generate all supported document types containing the sample report in the `demo/samples/tableofcontents/build/reports` directory. Then the report will open in the JasperReports internal viewer.
top
| File Resolver | Documented by Sanda Zaharia
| Description / Goal |  | Shows how to use file resolver implementations in order to locate file resources referenced from within reports using relative paths.
| Since |  | 2.0.5
| File Resolver - Overview By default, the JR engine interprets resource locations as file system paths that can be either absolute or relative to the current user directory (given by the Java `user.dir` system property). If the path constructed this way resolves to an existing file, the report resource will be loaded from this file. The default behavior can be changed by using the REPORT_FILE_RESOLVER parameter at fill time to pass a file resolver object. This object needs to be an instance of a class that implements the FileResolver interface. The interface contains a single method which is responsible for resolving a resource location to an abstract file path (represented by a `java.io.File` object). When a file resolver is set via the report parameter, the engine will use it to interpret resource locations as file paths. If a file resolver returns a non-null file path for a resource location, the file located at the returned path will be used to load the resource. JasperReports ships with the built-in SimpleFileResolver implementation that interprets resource locations as paths relative to one or several file system folders. To create such a file resolver, the user has to specify one or more resources folders. When the resolver is asked to resolve a resource location, it interprets the locations as a path relative to each of the resource folders, and returns the first path that corresponds to an existing file. The SimpleFileResolver is called in this sample at fill time to resolve paths to compiled reports saved with the `.jasper` extension. See the example in the `src/TableOfContentsApp.java`:
    public void fill() throws JRException
    {
      long start = System.currentTimeMillis();
      //Preparing parameters
      Map parameters = new HashMap();
      parameters.put("ReportTitle", "Orders Report");

      SimpleFileResolver fileResolver =
        new SimpleFileResolver(
          Arrays.asList(new File[]{new File("build/reports")})
          );
      fileResolver.setResolveAbsolutePath(true);

      parameters.put(JRParameter.REPORT_FILE_RESOLVER, fileResolver);

      JasperReport jasperReport = (JasperReport)JRLoader.loadObject("build/reports/TableOfContentsReport.jasper");

      JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, getDemoHsqldbConnection());

      jasperPrint = moveTableOfContents(jasperPrint);

      JRSaver.saveObject(jasperPrint, "build/reports/TableOfContentsReport.jrprint");

      System.err.println("Filling time : " + (System.currentTimeMillis() - start));
    }
Note: As shown in the API javadoc, the REPORT_FILE_RESOLVER parameter is now deprecated. It is highly recommended to switch to the JasperReportsContext approach wherever is possible.

© 2001- Jaspersoft Corporation www.jaspersoft.com