Package net.sf.jasperreports.engine.data

Provides various report data source implementations and allows creating custom data sources.

Data Sources

When filling the report, the JasperReports engine iterates through the records of the supplied data source object and generates every section according to the template defined in the report design.

Normally, the engine expects to receive a JRDataSource object as the data source of the report that it has to fill. But another feature lets users supply a JDBC connection object instead of the usual data source object when the report data is found in a relational database. The JRDataSource interface is very simple. It exposes only two methods:

 public boolean next() throws JRException; 
 public Object getFieldValue(JRField jrField) throws JRException;
The next() method is called on the data source object by the reporting engine when iterating through the data at report-filling time. The second method provides the value for each report field in the current data source record.

It is very important to know that the only way to retrieve data from the data source is by using the report fields. A data source object is more like a table with columns and rows containing data in the table cells. The rows of this table are the records through which the reporting engine iterates when filling the report and each column should be mapped to a report field, so that we can make use of the data source content in the report expressions.

There are several default implementations of the JRDataSource interface.

JDBC Data Sources

The JRResultSetDataSource is a very useful implementation of the JRDataSource interface because it wraps a java.sql.ResultSet object. Since most reports are generated using data in relational databases, this is probably the most commonly used implementation for the data source interface.

Interestingly, you might end up using this implementation even if you do not instantiate this class yourself when filling your reports. This is what happens: if you specify the SQL query in your report template, the reporting engine executes the specified SQL query and wraps the returned java.sql.ResultSet object in a JRResultSetDataSource instance. The only thing the engine needs to execute the query is a java.sql.Connection object.This connection object may be supplied instead of supplying the usual data source object.

In many other cases the SQL query is executed in the parent application, outside JasperReports. In such a situation, one could manually wrap the java.sql.ResultSet obtained using an instance of this data source class before calling the report-filling process.

The most important thing to know when using this type of data source is that one must declare a report field for each column in the result set. The name of the report field must be the same as the name of the column it maps, as well as the data type. If this is not possible for some reason, the data source also allows users to retrieve data from a particular column in the java.sql.ResultSet by index. The report field that maps the specified column can be named COLUMN_x, where x is the one-based index of the result set column.

For maximum portability, as stated in the JDBC documentation, the values from a java.sql.ResultSet object should be retrieved from left to right and only once. To ensure that they work this way, consider declaring the report fields in the same order as they appear in the SQL query.

JavaBeans Data Sources

The library provides two data source implementations that can wrap collections or arrays of JavaBean objects. Both implementations rely on Java reflection to retrieve report field data from the JavaBean objects wrapped inside the data sources. These data sources can be used to generate reports using data already available in-memory in the form of EJBs, Hibernate, JDO objects, or even POJOs.

The JRBeanArrayDataSource is for wrapping an array of JavaBean objects to use for filling a report with data, and the JRBeanCollectionDataSource is for wrapping a collection of JavaBeans. Each object inside the array or the collection will be seen as one record in this type of data source.

The mapping between a particular JavaBean property and the corresponding report field is made by naming conventions. The name of the report field must be the same as the name of the JavaBean property as specified by the JavaBeans specifications.

For instance, to retrieve the value of a report field named productDescription, the program will try to call through reflection a method called getProductDescription() on the current JavaBean object.

The JavaBeans data source implementation contain few methods that are useful in certain cases:

  • getData() - returns the underlying bean collection or array used by the data source.
  • getRecordCount() - returns the total number of beans contained in the collection or array used by the data source.
  • cloneDataSource() - returns a copy of data source by creating a fresh data source that uses the same underlying JavaBeans collection or array. This method can be used when a master report contains a subreport that needs to iterate on the same JavaBeans collection as the master.

Map-Based Data Sources

JasperReports library comes with two data source implementations that can wrap arrays or collections of java.util.Map objects.

The JRMapArrayDataSource wraps an array of java.util.Map objects, and JRMapCollectionDataSource can be used to wrap a java.util.Collection of Map objects.

These implementations are useful if the parent application already stores the reporting data available in-memory as java.util.Map objects. Each Map object in the wrapped array or collection is considered a virtual record in the data source, and the value of each report field is extracted from the map using the report field name as the key.

Map-based data source implementations contain the same set of utility methods as JavaBeans data sources:

  • getData() to access the underlying map collection or array
  • getRecordCount() to return the total number of maps/records
  • cloneDataSource() - to create a fresh copy of the data source

TableModel Data Sources

In some Swing-based desktop client applications, the reporting data might already be available in the form of a javax.swing.table.TableModel implementation used for rendering javax.swing.JTable components on various forms. JasperReports can generate reports using this kind of data if a given javax.swing.table.TableModel object is wrapped in a JRTableModelDataSource instance before being passed as the data source for the report-filling process.

