自定义函数用法介绍
◆ 背景说明:
润乾报表提供了大量的内置函数,但是再多的函数也无法完全满足五花八门的业务需求,用户往往需要自定义函数。
自定义函数的标准写法是:继承润乾报表提供的Function类或者DSFunction类,实现其中的calculate方法,并返回运算结果。
自定义函数的登记:在java的类路径的config目录下,找到customFunctions.properties文件(如果没有需要自己创建),并在其中进行自定义函数类及函数名的登记。customFunctions.properties的位置路径如下图所示:
◆ 代码示例1:
import com.raqsoft.report.model.expression.Function; //普通函数抽象类
import com.raqsoft.report.usermodel.Context;
public class SimFunction extends Function { //继承普通函数抽象类
public Object calculate(Context ctx) { //标准接口,ctx为运算环境
Object value=null;
…… //函数主体代码
return value;
}
}
◆ 代码示例2:
import com.raqsoft.report.model.expression.DSFunction; //数据集函数抽象类
import com.raqsoft.report.usermodel.Context;
public class DsFun extends DSFunction { //继承数据集函数抽象类
public Object calculate(Context ctx) { //标准接口,ctx为运算环境
Object value=null;
…… //函数主体代码
return value;
}
}
◆ customFunctions.properties示例:
//0代表普通函数,1代表数据集函数
sim=0,com.raqsoft.report.selfdefine.SimFunction
dsfun=1,com.raqsoft.report.selfdefine.DsFun
参考文件:customFunctions.properties
普通函数
◆ 背景说明:
普通函数的逻辑比较简单,一般是传入参数,然后根据业务逻辑,对传入的参数进行各种运算,最后返回结果值对象。
常用的方法是:接受函数的传入参数表达式,判断参数个数,计算参数表达式,判断结果参数值的数据类型,进行业务逻辑运算,返回运算结果值
可能比较复杂的情况:需要获取系统数据源,和数据库进行交互
◆ 单参数代码示例:
import com.scudata.common.PwdUtils;
import com.raqsoft.common.ReportError;
import com.raqsoft.report.model.expression.Expression;
import com.raqsoft.report.model.expression.Function;
import com.raqsoft.report.model.expression.Variant2;
import com.raqsoft.report.usermodel.Context;
public class Encrypt extends Function {
public Object calculate(Context ctx) {
// 判断参数是否为空
if (this.param == null) {
throw new ReportError("参数列表不能为空");
}
//参数是否为单值
if (this.param.isLeaf()){
//取得第一个参数,默认为表达式,需要把该表达式算出来,结果才是函数的参数值
Expression param1=(Expression)this. param.getLeafExpression();
//算出第一个参数值
Object result1 = Variant2.getValue(param1.calculate(ctx), false);
//判断第一个参数值是否为空
if (result1 == null) {
return null;
}
//判断第一个参数值的数据类型
if (! (result1 instanceof String)) {
MessageManager mm = EngineMessage.get();
throw new ReportError("参数类型错误");
// 对第一个参数进行加密,并返回加密结果
String value = PwdUtils.encrypt((String) result1);
return value;
}else{
throw new ReportError("参数无效");
}
}
}
}
◆ 多参数代码示例:
import com.scudata.common.MessageManager;
import com.raqsoft.common.ReportError;
import com.raqsoft.report.model.expression.Expression;
import com.raqsoft.report.model.expression.Function;
import com.raqsoft.report.model.expression.Variant2;
import com.raqsoft.report.resources.EngineMessage;
import com.raqsoft.report.usermodel.Context;
public class SignFunc extends Function {
public Object calculate(Context ctx) {
if (this.param == null || this.param.getSubSize() ==0) { // 判断参数是否为空
MessageManager mm = EngineMessage.get();
throw new ReportError(“encrypt:” + mm.getMessage(“function.invalidParam”));
}
// 获取第一个参数的表达式,从 0 开始
Expression param1=(Expression)this.param.getSub(0).getLeafExpression();
// 算出第一个参数值
Object result1 = Variant2.getValue(param1.calculate(ctx),false);
/*
* 当存在多个时,参考以下代码。 this.param.getSubSize() 可以获取到参数的总个数,可以将下面的示例改为遍历取值
**/
// 获取第二个参数的表达式
Expression param2=(Expression)this.param.getSub(1).getLeafExpression();
// 算出第二个参数值
Object result2 = Variant2.getValue(param2.calculate(ctx),false);
/*
*….., 当多余 2 个时,按照上面代码以此类推,可定义为遍历方式
**/
return result1.toString();
}
}
◆ 获取数据源代码示例:
import …… //引入相关类包
public Object calculate(Context ctx) {
IConnectionFactory conFactory = null; //数据连接工厂
Connection cn = null; //数据库连接对象
try {
String dbName = ctx.getDefDataSourceName(); //获取系统数据源名称
DataSourceConfig dsc = ctx.getDataSourceConfig(dbName); //获取系统数据源配置
cn = ctx.getConnection(dbName); //获取数据库连接对象
if (cn == null ) {
conFactory = ctx.getConnectionFactory(dbName); //未取到数据库连接,取同名数据连接工厂
if (conFactory == null){ //无法获取正确数据库连接,返回错误信息
MessageManager mm = DataSetMessage.get();
throw new ReportError(mm.getMessage("error.noConnection", dbName, dbName));
}
cn = conFactory.getConnection();
}
if (cn == null || cn.isClosed()) { //无法获取正确数据库连接,返回错误信息
MessageManager mm = EngineMessage.get();
throw new ReportError("query:" + mm.getMessage("function.noConnetion"));
}
String dbCharset = dsc.getDBCharset(); //获取数据库字符集
if (dbCharset == null) dbCharset = "GBK";
String clientCharset = dsc.getClientCharset(); //获取本地字符集
if (clientCharset == null) clientCharset = "GBK";
…… //其它函数主体代码
return …… //返回函数结果值
}
参考文件:Encrypt.java、Query.java
数据集运算函数
◆ 背景说明:
数据集函数除了涉及到2.4.1.2中介绍的参数输入输出以外,最重要的是还涉及了数据集的调用与访问,本小节重点介绍如何在自定义函数中访问数据集。
◆ 代码示例:
public Object calculate(Context ctx) {
//…… //获取参数,以及对参数的合法性进行各种判断,参见2.4.1.2
boolean isRoot=true; //是否根集
Group group = null;
if (!isRoot) { //获取当前行集,其中isRoot表示是否根集
DsValue cur = this.ds.getCurrent(); //当前行集
if (cur instanceof Row) {
( (Row) cur).setValue(selectExp.calculate(ctx));
return cur;
}
group = (Group) cur;
}
else {
group = this.ds.getRootGroup(); //获取根集
}
Expression filterExp=……; //过滤表达式
Expression sortExp=……; //排序表达式
Expression selectExp=……; //取出表达式
group = group.filter(filterExp, ctx); //对当前行集过滤
group = group.sort(sortExp, true, ctx); //对当前行集排序
int count = group.getRowCount(); //获得当前行集的记录数
Object result=null; //用于存放最大值的变量
Row retRow=null; //用于存放最大值对应的记录行对象
for (int i = 0; i < count; i++) { //逐行获取表达式值,并做相应业务处理
Row row = group.getRow(i);
Object v = Variant2.getSingleValue(row.evaluate(selectExp, ctx), false);
//…… //做相应的业务处理,例如求最大值等
}
if (result != null) { //将求得的最大值放进最大值对应的行对象并返回
retRow.setValue(result);
return retRow;
}
return ds.getNullRow();
}
参考文件:DSMax.java