BIRT provides a rich scripting model based on the Mozilla Rhino engine. The processes that the Report engine uses to create reports can be classified in two phases, Report Generation and Presentation. The Generation phase consumes the report design and creates an intermediate file, called the report document. The Presentation phase uses the report document to render to HTML or PDF. The report creation pipeline can execute these phases as one task or in two task. If executed in one phase the report document is created in memory. This is the default behavior of the designer when "Preview as HTML" is chosen. If executed in two phases the report document is created and stored to disk. This is the default behavior of the designer when "Preview in Web Viewer" is selected.
Events within each phase can be overridden, by creating event handlers, to alter report content. BIRT allows these event handlers to be written in either JavaScript or Java. If you code your event handler in both Java and JavaScript the JavaScript version will be executed by default.

Script events are defined for three objects, Report Object, Report Elements, and Data Source(Sets). The phase the engine is processing determines what events and which properties of the object are available for customization. The diagram below illustrates what Script Events are available in a particular phase, for a given object.

Pictured below is a representation of the event firing order for a simple report containing a table and a data element.
The event order is shown for separate generation and presentation phases.
Generation phase.

Presentation phase.

Selecting the Palette view while in the Script editor will display functions and variables that are available in the given event for the selected report element. For example the screenshot below is for the onCreate event handler of a data element.

Using the this operator list available methods and properties for the element in the given event and is used to view or alter styles, values etc. See examples illustrated later.
Use of reportContext allows accessing or modifying of report wide properties. Using reportContext a developer can set global variables to share between elements. For example:
onInitialize of the report
reportContext.setPersistentGlobalVariable("testglobal", "test global string");
use of the Persistent version allows the variable to be persisted across generation and presentation.
Also note that the variable is an Object type allowing greater flexibility.
onPrepare of a label element in a table
this.text = reportContext.getPersistentGlobalVariable('testglobal');
This global variable can be assigned to a data element using the binding editor. Simply reference the variable in the expression builder for the
desired column. For example, if a column exist in the binding editor that retrieves a string from a data base, you could append your global variable
to the value by entering the following expression:
dataSetRow["MyString"] + "-" + reportContext.getPersistentGlobalVariable('testglobal');
//attributeBean is a Birt Viewer supplied session variable
myAttributeBean = reportContext.getHttpServletRequest().getAttribute('attributeBean');
reportDoc = myAttributeBean.getReportDocumentName( );
this.text = reportDoc;
The reportContext allows reading and modifying the context for use within scripts. For example:
onInitialize of the report
appContext = reportContext.getAppContext();
importPackage(Packages.java.util)
myArrList = new ArrayList();
myArrList.add("one");
myArrList.add("two");
appContext.put("AppContextTest", myArrList);
This loads the current application context and modifies it for later use.
Within a label element's onPrepare event handler, it could then be used as follows:
appContext = reportContext.getAppContext();
myObject = appContext.get("AppContextTest");
this.text = myObject.size();
The above example could have also been implemented using:
reportContext.setPersistentGlobalVariable("testglobal", myArrList);
The reportContext can also be used to retrieve the current locale and messages stored within a resource file.
Within the initialize event you can define global functions, variables and objects. For example to create a global JavaScript function:
function gTest(v)
{
return "Global Function:" + v;
}
//this line is only required if using the function within Chart Scripts
reportContext.setPersistentGlobalVariable("gTest", gTest);
To use this function just call
gTest("MyTest");
or
gTest = reportContext.getPersistentGlobalVariable("gTest");
val = gTest("Use Persistent");
To access the reportContext object within a chart script use the following:
context.getExternalContext().getScriptable()To illustrate, the chart title could be altered with the following chart script:
function beforeGeneration( chart, context ){
importPackage(Packages.org.eclipse.birt.chart.model.type.impl);
newChartTitle = context.getExternalContext().getScriptable().getPersistentGlobalVariable("testglobal");
chart.getTitle().getLabel().getCaption().setValue(newChartTitle);
}
In the beforeFactory event there are several methods that allow accessing elements within the report.
The elements usually require a name. For example, using a Data Set named "orders", I want to display the
query that was executed in a dynamic text element named "TestHeader". This can be achieved by entering the following
script in beforeFactory
query = this.getDataSet("orders").queryText
this.getDynamicText("TestHeader").valueExpr = "query;";
Here are is a very simple scripting example: For a detail row in a table:
onPrepare; this.getStyle().backgroundColor = "red";//This would change all row instances onCreate: if (this.getRowData().getExpressionValue(3) > 100) this.getStyle().backgroundColor="red";//This will only change the row instance For a table with 100 rows, onPrepare will be called once (to change the design), and onCreate and onRender will be called 100 times (to change the instance).
Setting the value of a label element can be achieved by writting an event handler for the onPrepare or onCreate event and entering similar code presented below:
this.text = "My New Label"Obviously this is a simple example and the value could be set simply by double clicking on the text element. Using the JavaScript editor allows complex logic to be implemented.
this.content= = "My New Text"When setting the value of a dynamic text element you will need to specify a value expression. This value expression gets evaluated when generating the report. Creating an event handler for the onPrepare event allows changing the value expression.
this.valueExpr = "row['CITY']";It is important to note that valueExpr expects a string. This is similar to what you would enter in the Expression Builder, but wrapped in quotes. So if you want to enter a string or partial string use single quotes.
this.valueExpr = this.valueExpr = "'my row count: ' + (row[0] + 1)";Setting values for data elements is accomplished by using the binding editor. If you wish to change this value in script you can set the value in the binding editor to a JavaScript variable. This variable can then be changed within script. If you use this method remember that order of execution is important. Changing the variable after the element is created will not change the data element value.
this.tocExpression=this.tocExpression = "'tocbyrownumber: ' + row[0]";
this.getRowData().getExpressionValue(int)should give you the value of the expression in the i:th column in your table.
this.getRowData().getExpressionValue("some_expression")
should give you the value of
the provided expression for the row. This expression has to be defined on the table.
Table(2 columns):
row["product"] | row["price"]
Result:
product1 | $20
product2 | $30
Script on row.onCreate:
if (this.getRowData().getExpressionValue(1) == "product1")
this.getStyle().backgroundColor = "red";
if (this.getRowData().getExpressionValue("row[price]") == "$30")
this.getStyle().backgroundColor = "blue";
Result of script:
First row should be red, second row should be blue.
Remember that the the expressions are the ones defined on the table using the binding editor, not the
dataset. Using the row[name] syntax corresponds to the name field defined in the binding editor.
this.getAction().URI = "'http://www.google.com'";Notice the single quotes within the double quotes.
Using the getStyle method allows customizing properties for a given element.

