Chapter 14. Programming Using 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 creates a report document (.rptdocument) file that contains the report content in binary form and renders the report to one of the supported output formats 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 that a reporting application performs.

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. Using 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 the online help and navigate to BIRT ProgrammerReference→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 complete the required tasks described in the following sections. Additional tasks, such as supplying user-entered values for parameters, are optional.

Creating and configuring a report engine

A single report engine object can generate multiple reports from multiple report designs. Use the Platform.createFactoryObject( ) method to create a ReportEngine object. Set the BIRT home directory, which defines the location of required plug-ins and libraries.

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 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 RenderOption.

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.

Destroying the engine

Destroy the report engine after the application has generated all required 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 development environment

The development environment for a reporting application must provide access to all the required BIRT plug-ins and API libraries. The key requirement for the application is the path to the BIRT home directory. The BIRT home directory contains the BIRT engine, which is made up of the required BIRT plug-ins and libraries.

Installing the BIRT Report Engine package, as described in Chapter 3, “Installing Other BIRT Packages,”provides a complete BIRT home in the ReportEngine subdirectory.

About plug-ins in BIRT home

The plugins subdirectory of the BIRT home directory contains the org.eclipse.birt and other plug-ins that provide the functionality of a reporting application. 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 in BIRT home

The lib subdirectory of the BIRT home directory contains JAR files providing BIRT API classes and classes that support the APIs. This location within the report engine ensures that the class loader of the application server or JVM in which you deploy the report engine can locate the libraries. 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 14-1 lists and describes all the JAR files in the lib directory.

Table 14-1 Libraries in the BIRT home lib directory

image

image

About required JDBC drivers

BIRT applications load JDBC drivers from the BIRT home directory plugins/org.eclipse.birt.report.data.oda.jdbc_<version>/drivers. Place the driver classes or Java archive (.jar) files that you require to access JDBC data sources in this location.

Setting up the build path and accessing Javadoc

To access the BIRT APIs during application development using the Java perspective in the Eclipse Workbench, add API JAR files to the build path. The org.eclipse.birt.chart.doc.isv and org.eclipse.birt.doc.isv plug-ins provide the Javadoc for the public BIRT APIs. Extract the plug-ins’ JAR files to a convenient location, for example in your workspace or a resource folder. Then, use the Java Build Path settings in Eclipse to set the Javadoc location for each public API JAR file. Table 14-2 lists the source plug-in and the extracted location of the Javadoc.

Table 14-2 Javadoc locations for BIRT API libraries

image

How to add BIRT libraries and API Javadoc to a Java application’s build path

Use the Java perspective in the Eclipse Workbench to perform these steps on an existing Java project.

1 Install the BIRT Engine package to create a BIRT home.

2 To make the BIRT Javadoc files accessible to the Eclipse Java perspective, navigate to the eclipseplugins directory. Extract the org.eclipse.birt.chart .doc.isv and org.eclipse.birt.doc.isv plug-in JAR files to a workspace or resource directory.

3 In Package Explorer, right-click the Java project. Choose Build Path→Configure Build Path.

4 In Properties for <project name>—Java Build Path, select Libraries. To add a JAR file to the build path, choose Add External JARs.

5 In JAR Selection, navigate to the BIRT home lib directory. Select a required JAR file. Choose Open. The JAR file appears in JARs and class folders on the build path.

6 To associate the Javadoc with the class files in a BIRT API library, perform the following steps:

1 Expand the API JAR file in JARs and class folders on the build path.

Select Javadoc location: (None). Choose Edit.

2 In Javadoc for <JAR file name>, type or browse to the directory that contains the Javadoc extracted in step 2. Type or browse to the subdirectory specified in Table 14-2.

3 Choose Validate. Validating Javadoc Location displays a message about whether the specified directory contains Javadoc. Choose OK.

4 If necessary, correct the path to the Javadoc. In Javadoc for <JAR file name>, choose OK.

7 Repeat steps 4 through 6 for each required BIRT JAR file.

Modifying a report design using the API

A reporting application can modify a report design before generating the report. For example, the application can add or remove items from the report design based on the user who generates the report. To provide further customization of a report, during the generation of the output, a report design can use JavaScript classes or embedded JavaScript code to handle events. The classes in the Design Engine API support making changes to a report design and including or changing scripts. The key Design Engine classes are in the org.eclipse.birt.model.api package.

About the deployment environment

A deployed application must be able to access all the classes required for BIRT, the external data sources, and any other necessary classes. Set up the BIRT home directory and the CLASSPATH for the application.

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

Generating reports from an application

The key tasks that a reporting application must perform are to set up the report engine, any required parameter values, and the task objects to generate the report document, and finally to render the report. The reporting application does not require the BIRT Report Designer user interface to generate a 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. Prepare the report engine’s properties with an EngineConfig object. After setting all the required properties, use the BIRT engine factory to create the report engine. The following sections describe the various configuration options and creation of the engine.

Configuring the BIRT home

The BIRT home, which is the location of the BIRT plug-ins and libraries, is the key property that the report engine requires. The report engine cannot parse a report design nor render the report without a defined BIRT home. For a stand-alone application, the BIRT 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 BIRT home is a relative path in the WAR file. To set the BIRT home location, use one of the following techniques:

• For a stand-alone application, use one of the following techniques:

• Call EngineConfig.setBIRTHome( ) with an argument that is the path to the BIRT home directory, for example:

config.setBIRTHome
   ( "C:/birt-runtime-<version>/ReportEngine" );

• In the application’s environment, set up the BIRT_HOME and CLASSPATH variables to access the required libraries. For example, in a Windows batch file, include commands similar to the following ones before launching the stand-alone application:

set BIRT_HOME="C:irt-runtime-<version>ReportEngine"
SET CLASSPATH=%BIRT_HOME%<required library 1>;
   %BIRT_HOME%<required library 2 and so on>;
   %CLASSPATH%

To develop an application that uses a BIRT_HOME environment variable, set BIRT_HOME in the VM arguments in the Eclipse Run dialog. For example, in VM arguments, type text similar to the following line:

-DBIRT_HOME="C:irt-runtime-<version>ReportEngine"

• For a deployed web application, use one of the following techniques:

• If the application has a location in the file system, use the servlet context to find the real path of the BIRT home, for example:

config.setBIRTHome
   ( servletContext.getRealPath( "/WEB-INF" ) );

• If the application runs from a WAR file, use a relative path from the WAR file root, as shown in the following example. This configuration uses PlatformServletContext.

config.setBIRTHome( "" );

• If you use BIRT in an Eclipse-based application, such as an RCP application, and the BIRT plug-ins are located in the application’s plugins directory, you do not need to set BIRT_HOME.

Configuring the report engine

Optionally, set other configuration properties using methods on an EngineConfig object. Table 14-3 describes these properties and how to set them using EngineConfig methods. The EngineConfig class also provides getter methods to access these properties.

Table 14-3 EngineConfig properties

image

image

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 on 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 plug-ins. The default platform context provides direct file system access to these plug-ins, as used by a stand-alone application. The HTML emitter configuration provides the functionality to process images and handle hyperlinking and bookmark actions.

Setting up the platform context

