Chapter 11
[
283
]
Now that you've dened the
Expression
class, you must also dene a properties
le describing the expression. Create the
RegexExpressionBundle.properties
le in the
src
folder, and add the following contents:
# The name and grouping of the expression
expression.RegexExpression.display-name=Regex Expression
expression.RegexExpression.grouping=Other
# The field property
expression.RegexExpression.property.field.display-name=Field Name
expression.RegexExpression.property.field.grouping=Other
# The regex property
expression.RegexExpression.property.regex.display-name=Regex
expression.RegexExpression.property.regex.grouping=Other
# common properties, name and dependencyLevel
expression.RegexExpression.property.name.display-name=Name
expression.RegexExpression.property.name.grouping=Common
expression.RegexExpression.property.dependencyLevel.display-
name=Dependency Level
expression.RegexExpression.property.dependencyLevel.grouping=Common
To nish dening all the necessary metadata for the expression, create a
src/meta-expressions.xml
le, which contains details about the expression:
import org.pentaho.reporting.engine.classic.core.metadata.
ElementMetaDataParser;
import org.pentaho.reporting.libraries.base.boot.AbstractModule;
import org.pentaho.reporting.libraries.base.boot.
ModuleInitializeException;
import org.pentaho.reporting.libraries.base.boot.SubSystem;
public class Chapter11Module extends AbstractModule {
// Constructor. This loads the module specification
public Chapter11Module() throws ModuleInitializeException {
loadModuleInfo();
}
// initialize the module by loading the expression metadata
public void initialize(final SubSystem subSystem) throws
ModuleInitializeException {
ElementMetaDataParser.initializeOptionalExpressionsMetaData(
"meta-expressions.xml");
}
}
Now, create a
src/module.properties
le with the following content:
module.name: chapter11module
module.producer: Pentaho Reporting for Java Developers
module.description: Example classes to demonstrate extending Pentaho
Reporting.
module.version.major: 1
module.version.minor: 0
module.version.patchlevel: 0
dependency.core.module: org.pentaho.reporting.engine.classic.core.
ClassicEngineCoreModule
</path>
<target name="clean">
<delete dir="classes"/>
</target>
<target name="compile">
<mkdir dir="classes"/>
<javac classpathref="classpath" destdir="classes"
fork="true" srcdir="src"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="dist"/>
<copy todir="classes" overwrite="true">
<fileset dir="src">
<exclude name="**/*.java"/>
</fileset>
</copy>
<jar destfile="dist/chapter11.jar" basedir="classes"/>
</target>
</project>
After creating the build le, run
ant jar
at the root of the project. The
chapter11.
jar
will be created in the
dist
folder. Now copy this JAR le into the Report
Designer's
lib
folder, and restart the Report Designer. You should see the function
287
]
Implementing functions
As mentioned earlier, functions are stateful expressions. The
Function
interface
extends the
Expression
interface, as well as the
ReportListener
interface dened
in the
org.pentaho.reporting.engine.classic.core.event
package. Functions
receive event notications while the report is being generated, allowing functions
to detect progress in report generation. See the
ReportListener
Javadoc for the
various callbacks that the
Function
interface receives.
An additional class called
FunctionUtilities
, located in the
org.pentaho.
reporting.engine.classic.core.function
package, provides useful methods
for accessing elements within a report, as well as determining the exact state of
report generation. Knowing that the report is in the prepare run state is important
for functions calculating values. This is possible with the
// This is the name of the function as it appears in the formula
public String getCanonicalName();
// this method evaluates a result based on the incoming parameters
public TypeValuePair evaluate(FormulaContext context,
ParameterCallback parameters)
throws EvaluationException;
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Extending Pentaho Reporting
[
288
]
The
TypeValuePair
class simply contains a variable value and its type.
The
FormulaContext
class provides access to the formula system, and the
ParameterCallback
class provides information about the parameters being
passed into the current function.
The
FunctionDescription
interface describes details about the function, including
its inputs and output type. The
AbstractFunctionDescription
class is available to
simplify the implementation of your
FunctionDescription
class. When using the
The properties bundle contains information about the function. Required
properties include:
Property Description
display-name
The canonical name of the formula function.
Description
The description of the formula function.
parameter.<N>.display-name
The display name of the Nth parameter.
parameter.<N>.description
The description of the Nth parameter.
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Chapter 11
[
289
]
Finally, to register the function with libformula, you need to create a
libformula.
properties
le at the root of the module JAR, and add the property
org.pentaho.
reporting.libraries.formula.functions.information.<Function Name>.
class
, which references the implemented
Formula
class, as well as
org.pentaho.
reporting.libraries.formula.functions.information.<Function Name>.
description
EvaluationException {
// throw an exception if the function doesn't have
// both parameters
if (parameters.getParameterCount() != 2) {
throw new EvaluationException(
LibFormulaErrorValue.ERROR_ARGUMENTS_VALUE);
}
final TypeRegistry typeRegistry = context.getTypeRegistry();
final String param1 = typeRegistry.convertToText(parameters.
getType(0), parameters.getValue(0));
final String param2 = typeRegistry.convertToText(parameters.
getType(1), parameters.getValue(1));
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Extending Pentaho Reporting
[
290
]
try {
// create a pattern based on the regex input
final Pattern p = Pattern.compile(param1);
final Matcher m = p.matcher(param2);
// find the first match in the string
m.find();
// return the first group found within the match
return new TypeValuePair(TextType.TYPE, m.group(1));
} catch (Exception e) {
// return the error message as the result
return new TypeValuePair(TextType.TYPE,
// make sure to call the super constructor, with
// the function name and the function resource bundle
super("REGEX", "Regex-Function");
}
// place this function in the user defined category
public FunctionCategory getCategory() {
return UserDefinedFunctionCategory.CATEGORY;
}
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Chapter 11
[
291
]
// this function requires two parameters,
// regex and input string
public int getParameterCount() {
return 2;
}
// both of the parameters are of type text
public Type getParameterType(int position) {
return TextType.TYPE;
}
// the output type is of type text
public Type getValueType() {
return TextType.TYPE;
}
// both parameters are required for execution
public boolean isParameterMandatory(int position) {
return true;
Launch the Report Designer, and use the earlier dened report example. Add an
Open Formula function with the following formula:
=REGEX("(\d{3}-\d{3}-\d{4})";[Field])
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Extending Pentaho Reporting
[
292
]
In the Details band, drag-and-drop the expression below the already dened
RegexExpression. Congratulations! Your new formula results should look
identical to the regex expression example dened earlier.
Implementing BeanShell expressions
Another approach to implementing your own report expressions is using the
BSHExpression report expression, which uses BeanShell to evaluate an expression.
BeanShell is a Java code interpreter, so you can write Java for direct execution
within a report with this expression. The BSHExpression contains a property called
expression, which should contain the necessary BeanShell script. This script must
contain the
Object getValue()
method, which is called to evaluate the expression.
Imports and additional functions may be included in the expression. The expression
also has access to the
DataRow
class instance named
dataRow
. This allows for easy
access to the current row of data for the expression to use.
Example BSHExpression
Open up the already dened
With Pentaho Reporting's API, it is possible to implement your own report elements.
In this section, you'll walk through the necessary steps to implement a basic report
element. You'll learn how to dene an
ElementType
, XML read and write handlers,
as well as all the metadata necessary to have your report element appear in the
Report Designer.
Dening an ElementType class
The rst step in dening a new report element is implementing the
ElementType
interface, located in the
org.pentaho.reporting.engine.classic.core.metadata
package
. This interface denes a set of methods that allow the creation and
rendering of an element within a report.
// the getMetaData method returns the element type's
// metadata, including the element name, attributes and styles
public ElementMetaData getMetaData();
// Inherited from the DataSource interface, the getValue
// method generates a renderable object that will appear
// in a report.
public Object getValue(final ExpressionRuntime runtime, final Element
element)
// the getDesignValue returns a design time rendering of the
// element. This is useful if you have an element
// without access to its source data.
public Object getDesignValue(ExpressionRuntime runtime, final Element
element);
// the configureDesignTimeDefaults method is used in
]
The Report Engine renders in multiple formats, and each format
handles the rendering of Java graphics differently. For instance,
PDF rendering, which uses iText, translates the rendered items
within the Graphics2D context into PDF elements.
In the
getValue
method, the current element instance is provided for access to its
attributes and styles. See the
org.pentaho.reporting.engine.classic.core.
Element
Javadoc API for details on how to access attributes and styles.
Dening element metadata
Now that you've dened the main
ElementType
class, you need to dene the metadata
to go along with the element. Element metadata is similar to expression metadata
dened earlier. The element should be dened in an XML le with a root node of
meta-data
, as a child XML element called
element
. This XML le should use the
namespace:
/>metadata/1.0
. The
element
XML element should contain the following attributes:
XML attribute Description
Name
The name of the element type.
Chapter 11
[
295
]
Attribute Description
Hidden
This attribute can be set to true or false. If true, the attribute
will not appear in the Report Designer.
Computed
This attribute can be set to true or false. If true, this attribute
will not be serialized.
design-time-value
If computed, and design-time-value is set, the attribute will
still be serialized.
Deprecated
This attribute can be set to true or false. This attribute is not
used at this time.
prefer-bulk
This attribute determines where in the element XML the
attribute is serialized.
value-type
The fully qualied Java class name of the attribute value.
value-role
The role this value is used for. This helps determine the type
of editor to display in the Report Designer. The following are
valid options—Value, Resource, ElementType, Query, Field,
and Group.
propertyEditor
Denes the java.beans.PropertyEditor class for
this attribute.
replaced-content
. See the
global-meta-elements.
xml
for additional groups.
Elements, along with their attributes and styles, may refer to resource bundles
for dening localized property information. For element properties, the
syntax is
element.<ELEMENT_NAME>.<PROPERTY>=<VALUE>
. For attributes
and styles, the syntax is
element.<ELEMENT_NAME>.attribute.<ATTRIBUTE_
NAMESPACE>.<ATTRIBUTE_NAME>.<PROPERTY>
. Localized properties for elements,
styles, and attributes include:
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Extending Pentaho Reporting
[
296
]
Property Description
display-name
The name displayed in the Report Designer.
grouping
The group in which the item appears.
grouping.ordinal
The dened group location relative to other groups.
ordinal
The ordinal location of this item related to other items
org.pentaho.reporting.
engine.classic.core.modules.parser.bundle.layout
package, and the
BundleElementWriteHandler
is located in the
org.pentaho.reporting.engine.
classic.core.modules.parser.bundle.writer
package. Luckily for us, the
reporting engine denes abstract classes that do most of the serialization work,
based on the metadata you dened for your element.
Read and write handlers are registered through the module's
configuration.
properties
le. A demonstration using the
AbstractElementReadHandler
and
the
AbstractElementWriteHandler
class is provided in the following section.
An example report element
This example will demonstrate what you just learned by walking through a full
implementation of a new
ElementType
, a star shape, and seeing it run within the
Report Designer.
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Chapter 11
[
297
// meta-elements.xml file
private transient ElementMetaData elementType;
// a default constructor
public StarType() {
}
// load the default metadata about the star element type
public ElementMetaData getMetaData() {
if (elementType == null) {
elementType = ElementTypeRegistry.getInstance().
getElementType("star");
}
return elementType;
}
// renders a star, using inner-percent, start-angle,
// and points as custom attributes
public Object getValue(final ExpressionRuntime runtime,
final Element element) {
if (element == null) {
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Extending Pentaho Reporting
[
298
]
throw new NullPointerException(
"Element must never be null.");
}
// read in the star's custom parameters
final float innerPercent = parseParam(element,
"inner-percent", 0.5f);
}
// move the star's points to 0,0 for
// appropriate rendering
if (minX > 0 || minY > 0) {
final Polygon p2 = new Polygon();
for (int i = 0; i < p.npoints; i++) {
p2.addPoint(p.xpoints[i] - minX, p.ypoints[i]
- minY);
This material is copyright and is licensed for the sole use by David Martone on 16th September 2009
710 South Avenue West, , Westfield, , 07090Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Chapter 11
[
299
]
}
return p2;
} else {
return p;
}
}
// returns the design time value of this element, rendered
// in the Report Designer
public Object getDesignValue(final ExpressionRuntime runtime,
final Element element) {
return getValue(runtime, element);
}
// this method is called when a star is first added to
// a report within the Report Designer. Set up
// the default values here.
public void configureDesignTimeDefaults(final Element element,
val = n.floatValue();
} else {
val = StringUtil.parseFloat(
String.valueOf(attrib),
defaultValue);
}
} else {
val = defaultValue;
}
return val;
}
// clone is required, because the reporting engine may
// create new instances of the StarType when new reports
// are rendered.
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Note that you've dened three custom attributes—
inner-percent
,
start-angle
,
and
points
. These three attributes combine to dene any shape of star the report
may need.
Now you need to dene the element metadata, dening the three custom attributes,
as well as including groups of attributes that are common across elements. Create a
<attribute namespace=" />pr4jd"
name="points"
mandatory="true"
hidden="false"
value-type="java.lang.Number"
value-role="Value"/>
<style-group-ref ref="borders"/>
<style-group-ref ref="common"/>
<style-group-ref ref="layout"/>
<style-group-ref ref="replaced-content"/>
</element>
</meta-data>
You must also dene a localized bundle that describes the element and its attributes.
Create a
metadata.properties
le in the
src
folder with the following content:
element.star.display-name=star
element.star.grouping=s
element.star.grouping.ordinal=1100
element.star.ordinal=98
element.star.description=
element.star.deprecated=
element.star.icon=star.png
element.star.attribute.pr4jd.inner-percent.display-name=inner-percent
element.star.attribute.pr4jd.inner-percent.grouping=star
element.star.attribute.pr4jd.inner-percent.grouping.ordinal=350
element.star.attribute.pr4jd.inner-percent.ordinal=10
element.star.attribute.pr4jd.inner-percent.description=
StarReadHandler.java
in the
src
folder, with the following contents:
import org.pentaho.reporting.engine.classic.core.modules.parser.
bundle.layout.elements.AbstractElementReadHandler;
import org.pentaho.reporting.libraries.xmlns.parser.ParseException;
// this class handles reading in of the star element
public class StarReadHandler extends AbstractElementReadHandler {
// all you need to do is pass the name of the element
// to the parent class
public StarReadHandler() throws ParseException {
super("star");
}
}
The
AbstractElementReadHandler
does the work of loading in all the report
element's attributes and styles. You now need to create the le
StarWriteHandler.
java
in the
src
folder with the following code:
import java.io.IOException;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.modules.parser.
bundle.writer.BundleWriterException;