Chapter 13. Programming with the BIRT Reporting APIs

A reporting application uses the BIRT report engine API to generate reports from report design (.rptdesign) files. Typically, the application produces the report as a formatted file or stream, in HTML or PDF format. Alternatively, the application can create a report document (.rptdocument) file that contains the report content in binary form, then renders the report to HTML or PDF later.

This chapter describes the fundamental requirements of a reporting application and describes the BIRT API classes and interfaces that you use in the application. This chapter also provides detailed information about the tasks to perform.

The BIRT APIs in the org.eclipse.birt.report.engine.api package support the process of generating a report from a report design. This package provides the ReportEngine class and supporting interfaces and classes.

Optionally, the reporting application can use the BIRT design engine API to access the structure of report designs, templates, and libraries. With this API, the application can create and modify report designs before generating a report. This API supports creating and modifying the report items and other elements within designs.

The org.eclipse.birt.report.model.api package and its subpackages provide access to all the items that comprise a report design.

For complete information about all the methods and fields of the classes and interfaces in these packages, see the online Javadoc. To view the Javadoc, open BIRT Report Designer and choose Help→Help Contents→BIRT Programmer Reference→Reference→API Reference. Choose Report Engine API Reference for the report engine API and Report Object Model API Reference for the design engine API. The Javadoc also shows supporting packages in the public API.

Building a reporting application

An application that generates a report must carry out at least the required tasks described in the following sections. Further tasks, such as supplying user-entered values for parameters, are optional.

Creating and configuring a report engine

Instantiate a ReportEngine object. Set the engine home directory and handler configuration for the desired output format. The engine home directory defines the location of required plug-ins and libraries.

A single report engine object can generate multiple reports from multiple report designs.

Opening a report design or report document

Use one of the openReportDesign( ) methods of the ReportEngine class to open a report design from a String file name or an input stream. These methods return an IReportRunnable object.

Use the openReportDocument( ) method of the ReportEngine class to open a report document (.rptdocument) file from a String file name. This method returns an IReportDocument object.

Ensuring access to the data source

Ensure that the report engine can locate the classes that connect to the data source and supply data to the data set. The report engine can either create a connection to the data source or use a Connection object that the application provides.

Preparing to create a report in the supported output formats

Use an IRenderOption object to set the output format, the output file name or stream, the locale, and format-specific settings. The HTMLRenderOption class supports the HTML output format. For PDF output, use RenderOptionBase.

Generating a report in one of the supported output formats

Use an IRunAndRenderTask object to create the report from the IReportRunnable object. Use an IRenderTask object to create the report from an IReportDocument object.

Alternatively, use a URL to access the report viewer servlet, such as when deploying a BIRT report to an application server, as described earlier in this book. The report viewer can generate a report from either a design file or a document file.

Shutting down the engine

Shut down the report engine if the application does not need to generate more reports.

Optional tasks

The tasks in the following list are optional for a reporting application. Typically, the application performs one or more of these tasks.

  • Gather values for parameters.

    If the application uses a report design that has parameters, use the default values for the parameters or set different values.

  • Create a report document file.

    A report document file contains the report in a binary storage form. If the application uses a report design, use an IRunTask object to create the report document as an IReportDocument object.

  • Export data from a report document.

    Use an IDataExtractionTask object to extract data values from any item or set of items in the report document. Export the data values to a file or another application, or perform further processing on the data.

About the environment for a reporting application

You must ensure that the deployed application can access all the classes required for BIRT, your external data sources, and any other classes you need. The key requirement for BIRT is the location of the engine home. The engine home is the directory that contains the BIRT plug-ins and libraries needed to generate a report from a report design.

If you use the BIRT source code, you must ensure that the version that your application uses matches the version of the plug-ins and libraries in the BIRT engine home directory. If these versions are not the same, your reporting application is likely to fail.

The BIRT Report Engine package provides the complete environment for the reporting application. Earlier chapters in this book provide information about prerequisites and installing this package. The subdirectory, ReportEngine, of BIRT Report Engine contains all the plug-ins and libraries that the report engine uses. This directory contains a complete engine home that a reporting application requires.

About plug-ins used by the report engine

The engine home directory has a subdirectory, plugins, that contains the org.eclipse.birt and other plug-ins that a reporting application can use. Depending on the requirements of your reporting application and the items in your report designs, you can omit plug-ins that provide functionality that the application and designs do not use.

About libraries used by the report engine

The lib subdirectory of the engine home directory contains the JAR files described in Table 13-1. This location within the report engine ensures that the class loader of the application server in which you deploy the report engine can locate the libraries. To support generation of reports in PDF format, the library itext-1.3.jar must exist in the lib subdirectory of the report.engine.emitter.pdf plug-in.

Depending on the requirements of your reporting application and the items in your report designs, you can omit libraries that provide functionality that the application and designs do not use.

Table 13-1. Libraries in the engine home lib directory

Library

Description

chartengineapi.jar

From the chart.engine plug-in. Contains chart model and factory classes. Supports generation of charts in a report.

com.ibm.icu_3.4.4.1.jar

From IBM. Provides International Components for Unicode to support text in multiple locales.

commons-cli-1.0.jar

Used by the ReportRunner application in the report.engine plug-in. From the Apache Jakarta project. Provides command-line parsing. Reporting applications in a web environment do not require this library.

commons-codec-1.3.jar

Used by the report.engine plug-in. From the Apache Jakarta project. Provides encoding and decoding functionality.

coreapi.jar

From the core plug-in. Contains framework and utility classes.

dataadapterapi.jar

From the report.data.adapter plug-in.

dteapi.jar

From the data plug-in. Provides access to data sources. Transforms data that the data set provides.

engineapi.jar

From the report.engine plug-in. Required for generating a report from a report design.

flute.jar

Used by the report.model plug-in. From the W3 Consortium. Provides access to CSS functionality.

js.jar

From the core plug-in. Provides scripting functionality.

modelapi.jar

From the report.model plug-in. Describes the report design.

org.eclipse.emf.common_2.2.0.v<version>.jar

From the Eclipse EMF plug-in. Required for charts.

org.eclipse.emf.ecore.xmi_2.2.0.v<version>.jar

From the Eclipse EMF plug-in. Required for charts.

org.eclipse.emf.ecore_2.2.0.v<version>.jar

From the Eclipse EMF plug-in. Required for charts.

sac.jar

Used by the report.model plug-in. From the W3 Consortium. Required for CSS functionality.

scriptapi.jar

From the report.engine plug-in. Required for Java-based scripting.

About required JDBC drivers

The engine home plugins/org.eclipse.birt.report.data.oda.jdbc_<version> subdirectory contains a subdirectory, drivers. Place the driver classes or Java archive (.jar) files that you require to access JDBC data sources in this location.

Modifying a report design with the API

A reporting application can also modify a report design before generating the report. The application uses classes in org.eclipse.birt.model.api to change the structure of a report design. Sections later in this chapter provide more information about the types of change you can make to a report design and how to use these classes.

To provide further customization of a report, during the generation of the output, a report design can use Java script classes or embedded JavaScript code to handle events. The reporting application can use the model API classes to include new scripts. Earlier chapters in this book provide more information about the functionality of scripting.

Generating reports from an application

To build a stand-alone reporting application, code the tasks listed earlier in this chapter. The following sections describe these tasks in greater detail. The application does not require the BIRT Report Designer user interface to generate a report.

The key tasks are to ensure that the report engine has access to an engine home, set any parameter values, set up the tasks to generate the report, and run the report.

The org.eclipse.birt.report.engine.api package contains the classes and interfaces that an application uses to generate reports. The main classes and interfaces are ReportEngine, EngineConfig, IReportRunnable, IRenderOption and its descendants, and IEngineTask and its descendants.

Setting up the report engine

A report engine is an instantiation of the ReportEngine class. This object is the key component in any reporting application. It provides access to runnable report designs, parameters, the structure of a report design, and the task for generating a report from a report design. You set the report engine’s properties with an EngineConfig object. The following sections describe the various configuration options.