Because BIRT is an Eclipse-based application, it uses the OSGi platform to start up the plug-ins that make up the report and design engines. The BIRT application locates the plug-ins in the BIRT home using the platform context, which is an implementation of the org.eclipse.birt.core.framework .IPlatformContext interface. This interface defines the method, getPlatform( ) that returns the location of the plugins directory. Use the IPlatformContext object as the argument to the EngineConfig object’s setPlatformContext( ) method.

The BIRT framework provides two implementations of the IPlatformContext interface. These implementations provide all the required functionality for the platform context.

The default implementation, PlatformFileContext, accesses the plug-ins in the BIRT home folder on the file system. If the application sets the BIRT home location in the EngineConfig object or the application environment contains BIRT_HOME, creating a PlatformFileContext object is not necessary.

For a web deployment, such as an application running from a WAR file on an application server, use the PlatformServletContext implementation. This class uses the resource-based access provided by the J2EE ServletContext class to locate the required plug-ins. The constructor for this class takes one argument, a ServletContext object. By default, PlatformServletContext finds plug-ins in the /WEB-INF/platform/ directory by using the ServletContext .getRealPath( ) method. Some application servers return null from this method. In this case, the PlatformServletContext object creates a platform directory in the location defined by the system property, javax.servlet .context.tempdir. The PlatformServletContext object copies the plug-ins and configuration folders to this location. Listing 14-2 sets up a platform context for a reporting application that runs from a WAR file.

If neither of these IPlatformContext implementations meets your needs, implement your own version of IPlatformContext. In the same way as for the built-in platform contexts, call EngineConfig.setPlatformContext( ) to configure the engine to use the new implementation.

Setting up the HTML emitter

To generate a report in HTML format, the report engine uses an org.eclipse .birt.report.engine.api.HTMLRenderOption object to determine how to handle resources that the HTML emitter uses or creates. These resources include new image files for image elements and chart elements, and the locations of jumps from bookmark and drill-through actions. BIRT uses different image handlers for file system-based applications and applications deployed on the web. Use the EngineConfig object to set up the HTML emitter options when creating the report engine, as shown in Listing 14-1, or set the options later when rendering a report to HTML.

To set up the HTML emitter, instantiate an HTMLRenderOption object. Use this object as the argument to ReportEngine.setEmitterConfiguration( ) to define the HTML rendering options when creating the report engine.

Call the HTMLRenderOption.setImageHandler( ) method to configure the image handler. This method determines how to save the image files by using an org.eclipse.birt.report.engine.api.IHTMLImageHandler object. Images defined in a report as a URL are not saved locally. BIRT provides two implementations of the IHTMLImageHandler interface. Both implementations create unique image file names for temporary images.

The default image handler implementation, HTMLCompleteImageHandler, saves images to the file system. Use this implementation for a stand-alone application or for a web application that uses file system deployment on the application server. This image handler finds the location for images by searching first for the image directory set in the HTMLRenderOption object, next the temporary files directory as set by the EngineConfig.setTempDir( ) method, and finally the location set by the system setting, java.io.tmpdir. All images in the generated HTML use file references to the created images.

For a web deployment, such as an application running from a WAR file on an application server, use the HTMLServerImageHandler implementation. This class saves images to the image directory set in the HTMLRenderOption object. The src attribute of images in the generated HTML appends the image name to the base image URL, also set in the HTMLRenderOption object. In this way, the report engine produces the images locally and shares the images using a URL. To use this implementation, set the image directory and the base image URL, as shown in Listing 14-3. The example BIRT Web Viewer in the org.eclipse.birt.report.viewer plug-in uses this implementation.

If neither IHTMLImageHandler meets your needs, create a class that implements IHTMLImageHandler or extends an existing image handler. Typically, HTMLCompleteImageHander provides sufficient functionality for file system access, so an application does not extend the class. For the application server environment, extend from HTMLServerImageHandler. To configure the engine to use the new implementation, in the same way as for the built-in image handlers, call EngineConfig.setEmitterConfiguration( ).

Starting the platform

After setting up the PlatformContext, an application starts the platform by using the org.eclipse.birt.core.framework.Platform class. This class acts as a wrapper around the Eclipse OSGILauncher class and provides the method, startup( ), to start the platform. Calling this synchronized static method uses substantial system resources, so an application should call this method only once. If the report engine is deployed in a web application, call Platform .startup( ) in the servlet’s init( ) method or in the first request that uses the platform. To achieve this behavior, wrap the platform start-up code in a singleton, as shown in Listing 14-2. When an application finishes using the platform, call Platform.shutdown( ). If you use the example Web Viewer or deploy the report engine in an Eclipse-based project such as an Rich Client Platform (RCP) application, do not start up or shut down the platform, because these applications control the OSGi start-up and shutdown.

Creating the report engine

BIRT provides a factory service to create the ReportEngine object. Call Platform.createFactoryObject( ) to create this factory, which implements the org.eclipse.birt.report.engine.api.IReportEngineFactory interface. The createFactoryObject( ) method uses a PlatformConfig object. Call IReportEngineFactory.createReportEngine( ) to create the report engine.

How to set up a report engine as a stand-alone application

Listing 14-1 shows an example of setting up a report engine as a stand-alone application on a Windows system. The application uses the BIRT home located in the BIRT run-time directory. The report output format is HTML.

Listing 14-1 Setting up the report engine for a stand-alone application


// Create an EngineConfig object.
EngineConfig config = new EngineConfig( );
// Set up the path to your BIRT home directory.
config.setBIRTHome
   ( "C:/Program Files/birt-runtime-2_6_0/ReportEngine" );
