- Introduction
- Script Function Service Extension Point
- Exporting and Deploying an Extension Point Plug-in
- Aggregation Extension Point
- Driver Bridge Extension Point
- Encryption Helper Extension Point
- Conclusion
Script Function Service Extension Point
When creating a BIRT report, a developer uses the BIRT Expression Builder in many areas. These areas include computed columns, chart categories and series, grouping, aggregations, sorting, and filtering. The Expression Builder returns a value after evaluating one or more lines of JavaScript.
The Expression Builder provides prebuilt functions that can be used in conjunction with data retrieved from data sources to construct an expression. While these functions can be used in many ways, such as financial calculations, or date-time and string manipulations, sometimes you need to use a proprietary function.
Typically, you implement this type of function using the importPackage method in the Expression Builder to call out to a Java class. This approach works, but it's not very elegant and leads to maintenance issues. To work around this issue, BIRT provides the script function service extension point. This extension point allows you to add a custom function to the Expression Builder GUI and expose it.
For example, BIRT string functions in the Expression Builder currently don't support an "abbreviate" method. The Apache project contains a class called WordUtils that can be used to abbreviate a string. You can import this functionality into a script, but then you'd have to repeat this process anytime you need string abbreviation. This case is an ideal candidate for implementing a script function service extension (see Figure 1).
Figure 1 Adding a script function service extension to the BIRT Expression Builder.
To implement this feature, create a plug-in by using the plugin.xml shown in Listing 1.
Listing 1
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.4"?> <plugin> <extension id="stringHelper" name="StringHelper" point="org.eclipse.birt.core.ScriptFunctionService"> <Category factoryclass= "my.expression.functions.StringHelperScriptFactory" name="StringHelper"> <Function desc="Abbreviate" isConstructor="false" isStatic="true" isVisible="true" name="Abbreviate" variableArguments="false"> <Argument isOptional="false" name="String"> <DataType value="String"> </DataType> </Argument> <Argument isOptional="false" name="Lower"> <DataType value="Iteger"> </DataType> </Argument> <Argument isOptional="false" name="Upper"> <DataType value="Integer"> </DataType> </Argument> <Argument isOptional="false" name="Append"> <DataType value="String"> </DataType> </Argument> <DataType value="String"> </DataType> </Function> </Category> </extension> </plugin>
Specify the Category factory class and name. For each new function, you add a Function block below the category, containing the argument names, data types, and function return type.
In Listing 1, the category is StringHelper and the factory class is StringHelperScriptFactory. The new category contains only one function, which is named Abbreviate. This function contains four arguments.
The factory class must implement the IScriptFunctionFactory interface, which contains only one method declaration, getFunctionExecutor. This method returns an instance of a class that implements the IScriptFunctionExecutor interface. This interface also has only one method declaration: execute. The execute method accepts the parameters, executes the function, and returns a result. Listing 2 contains the code for the factory class.
Listing 2
package my.expression.functions; import org.eclipse.birt.core.exception.BirtException; import org.eclipse.birt.core.script.functionservice .IScriptFunctionExecutor; import org.eclipse.birt.core.script.functionservice .IScriptFunctionFactory; public class StringHelperScriptFactory implements IScriptFunctionFactory { public IScriptFunctionExecutor getFunctionExecutor(String arg0) throws BirtException { return new StringHelperScriptExecutor(arg0); } }
This class returns an instance of the StringHelperScriptExecutor class. The arg0 parameter contains the name of the function. In this example, the function name is Abbreviate. The StringHelperScriptExecutor class implementation executes the Abbreviate function from the WordUtils class in the execute method of a private class, as shown in Listing 3.
Listing 3
private class Abbreviate implements IScriptFunctionExecutor { public Abbreviate() { } private static final long serialVersionUID = 1L; public Object execute(Object[] args, IScriptFunctionContext context) throws BirtException { if ( args == null ) throw new IllegalArgumentException( "The number of arguments is incorrect." ); if( args[0] == null) { return null; } else { int lower = 0; int upper = 0; if( args[1] instanceof Double ){ lower = ((Double)args[1]).intValue(); }else if( args[1] instanceof String){ lower = Integer.parseInt((String)args[1]); }else if( args[1] instanceof Integer){ lower = ((Integer)args[1]).intValue(); }else{ lower = 3; } if( args[2] instanceof Double ){ upper = ((Double)args[2]).intValue(); }else if( args[2] instanceof String){ upper = Integer.parseInt((String)args[2]); }else if( args[2] instanceof Integer){ upper = ((Integer)args[2]).intValue(); }else{ upper = 5; } return WordUtils.abbreviate((String)args[0], lower, upper, (String)args[3]); } } }
The execute method casts the parameters to the appropriate type, and then calls the WordUtils abbreviate function. Notice that the code has very little error checking.
To build this example, first create a lib folder in the project, and then add the commons-lang.jar file from the Apache project to the folder. Add this JAR file to the project build path, as shown in Figure 2, and the runtime classpath, as shown in Figure 3. Figure 4 shows the output.
Figure 2 Configuring the project build path.
Figure 3 Configuring the runtime classpath.
Figure 4 Viewing the Expression Builder with example output.