After setting all the required properties, instantiate the report engine with new ReportEngine( ). The constructor takes an EngineConfig object as its argument.

Configuring the engine home

The engine home is the key property that the report engine requires. The report engine cannot parse the report design nor run the report without a defined engine home.

For a stand-alone application, the engine home is an absolute path to a file system location. For an application running from a web archive (.war) file on an application server, the engine home is a relative path in the WAR file.

To set the engine home location, you use one of the following techniques:

  • For a stand-alone application, call the EngineConfig.setEngineHome( ) method with an argument that is the path to your engine home directory, for example:

    config.setEngineHome
       ("C:/Program Files/birt-runtime-2_1_0/ReportEngine" );
  • In your application’s environment, set the BIRT_HOME environment variable and set your CLASSPATH variable to access the required libraries. For example, in a Windows batch file that launches a stand-alone application, include commands similar to the following before running your application:

    set BIRT_HOME=
       "C:Program Filesirt-runtime-2_1_0ReportEngine"
    SET CLASSPATH=%BIRT_HOME%<required library 1>;
       %BIRT_HOME%<required library 2 and so on>;
       %CLASSPATH%
  • For a web application that has a location in the file system, use the servlet context to find the real path of the engine home, for example:

    config.setEngineHome
       (servletContext.getRealPath( "/WEB-INF" ) );
  • For a web application that runs from a WAR file, use a relative path from the WAR file root, for example:

    config.setEngineHome( "" );
  • In Eclipse, set BIRT_HOME in the VM arguments in the Run dialog. For example, in VM arguments, type text similar to the following:

    -DBIRT_HOME=
       "C:Program Filesirt-runtime-2_1_0ReportEngine"

Configuring the report engine

Optionally, you can also set other configuration properties using methods on an EngineConfig object. Table 13-2 describes these properties and how to set them with EngineConfig methods. The EngineConfig class also provides getter methods to access these properties. Sections later in this chapter provide examples of setting engine configuration properties and how an application uses them.

Table 13-2. EngineConfig properties

Property type

Setting the property

HTML emitter configuration

For custom handling of images or actions for HTML output. Create a new HTMLEmitterConfig object and set up the handlers. Then call setEmitterConfiguration( ).

Logging

To set the logging file location and level, call setLogConfig( ).

Platform context

To indicate whether the application and engine home are in a stand-alone environment or packaged as a web archive (.war) file, create an implementation of the IPlatformContext interface. Then call setEngineContext( ).

Resource files

To set the location where the reporting application can access resource files such as libraries and properties files that contain localized strings, call setResourcePath( ).

Scripting configuration

To provide external values to scripting methods, call setConfigurationVariable( ). To provide additional Java resources to scripting methods, call addScriptableJavaObject( ).

Status handling

To provide a custom status handler, create an implementation of the IStatusHandler interface. Then call setStatusHandler( ).

Temporary file location

To set up a custom location for temporary files, call setTempDir( ).

Setting up a stand-alone or WAR file environment

Two engine configuration properties depend on whether the environment in which the application runs is stand-alone or in a web archive (.war) file in an application server. These properties are the platform context and the HTML emitter configuration. The platform context provides the report engine with the mechanism to access files. The HTML emitter configuration provides the functionality to process images and handle hyperlinking and bookmark actions.

Setting up the platform context

For the platform context, the BIRT framework provides two implementations of the org.eclipse.birt.core.framework.IPlatformContext interface. These implementations provide all the required functionality for the platform context. For a stand-alone application, the context provides direct file system access to files. For an application running on an application server, the context uses the J2EE ServletContext class for file access. In the case of an application that runs from a WAR file, the platform context uses the resource-based access provided by the ServletContext class.

By default, BIRT uses a PlatformFileContext object, which provides the platform context for a stand-alone application. This context is also suitable for a web application that uses file system deployment on the application server.

For an application that runs from a WAR file on an application server, you must instantiate a PlatformServletContext object. The constructor for this class takes two arguments, a ServletContext object and a URL that is the path to the application. Use the PlatformServletContext object as the argument to the EngineConfig object’s setEngineContext( ) method. For example, the code in Listing 13-1 sets up a platform context for a reporting application that uses MarketApp as its context root.

Setting up the HTML emitter

When you generate a report in HTML format, BIRT’s HTML emitter creates image files for image elements and chart elements. The emitter also handles hyperlink, bookmark, and drill-through actions.

To set up an image handler, instantiate an implementation of the IHTMLImageHandler interface. For a stand-alone application, create an HTMLCompleteImageHandler object. Typically, the functionality that this class provides is sufficient, so your application does not need to extend it. For an application that runs from a WAR file on an application server, instantiate an HTMLServerImageHandler object. Typically for the application server environment, you need to extend from the base class that BIRT provides.

To set up an action handler, instantiate an implementation of the IHTMLActionHandler interface. This object handles the hyperlink, bookmark, and drill-through actions that the IAction interface defines.

To set up the HTML emitter, instantiate an HTMLEmitterConfig object. To configure the image handler, call the HTMLEmitterConfig.setImageHandler( ) method. To configure the action handler, call the HTMLEmitterConfig.setActionHandler( ) method. Call the EngineConfig.setEmitterConfiguration( ) method to complete the emitter configuration. This method takes two arguments, the output format type, which is RenderOptionBase.OUTPUT_FORMAT_HTML, and an HTMLEmitterConfig object. For a reference implementation of an emitter configuration for HTML format, see the org.eclipse.birt.report.viewer plug-in.

Example 13-1. Setting up the platform context for WAR file deployment

// Instantiate an engine configuration object.
EngineConfig config = new EngineConfig( );
// Set the relative path of the engine home in the WAR file.
config.setEngineHome( "" );
// Create the platform context as a hard-coded string.
// Alternatively, use HTTPServletRequest methods to retrieve
// the context dynamically.
// In this code, servletContext is a ServletContext object.
IPlatformContext context = new PlatformServletContext
   ( servletContext, "http://localhost:8080/MarketApp" );
// Set the engine context in the configuration object.
config.setEngineContext( context );

How to set up a report engine

Listing 13-2 shows an example of setting up a report engine as a stand-alone application on a Windows system. The application uses the engine home located in the BIRT run-time directory. The report output format is HTML. The application configures the HTML emitter, then creates the engine with completed EngineConfig object.

Example 13-2. Setting up the report engine

// Create an EngineConfig object.
EngineConfig config = new EngineConfig( );
// Set up the path to your engine home directory.
config.setEngineHome
   ( "C:/Program Files/birt-runtime-2_1_0/ReportEngine" );
// Explicitly set up the stand-alone application
IPlatformContext context = new PlatformFileContext( );
config.setEngineContext( context );
// Set up writing images or charts embedded in HTML output.
HTMLCompleteImageHandler imageHandler =
   new HTMLCompleteImageHandler( );
HTMLEmitterConfig hc = new HTMLEmitterConfig( );
hc.setImageHandler( imageHandler );
config.setEmitterConfiguration
   ( RenderOptionBase.OUTPUT_FORMAT_HTML, hc );
// Create the engine.
ReportEngine engine = new ReportEngine( config );

Using the logging environment to debug an application

BIRT Report Engine uses the java.util.logging classes, Logger and Level, to log information about the processing that the engine performs. When you run an application in the Eclipse workbench, by default, the messages appear in the console. When you run an application external to Eclipse, the default location of the log messages depends on your environment. The default logging threshold is Level.INFO. Typically, you change this level in your application to reduce the number of internal logging messages.

To set up the logging environment to write the engine’s log messages to a file on disk, use the EngineConfig.setLogConfig( ) method. This method takes two arguments, the directory in which to create the log file and the lowest level at which to log information. BIRT Report Engine creates a log file with a name whose format is ReportEngine_YYYY_MM_DD_hh_mm_ss.log. Set the logging level to a high threshold so that the engine logs a reduced number of messages.