// Start the platform for a non-RCP application.
try
{
   Platform.startup( config );
   IReportEngineFactory factory =
      ( IReportEngineFactory ) Platform.createFactoryObject
      ( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
   // Set up writing images or charts embedded in HTML output.
   HTMLRenderOption ho = new HTMLRenderOption( );
   ho.setImageHandler( new HTMLCompleteImageHandler( ));
   config.setEmitterConfiguration
      ( RenderOption.OUTPUT_FORMAT_HTML, ho );
   // Create the engine.
   IReportEngine engine = factory.createReportEngine( config );
}
catch ( BirtException e ) { e.printStackTrace(); }

How to set up a report engine as a web application

1 Set up the platform context, as shown in Listing 14-2.

Listing 14-2 Setting up the platform context for WAR file deployment


// Example class to create the report engine
public class BirtEngine
{
   private static IReportEngine birtEngine = null;

   public static synchronized IReportEngine
      getBirtEngine( ServletContext sc )
   {
      if (birtEngine == null) {
         EngineConfig config = new EngineConfig( );
         config.setBIRTHome( "" );
         IPlatformContext context =
            new PlatformServletContext( sc );
         config.setPlatformContext( context );
         try
         {
            Platform.startup( config );
            IReportEngineFactory factory =
               ( IReportEngineFactory )
                Platform.createFactoryObject
( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
           birtEngine =
              factory.createReportEngine( config );
        }
        catch ( Exception e ) { e.printStackTrace( ); }
      }
      return birtEngine;
   }
}

2 Set up further configuration options on the engine after instantiating the class, as shown in Listing 14-3.

Listing 14-3 Setting up HTML options for WAR file deployment


// In a different class, get the report engine
IReportEngine reportEngine = BirtEngine.getBirtEngine
   ( request.getSession( ).getServletContext( ));
// Set up the engine
EngineConfig config = reportEngine.getConfig( );
HTMLRenderOption ho = new HTMLRenderOption( );
ho.setImageHandler( new HTMLServerImageHandler( ));
ho.setImageDirectory("output/image");
ho.setBaseImageURL("http://myhost/prependme?image=");
config.setEmitterConfiguration
   ( RenderOption.OUTPUT_FORMAT_HTML, ho );

In this listing, request is an HttpServletRequest object.

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 an application runs in the Eclipse workbench, by default, the messages appear in the console. When an application runs outside Eclipse, the default location of the log messages depends on the 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, use the EngineConfig.setLogConfig( ) method. This method takes two arguments. The first argument is the directory in which to create the log file. The second argument is the lowest level at which to log information. Set the logging level to a high threshold so that the engine logs fewer messages. Typically, you want to see information at INFO level when you first develop a block of code. Use the ReportEngine.changeLogLevel( ) method to modify the amount of information that the engine logs. 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, delete or comment out the call to changeLogLevel( ). BIRT Report Engine creates a log file with a name of the format ReportEngine_YYYY_MM_DD_hh_mm_ss.log. To change this name, call EngineConfig.setLogFile( ). Use the EngineConfig methods, setLogRollingSize( ) and setLogMaxLogBackupIndex( ), to control the size and number of log files.

How to use BIRT logging

The following example shows how to use logging in an application. You set up the logging environment and modify it later in your application. The variable, config, is the EngineConfig object that the code’s active ReportEngine object, engine, is using.

1 Configure logging on the report engine object.

// Set up the location and level of the logging output.
config.setLogConfig( "C:/Temp", Level.SEVERE );
config.setLogFile( "myBIRTApp.log" );

2 In any newly written code, increase the amount of logging.

engine.changeLogLevel( Level.INFO );

Opening a source for report generation

BIRT Report Engine classes generate a formatted report from either a report design or a binary report document. Typically, these files have the extensions RPTDESIGN and RPTDOCUMENT respectively. The engine can also generate a binary report document from a report design.

To open a report design, 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, call ReportEngine.openReportDocument( ). This method instantiates an IReportDocument object, using a String that specifies the path to a report document. 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 getProperty( ) with a String argument that contains one of these fields.

To generate a report from a design, open the report design as shown in Listing 14-4. Then, perform the tasks shown later in this chapter.

How to access a report design

Listing 14-4 shows how to open a report design and find a property value. If the engine cannot open the specified report design, the code destroys the engine. The variable, engine, is a ReportEngine object.

Listing 14-4 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.destroy( );
   System.exit( -1 );
}
// Get the value of a simple property.
String title = ( String ) runnable.getProperty
   ( IReportRunnable.TITLE );

Understanding an IReportDocument object

The IReportDocument object provides access to the structure and contents of a binary report document. IReportDocument provides methods to retrieve bookmark, page, and report design information. A bookmark is a String object that locates an instance of a report element in the document and the page on which the element exists. Use page information to render individual pages in the document to an output format. Bookmarks also provide access to the design elements that generated the element instance.

Use the getBookmarks( ) method to get a List object containing all the bookmarks in the document. Call the getBookmarkInstance( ) method with a String argument containing a bookmark to access the instantiated report element. Calling the getReportRunnable( ) method returns an IReportRunnable object, which an application can use to access report design information or generate another report document.

Close the document after use by calling IReportDocument.close( ).

How to access a report document

Listing 14-5 shows how to open a report document and find a page. If the engine cannot open the specified report design, the code logs the error. The variable, engine, is a ReportEngine object.

Listing 14-5 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!" );
   return;
}
// Get the second bookmark in the document.
java.util.List bookmarks = doc.getBookmarks( );
String bookmark = ( String ) bookmarks.get( 1 );
long pageNumber = doc.getPageNumber( bookmark );
logger.log(Level.INFO, bookmark + " Page number: " + pageNumber);
// Close the document.
doc.close( );

Accessing a report parameter

A report parameter is a report element that provides input to a report design before an application generates the report. A report document does not use report parameters to create a formatted report. If an application already has the parameter names and the values to set, or the default values for all report parameters for a report design are always valid, or the report source is a report document, 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 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 application uses the BIRT report engine API classes and interfaces shown in Table 14-4.

Table 14-4 Classes and interfaces that support report parameters

image

An application can access report parameters by name or using generic code. Use generic code if the application must be able to run any report design, for example, to access report designs from a list that depends on user input. An application that runs only a fixed set of known report designs 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 calling ReportEngine.createGetParameterDefinitionTask( ). Close the parameter definition task object after use by calling its close( ) 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. An application that runs only known report designs does not need to perform this task.

Getting the report parameters in a report design

To access a single report parameter of a known name, use the IGetParameterDefinitionTask.getParameterDefn( ) method. This method returns an object of type IParameterDefnBase.

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. For an argument value of false, the method returns an ungrouped set of report parameters. For a value of 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 if the parameter is a group or IParameterDefnBase.CASCADING_PARAMETER_GROUP if 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 of 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. Call IGetParameterDefinitionTask.getDefaultValues( ) to get the default value of all parameters in the report design. 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 method, IGetParameterDefinitionTask.getSelectionList( ). 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.

Call IGetParameterDefinitionTask.setParameterValue( ) to set the value of the parameter. If a user interface returns String values to your application for date and number parameters, convert the String into a locale-independent format before setting the value. To perform this task, call the method, ReportParameterConverter.parse( ), before calling setParameterValue( ).

After setting the report parameter values, call the method, IGetParameterDefinitionTask.getParameterValues( ). This method returns a HashMap object containing the current parameter values as set by calls to IGetParameterDefinitionTask.setParameterValue( ). Use this HashMap object to set 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 14-6 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.

Listing 14-6 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. In this case,
// the data type of the parameter, customerID, is Double.
int customerID =
   ((Double) task.getDefaultValue( param )).intValue( );
// Get a value for the parameter. 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( );
// Close the parameter definition task.
task.close( );

How to use the Collection of report parameters

The code sample in Listing 14-7 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 IReportRunnable object. 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.

Listing 14-7 Setting the values of multiple parameters without grouping


// Create a parameter definition task.
IGetParameterDefinitionTask task =
   engine.createGetParameterDefinitionTask( runnable );

// Create a flat collection of the parameters in the design.
Collection params = task.getParameterDefns( false );
// Get the default values of the parameters.
HashMap parameterValues = task.getDefaultValues( );

// Get values for the parameters. 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
// in standard locale-independent format.
Iterator iterOuter = params.iterator( );
ReportParameterConverter cfgConverter = new
   ReportParameterConverter( "", Locale.getDefault() );
while ( iterOuter.hasNext( ) ) {
   IScalarParameterDefn param =
      (IScalarParameterDefn) iterOuter.next( );
   String value = (String) inputValues.get( param.getName( ));
   if ( value != null ) {
      parameterValues.put( param.getName( ),
         cfgConverter.parse( value, param.getDataType( ) ) );
   }
}
// Close the parameter definition task.
task.close( );