There are two ways to use this type of data source. Normally, to retrieve data from it, one must declare a report field for each column in the javax.swing.table.TableModel object bearing the same name as the column it maps. Sometimes it is not possible or desirable to use the column name, however, because the report field name and columns could still be bound to report fields using their zero-based index instead of their names. For instance, if you know that a particular column is the third column in the table model object (index=2), then you could name the corresponding field "COLUMN_2" and use the column data without problems.

XML Data Sources

XML documents can be used as report data sources by means of a data source implementation. JasperReports features a built-in XML data source implementation (JRXmlDataSource) that is based on DOM and uses XPath expressions to select data from the XML document.

An XML data source instantiation involves the following inputs:

  • An XML document. The parsed document, its location, or its source is provided as an argument to the data source constructor.
  • An XPath expression to select the node set that corresponds to the data source record list. The expression is passed to the data source as a constructor argument. The default XPath expression selects the document node itself; in this case the data source would produce a single record. The XPath expression is executed when the data source is instantiated; each item in the resulting node set will generate a record/row in the data source.
  • For every field in the report/data set, an XPath expression to select the field value for each record is needed. The field's XPath expression is provided by the AbstractXmlDataSource.PROPERTY_FIELD_EXPRESSION custom field property. The use of the field description to specify the XPath expression is still supported, but is now discouraged, the above mentioned custom property taking precedence over the field description. The field's XPath expression is executed for each record using as a context node the current node from the main node set.
An XML data source can be used create sub-data sources to be used for subreports or subdatasets. There are two methods of creating a sub-data source from a parent XML data source:
  • A sub-data source can be created for a new document that uses the current node as a root node. An XPath expression can additionally be specified to select the list of nodes for the sub-data source. The subDataSource() and subDataSource(String selectExpression) methods should be used to create sub-data sources in this scenario.
  • The same document can be reused for a new sub-data source, which would specify a different XPath expression for the main node set. This can be accomplished via dataSource() and dataSource(String selectExpression) methods calls.
The XML data source provides localization support for both number and date/time values rendered as text in the wrapped XML document.

In order to parse these text values into java.lang.Number or java.util.Date values according to the declared report field type in the report template, the program needs to know which pattern and locale to use. For date/time report fields, if the text value inside the XML representing time is rendered in a specific time zone, then this time zone needs to be provided to the data source so that it is taken into account when parsing.

There are four setter methods in the JRXmlDataSource class for specifying:

  • setNumberPattern(java.lang.String) - to use for parsing all text values corresponding to report fields of type java.lang.Number or any subclass of it
  • setDatePattern(java.lang.String) - to use for parsing all date/time values corresponding to report fields of type java.util.Date or any subclass of it
  • setLocale(java.util.Locale) - to use for getting localized number and date parsers
  • setTimeZone(java.util.TimeZone) - to use for properly translating time values when they are not expressed in GMT
Patterns should be non-localized and in accordance with the java.text.DecimalFormat and java.text.SimpleDateFormat pattern syntax. If specific patterns are not supplied, the defaults for these two format classes apply.

XML data sources work by interpreting XPath expressions and selecting nodes and values from the XML document based on these expressions. This functionality related to XPath processing has been extracted into a generic service interface called JRXPathExecuter.