Typically, you want to see information at INFO level when you first develop a block of code. To modify the amount of information that the engine logs, use the ReportEngine.changeLogLevel( ) method. This method takes a single argument, which is a Level constant. When the code is stable, you no longer need to see all the engine’s INFO messages. At that point, you can delete or comment out the call to changeLogLevel( ).

How to use BIRT logging

The following example shows how to use logging in an application. You set up the logging environment, then modify it later in your application.

  1. Set up the logging configuration on the report engine object.

    // Create an EngineConfig object.
    EngineConfig config = new EngineConfig( );
    //Set up the location and level of the logging output.
    config.setLogConfig( "C:/Temp", Level.ERROR );
    // Set up any other required configuration settings here.
    // Create the report engine.
    ReportEngine engine = new ReportEngine( config );
  2. In any newly written code, increase the amount of logging.

    engine.changeLogLevel( Level.INFO );

Opening a source for report generation

BIRT Report Engine can generate a report from either a report design or a report document. The engine can also generate a report document from a report design.

To open a report design, you call one of the openReportDesign( ) methods on ReportEngine. These methods instantiate an IReportRunnable object, using a String that specifies the path to a report design or an input stream.

To open a report document, you call the ReportEngine.openReportDocument( ) method. This method instantiates an IReportDocument object, using a String that specifies the path to a report document. You must handle the EngineException that these methods throw.

Understanding an IReportRunnable object

The IReportRunnable object provides direct access to basic properties of the report design. The names of report design properties are static String fields, such as IReportRunnable.AUTHOR. To access a report design property, use the getProperty( ) method with a String argument that contains one of these fields.

To access and set the values of parameters, you use methods on a parameter definition task object, described later in this chapter. To generate a report from a design, open the report design as shown in the following example, then perform the tasks shown later in this chapter.

How to access a report design

Listing 13-3 shows how to open a report design and find a property value. If the engine cannot open the specified report design, the code shuts down the engine. The variable, engine, is a ReportEngine object.

Example 13-3. Accessing a report design

String designName = "./SimpleReport.rptdesign";
IReportRunnable runnable = null;
try {
  runnable = engine.openReportDesign( designName );
}
catch ( EngineException e )
{
  System.err.println
     ( "Design " + designName + " not found!" );
  engine.shutdown( );
  System.exit( -1 );
}
// Get the value of a simple property.
String author = ( String ) runnable.getProperty
   ( IReportRunnable.AUTHOR );

Understanding an IReportDocument object

The IReportDocument object provides access to the data in a report and the report’s structure. IReportDocument provides methods to retrieve table of contents entries, bookmarks, and page information.

To access table of contents entries, use the findTOC( ) method. This method takes a TOCNode argument and returns a TOCNode object. To find the root table of contents entry, use an argument of null. To find the subentries of a table of contents entry, use the getChildren( ) method. This method returns a List of TOCNode objects. From a TOCNode object, you can retrieve the display value of the entry and a Bookmark object.

In turn, you can use the Bookmark object as an argument to the getPageNumber( ) method, which returns the number of the page to which the bookmark links. With this information, you can specify particular pages to render to a formatted report.

How to access a report document

Listing 13-4 shows how to open a report document and navigate its table of contents to find a page. If the engine cannot open the specified report design, the code shuts down the engine. The variable, engine, is a ReportEngine object.

Example 13-4. Accessing a report document

String dName = "./SimpleReport.rptdocument";
IReportDocument doc = null;
try { doc = engine.openReportDocument( dName );
} catch ( EngineException e ) {
   System.err.println( "Document " + dName + " not found!" );
   engine.shutdown( );
   System.exit( -1 );
}
// Get the root of the table of contents.
TOCNode td = doc.findTOC( null );
java.util.List children = td.getChildren( );
long pNumber;
// Loop through the top level table of contents entries.
if ( children != null && children.size( ) > 0 ) {
  for ( int i = 0; i < children.size( ); i++ ) {
     // Find the required table of contents entry.
     TOCNode child = ( TOCNode ) children.get( i );
     if ( child.getDisplayString( ).equals( "103" ) ) {
        // Get the number of the page that contains the data.
        pNumber = doc.getPageNumber( child.getBookmark( ) );
        System.out.println( "Page to print is " + pNumber );
     }
  }
}

Accessing a report parameter programmatically

A report parameter is a report element that provides input to a report design before the application generates the report. A report document does not use report parameters for generating a report. If your report source is a report document or the default values for all report parameters for a report design are always valid, you do not need to perform the tasks in this section.

Report parameters have attributes that a reporting application can access. The most commonly used attributes are name and value. The report engine uses the report design logic and the report parameter values to perform tasks such as filtering a data set or displaying an external value in the report.

After the reporting application sets the values for the report parameters, it must pass these values to the task that generates the report, as shown later in this chapter. To access report parameters and their default values and to set user-supplied values to a parameter, the reporting application uses the BIRT report engine API classes and interfaces shown in Table 13-3.

Table 13-3. Classes that support report parameters

Class or interface

Description

ReportEngine

Call the createGetParameterDefinitionTask( ) method to access parameters. This method returns an IGetParameterDefinitionTask object.

IGetParameterDefinition Task

The interface to access a single report parameter or a collection of all the report parameters in a report design. This interface also provides access to valid values for parameters that use restricted sets of values, such as cascading parameters.

IParameterDefnBase

The base interface for report parameter elements. Scalar parameters implement the derived interface, IScalarParameterDefn. Parameter groups implement the derived interface IParameterGroupDefn. To get information about parameter attributes, use objects of these types.

IParameterGroupDefn

The base interface for report parameter groups. Cascading parameter groups implement the derived interface ICascadingParameterGroup.

IParameterSelectionChoice

The interface for valid values for a report parameter that uses a restricted set of values, such as a cascading parameter.

ReportParameter Converter

The class that converts a String value provided by a user interface into a locale-independent format.

The following sections describe how to access report parameters by name or with generic code. You use generic code if the application must be able to run any report design, for example, if you access report designs from a list that depends on user input. If the application runs only a fixed set of known report designs, you can access the report parameters by name.

Creating a parameter definition task object for the report design

A single IGetParameterDefinitionTask object provides access to all parameters in a report design. Create only one of these objects for each report design, by using the ReportEngine.createGetParameterDefinitionTask( ) method.

Testing whether a report design has report parameters

To test if a report design has report parameters, call the getParameterDefns( ) method on IGetParameterDefinitionTask. This method returns a Collection. To test whether the Collection has elements call the Collection.isEmpty( ) method. If the application runs only known report designs, you do not need to perform this task.

Getting the report parameters in a report design

To access a single report parameter with a known name, use the IGetParameterDefinitionTask.getParameterDefn( ) method. This method returns an object of type IParameterDefnBase. Alternatively, use the IGetParameterDefinitionTask.getParameterDefns( ) method to return a Collection of IParameterDefnBase objects. The application can then use an Iterator to access each report parameter from this Collection in turn.

The getParameterDefns( ) method takes a Boolean argument. When the argument is false, the method returns an ungrouped set of report parameters. When the argument is true, the method returns parameter groups, as defined in the report design. To create a user interface that replicates the parameter group structure, use a value of true.

To check whether a report parameter is a group, the application must call IParameterDefnBase.getParameterType( ). This method returns IParameterDefnBase.PARAMETER_GROUP when the parameter is a group or IParameterDefnBase.CASCADING_PARAMETER_GROUP when the parameter is a cascading parameter group.

To access the group’s report parameters, use the IParameterGroupDefn.getContents( ) method. This method returns an ArrayList object that contains objects of type IScalarParameterDefn.

Getting the default value of each report parameter

This task is optional. To get the default value of a single known report parameter, use IGetParameterDefinitionTask.getDefaultValue( ). This method returns an Object. To determine the effective class of the Object, use IScalarParameterDefn.getDataType( ). This method returns an int value, which is one of the static fields in IScalarParameterDefn.

To get the default value of all parameters in the report design, use IGetParameterDefinitionTask.getDefaultValues( ). This method returns a HashMap object, which maps the report parameter names and their default values.

Getting valid values for parameters using a restricted set of values