Getting the values for cascading parameters

A cascading parameter group contains an ordered set of parameters that provide lists of acceptable values for the end user to select. The value chosen for the first parameter limits the available values for the second one, and so on. The parameters use one or more queries to retrieve the values to display to the user from a data set. The parameter definition task uses the data rows from the queries to filter the values for each parameter, 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

The group has two parameters, ProductLine on PRODUCTS.PRODUCTLINE and ProductCode on PRODUCTS.PRODUCTCODE. The display text for ProductCode uses values from PRODUCTS.PRODUCTNAME. Figure 14-1 shows the appearance of the requester that prompts for values for these parameters when a user previews the report in BIRT Report Designer.

Figure 14-1 Cascading report parameters

image

To use the report engine API to get the values for cascading parameters, perform the tasks in the following list.

• To populate the list of values for the first report parameter in the group, call IGetParameterDefinitionTask.getSelectionListForCascadingGroup( ). 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 14-1, the Object[ ] array is:

new Object[ ] { "Trains" }

How to use cascading parameters

The code sample in Listing 14-8 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.

Listing 14-8 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;
      String groupName = group.getName();
      Iterator i2 = group.getContents( ).iterator( );

      Object[ ] userValues =
         new Object[group.getContents( ).size( )];

      // Get the report parameters in the cascading group.
      int i = 0;
      while ( i2.hasNext( ) ) {tOptio
         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 );
      String[ ] choiceValue = new String[c.size( )];
      String[ ] choiceLabel = new String[c.size( )];

      // Iterate through the values for the parameter.
      Iterator i3 = c.iterator();
      for ( int j = 0; j < c.size( ); j++ ) {
         IParameterSelectionChoice s =
            ( IParameterSelectionChoice ) i3.next( );
         choiceValue[j] = ( String ) s.getValue( );
         choiceLabel[j] = 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 output emitters for HTML, Adobe PDF, Adobe PostScript (PS), Microsoft Excel (XLS), Microsoft PowerPoint (PPT), and Microsoft Word (DOC) formats. BIRT also supports custom output formats provided by renderers developed from the rendering extension points.

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 following tasks support generating a report from the source:

• IRunAndRenderTask. An object of this type creates a report in one of the supported formats 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 binary format report document, create the report output using 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 binary 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 supports reading but not 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 values for all the parameters in a report design. This method takes a HashMap as an argument. To create a suitable HashMap, use the techniques shown in Listing 14-6 or Listing 14-7, 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 having unset values.

Adding to the report engine’s class path

Some report designs require access to external Java classes. The BIRT Report Engine uses class path information from various settings in its environment to locate the external classes. BIRT supports defining some of these locations by setting properties programmatically on the application context or on the EngineConfig object, or with the Java System class. To set the properties, use constants from the org.eclipse.birt.report.engine.api.EngineConstants class. To set a property on the application context, use the EngineTask object or the EngineConfig object, as shown in the following lines of code, where MyClass is the class that starts the report engine:

configOrTask.getAppContext( ).put
   ( EngineConstants.APPCONTEXT_CLASSLOADER_KEY,
   MyClass.class.getClassLoader( ));

To set a CLASSPATH property on the EngineConfig object, use code similar to the following lines. The property value must be a valid CLASSPATH.

config.setProperty( EngineConstants.WEBAPP_CLASSPATH_KEY,
   "c:/myjars/jar1.jar;c:/myclasses" );

To use the Java System class to set a CLASSPATH property, use code similar to the following lines. The property value must be a valid CLASSPATH.

System.setProperty( EngineConstants.WEBAPP_CLASSPATH_KEY,
   "c:/myjars/jar1.jar;c:/myclasses" );

BIRT searches locations for external classes in the order shown in the following list:

• The CLASSPATH for the report engine plug-in.

• The CLASSPATH of the parent class loader that is set as the EngineConstants.APPCONTEXT_CLASSLOADER_KEY. Define this property on the application context.

• The CLASSPATH set as EngineConstants.WEBAPP_CLASSPATH_KEY. Define this property using the Java System class or on the EngineConfig object.

• The CLASSPATH set as EngineConstants.PROJECT_CLASSPATH_KEY. Define this property using the Java System class or on the EngineConfig object.

• The CLASSPATH in EngineConstants.WORKSPACE_CLASSPATH_KEY. Define this property using the Java System class or on the EngineConfig object.

• JAR files included in the report design.

Providing an external object to a report design

BIRT supports an application passing previously instantiated objects into the report engine. Using this technique, the engine does not have to provide the code to create the object using information external to BIRT. The calling application can manipulate the object in memory immediately before calling the report engine. A typical use of external objects is in the BIRT scripting environment. After the application passes the object to the report engine, script expressions can reference the object by name at the appropriate stage in the report generation process. To supply an object to the report engine, use the application context, accessed from either the EngineConfig object or the task object, as shown in the code in Listing 14-9.

Listing 14-9 Setting up an external Connection object


myConnection mc = DriverManager.getConnection( myURL );
config = new EngineConfig( );
// Get the application context from the config or the task
HashMap hm = configOrTask.getAppContext( );
hm.put( "MyConnectionObject", mc );
configOrTask.setAppContext( hm );

// To refer to this object in a BIRT script
// or expression, use MyConnectionObject.myMethod()

Generating a binary report document

A binary report document contains all the information required to create a formatted report. The document contains appearance, data, and pagination information. Generate a binary report document if the same information must be rendered to multiple formats, if the output format is unknown at the time the report runs, or to render a subset of the pages in the report. Call an IRunTask.run( ) method to create a binary report document.

How to generate a binary report document

The code sample in Listing 14-10 shows the use of an IRunTask object to set 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. The variable, str_DateStamp, is a String representing today’s date.

Listing 14-10 Using an IRunTask object to create a binary report document


// Create a run task object.
IRunTask task = engine.createRunTask( runnable );
String output = name.replaceFirst( ".rptdesign", str_DateStamp +
   ".rptdocument" );
try {
   task.run( output );
   task.close( );
   System.out.println( "Created document " + output + "." );
}
catch ( EngineException e1 ) {
   System.err.println( "Report " + output + " creation failed." );
   System.err.println( e1.toString( ) );
}

Preparing to render a formatted report

Render a formatted report from a report design by calling the method IRunAndRenderTask.run( ). Render a formatted report from a binary report document by calling the method IRenderTask.render( ).

Setting up the rendering options

Before rendering a report to one of the supported output formats, 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 settings, such as the type of HTML to generate, 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 <body> or <html> tags.

The application uses a rendering options object to set the output options on an IRunAndRenderTask or an IRenderTask object. Format-specific rendering options classes implement IRenderOption and extend RenderOption. The rendering options class for the Excel format is EXCELRenderOption, for the HTML format is HTMLRenderOption, and for the PDF format is PDFRenderOption. All other output formats use the RenderOption class.

After creating the rendering option object, call the task’s setRenderOption( ) method. This method takes a IRenderOption object as an argument. Use the setRenderOption( ) 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. Listing 14-11 includes code that sets the rendering option for HTML.

To apply a rendering setting, use setter methods on the rendering option object. These objects also have getter methods that retrieve render settings. Common option settings are a base URL for hyperlinks, an action handler, an image handler, output format, and supported image output formats.

The supported image formats setting is used 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 option value. To set the supported formats, use setSupportedImageFormats( ) with an argument of a String that contains a list of the supported image formats. The image formats are standard types, such as BMP, GIF, JPG, PNG, and SVG. Semicolons separate the items in the list. The method getSupportedImageFormats( ) returns a String of the same format.

Understanding HTML rendering options

Before generating an HTML report that uses images on disk or creates images or charts in a report, the application must provide additional settings. The HTMLRenderOption class provides many ways to customize the emitted HTML. The following list describes some of the commonly used options and how the options interact with each other:

• Image directory. Many reports include either static or dynamic images. For example, charts are dynamic images. HTML reports place all these images in a defined location. To set this location, call the setImageDirectory( ) method.

• Base image URL. For a web deployment, such as a WAR file for the reporting application, the images created in the image directory must be available to a user’s web browser. Provide the engine with a URL prefix to apply to the image directory. To set the URL, use the setBaseImageURL( ) method, as shown in Listing 14-3.

• Image handler. Use the setImageHandler( ) method to set up an image handler, as described earlier in this chapter.

• Embeddable HTML. Call the setEmbeddable( ) method with the argument, true, to set this option. The embeddable produced HTML does not have <html> and <body> tags.

• Right to left generation. To set the HTML emitter to generate content right to left, call the setHtmlRtLFlag( ) method with the argument, true.

• Title tag. Use the setHtmlTitle( ) method to write a <title> tag to the produced HTML. This title appears in the title bar of the browser and in the tab on a multi-tabbed browser window.

• Master page content. In a BIRT report design, the developer uses the Master Page to set up page sizes, header and footers. These settings affect the produced HTML. To suppress the master page content, call the setMasterPageContent( ) with the argument, false. The master page content setting affects the results of setting paginated HTML and fixed layout.

• Floating footer. By default, the master page footer appears directly below the content of each HTML page. Pass the argument, false, to the setPageFooterFloatFlag( ) method to force the footer to the bottom of a fixed-size master page. This setting adds a div statement with a height attribute to the produced HTML.

• Master page margins. Master page margins affect the appearance of the HTML output if the report uses fixed layout or if the value, true, is the argument to the setOutputMasterPageMargins( ) method.

• Paginated HTML. When rendering the report in HTML, the engine supports paginating the results. The report design’s master page defines the size of the page and the content of its page headers and footers. To display the entire or partial report as a single HTML page, call the setHtmlPagination( ) method with the argument, false. In this case, setting a render task page range of 5-7 renders pages 5 to 7 as one HTML page. Header and footer information appear several times within this HTML page if you choose to display master page content. For your application to support pagination, you need to set up pagination controls similar to the example Web Viewer.

• Fixed or automatic layout. By default, BIRT generates tables with columns that are fixed in size. When the user changes the width of the browser window, the column widths do not change. BIRT also supports automatic layout, in which column widths adjust according to the width of the window. Fixed layout produces reports with a consistent layout, but this choice can increase rendering time. The table-layout:fixed attribute is applied to all tables. Appropriate div statements set column widths and padding. The div settings are also applied if master page content is used. To change this setting, use the setLayoutPreference( ) method. Pass the LAYOUT_PREFERENCE_AUTO or LAYOUT_PREFERENCE_FIXED value as its argument.

• Style calculation. The setEnableAgentStyleEngine( ) method provides the developer control over how the styles are calculated. Passing a value of true to this method emits the styles directly to the produced HTML. In this case, the browser performs the style calculations. Passing a value of false to the method causes the emitter to use the BIRT style engine to do the calculations. As with the fixed or automatic layout setting, this setting affects the consistency of the report’s appearance.

• Base URL. Most BIRT report items have a hyperlink property. When building this hyperlink in the report designer, the report developer uses a URL, an internal bookmark, or a drill-through operation. The report design stores the results of the design session. The setBaseURL( ) method defines the base URL to prefix to the hyperlinks built using the designer. Use this setting for applications deployed in a web environment.

• Action handler. The emitter handles hyperlink, bookmark, and drill-through actions. When rendering the report as HTML, BIRT uses the HTMLActionHandler class to determine how to build the hyperlink for these actions. The HTMLActionHandler class implements the IHTMLActionHandler interface. If the default HTMLActionHandler does not meet your needs, use a custom implementation of IHTMLInterface. To set up the new action handler, use the setActionHandler( ) method.

The HTMLRenderOption class provides equivalent getter methods for the setter methods described in the previous list.

How to configure properties for a report in HTML format

The code sample in Listing 14-11 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.

Listing 14-11 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 and other HTML options.
HTMLRenderOption options = new HTMLRenderOption( );
String output = name.replaceFirst( ".rptdesign", ".html" );
options.setOutputFileName( output );
options.setImageDirectory( "image" );
options.setEmbeddable( false );
// Apply the rendering options to the task.
task.setRenderOption( options );

Understanding Excel rendering options

The EXCELRenderOption class provides the following methods to modify the appearance of the generated spreadsheet:

• setHideGridlines( ) uses a boolean value of true or false to hide or show gridlines in the spreadsheet.

• setOfficeVersion( ) sets the compatibility level of the spreadsheet. Valid values for the String parameter are office2003 and office2007. The default value is office2003.

• setWrappingText( ) uses a boolean value of true or false to set or unset text wrapping in cells in the spreadsheet.

The EXCELRenderOption class provides equivalent getter methods to retrieve the current values of the options.

Using generic methods to access rendering options

The RenderOption class provides getter and setter methods for accessing the most commonly used rendering options. For example, this class provides the getOutputFileName( ) and setOutputFileName( ) methods to get and set the file name of rendered report. RenderOption also provides fields to identify the options. An application accesses the less commonly used options by calling the following generic methods, inherited from TaskOption, using a RenderOption field as an argument:

• getOption( )

This method returns an Object containing the option value. For boolean or int values, the returned Object is a Boolean or Integer respectively.

• getBooleanOption( ), getIntOption( ), getStringOption( )

Use these methods if the code is accessing an option of known type. These methods return a boolean value, an int value, and a String object respectively.

• getOptions( )

This method returns a Hashmap object containing all the rendering options that have been set explicitly using setOption( ).

• hasOption( )

Use this method to check whether the rendering options object supports a particular option. If the object supports an option, this method returns true even if the value of the option is null.

• setOption( )

This method takes a second argument, an Object that contains the value of the option. For boolean or int values, use a Boolean or Integer object respectively.

The code sample in Listing 14-12 provides examples of these methods.

Listing 14-12 Using generic rendering options


RenderOption ro = new RenderOption( );
if ( ro.getOption( RenderOption.RENDER_DPI ) == null )
{
   ro.setOption( RenderOption.RENDER_DPI, new Integer( 96 ) );
}
ro.setOption( RenderOption.RTL_FLAG, new Boolean( true ) );

// The following line returns:
// INFO: Options: {RenderDpi=96, RTLFlag=true}
logger.log( Level.INFO, "Options: " + ro.getOptions().toString());

Rendering formatted output

To generate a formatted report, the application calls the run( ) method on an IRunAndRenderTask or the render( ) method on an IRenderTask object. The application must handle the EngineException that these methods can throw. After generating the report, the application can reuse the report engine and the task to generate further reports. If the application generates only a single report, close the task and destroy the engine after performing the report generation.

The IRunAndRenderTask.run( ) method creates a formatted document containing all the data in the report. The IRenderTask.render( ) method is more versatile. This method supports rendering the whole report or a subset of pages based on a bookmark, a page number, or a page range. A bookmark is a String that identifies a location in the report document. Use a value defined by the report developer using BIRT Report Designer or navigate the table of contents to identify the required portion of a report.

To access table of contents entries, use ITOCTree and TOCNode objects. The ITOCTree interface provides access to the tree of table of contents entries. A TOCNode object defines each table of contents entry. Each entry contains three strings: the identifier, the display value, and the bookmark of the entry. Get an ITOCTree object by calling the IRenderTask.getTOCTree( ) method. Then, use the ITOCTree.getRoot( ) method to retrieve the root TOCNode object. To find the subentries of a table of contents entry, use the TOCNode.getChildren( ) method. This method returns a List of TOCNode objects. Alternatively, call the findTOCByValue( ) method to retrieve a List of all TOCNode objects having a particular value. Use the bookmark String to locate the page to which the bookmark links by calling the getPageNumber( ) method. Using this information, the application calls the setPageNumber( ) or setPageRange( ) methods to specify pages to render to a formatted report.

How to generate a report from a report design

The code sample in Listing 14-13 generates a report from a report design and then closes the task. The variable, task, is an IRunAndRenderTask object. The variable, htmlRO, is an HTMLReportOption object. The variable, name, is the name of the report design. The variable, output, is the name of the output file.

Listing 14-13 Generating a report from a report design


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

How to generate a partial report from a binary report document

The code sample in Listing 14-14 generates a single page of a report from a binary report document. The sample shows two techniques to locate the page to generate. The variable, htmlRO, is an HTMLReportOption object. The variable, name, is the name of the report design. The variable, output, is the name of the output file. The variable, engine, is a ReportEngine object.

Listing 14-14 Generating part of a report from a binary report document


IReportDocument binaryDoc = engine.openReportDocument(output);
// Create a render task object.
IRenderTask task = engine.createRenderTask( binaryDoc );
// Get the root of the table of contents.
ITOCTree tocTree = task.getTOCTree( );
TOCNode td = tocTree.getRoot();
// Traverse the tree of top-level items in the table of contents.
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( "157" ) )
       {
          // Get the number of the page that contains the data.
          pNumber = binaryDoc.getPageNumber( child.getBookmark( ));
          task.setPageNumber( pNumber );
       }
   }
}