The XPath executer implementation used by XML data sources can be configured via a JasperReports property named net.sf.jasperreports.xpath.executer.factory. This property gives the name of a XPath executer factory class, which has to implement the JRXPathExecuterFactory. JasperReports has two XPath executer implementations, one based on Apache Xalan (http://xml.apache.org/xalan-j/), now moved into a separate jasperreports-xalan artifact, and the second based on Jaxen (http://www.cafeconleche.org/jaxen/), now used by default.

In many cases, the Jaxen XPath executor provides better performance than the executor that uses Xalan. But if backward compatibility is required, for switching to the Xalan XPath executer, one needs to set the net.sf.jasperreports.xpath.executer.factory property to the net.sf.jasperreports.xalan.util.XalanXPathExecuterFactory class, which is usually done by including the following line in the jasperreports.properties configuration file:

 net.sf.jasperreports.xpath.executer.factory=net.sf.jasperreports.xalan.util.XalanXPathExecuterFactory
 
and make sure the optional jasperreports-xalan-x.x.x.jar is in the classpath of the application. To switch back to Jaxen, one would comment or remove the property line, or explicitly set the property to JaxenXPathExecuterFactory.

CSV Data Sources

Sometimes data that users need to fill the report with is found in plain text files, in a certain format, such as the popular CSV (comma-separated value).

JasperReports provides an implementation for such a data source, by wrapping the CSV data from a text file into a JRCsvDataSource. The CSV data source usually needs to read a file from disk, or at least from an input stream. Thus, the JRCsvDataSource can be initialized in three ways, depending on where it gets the data:

  • a file - new JRCsvDataSource(File)
  • an input stream - new JRCsvDataSource(InputStream)
  • a reader - new JRCsvDataSource(Reader)
The CSV format employs certain formatting rules. Data rows are separated by a record delimiter (text sequence) and fields inside each row are separated by a field delimiter (character). Fields containing delimiter characters can be placed inside quotes. If fields contain quotes themselves, these are duplicated (for example, "John ""Doe""" will be displayed as John "Doe").

The default values in JasperReports (and also the most common for CSV files) are a comma for field delimiter and a newline (\n) for record delimiter. Users can override these default values by calling setFieldDelimiter(char) and setRecordDelimiter(String). For example, on some systems, users may need to replace the default \n delimiter with \r\n.

Since CSV does not specify column names, the default convention is to name report fields COLUMN_x and map each column with the field found at index x in each row (these indices start with 0). To avoid this situation, users have two possible solutions:

  • using the setUseFirstRowAsHeader(true) method to force the program to read the column name from the first line of the CSV file.
  • Providing an array of column names using the setColumnNames(String[]) method.
Note that in both cases, the number of provided column names must be at least equal with the number of actual fields in any record, otherwise an exception will be thrown. Also, for any column name in the data source, an equivalent report field must exist.

Handling data types for fields in CSV data sources is special since the CSV file format does not provide such information. This matter is solved by trying to match each field in the data source to its corresponding report field type. For number and date/time fields, converting text values to java.lang.Number and java.util.Date values respectively requires parsing using format objects. This is controlled by specifying the date and number format objects to be used with the JRCsvDataSource instance by calling its setDateFormat(DateFormat) and setNumberFormat(NumberFormat) methods before passing it to the report-filling process.

The CSV data source implementation also has a JRCsvDataSourceProvider class, useful for design tools creators.

Excel Data Sources

When reporting data is in Microsoft Excel files (XLS or XLSX), the ExcelDataSource data source implementation can be used to read it and feed it into the report.

The XLS data source uses the Apache POI library to load the Excel workbook and read from it. Instances of this data source can be created by supplying either an in-memory workbook object, a file, or an input stream to read the data from.

Report-field mapping for this data source implementation is very similar to the CSV data source field-mapping explained in the previous section. It works on the assumption that the workbook contains data in a tabular form (rows are records and columns contain report-field values).

Empty Data Sources

The JREmptyDataSource class is a very simple data source implementation that simulates a data source with a given number of virtual records inside. It is called "empty data source" because even though it has one or more records inside, all the report fields are null for all the virtual records of the data source.

Such a simple data source implementation is used by the UI tools to offer basic report preview functionality, or in special report templates, or for testing and debugging purposes.

Rewindable Data Sources

The JRRewindableDataSource is an extension of the basic JRDataSourceinterface, to which it adds the possibility of moving the record pointer back before the first virtual record. It adds only one method, called moveFirst(), to the interface.

Rewindable data sources are useful when working with subreports. If a subreport is placed inside a band that is not allowed to split due to the isSplitAllowed="false" setting and there is not enough space on the current page for the subreport to be rendered, then the engine has to give up rendering the current band, introduce a page break, and restart the band and the subreport on the next page. But since the subreport has already consumed some of the supplied data source records when trying to render the band on the previous page, it needs to move the record pointer of the data source back before the first data source for the subreport to restart properly.

All built-in data source implementations are rewindable except for the JRResultSetDataSource, which does not support moving the record pointer back. This is a problem only if this data source is used to manually wrap a java.sql.ResultSet before passing it to the subreport. It is not a problem if the SQL query resides in the subreport template because the engine will reexecute it when restarting the subreport on the next page.

Data Source Provider

To simplify integration with the GUI tools for creating and previewing report templates, the JasperReports library has published an interface that allows those tools to create and dispose of data source objects. This is the standard way to plug custom data sources into a design tool.

This is very useful when the developer wants to preview the reports with the design tool and use the actual data that the target application will supply at runtime. In order to achieve this, simply create a custom implementation of the JRDataSourceProvider interface and make it available to the design tool to create the required data sources to use during report preview.

The data source provider interface has only a few methods that allow creating and disposing of data source objects and also methods for listing the available report fields inside the data source if possible. Knowing which fields will be found in the created data sources helps you to create report field wizards inside the design tools to simplify report creation.

The library also comes with an abstract implementation of the JRDataSourceProvider interface that can be used as the base class for creating data source provider implementations that produce JavaBean-based data sources.

The JRAbstractBeanDataSourceProvider uses Java reflection to provide available report fields names for a given JavaBean class.

Related Documentation

JasperReports Tutorial