Some report parameters accept only values from a restricted list. In some cases, this list is a static list of values, such as RED, BLUE, or GREEN. In other cases, the list is dynamic and a query to a database provides the valid values. For example, a query can return the set of sales regions in a sales tracking database.

To determine the list of valid values, call the IGetParameterDefinitionTask.getSelectionList( ) method. This method returns a Collection of IParameterSelectionChoice objects. IParameterSelectionChoice has two methods.getLabel( ) returns the display text and getValue( ) returns the value. If the Collection is null, the report parameter can take any value.

Getting the attributes of each report parameter

This task is optional. To get the attributes of a report parameter, use the IScalarParameterDefn methods. The application can use the attributes to generate a customized user interface. For example, to get the data type of a report parameter, use the getDataType( ) method.

Collecting an updated value for each report parameter

To provide new values for the report parameters, provide application logic such as a user interface or code to retrieve values from a database. To set the value of the parameter, call IGetParameterDefinitionTask.setParameterValue( ).

If you provide a user interface that returns String values to your application for date and number parameters, you must convert the String into a locale-independent format before setting the value. To perform this task, first call ReportParameterConverter.parse( ) to set the value to a locale-independent format. Next, call IGetParameterDefinitionTask.setParameterValue( ).

After setting the report parameter values, call the IGetParameterDefinitionTask.getParameterValues( ) method. This method returns a HashMap object that contains values that calls to IGetParameterDefinitionTask.setParameterValue( ) set. You can later use this HashMap object to set the report parameter values for report generation, as described later in this chapter.

How to set the value of a known report parameter

The code sample in Listing 13-5 shows how to set the value of a report parameter that has a known name. The sample creates a HashMap object that contains the parameter values to use later to run the report. The variable, engine, is a ReportEngine object. The variable, runnable, is an object of type IReportRunnable.

This sample does not show details of code for retrieving the parameter value from a user interface or a database. The code to perform these tasks depends on your application’s requirements.

Example 13-5. Setting the value of a single parameter

// Create a parameter definition task.
IGetParameterDefinitionTask task =
   engine.createGetParameterDefinitionTask( runnable );
// Instantiate a scalar parameter.
IScalarParameterDefn param = (IScalarParameterDefn)
   task.getParameterDefn( "customerID" );
// Get the default value of the parameter.
// The code assumes that the data type of the parameter,
// customerID, is Double.
int customerID =
   ((Double) task.getDefaultValue( param )).intValue( );

// Get a value for the parameter. This example does not
// provide the code for this task. This example assumes that
// this step creates a correctly typed object, inputValue.

// Set the value of the parameter.
task.setParameterValue( "customerID", inputValue );
// Get the values set by the application for all parameters.
HashMap parameterValues = task.getParameterValues( );

How to use the Collection of report parameters

The code sample in Listing 13-6 shows how to use the Collection of report parameters. The sample uses the ReportParameterConverter class to convert the String values that the user interface supplies into the correct format for the parameter. The sample creates a HashMap object that contains the parameter values to use later to run the report. The variable, engine, is a ReportEngine object. The variable, runnable, is an object of type IReportRunnable.

This sample does not show details of code for retrieving the parameter values from a user interface or a database. The code to perform these tasks depends on your application’s requirements.

Getting the values for cascading parameters

A cascading parameter group uses a query to retrieve values from a database. The parameter definition task filters the values for each parameter in the group, based on the values of preceding parameters in the group. For example, consider a cascading parameter group that uses the following query.

SELECT
   PRODUCTS.PRODUCTLINE,
   PRODUCTS.PRODUCTNAME,
   PRODUCTS.PRODUCTCODE
FROM CLASSICMODELS.PRODUCTS

Example 13-6. Setting the values of multiple parameters

// Create a parameter definition task.
IGetParameterDefinitionTask task =
   engine.createGetParameterDefinitionTask( runnable );
// Create a collection of the parameters in the report design.
// This example does not use the parameter grouping.
Collection params = task.getParameterDefns( false );
// Get the default values of the parameters.
HashMap parameterValues = task.getDefaultValues( );

// Get values for the parameters. This example does not
// provide the code for this task. Later code in this example
// assumes that this step creates a HashMap object,
// inputValues. The keys in the HashMap are the parameter
// names and the values are those that the user provided.

// Iterate through the report parameters, setting the values
// to use for generating the report. Ensure that the values
// are in standard locale-independent format.
Iterator iterOuter = params.iterator( );
while ( iterOuter.hasNext( ) )
{
   IParameterDefnBase param =
     (IParameterDefnBase) iterOuter.next( );
   String pname = param.getName( );
   String value = (String) inputValues.get( pname );
   if ( value != null )
   {
     ReportParameterConverter cfgConverter = new
        ReportParameterConverter( "", Locale.getDefault() );
     Object obj =
       cfgConverter.parse( value, param.getDataType( ) );
     parameterValues.put( pname, obj );
   }
}

The group contains two parameters, ProductLine on PRODUCTS.PRODUCTLINE and ProductCode on PRODUCTS.PRODUCTCODE. The display text for ProductCode is PRODUCTS.PRODUCTNAME. Figure 13-1 shows the appearance of the requester that prompts for values for these parameters when you preview the report in BIRT Report Designer.

Cascading report parameters

Figure 13-1. Cascading report parameters

To use the report engine API to get the values for cascading parameters, perform the tasks in the following list. Figure 13-1 shows an example of performing these tasks.

  • To prepare the data values for the cascading parameters, call the method, IGetParameterDefinitionTask.evaluateQuery( ). This method takes the String name of the parameter group as a parameter.

  • To populate the list of values for the first report parameter in the group, call the IGetParameterDefinitionTask.getSelectionListForCascadingGroup( ) method. This method takes two parameters, the String name of the parameter group and an array of Object. For the first parameter, this array is empty. The method returns a Collection of IParameterSelectionChoice objects.

  • To populate the list of values for further report parameter in the group, call getSelectionListForCascadingGroup( ) again. In this case, the Object[] array contains the values for the preceding report parameters in the group. In the example shown in Figure 13-1, the Object[] array is:

    new Object[] {"Trains" }

How to use cascading parameters

The code sample in Listing 13-7 first shows how to run the query for cascading parameters. Next, the sample accesses the set of valid values for each parameter in the cascading parameter group in turn. The variable, task, is an object of type IGetParameterDefinitionTask.

Example 13-7. Getting the valid values for cascading parameters