// Alternatively, use the first entry having the correct value.
java.util.List desiredNodes = tocTree.findTOCByValue( "157" );
if ( desiredNodes != null && desiredNodes.size( ) > 0 )
{
   TOCNode child = (TOCNode) desiredNodes.get(0);
   pNumber = binaryDoc.getPageNumber( child.getBookmark( ) );
   task.setPageNumber( pNumber );
}

// Render the page. Then, close the document and the task.
output = output.replaceFirst( ".rptdocument", ".html" );
htmlRO.setOutputFileName( output );
task.setRenderOption( htmlRO );
task.render(); binaryDoc.close();
task.close();

Accessing the formatted report

After generating a formatted report document as a file on disk, access the file in the same way as any other file. For example, open an HTML document in a web browser and a PDF document using Adobe Reader. If the application sends the report to a stream, the stream must be able to process the content.

Checking the status of a running report task

The BIRT Report Engine supports checking the status of any engine task and cancelling that task. Typically, an application uses a separate thread to perform these actions. To check the status of a running report, use the EngineTask.getStatus( ) method, which returns one of the following values:

• IEngineTask.STATUS_NOT_STARTED. The task has not started.

• IEngineTask.STATUS_RUNNING. The task is currently running.

• IEngineTask.STATUS_SUCCEEDED. The task completed with no errors.

• IEngineTask.STATUS_FAILED. The task failed to execute.

• IEngineTask.STATUS_CANCELLED. The task was cancelled.

Cancelling a running report task

To cancel a running task call the IEngineTask.cancel( ) method. All the tasks that run and render reports implement and extend the IEngineTask interface. If the application cancels a task that is running a report, the task stops generating the report content. If the task has started a query on a database, the database continues to run the query to completion.

How to cancel a running report task

1 Define a thread class as an inner class to cancel the report. In Listing 14-15, the name of this class is CancelReport.

2 Within the code that creates the task, create an instance of the CancelReport class, as shown in the following line:

CancelReport cancelThread = new CancelReport( "cancelReport",
  task);

3 Test for a condition. Then, use the CancelReport object to cancel the report, as shown in the following line:

cancelThread.cancel( );

Listing 14-15 Defining a class to cancel an engine task


private class CancelReport extends Thread
{
   private IEngineTask eTask;

   public CancelReport( String myName, IEngineTask task )
   {
      super( myName );
      eTask = task;
   }

   public void cancel( )
   {
      try {
         Thread.currentThread( ).sleep( 100 );
         eTask.cancel( );
         System.out.println( "#### Report cancelled #####" );
      }
      catch( Exception e )
      {
        e.printStackTrace();
      }
   }
}

Programming the structure of a report design

A reporting application typically generates reports from report designs. When deploying this type of application, the report designs are deployed with the application. Any changes to the generated reports depend on the values of report parameters and the data from the data set. To access a report design, the application uses an IReportRunnable object.

Sometimes business logic requires changes to the report design before generating the report. Some changes are possible through using parameters and scripting. Other changes can only occur through modification of the report design itself. The API to perform these tasks is known as the model API. The package containing the classes and interfaces to work with the items in a report design, library, or template is org.eclipse.birt.report.model.api.

To access the structure of the report design, the application obtains a ReportDesignHandle object from the design engine. ReportDesignHandle 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 Report Object Model (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 writes the result to a stream or a file. To generate a formatted report or a binary report document, use the report engine to open an IReportRunnable object on the updated design. Then, use the report engine as described earlier in this chapter. To access the design engine, an application must first instantiate a report engine.

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 complete report design and generate report output in a single application.

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 as applicable to these file types 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.

About BIRT model API capabilities

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

• Adding a report item to a report design:

• Add a simple report item such as a data item, label, or image. Set a 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 the grid, table, or list.

• Changing the properties of a report item in a report design:

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

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

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

• Changing the structure of a report design:

• Add a report parameter.

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

• Modifying non-visual elements in a report design:

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

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

• Specify a data source for a data set.

Opening a report design for editing

To access a report design and its contents, the application must instantiate first a report engine and secondly a ReportDesignHandle object. The ReportDesignHandle object provides access to the ROM elements in the design opened by the report engine. Instantiate a ReportDesignHandle by calling a method on either 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 ROM in the same way that the ReportEngine class provides access to report generation functionality. Before creating a DesignEngine object, create a DesignConfig object to contain configuration settings for the design engine such as the BIRT home location. The BIRT home is the same location that the report engine uses. The DesignConfig object sets up custom access to resources and custom configuration variables for scripting.

Use a factory service to create a DesignEngine in the same way as creating a ReportEngine. After setting configuration properties, create a design engine by calling the Platform.createFactoryObject( ) and the IDesignEngineFactory .createDesignEngine( ) methods. The DesignConfig object defines the settings for this process. If the application has already started the platform in order to set up a report engine, use the same platform instance to create the design engine. If the application runs in an RCP environment, do not start the platform.

Create the SessionHandle object by calling the method, newSessionHandle( ) on the DesignEngine 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.

How to open a report design for editing

The code sample in Listing 14-16 creates a DesignEngine object from which to create a SessionHandle object. The code uses the SessionHandle object to open a report design.

Listing 14-16 Opening a report design for editing


// Create a design engine configuration object.
DesignConfig dConfig = new DesignConfig( );
dConfig.setBIRTHome( "C:/birt-runtime-2_6_0/ReportEngine" );
// Start the platform for a non-RCP application.
Platform.startup( dConfig );
IDesignEngineFactory factory =
   ( IDesignEngineFactory ) Platform.createFactoryObject
   ( IDesignEngineFactory.EXTENSION_DESIGN_ENGINE_FACTORY );
IDesignEngine dEngine = factory.createDesignEngine( 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 an IReportRunnable object to access a design handle

An alternative way to open a report design is by calling the getDesignHandle( ) method on an IReportRunnable object. Use a design engine to access the elements in this report design in the same way as for any other design handle.

Using a report item in a report design

A report item is a visual element in the report design. Typically, a report developer uses BIRT Report Designer to add a report item to the design by dragging an item from the palette to the layout editor. Sometimes a reporting application must change the properties of certain report items in the design before running the report. The 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. These methods return a DesignElementHandle object. All report items derive from this class.

A slot is a logical component of a container report item. A slot holds zero or more members of the appropriate report item type. For example, a table element has four slots: Header, Detail, Footer, and Groups. Each of these slots is a container for further slots. 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. The Outline view in BIRT Report Designer provides a visual representation of the slots in an individual report item.

Accessing a report item by iterating through a slot

To access a report item through the report design’s structure, first get the slot handle of the report body by calling the ReportDesignHandle.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 of header information, a table containing grouped data, and a label that displays a report footer. Figure 14-2 shows its outline view in BIRT Report Designer.

Figure 14-2 Slots in a report design

image

To access the top-level items in this report design, iterate over the contents of the body slot handle. To access the iterator for a slot handle, call SlotHandle.iterator( ). Each call to Iterator.getNext( ) returns ReportDesignHandle object, which is a handle to a report item or another slot. Alternatively, to access a report item at a known slot index, call SlotHandle.get( ). The slot index number is zero-based.

Accessing a report item by name

To make a report item accessible by name, the item must have a name. Either a report developer sets the name in BIRT Report Designer or an application sets the name programmatically by using the item’s setName( ) method. To find a report item by name, use ReportDesignHandle.findElement( ).

Examining a report item

To examine a report item, check the class of the report item and 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. Access properties for the whole of a structured item in the same way as for a simple item. To examine the contents of a structured item, iterate over its slots. Use this technique to determine the contents of a cell in a table. First, call a method to retrieve a slot handle for the table’s rows. Next, iterate over the contents of the slot to access the cells. 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.

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 of a Java type or type wrapper class. An example of a simple property is the name property, which is a String object. Some simple properties, like name, have an arbitrary value. Other simple properties have restricted values from a set of BIRT String constants. The interface, DesignChoiceConstants in the package, org.eclipse.birt.report.model.api.elements, defines these constants. For example, the image source property of an image element can have one of only 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 SharedStyleHandle 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 attributes of the complex properties. For example, StyleHandle classes provide access to background color, font, and highlights.

How to access a report item by name

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

Listing 14-17 Finding a report item having 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 14-18 finds an image item in a grid, checks its type, and examines its URI. Use this technique for generic code to navigate a report design structure or if to find an item that does not have a name. The variable, design, is a ReportDesignHandle object.

Listing 14-18 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 is = ( ( ImageHandle )
                    cellContents ).getSource( );
                 // Check that the image has a URI.
if ( ( is.equals( DesignChoiceConstants.IMAGE_REF_TYPE_URL ))
|| ( is.equals( DesignChoiceConstants.IMAGE_REF_TYPE_FILE ))) {
                      // Retrieve the URI of the image.
                      String imageURI = ( ( ImageHandle )
                           cellContents ).getURI( );
                  }
               }
            }
         }
      }
   }
}

Modifying a report item in a report design

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 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, 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.

How to change a simple property of a report item

The code sample in Listing 14-19 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. Alternatively, access a report item by navigating the report structure.

Listing 14-19 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 org.eclipse.birt.report.model.api.elements.DesignChoiceConstants interface. For example, the font style property can have one of only the values, FONT_STYLE_ITALIC, FONT_STYLE_NORMAL, or FONT_STYLE_OBLIQUE.

Using a BIRT property handle

To access complex properties, 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, use getter and setter methods on the property handle object. For example, to get and set the size of a dimension, use the DimensionHandle methods getMeasure( ) and setAbsolute( ), respectively. Calling a method on a property handle object to set a value on a complex property affects the report item straight away. You do not call another 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, 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 using BIRT Report Designer. Shared styles appear in the Outline view in BIRT Report Designer. 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 14-20 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. Alternatively, access a report item by navigating the report structure.

Listing 14-20 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 the exception
}

Understanding property structure objects

Complex property structures represent many optional report element features within BIRT. For example, computed columns, highlight rules, sort keys, and table of contents entries are all optional complex properties of report elements. The classes for these properties all derive directly or indirectly from org.eclipse.birt.report.model.core.Structure. An application can access existing structure property objects on a report element or create new ones.

Using an existing property structure object

Use the property handle, which is an object of class PropertyHandle, to apply a new structure object to a report element’s property or to modify an existing property structure. To access the property handle call the method, DesignElementHandle.getPropertyHandle( ), which is available on all report elements. This method takes a String argument that defines the property to access. Class fields having names of the form XXX_PROP define the available property structures for a report element class. The property handle object provides access to one or more property structure objects. Use the PropertyHandle getter methods to access settings on existing property structures.

Typically, the property structure class provides setter methods only for a few key values. Set other values by calling the property structure object’s setProperty( ) method. This method takes two arguments: a String that identifies the property and an object that specifies the value. Class fields defined on the property structure class or on the DesignChoiceConstants class provide the values for the property identifier.

How to set values on an existing property structure object

The code sample in Listing 14-21 shows how to change the sort direction value on a table item’s first sort key.

Listing 14-21 Changing the sort direction on a table


void modSortKey( TableHandle th ) {
   try {
      SortKeyHandle sk;
      PropertyHandle ph =
         th.getPropertyHandle( TableHandle.SORT_PROP );
      if ( ph.isSet( ) ) {
         sk = ( SortKeyHandle ) ph.get( 0 );
         sk.setDirection
            ( DesignChoiceConstants.SORT_DIRECTION_DESC );
      }
   } catch ( Exception e ) {
      e.printStackTrace( );
   }
}

Creating a property structure object

The design engine class, StructureFactory, provides static methods for creating property structure objects. Most StructureFactory methods take the form createXXX, where XXX is the structure to create. After creating a new structure object, set its simple or complex properties using its setter methods. Report element handles provide methods to add key structure properties to their property handles. For example, add a filter condition to a data set object by using the addFilter( ) method. To add other properties to a report element, use the method, PropertyHandle.addItem( ).

How to create a complex property object

The code sample in Listing 14-22 shows how to create a highlight rule object and apply that rule to a row handle. The variable, rh, is a RowHandle object. Because RowHandle does not have a specific method to add a highlight rule property, the code uses the PropertyHandle.addItem( ) method.

Listing 14-22 Adding a highlight rule to a table or grid row


try {
  HighlightRule hr = StructureFactory.createHighlightRule( );
  hr.setOperator( DesignChoiceConstants.MAP_OPERATOR_GT );
  hr.setTestExpression( "row["CustomerCreditLimit"]" );
  hr.setValue1( "100000" );
  hr.setProperty
     ( HighlightRule.BACKGROUND_COLOR_MEMBER, "blue" );

  PropertyHandle ph =
     rh.getPropertyHandle( StyleHandle.HIGHLIGHT_RULES_PROP );
  ph.addItem( hr );
} catch ( Exception e ){ e.printStackTrace(); }

The code samples in Listing 14-23 provide further examples of using the structure factory to create complex properties.

Listing 14-23 Various structure factory examples


// Use the structure factory to add a sort key to a table.

void addSortKey( TableHandle th ) {
   try {
      SortKey sk = StructureFactory.createSortKey( );
      sk.setKey( "row["CustomerName"]" );
      sk.setDirection
         ( DesignChoiceConstants.SORT_DIRECTION_ASC );

      PropertyHandle ph =
         th.getPropertyHandle( TableHandle.SORT_PROP );
      ph.addItem( sk );
   } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add a column binding to a table.
void addColumnBinding( TableHandle th ) {
   try {
      ComputedColumn cs1;
      cs1 = StructureFactory.createComputedColumn( );
      cs1.setName( "CustomerName" );
      cs1.setExpression( "dataSetRow["CUSTOMERNAME"]" );
      th.addColumnBinding( cs1, false );
   } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add a filter condition to a table.
void addFilterCondition( TableHandle th ) {
   try {
      FilterCondition fc = StructureFactory.createFilterCond();
      fc.setExpr( "row["CustomerCountry"]" );
      fc.setOperator(DesignChoiceConstants.MAP_OPERATOR_EQ);
      fc.setValue1("'USA'");
      PropertyHandle ph =
         th.getPropertyHandle( TableHandle.FILTER_PROP );
      ph.addItem( fc );
  } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add a filter condition to a data set.
void addFilterCondition( OdaDataSetHandle dh ) {
   try {
      FilterCondition fc = StructureFactory.createFilterCond();
      fc.setExpr( "row["COUNTRY"]" );
      fc.setOperator( DesignChoiceConstants.MAP_OPERATOR_EQ );
      fc.setValue1( "'USA'" );
      // Add the filter to the data set.
      dh.addFilter( fc );
  } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add a hyperlink to a label.
void addHyperlink( LabelHandle lh ) {
   try {
      Action ac = StructureFactory.createAction( );
      ActionHandle actionHandle = lh.setAction( ac );
      actionHandle.setURI( "'http://www.google.com'" );
      actionHandle.setLinkType
         ( DesignChoiceConstants.ACTION_LINK_TYPE_HYPERLINK );
   } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add a table of contents entry to a data item.
void addToc( DataItemHandle dh ) {
   try {
     TOC myToc = StructureFactory.createTOC
        ( "row["CustomerName"]" );
     dh.addTOC( myToc );
   } catch ( Exception e ) { e.printStackTrace( ); }
}

// Add an embedded image to the report design.
void addImage( ) {
   try {
      EmbeddedImage image =
         StructureFactory.createEmbeddedImage( );
      image.setType
         ( DesignChoiceConstants.IMAGE_TYPE_IMAGE_JPEG );
      image.setData( load( "logo3.jpg" ));
      image.setName( "mylogo" );
      designHandle.addImage( image );
   } catch ( Exception e ) { e.printStackTrace( ); )
}

// Load the embedded image from a file on disk.
public byte[ ] load( String fileName ) throws IOException
{
   InputStream is = null;
   is = new BufferedInputStream
      ( this.getClass( ).getResourceAsStream( fileName ));
   byte data[ ] = null;
   if ( is != null ) {
      try {
         data = new byte[is.available( )];
         is.read( data );
      }
      catch ( IOException e1 ) {
         throw e1;
      }
   }
   return data;
}

Adding a report item to a report design

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, the application can add extra information to the report for that category of user. To create a design entirely with the API, use the same techniques to add content to the design.

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 type of report 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.

Place new report items directly in the body slot, within containers such as a cell in a table or grid, or on the master page. The model API supports adding a simple item, such as a label, or complex items, such as a table with contents in its cells. The location of the new report item 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, 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 iterate over the rows that a data set provides. To support access to the data rows for these report item types, bind the item to a data set. The table or list element provides data rows to the report items that it contains. For this reason, 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 14-24 creates a grid item and 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.

Listing 14-24 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, employ similar techniques as for report items.

Access the report design’s data sources and data sets using 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 item’s setDataSet( ) method.

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, 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, 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. 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, install the Javadoc for the ODA API. 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. 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. 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, access data from the sales database for a particular geographical region, such as Europe or 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 the application changes the data source of a data set, the type of the data source must be appropriate for the type of the data set. Also ensure that the new data source provides the same data fields as the original data source.

How to change the data source for a data set

The code sample in Listing 14-25 shows how to check for a particular data source and data set in a report design and 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.

Listing 14-25 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( "EuropeSales" );
} catch ( SemanticException e1 )
{
   e1.printStackTrace( );
}

Changing the data set binding of a report item

Call the report item’s setDataSet( ) method to set or change the data set used by a report item. If the application changes the data set used by a report item, ensure that the new data set supplies all the data bindings that the contents of the report item require. If necessary, 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, the application or a report developer must resolve the data bindings before generating 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( ). Then, 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 14-26 shows how to check for a particular data set in a report design and changes the data set for a table. The code finds the table and data set by name. Alternatively, use slot handles to navigate the design structure. The variable, design, is a ReportDesignHandle object.

Listing 14-26 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

After making changes to an existing report design or creating a new report design, an application can 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, saving the changes to the report design is unnecessary, call ReportDesignHandle.serialize( ) method. This method returns an output stream. The report engine can generate a report by opening this stream as an input stream.

If no further changes to the report design are necessary, call the method, ReportDesignHandle.close( ), to close the report design, as shown in the following code. The variable, design, is a ReportDesignHandle object.

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

Creating a report design

The BIRT APIs support using an application to build a report design and generate the report output without using BIRT Report Designer. Use the createDesign( ) method on the session handle class, SessionHandle, to create a report design. Use the other model classes to create its contents, as shown earlier in this chapter.

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