Formula Expander Classes - Formula Functions
It is possible to use your own functions in a formula of a report, if these functions are implemented as methods in public (formula expander) Java class(es). To use such "User Defined Functions" in a formula, the following steps are required:
The best practice case is to write a plugin and to register your classes with the marker interface com.inet.report.formula.UserDefinedFunction
in the ServerPluginManager
. Details on writing a plugin can be found in the chapter Plugins. The following example shows how to register a custom formula expander class.
import com.inet.plugin.*; import com.inet.userdefined.formular.MyFunction; ... public void registerExtension( ServerPluginManager spm ) { if( spm.isPluginLoaded( "reporting" ) ) { spm.register( com.inet.report.formula.UserDefinedFunction.class, new MyFunction() ); } }
Alternatively you can use a regular jar file:
-
Copy this jar file into the "lib" directory in the installation directory of i-net Clear Reports.
-
Open the Configuration Manager and add the
<classname>
to the property 'Formula Expander Class(es)', whereclassname
means the fully qualified name of your class. This property can as well be a semicolon separated list of class names. Please check the classpath used by i-net Clear Reports and the package structure in caseClassNotFoundException
occurs at runtime. Alternatively you can use the API to modify the configuration in use.
Now you can write a formula using your user-defined functions by calling them by name. You can create a formula using i-net Designer or i-net Clear Reports API (RDC).
Note: The methods in the specified class have to be public and could be static. In case of non-static methods, a default Constructor without parameters is required.
Note: The return value and the parameter values of the methods have to be one of the following types:
-
com.inet.report.FormulaRange
-
java.lang.Number
-
java.lang.Boolean
-
java.lang.String
-
java.sql.Date
-
java.sql.Time
-
java.sql.Timestamp
-
Object[] of these types
As of version 13 it is possible to use one or more of the following hidden parameters as method parameter in the formula expander class: Engine
, HttpSession
and HttpServletRequest
. This can be useful, for example, to use the Engine API to get user-defined parameters from the report engine. Please note that HttpServletRequest
does not work in the i-net Designer.
Formula Optimization
By default, all user-defined functions with static or no parameters are always called once to optimize the internal formula tree. This allows users to use the user-defined function in the record selection, e.g. for i18n - which is stable per locale.
However, the downside of running the user-defined formula is, that statements that should only be called once and only by when actually running the report for a user, e.g. sending an email, also get triggered.
To mitigate this issue, you can use the annotation @DoNotOptimize
. Using this annotation, the user-defined function is only run when actually hit in a formula of the report. The downside of using the annotation is, that you can't use this user-defined function in a record selection formula for filtering report data anymore.
Examples
static public String aFunction(String str) { return "[" + str + "]"; } static public String getUserProperty(String propKey, Engine engine) { Properties properties; String userProperty = ""; try { properties = engine.getUserProperties(); if (properties != null) { userProperty = (String)properties.get(propertyKey); } } catch( ReportException e ) { e.printStackTrace(); } return userProperty; } static public Number aFunction(Number d) { return new Double( 2.0 * d.doubleValue()); } static public String aFunctionToo( Object obj) { if (obj instanceof String) { return ((String) obj) + " Ha"; } else { return null; } } static public Number aFunction(Number i) { return new Integer(i.intValue() * 2); } public Object[] aFunction(Object[] i){ Object[] o = new Object[i.length]; for (int k=0;k<i.length;k++) { o[k] = i[i.length-k-1]; } return o; } @DoNotOptimize public Boolean aFunction( Number i, FormulaRange range ){ double lower = ((Number)range.getFrom()).doubleValue(); double upper = ((Number)range.getTo()).doubleValue(); double n = i.doubleValue(); if( range.isLowLimitIncluded() ? n < lower : n <= lower ){ return Boolean.FALSE; } if( range.isHighLimitIncluded() ? n > upper : n >= upper ){ return Boolean.FALSE; } return Boolean.TRUE; } } // Note: This function should do a type check on the range bounds first.
You can find more samples in the Java code samples.