// Create a grouped collection of the design’s parameters.
Collection params = task.getParameterDefns( true );
// Iterate through the parameters to find the cascading group.
Iterator iter = params.iterator( );
while ( iter.hasNext( ) ) {
   IParameterDefnBase param = (IParameterDefnBase)
   iter.next();
   if ( param.getParameterType() ==
     IParameterDefnBase.CASCADING_PARAMETER_GROUP ) {
     ICascadingParameterGroup group =
        (ICascadingParameterGroup) param;
     Iterator i2 = group.getContents( ).iterator( );
       // Run the query for the cascading parameters.
       task.evaluateQuery( group.getName() );
       Object[] userValues =
          new Object[group.getContents( ).size( )];
       // Get the report parameters in the cascading group.
       int i = 0;
       while ( i2.hasNext( ) ) {
          IScalarParameterDefn member =
             (IScalarParameterDefn) i2.next( );
          // Get the values for the parameter.
          Object[] setValues = new Object[i];
          if ( i > 0 )
             System.arraycopy( userValues, 0, setValues, 0, i );
          Collection c =
             task.getSelectionListForCascadingGroup
                ( group.getName(), setValues );
          // Iterate through the values for the parameter.
          Iterator i3 = c.iterator();
          while ( i3.hasNext( ) ) {
             IParameterSelectionChoice s =
                ( IParameterSelectionChoice ) i3.next( );
             String choiceValue = s.getValue( );
             String choiceLabel = s.getLabel( );
          }
// Get the value for the parameter from the list of choices.
// This example does not provide the code for this task.
        userValues[i] = inputChoiceValue;
        i++;
     }
  }

Preparing to generate the report

BIRT provides two output formats for reports, HTML and PDF. You can also provide custom output formats by creating a new renderer from the rendering extension points, as discussed later in this book.

Three task classes support generating a report from a source. Sections earlier in this chapter described how to open the two types of source, a report design and a report document. The tasks that you use to generate a report from the source are:

  • IRunAndRenderTask. An object of this type creates a report in unpaginated HTML format by running a report design directly. To instantiate this object, call the ReportEngine method, createRunAndRenderTask( ).

  • IRunTask. An object of this type creates a report document (.rptdocument) file from a report design. To instantiate this object, call the ReportEngine method, createRunTask( ). After creating the report document, you create the report with an IRenderTask object.

  • IRenderTask. An object of this type creates a complete report or a set of pages from a report by formatting the contents of a report document. To instantiate this object, call the ReportEngine method, createRenderTask( ).

Each type of task object can act on multiple sources. When the application no longer needs the task object, call the task’s close( ) method.

Setting the parameter values for running a report design

To set the values for parameters for generating a report, use methods on an IRunAndRenderTask or an IRunTask object. These tasks run a report design to generate output. IRenderTask does not support changing the parameters for a report because its source is a report document. The IRunTask object that created the report document already specified the parameter values.

Call setParameterValues( ) to set the values for all the parameters. This method takes a HashMap as an argument. To create a suitable HashMap, perform the tasks described in “Accessing a report parameter programmatically,” earlier in this chapter.

To set the value for a single parameter when generating a report, call the setParameterValue( ) method. When the task generates the report or the report document, it uses the default values for any parameters that were not set by either of these methods.

Setting up the rendering options

Before generating a report to either HTML or PDF, the application must set options that determine features of the output. The options must specify either an output file name or a stream. Other configuration options, such as setting whether to create embeddable HTML, are optional. BIRT supports two types of HTML output, HTML and embeddable HTML. Embeddable HTML is suitable for including inside another web page. This format contains no header information nor the <html> tag.

The application uses a rendering options object to set the output options on an IRunAndRenderTask or an IRenderTask object. The format-specific rendering options classes implement IRenderOption and extend RenderOptionBase. The rendering options class supporting the HTML format is HTMLRenderOption. There are no format-specific options for PDF output. To set options for PDF output, use the RenderOptionBase class.

Setting up the rendering context

Before generating a report, the application must provide context settings to the rendering task. Use the setAppContext( ) method on an IRenderTask or an IRunAndRenderTask object. This method performs no function on an IRunTask object because this task does not render to an output format.

To set up the context for HTML rendering, use an HTMLRenderContext object. To set up the context for PDF rendering, use a PDFRenderContext object. To apply a context setting, use setter methods on the rendering context object. These classes also have getter methods that retrieve context settings.

Both HTML and PDF rendering contexts support a base URL for an action handler and a list of the image formats that the output format accepts, as shown in the following list. The output format of the report defines which other context settings are available.

  • Action handler base URL. If you use a custom action handler, set its base URL with the setBaseURL( ) method.

  • Supported image formats for extended report items such as charts or custom extended items. The final rendering environment for the report, such as the browser for a report in HTML format, affects this context value. To set the supported formats, use setSupportedImageFormats( ) with a String that contains a list of the supported image formats as its argument. The image formats are standard types, such as BMP, GIF, JPG, and SVG. Semicolons (;) separate the items in the list. The method getSupportedImageFormats( ) returns a String of the same format.

After creating the rendering context object, call the task’s setContext( ) method. This method takes a HashMap object as an argument. The key to the hash-map entry is one of the constants APPCONTEXT_HTML_RENDER_CONTEXT or APPCONTEXT_PDF_RENDER_CONTEXT in the EngineConstants class. The value of the entry is the rendering context object. Listing 13-8 includes code that sets the rendering context for HTML.

Setting up the HTML rendering context

Before generating an HTML report that uses images on disk or creates images or charts in a report, the application must provide additional context settings. The HTMLRenderContext class provides the following settings:

  • Image base directory. If your report designs use relative paths to access static images, set the path to their location with the HTMLRenderContext.setBaseImageURL( ) method.

  • Image output directory. Many reports include images, either as static images or dynamically created images, such as charts. HTML reports place all images in a defined location. To set this location, call the HTMLRenderContext.setImageDirectory( ) method. If you do not set the base location, the report engine cannot place images in your report.

Setting up the PDF rendering context

Before generating a PDF report that uses fonts from non-standard locations or needs to embed fonts in a report, the application must provide additional context settings. The PDFRenderContext class provides the following settings:

  • Font directory. If your deployment platform uses fonts from a custom location, set the path to their location with the setFontDirectory( ) method. This method takes a String argument, which is a list of directories separated by semicolon characters.

  • Embedding fonts. If you need to embed custom fonts in the PDF document, call PDFRenderContext.setEmbededFont( ) with an argument of true.

How to configure properties for a report in HTML format

The code sample in Listing 13-8 shows the use of rendering options on an IRunAndRenderTask object to set report parameter values, the output format of the report, and the output file name. The variable, engine, is a ReportEngine object. The variable, runnable, is an object of type IReportRunnable. The variable, name, is the name of the report design.

Example 13-8. Configuring properties on an IRunAndRenderTask object

// Create a run and render task object.
IRunAndRenderTask task =
   engine.createRunAndRenderTask( runnable );
// Set values for all parameters in a HashMap, parameterValues
task.setParameterValues( parameterValues );
// Validate parameter values.
boolean parametersAreGood = task.validateParameters( );
// Set the name of an output file.
HTMLRenderOption options = new HTMLRenderOption( );
String output = name.replaceFirst( ".rptdesign", ".html" );
options.setOutputFileName( output );
// Apply the rendering options to the task.
task.setRenderOption( options );
// Instantiate an HTML rendering context object and
// set the name of the directory for images.
HTMLRenderContext renderContext = new HTMLRenderContext();
renderContext.setImageDirectory("image");
// Apply the rendering context to the task.
HashMap appContext = new HashMap( );
appContext.put
  ( EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
  renderContext );
task.setAppContext( appContext );

Providing an external connection to run a report design

In many application server environments, web applications have access to a pool of Connection objects. In order to use an external connection from such a pool for the data source in a report design, you must pass information to the data driver plug-in that the report design uses. You pass the information in the rendering context HashMap object. For example, the code in Listing 13-9 sets up the connection to a custom driver, mydatapluginname. The variable task is an IRunAndRenderTask object or an IRunTask object.

The standard data drivers in BIRT do not support using an external connection. You must extend the drivers to perform this task. Later chapters in this book explain this process.

Example 13-9. Setting up an external Connection object

HashMap contextMap = new HashMap( );
HTMLRenderContext renderContext = new HTMLRenderContext();
contextMap.put
   ( EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
   renderContext );
// Get a connection from the pool
Connection myConnection = getConnectionFromPool( );
contextMap.put( "org.eclipse.birt.mydatapluginname",
   myConnection );
task.setContext( contextMap );

Generating the formatted output programmatically

To generate a report, the application must call the run( ) method on an IRunAndRenderTask or an IRunTask object. The application must handle the EngineException that run( ) can throw.

After generating the report, the application can reuse the report engine to generate further reports. If your application only generates a single report, shut down the engine after performing the report generation.

How to generate a report

The code sample in Listing 13-10 generates a report, then shuts down the report engine. The variable, engine, is a ReportEngine object. The variable, task, is an IRunAndRenderTask or an IRunTask object. The variable, name, is the name of the report design. The variable, output, is the name of the output file.

Example 13-10. Generating a report from a report design or a report document

try {
   task.run( );
   System.out.println( "Created Report " + output + "." );
}
catch ( EngineException e1 ) {
   System.err.println( "Report " + name + " run failed." );
   System.err.println( e1.toString( ) );
}
engine.shutdown( );

Accessing the formatted report

When you generate a report document as a file on disk, you can access the report in the same way as any other file. For example, you open HTML documents in a web browser and PDF documents using Adobe Reader. If you send the report to a stream, the stream must be able to process the information.

About programming with a report design

A reporting application typically generates a report from a report design. In this type of reporting application, you typically develop a report design and include the design along with your application at deployment time. Any changes to the generated report depend on the values of report parameters and the data from the data set. To access the report design, the application uses an IReportRunnable object.

Sometimes business logic requires changes to the report design before generating the report. You can make some changes through using parameters and scripting. Other changes can only occur through modification of the report design itself.

A reporting application can make changes to the report design and the ROM elements that make up the design. To access the structure of the report design, the application obtains a ReportDesignHandle object from the design engine. To access the design engine, an application must first instantiate a report engine, as in any other reporting application.

The ReportDesignHandle object provides access to all properties of the report design and to the elements that the report design contains. The model API provides handle classes to access all ROM elements. For example, a GridHandle object provides access to a grid element in the report design. All ROM element handles, including the report design handle, inherit from DesignElementHandle. Report items inherit from ReportElementHandle and ReportItemHandle.

After making changes to a report design or its elements, the application can write the result to a stream or a file. The report engine can then open an IReportRunnable object on the resulting design and generate a report.

An application typically accesses the items in a report design to perform one of the following tasks:

  • Modify an existing report design programmatically to change the contents and appearance of the report output.

    An application can modify page characteristics, grids, tables, and other report items in the design, the data source, and the data set that extracts data from a data source.

  • Build a report design and generate report output entirely in an application without using BIRT Report Designer.

A reporting application can access and modify the structures in a template or a library file in the same way as the structures in a report design. The techniques described in the rest of this chapter are applicable to these files as well as to report designs.

A template has identical functionality to a report design. For this reason, the ReportDesignHandle class provides access to a template. The LibraryHandle class provides access to a library. Both these classes derive from the ModuleHandle class, which provides the fields and methods for the common functionality, such as accessing elements in the file.

The package that contains the classes and interfaces to work with the items in a report design, library, or template is org.eclipse.birt.report.model.api.

About BIRT model API capabilities

A report developer can write an application that creates and modifies a report design programmatically. The BIRT model API has the same capabilities as BIRT Report Designer. For example, the following list shows some of the ways in which you can use the BIRT model API to manipulate a report design programmatically:

  • Modifying a report item in a report design:

    • Format a report item, changing the font, font color, fill color, format, alignment, or size.

    • Modify the expression or other property of a report item.

    • Change the data set bound to a table or list.

  • Adding a report item to a report design:

    • Add a simple report item such as a data item, label, or image.

    • Set the value to display in the new report item, such as the expression of a data item or the text in a label item.

    • Create a complex item such as a grid, table, or list.

    • Add other items into a grid, table, or list.

  • Changing the structure of a report design:

    • Add or delete a group or column in a table or list.

    • Add a report parameter.

  • Modifying non-visual elements in a report design:

    • Specify a data source for a data set.

    • Set a design property such as a report title, author, wallpaper, or comment.

    • Set a page property, such as height, width, or margins.

Opening a report design programmatically for editing

To access a report design and its contents, the application must instantiate a report engine, then use a ReportDesignHandle object. You instantiate a ReportDesignHandle by calling a method on another class, such as the model class, SessionHandle, or the report engine interface, IReportRunnable.

The SessionHandle object manages the state of all open report designs. Use a SessionHandle to open, close, and create report designs, and to set global properties, such as the locale and the units of measure for report elements. The SessionHandle can open a report design from a file or a stream. Create the session handle only once. BIRT supports only a single SessionHandle for a user of a reporting application.

Configuring the design engine to access a design handle

The DesignEngine class provides access to all the functionality of the Report Object Model (ROM) in the same way that the ReportEngine class provides access to report generation functionality. To create a DesignEngine object, you first create a DesignConfig object to contain configuration settings for the design engine. The DesignConfig object sets up custom access to resources and custom configuration variables for scripting. Instantiate a DesignEngine object with the DesignConfig object as an argument to the constructor.

Create the SessionHandle object by calling the method, newSessionHandle( ) on the DesignHandle object. To open the report design, call the method, openDesign( ), on the SessionHandle object. This method takes the name of the report design as an argument and instantiates a ReportDesignHandle object.

Using an IReportRunnable object to access a design handle

You can also open a report design from an IReportRunnable object by using the getDesignHandle( ) method. The ReportDesignHandle object provides access to the design opened by the report engine. Changes to the report design do not affect the IReportRunnable object. To generate a report from the changed report design, you must reopen the design as an IReportRunnable object.

How to open a report design for editing

The code sample in Listing 13-11 creates a DesignEngine object, which it uses to create a SessionHandle object. The code then uses the SessionHandle object to open a report design.

Example 13-11. Opening a report design for editing

// Create a design engine configuration object.
DesignConfig dConfig = new DesignConfig( );
DesignEngine dEngine = new DesignEngine( dConfig );
// Create a session handle, using the system locale.
SessionHandle session = dEngine.newSessionHandle( null );
// Create a handle for an existing report design.
String name = "./SimpleReport.rptdesign";
ReportDesignHandle design = null;
try {
   design = session.openDesign( name );
} catch (Exception e) {
   System.err.println
      ( "Report " + name + " not opened!
Reason is " +
      e.toString( ) );
   return null;
}

Using a report item in a report design

A report item is a visual element in the report design. Typically, a report developer adds a report item to the design in BIRT Report Designer by dragging an item from the palette to the layout editor. Sometimes you need to change the properties of certain report items in the design before running the report. An application uses methods on the ReportDesignHandle class to access a report item either by name or from a list of items in a slot in a container report item.

A slot is a logical component of a report item. For example, a table element has five slots: Header, Detail, Footer, Groups, and Columns. In turn, each of these slots can have further slots. Each slot has zero or more members of the appropriate report item type. For example, the Header, Detail, and Footer slots all contain elements of type RowHandle. RowHandle has a Cell slot that contains all the cells in the row. For a visual representation of the slots in an individual report item, see the Outline view in BIRT Report Designer.

Accessing a report item by name

To make a report item accessible by name, the item must have a name. A report developer can set the name in BIRT Report Designer or programmatically by using the item’s setName( ) method. To find a report item by name, use the findElement( ) method. This method returns a DesignElementHandle object. All report items derive from this class.

Accessing a report item by iterating through a slot

To access a report item through the report design’s structure, the application first gets the slot handle of the report body by calling the getBody( ) method. This slot handle holds the top-level report items in the report design. For example, consider a simple report structure that has three top-level items: a grid containing header information, a table containing data, and a label that displays a report footer. Figure 13-2 shows its outline view in BIRT Report Designer.

Slots in a report design

Figure 13-2. Slots in a report design

To access the top-level items in this report design, you iterate over the contents of the body slot handle. These contents all derive from DesignElementHandle. To access the iterator for a slot handle, call SlotHandle.iterator( ). Each call to Iterator.getNext( ) returns a report item. Alternatively, to access a report item at a known slot index, call SlotHandle.get( ). The slot index number is zero-based. The ReportDesignHandle class also provides finder methods, which can access an item or other report element by name.

Examining a report item programmatically

To examine a report item, check the class of the report item, cast the object to its actual class, then call methods appropriate to that class. For example, the class of a label element handle is LabelHandle. To get the text that the label displays, call LabelHandle.getText( ).

Some report items, such as a label or a text element, are simple items. Other items, such as a grid or a table element, are structured items. You can access properties for the whole of a structured item in the same way as for a simple item.

You can also iterate over the contents of the structured item. For example, use this technique to determine the contents of a cell in a table. To access the contents of a structured item, you call a method to retrieve the slot handle for rows or columns. For example, to access the RowHandle objects that make up a table element’s footer, call TableHandle.getFooter( ). Table and list elements also have a slot for groups. Like the body slot handle, the slot handles for the contents of structured report items can contain zero, one, or multiple elements.

Accessing the properties of a report item

To provide information about report items, each class has getter methods specific to the report item type. For example, an image element handle, ImageHandle, has the getURI( ) method. This method returns the URI of an image referenced by URL or file path. The DesignElementHandle class and other ancestor classes in the hierarchy also provide generic getter methods, such as getName( ).

Some properties of a report item are simple properties, with types that are Java types or type wrapper classes. An example of this type of property is the name property, which is a String object. Some of these properties, like name, have arbitrary values.

Other simple properties have restricted values from a set of BIRT String constants. The interface, DesignChoiceConstants in the org.eclipse.birt.report.model.api.elements package, defines these constants. For example, the image source property of an image element can have only one of the values, IMAGE_REF_TYPE_EMBED, IMAGE_REF_TYPE_EXPR, IMAGE_REF_TYPE_FILE, IMAGE_REF_TYPE_NONE, or IMAGE_REF_TYPE_URL.

Other properties are complex properties and the getter method returns a handle object. For example, the DesignElementHandle.getStyle( ) method returns a StyleHandle object and ReportItemHandle.getWidth( ) returns a DimensionHandle object.

The handle classes provide access to complex properties of a report item, as described later in this chapter. These classes provide getter methods for related properties. For example, StyleHandle classes provide access to font and background color.

How to access a report item by name

The code sample in Listing 13-12 finds an image item by name, checks its type, then examines its URI. The variable, design, is a ReportDesignHandle object.

Example 13-12. Finding a report item with a given name

DesignElementHandle logoImage =
   design.findElement( "Company Logo" );
// Check for the existence of the report item.
if ( logoImage == null) {
   return null;
}
// Check that the report item has the expected class.
if ( !( logoImage instanceof ImageHandle ) ) {
  return null;
}
// Retrieve the URI of the image.
String imageURI = ( (ImageHandle ) logoImage ).getURI( );
return imageURI;

How to use the report structure to access a report item

The code sample in Listing 13-13 finds an image item in a grid, checks its type, then examines its URI. Use this technique for generic code to navigate a report design structure or if you need to find an item that does not have a name. The variable, design, is a ReportDesignHandle object.

Modifying a report item in a report design programmatically

To set the simple properties of report items, each class has setter methods specific to the report item type. For example, an image element handle, ImageHandle, has the setURI( ) method. This method sets the URI of an image referenced by URL or file path. The DesignElementHandle class and other ancestor classes in the hierarchy also provide generic setter methods, such as setName( ). Setter methods throw exceptions, such as NameException, SemanticException, and StyleException.

To set attributes of a complex property, such as a style, you must call methods on a handle object, as described later in this chapter. These classes provide setter methods for related properties. For example, StyleHandle classes provide access to style properties, such as font and background color.

Example 13-13. Navigating the report structure to access a report item

// Instantiate a slot handle and iterator for the body slot.
SlotHandle shBody = design.getBody();
Iterator slotIterator = shBody.iterator()
// To retrieve top-level report items, iterate over the body.
while (slotIterator.hasNext()) {
  Object shContents = slotIterator.next();
  // To get the contents of the top-level report items,
  // instantiate slot handles.
  if (shContents instanceof GridHandle) {
    GridHandle grid = ( GridHandle ) shContents;
    SlotHandle grRows = grid.getRows( );
    Iterator rowIterator = grRows.iterator( );
    while (rowIterator.hasNext()) {
       // Get RowHandle objects.
       Object rowSlotContents = rowIterator.next();
       // To find the image element, iterate over the grid.
       SlotHandle cellSlot =
          ( ( RowHandle ) rowSlotContents ).getCells( );
       Iterator cellIterator = cellSlot.iterator( );
       while ( cellIterator.hasNext( ) ) {
          // Get a CellHandle object.
         Object cellSlotContents = cellIterator.next( );
         SlotHandle cellContentSlot =
            ((CellHandle) cellSlotContents).getContent( );
         Iterator cellContentIterator =
            cellContentSlot.iterator( );
         while (cellContentIterator.hasNext( )) {
            // Get a DesignElementHandle object.
            Object cellContents =
               cellContentIterator.next( );
               // Check that the element is an image.
               if (cellContents instanceof ImageHandle) {
                  String imageSource = ( ( ImageHandle )
                     cellContents ).getSource( );
                  // Check that the image has a URI.
                  if ((imageSource.equals( IMAGE_REF_TYPE_URL ))
                  || (imageSource.equals(IMAGE_REF_TYPE_FILE))){
                     // Retrieve the URI of the image.
                     String imageURI = ( ( ImageHandle )
                        cellContents ).getURI( );
                  }
               }
            }
         }
      }
   }
}

Changes that you make to items in the report design do not affect the design file until you save the design to disk or to a stream. After saving the design, get an IReportRunnable handle for the modified design in order to generate a report.

How to change a simple property of a report item

The code sample in Listing 13-14 uses a method on LabelHandle to change the text in a label. The variable, design, is a ReportDesignHandle object. This sample accesses the label by name. You can also access a report item by navigating the report structure.

Example 13-14. Changing the text property of a label report item

// Access the label by name.
LabelHandle headerLabel =
   ( LabelHandle ) design.findElement( "Header Label" );
try {
   headerLabel.setText( "Updated " + headerLabel.getText( ) );
} catch ( Exception e ) {
   // Handle the exception
}

Accessing and setting complex properties

Complex properties use BIRT handle objects to access data structures. For example, a DimensionHandle object provides access to size and position properties, such as the absolute value and the units of the width of a report item.

Some String properties on a handle object, such as font style and text alignment on a style handle, have restricted values defined by constants in the interface, DesignChoiceConstants in the org.eclipse.birt.report.model.api.elements package. For example, the font style property can have only one of the values, FONT_STYLE_ITALIC, FONT_STYLE_NORMAL, and FONT_STYLE_OBLIQUE.

Using a property handle

To access complex properties, you use getter methods on the report item. For example, to access the width of a report item, call the method ReportItemHandle.getWidth( ). This method returns a DimensionHandle object. To work with complex properties, you use getter and setter methods on the handle object. For example, to get and set the size of a dimension, you use DimensionHandle.getMeasure( ) and DimensionHandle.setAbsolute( ), respectively.

When you set a value on a complex property, the change to the handle object affects the report item straight away. You do not call an additional setter method on the report item itself.

Using styles on a report item

The StyleHandle class provides access to many fundamental properties of a report item, such as margin size, text alignment, background color, borders, font, and so on. StyleHandle provides a full set of getter methods for each style property. For simple properties, StyleHandle provides setter methods. To modify complex properties, you use setter methods on the property handle object, not on the style handle itself.

A report item can use two styles: a private style and a shared style. The handle classes for these styles are PrivateStyleHandle and SharedStyleHandle, respectively. Both classes derive from StyleHandle.

A private style contains the settings that the report developer chose in the property editor when designing the report. Shared styles appear in the Outline view in BIRT Report Designer. You use shared styles to apply the same appearance to multiple items in a report design. Changes to a shared style affect all report items that use the style. Style settings in a private style override the settings in a shared style.

How to change a complex property of a report item

The code sample in Listing 13-15 shows how to use PrivateStyleHandle and ColorHandle objects to change the background color of a label. The variable, design, is a ReportDesignHandle object. This sample accesses the label by name. You can also access a report item by navigating the report structure.

Example 13-15. Changing a complex property of a report item

// Access the label by name.
LabelHandle headerLabel =
   ( LabelHandle ) design.findElement( "Header Label" );
try {
   // To prepare to change a style property, get a
   StyleHandle.
   StyleHandle labelStyle = headerLabel.getPrivateStyle();
   // Update the background color.
   ColorHandle bgColor = labelStyle.getBackgroundColor();
   bgColor.setRGB( 0xFF8888 );
} catch ( Exception e ) {
   // Handle any exception
}

Adding a report item to a report design programmatically

A reporting application can use a simple report design or a template to create more complex designs. The application can add extra report items to the design’s structure based on external conditions. For example, based on the user name of the user requesting generation of a report, you can add extra information to the report for that category of user. You use the same techniques to add content to a new design if you create a design entirely with the API.

The class that creates new elements, such as report items, in a report design is ElementFactory. This class provides methods of the form, newXXX( ), where XXX is the report item or element to create. The method newElement( ) is a generic method that creates an element of any type. To access the element factory, call the ReportDesign.getElementFactory( ) method.

You can place new report items at the top level of the report design, directly in the Body slot, within containers such as a cell in a table or grid, or on the master page. You can add a simple item, such as a label, or complex items, such as a table with contents in its cells. Wherever you add the new report item, the location is a slot, such as the body slot of the report design or a cell slot in a row in a table. To add a report item to a slot, you use one of the SlotHandle.add( ) methods. The method has two signatures that support adding the report item to the end of a slot, or to a particular position in a slot.

Table and list elements are container items that iterate over the rows that a data set provides. For these report items to access the data rows, you must bind them to a data set. The table or list element provides data rows to the report items that it contains. For this reason, you usually bind only the container item to a data set, as described later in this chapter.

How to add a grid item and label item to a report design

The code sample in Listing 13-16 creates a grid item, then adds a label item to one of the cells in the grid. An application can create any other report item in a similar manner. The variable, design, is a ReportDesignHandle object.

Example 13-16. Adding a container item to the Body slot

// Instantiate an element factory.
ElementFactory factory = design.getElementFactory( );
try {
   // Create a grid element with 2 columns and 1 row.
   GridHandle grid = factory.newGridItem( "New grid", 2, 1 );
   // Set a simple property on the grid, the width.
   grid.setWidth( "50%" );
   // Create a new label and set its properties.
   LabelHandle label = factory.newLabel( "Hello Label" );
   label.setText( "Hello, world!" );
   // Get the first row of the grid.
   RowHandle row = ( RowHandle ) grid.getRows( ).get( 0 );
   // Add the label to the second cell in the row.
   CellHandle cell = ( CellHandle ) row.getCells( ).get( 1 );
   cell.getContent( ).add( label );
   // Get the Body slot. Add the grid to the end of the slot.
   design.getBody( ).add( grid );
} catch ( Exception e ) {
   // Handle any exception
}

Accessing a data source and data set with the API

This section shows how to use ROM elements that are not report items. To use other ROM elements, such as the libraries that the report design uses, you employ similar techniques.

You access the report design’s data sources and data sets from methods on the ReportDesignHandle instance, in a similar way to other report elements. The model classes that define a data source and data set are DataSourceHandle and DataSetHandle, respectively. A data set provides a report item such as a table with data from a data source. For a report item to access the data set, use the setDataSet( ) method.

You can use a finder method on the report design handle to access a data source or data set by name. The finder methods are findDataSource( ) and findDataSet( ), respectively.

Alternatively, to access all the data sources or data sets, you can use a getter method that returns a slot handle. The getter methods are getDataSources( ) and getDataSets( ), respectively. To access the individual data sources or data sets in a slot handle, you iterate over the contents of the slot handle in the same way as for any other slot handle.

About data source classes

DataSourceHandle is a subclass of ReportElementHandle. You get and set report item properties for a data source in the same way as for any other report element. DataSourceHandle also provides methods to access the scripting methods of the data source.

The two subclasses of DataSourceHandle, OdaDataSourceHandle and ScriptDataSourceHandle, provide the functionality for the two families of BIRT data sources. For more information about ODA data sources, see the Javadoc for the ODA API, in Open Data Access (ODA) 3.0.0 API Reference. The scripting methods for a scripted data source fully define the data source, as described earlier in this book.

About data set classes

DataSetHandle is a subclass of ReportElementHandle. You get and set properties for a data set in the same way as for any other report element. DataSetHandle also provides methods to access properties specific to a data set, such as the data source, the data set fields, and the scripting methods of the data set.

The two subclasses of DataSetHandle, OdaDataSetHandle and ScriptDataSetHandle, provide the functionality for the two families of BIRT data sets. For more information about ODA data sets, see the Javadoc for the ODA API.

Using a data set programmatically

Typically, a reporting application uses data sets and data sources already defined in the report design. You can use the data set’s setDataSource( ) method to change the data source of a data set. For example, based on the name of the user of the reporting application, you can report on the sales database for a particular geographical region, such as Europe or for North America.

Changing the properties of a data set

Changing the properties of a data set requires consideration of the impact on the report design. If you change the data source of a data set, the type of the data source must be appropriate for the type of the data set. You must also be certain that the new data source can provide the same fields as the original data source.

How to change the data source for a data set

The code sample in Listing 13-17 shows how to check for a particular data source and data set in a report design, then changes the data source for the data set. The code finds the data source and data set by name.

Alternatively, use the getDataSets( ) and getDataSources( ) methods. Then use the technique for iterating over the contents of a slot handle. The variable, design, is a ReportDesignHandle object.

Example 13-17. Modifying a data set

// Find the data set by name.
DataSetHandle ds = design.findDataSet( "Customers" );
// Find the data source by name.
DataSourceHandle dso = design.findDataSource( "EuropeSales" );
// Check for the existence of the data set and data source.
if (dso == null) || ( ds == null )
{
  System.err.println( "EuropeSales or Customers not found" );
  return;
}
// Change the data source of the data set.
try
{
   ds.setDataSource( dso );
} catch ( SemanticException e1 ) {
   e1.printStackTrace( );
}

Changing the data set binding of a report item

You can also use the report item’s setDataSet( ) method to set or change the data set used by a report item. If you change the data set used by a report item, you must ensure that the contents of the report item access only data bindings that are supplied by the new data set. If necessary, you must change the references to data bindings in data elements, text elements, and scripting methods. If the data bindings in the old data set do not match the names or data types of the fields that the new data set provides, you must correct the data bindings before you generate a report from the modified report design. Use the ReportItemHandle method, columnBindingsIterator( ), to iterate over the column bindings that the report item uses. The items in the list are of type ComputedColumnHandle. This class provides methods to access the name, expression, and data type of the column binding.

To access the data set column and expression that a data item uses, call the methods, getResultSetColumn( ) and getResultSetExpression( ). You can compare the data type and name with the result set columns that the data set returns.

How to bind a data set to a table

The code sample in Listing 13-18 shows how to check for a particular data set in a report design, then changes the data set for a table. The code finds the table and data set by name. Alternatively, use slot handles to navigate the report design structure. The variable, design, is a ReportDesignHandle object.

Example 13-18. Binding a data set to a report item

// Find the table by name.
TableHandle table =
   ( TableHandle ) design.findElement( "Report Data" );
// Find the data set by name.
DataSetHandle ds = design.findDataSet( "EuropeanCustomers" );
// Check for the existence of the table and the data set.
if (table == null) || ( ds == null ) {
   System.err.println( "Incorrect report structure" );
   return;
}
// Change the data set for the table.
try {
   table.setDataSet( ds );
} catch (Exception e) {
   System.err.println( "Could not set data set for table" );
}

Saving a report design programmatically

After making changes to an existing report design or creating a new report design, you can choose to save the design for archival purposes, or for future use. To overwrite an existing report design to which the application has made changes, use the ReportDesignHandle.save( ) method. To save a new report design or to keep the original report design after making changes, use the ReportDesignHandle.saveAs( ) method.

Alternatively, if you do not need to save the changes to the report design, use the ReportDesignHandle.serialize( ) method. This method returns an output stream. The report engine can generate a report by opening a stream as an input stream.

If you do not need to make any further changes to the report design, use the ReportDesignHandle.close( ) method to close the report design.

How to save a report design

The following code saves the open report design. The variable, design, is a ReportDesignHandle object.

design.saveAs( "sample.rptdesign" );
design.close( );

Creating a report design programmatically

You can build a report design and generate the report output in an application without using BIRT Report Designer. You use the createDesign( ) method on the session handle class, SessionHandle, to create a report design. You use the other model classes to create its contents.

How to create a new report design

The following code creates a report design.

SessionHandle session = DesignEngine.newSession( null );
ReportDesignHandle design = session.createDesign( );

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset