自定义函数

阅读(1758) 标签: function, dsfunction, customfunctions,

自定义函数用法介绍

背景说明:

润乾报表提供了大量的内置函数,但是再多的函数也无法完全满足五花八门的业务需求,用户往往需要自定义函数。

自定义函数的标准写法是:继承润乾报表提供的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.javaQuery.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