for example to bold a particular element:
this.getStyle().fontWeight = "bold";
this.getParent().getParent().getParent() This corresponds to dataElement->Cell->Row->TableSo to change the color of the table background from a data element would look like this:
this.getParent().getParent().getParent().getStyle().backgroundColor = "Silver";
Often it is required to alter the visual appearance of an element based on its value. This can be usually done within the mapping or highlights tab. When more complex logic is required it can be accomplished by using the getValue method within the onCreate of a row or data element. On the data element
if( this.getValue() > 30 ){
this.getStyle().fontFamily = "Arial"
this.getStyle().backgroundColor = "Yellow"
}
on the row
if (this.getRowData().getExpressionValue("row[QtyOrdered]") > 30){
this.getStyle().fontFamily = "Arial"
this.getStyle().backgroundColor = "Yellow"
}
A named expression is an expression that is created on an element and given a name.
The expression definition can be edited in onPrepare, and the value of the evaluated expression can be accessed in onCreate and onRender.
These are often useful when scripting in Java and use of a function like Total is needed.
For example a named expression my be defined as totalCreditValue and it's value set to Total.sum(row[“CREDITLIMIT”]). The named expression would then
be available to other elements in JavaScript as well as Java.
So if you define a Named Expression on a table named 'RWC' and set it's value to row[0], you would access it on the
row like:
rc = this.parent.getNamedExpressionValue("RWC");
The events available with Data Source and Data Sets will depend on the type of source that is being used.
Data Source beforeOpen
currentPassword = this.getExtensionProperty("odaPassword");
DataSourceClass = new Packages.myExternalSecurity();
this.setExtensionProperty("odaPassword", DataSourceClass.getPassword());
beforeOpen of the Data Set
this.queryText = "SELECT * FROM Customers where CustomerID IN (" + params["customersInClause"] +")";
When implementing a Scripted Data Set, use the open event to initialize variables, classes etc.
Use the Fetch event to load your row data. Remember to return false when your data set is finished.
Use close to close any external objects.
To illustrate a Scripted Data Set, assume that you have an external Java Object that returns an ArrayList. To use this with the scripted data set do the following.
open event of the Data Set
importPackage(Packages.test.my.ds)
myDataSet = new DS();
myArrayList = myDataSet.getList();
myIter = myArrayList.iterator();
fetch event of the Data Set
if( !myIter.hasNext() ){
return false;
}
//myOnlyColumn must be manually defined or defined in the describe event
row["myOnlyColumn"] = iter.next();
return true;
BIRT event handlers can be written in Java. This section will describe setting up a Birt Events Java project, assigning the event handlers to elements and finally debugging the report.
When writing Java event handlers remember that a new event handler instance will be created for each invocation. The implication of this is that you can not use class data memeber to pass information between methods. For example, if you want to pass information from "onPrepare()" to "onCreate()", you need to use the reportContext to hold the shared information.
Within Eclipse, open your workspace that contains the reports that will use the Java event handlers. Create a new Java project and add scriptapi.jar from the Report Engine download.

The scriptapi.jar file includes the event adapters that are needed to implement event handlers.

Create a new class, specifying TableEventAdapter as the super class.

Enter the following code for the class.
package my.test.events;
import org.eclipse.birt.report.engine.api.script.eventadapter.TableEventAdapter;
import org.eclipse.birt.report.engine.api.script.element.ITable;
import org.eclipse.birt.report.engine.api.script.IReportContext;
public class TableEH extends TableEventAdapter {
/* table onPrepare event */
public void onPrepare( ITable table, IReportContext reportContext )
{
try
{
table.setNamedExpression( "total_limit_avg", "Total.ave(row[\"CREDITLIMIT\"])" );
} catch ( Exception e ) {
e.printStackTrace( );
}
}
}
This code will add a named expression on a table. The value of the named expression is set to
Total.ave(row["CREDITLIMIT"]). In order for this to work there must be a column on the table with a value of
row["CREDITLIMIT"].
Repeat the process above for the class RowEH which extends RowEventAdapter and enter the following code.
package my.test.events;
import org.eclipse.birt.report.engine.api.script.eventadapter.RowEventAdapter;
import org.eclipse.birt.report.engine.api.script.IReportContext;
import org.eclipse.birt.report.engine.api.script.instance.IRowInstance;
import org.eclipse.birt.report.engine.api.script.IRowData;
public class RowEH extends RowEventAdapter {
public void onCreate(IRowInstance row, IReportContext context) {
IRowData data = row.getRowData();
double avgCreditLimit = ((Double) row.getParent().getNamedExpressionValue("total_limit_avg")).doubleValue();
try
{
if (((Double) data.getExpressionValue("row[\"CREDITLIMIT\"]")).doubleValue() > avgCreditLimit ) {
row.getStyle( ).setFontWeight( "bolder" );
row.getStyle( ).setFontSize( "larger" );
row.getStyle( ).setColor( "olive" );
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
This event handler is executed on every row and looks for a column named "CREDITLIMIT". If this credit limit is above the average for all entries in the table, the font weight and size are changed and the color is changed to olive.
Now that the Java classes are built we can create a simple report and apply the event handler.
Build a listing report using the sample database "Classic Models". Enter the following query.
select CLASSICMODELS.CUSTOMERS.CUSTOMERNUMBER,CLASSICMODELS.CUSTOMERS.CUSTOMERNAME,CLASSICMODELS.CUSTOMERS.CREDITLIMIT from CLASSICMODELS.CUSTOMERSDrag the data set to the report view and a table should be created automatically. Select the newly created table and enter my.test.events.TableEH in the Event Handler entry on the Properties tab.

Repeat the process for the row event handler, by selecting the row and entering my.test.events.RowEH in the Event Handler entry on the Properties tab.
Selecting Preview should result in the following output.

Switch to the Java Perspective and select either of the Java classes implemented earlier. Add breakpoints as usual and select the run->debug menu. Select BIRT Report under Configurations and click the new button. Check the workspace containing the report created earlier and select debug.

This launch a new Eclipse instance with the selected workspace. Load the sample report completed earlier and select Preview. If breakpoints exist they will halt the code when the table or the row calls your code.
To deploy the Java Event Handlers you can just place the classes/jars in the WEB-INF/lib directory of the web application. This is not ideal, because it usually requires a restart of the application server. To handle this issue BIRT adds another directory within the web application that is searched when the engine executes reports containing Java event handlers. By default this directory is BIRT_HOME/scriptlib. To change this directory set the script lib directory in the web.xml file of the Birt Viewer.
<context-param> <param-name>BIRT_VIEWER_SCRIPTLIB_DIR</param-name> <param-value></param-value> </context-param>