扩展Pentaho Report Designer报表工具

前言:这里以一个例子来说明PRD报表工具强大的扩展功能。 我们用4种不同的方式来实现一个REGE()函数,其目的用来来提取想要的内容。需要传入两个参数,一个是原始字符串,一个是包含一个分组的正则表达式,表达式运算结果是正则表达式匹配的第一个分组。

相关说明:

        a)、示例数据库:Pentaho自带的SampleData

        b)、示例的SQL脚本:

SELECT
	"ORDERFACT"."PRODUCTCODE",
	"PRODUCTS"."PRODUCTNAME",
	"PRODUCTS"."PRODUCTLINE",
	SUM("ORDERFACT"."QUANTITYORDERED") AS QUANTITYORDERED,
	SUM("ORDERFACT"."TOTALPRICE") AS TOTALPRICE
FROM "ORDERFACT" INNER JOIN "PRODUCTS" ON "ORDERFACT"."PRODUCTCODE" ="PRODUCTS"."PRODUCTCODE"
GROUP BY 
	"PRODUCTS"."PRODUCTLINE","ORDERFACT"."PRODUCTCODE",
	"PRODUCTS"."PRODUCTNAME"
ORDER BY 
	"PRODUCTS"."PRODUCTLINE","ORDERFACT"."PRODUCTCODE",
	"PRODUCTS"."PRODUCTNAME"

     c)、关于如何创建一个PRD报表文件请参考链接文档

                             http://www.jianshu.com/p/e9b1762061fa

一、内置JAVA脚本

        这里用 BeanShell(BSH)来实现一个表达式。打开函数对话框,展开 Script 分组,选
择BeanShell(BSH)。
100652_BxzS_2453090.png

按下面设置 Expression 属性:

import java.util.regex.*;

Object getValue() {
	try{
		// 创建一个基于正则表达式的输入模式
		final Pattern pattern = Pattern.compile("S(\\d+)_.*");
		//final Matcher matcher = pattern.matcher(dataRow.get("PRODUCTCODE"));

		// 查找的字段,如果不为空,创建一个匹配
		final  Object object = dataRow.get("PRODUCTCODE");
		if( object == null ){
			return null;
		}
		final Matcher matcher = pattern.matcher(object.toString());
		// 在字符串中找到第一个匹配项
		matcher.find();
		// 返回第一组内找到匹配
		return matcher.group(1);
	}catch(Exception e){
		return e.getMessage();
	}
}

将上述代码添加到下图箭头所指处:

101239_6w1f_2453090.png

把函数拖入报表即可:

101436_aPog_2453090.png

展示样式:

101519_ncjp_2453090.png

二、内置JavaScript脚本

用 Javascript 来 实 现 。 选 择 Script 下 面 的 JavaScript 函 数 , 或 者 选 择 Bean-Scripting
Framework(BSF)并把表达式编程语言属性设置为 javascript。
Expression 属性按如下设置:

dataRow.get("PRODUCTCODE").match(/S(\d+)_.*/)[1]

方式一:

101857_eDEd_2453090.png

方式二:

101926_9ujt_2453090.png

把函数拖入报表即可:

102002_n1mo_2453090.png

展示样式:

102100_Lxzj_2453090.png

三、本地构建PRD源码的目录结构

        以上的2种方式为PRD报表工具内置的扩展功能的脚本化方式,不过通过这种方式实现的表达式仅限于当前报表有效,如果用户想要定义一套全局都有效的表达式该如何操作喃?辛运的是,Pentaho Reporting 有一套扩展系统的方式,能够无侵入性地增加功能。这主要是通过定义模块、加载模块的方式来实现的。

        接下来我们将PRD的源码从GitHub上Maven到本地,利用JAVA语言编写相应的功能模块,并将其update到报表工具中,进而扩展PRD的功能!

1、Maven项目到本地

    官方github地址:https://github.com/pentaho/pentaho-reporting

    小编github地址:https://github.com/TaoPengFei/pentaho-reporting

     (1)、 如果你使用IDEA自导的Maven的项目管理,请将settings.xml放置在你本地C盘/.m2目录下

     (2)、 如果你本地创建了Maven构件库,请将settings.xml放置在你Maven安装目录下

                    我的安装目录是:D:\Maven\apache-maven-3.3.9\conf

  • 用IDEA打开Maven项目 File Open -> pom.xml

2、报表引擎

Pentaho Reporting 包含几大部分:
 基础库(libraries)
 报表引擎核心(engine core)
        其依赖于基础库。代码是 org.pentaho.reporting.engine.classic.core.**
 报表扩展(extentions),包括各种数据源适配器,脚本支持等
 报表设计器(designer)

3、Libraries基础库

主要基础库的解释

3.1、libformula

        libformula是一个提供公式支持的基础库,包括:公式解析、公式运算以及一批预定义的表
达式和函数。公式的语法基于 OpenFormula 标准。

  • Function接口

        接口 Function 是 libformula 中所有预定义的函数实现的接口。 一个函数是一个任意的运算,
返回的值类型只有在函数运算完成后才可用。函数必须是无状态的,这意味着,使用完全相
同的参数调用相同的函数必须总是返回相同的计算结果。当需要自定义函数时,如果函数跟报表状态无关,并且内部不需要访问报表的当前数据行,那么可以选择实现 libformula 的 Function 接口。

Function 接口只有两个方法:


package org.pentaho.reporting.libraries.formula.function;

import org.pentaho.reporting.libraries.formula.EvaluationException;
import org.pentaho.reporting.libraries.formula.FormulaContext;
import org.pentaho.reporting.libraries.formula.lvalues.TypeValuePair;

import java.io.Serializable;

public interface Function extends Serializable {
  public String getCanonicalName();

  public TypeValuePair evaluate( FormulaContext context,
                                 ParameterCallback parameters )
    throws EvaluationException;
}
  • Expression 接口

        在报表引擎核心模块的 function 包有一个 Expression 接口,表示一个表达式。表达式不维护
状态,因此是轻量级的函数。表达式用于在报表的一行内计算值,可以用一个 dataRow 变
量来访问这个报表内当前行的其他字段、表达式或函数。

Expression 接口主要的方法有:

package org.pentaho.reporting.engine.classic.core.function;

import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.libraries.base.config.Configuration;
import java.io.Serializable;

public interface Expression extends Cloneable, Serializable {

  public String getName();
  public void setName( String name );
  public Object getValue();
  public boolean isActive();
  public DataRow getDataRow();
  public Object clone() throws CloneNotSupportedException;
  public int getDependencyLevel();
  public void setDependencyLevel( int level );
  public Expression getInstance();
  public ResourceBundleFactory getResourceBundleFactory();
  public Configuration getReportConfiguration();
  public void setRuntime( ExpressionRuntime runtime );
  public ExpressionRuntime getRuntime();
  public boolean isDeepTraversing();
  public boolean isPreserve();
}

4、实现SLEEP()函数

        现在自定义一个SLEEP()的函数。可以通过实现 LibFormula 库 Function 接口的方式来自定义函数。

132720_v5dU_2453090.png

4.1、定义函数分类类

用于创建新的Others分类。

package org.pentaho.reporting.libraries.formula.function.others;

import org.pentaho.reporting.libraries.formula.function.AbstractFunctionCategory;
import org.pentaho.reporting.libraries.formula.function.FunctionCategory;

/**
 * Created by 陶鹏飞 on 2017/3/6.
 */
public final class OthersFunctionCategory extends AbstractFunctionCategory {
    public static final FunctionCategory CATEGORY = new OthersFunctionCategory();

    private OthersFunctionCategory(){
        super("org.pentaho.reporting.libraries.formula.function.others.category");
    }
}

4.2、定义函数类

重点是实现 evaluate 方法。

package org.pentaho.reporting.libraries.formula.function.others;

import org.pentaho.reporting.libraries.formula.EvaluationException;
import org.pentaho.reporting.libraries.formula.FormulaContext;
import org.pentaho.reporting.libraries.formula.LibFormulaErrorValue;
import org.pentaho.reporting.libraries.formula.function.Function;
import org.pentaho.reporting.libraries.formula.function.ParameterCallback;
import org.pentaho.reporting.libraries.formula.lvalues.TypeValuePair;
import org.pentaho.reporting.libraries.formula.typing.Type;
import org.pentaho.reporting.libraries.formula.typing.coretypes.LogicalType;

/**
 * Created by 陶鹏飞 on 2017/3/3.
 */
public class SleepFunction implements Function {

    private  static  final long serialVersionUID = 4984027687466610131L;
    private  static  final TypeValuePair RETURN_INTERRUPTED = new TypeValuePair( LogicalType.TYPE, Boolean.FALSE );
    private  static  final TypeValuePair RETURN_UNINTERRUPTED = new TypeValuePair(LogicalType.TYPE,Boolean.TRUE);

    public SleepFunction(){
        // Constructor
    }

    @Override
    /* (non-Javadoc)
   * @see org.pentaho.reporting.libraries.formula.function.Function#getCanonicalName()
   */
    public String getCanonicalName() {
        return "SLEEP";
    }

    @Override
    /* (non-Javadoc)
   * @see org.pentaho.reporting.libraries.formula.function.Function#evaluate(org.pentaho.reporting.libraries.formula.FormulaContext, org.pentaho.reporting.libraries.formula.function.ParameterCallback)
   */
    public TypeValuePair evaluate(FormulaContext context, ParameterCallback parameters) throws EvaluationException {
        final  int parameterCount = parameters.getParameterCount();
        if ( parameterCount < 1 ){
            throw new EvaluationException(LibFormulaErrorValue.ERROR_ARGUMENTS_VALUE);
        }
        final Type type1 = parameters.getType(0);
        final Object value1 = parameters.getValue(0);
        final Number result = context.getTypeRegistry().convertToNumber(type1,value1);
        if ( result == null || (result.intValue() < 0) ){
            throw new EvaluationException(LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
        }
        Boolean  sleepInterrupted = false;
        // The number should be the number of milliseconds to sleep
        try {
            Thread.sleep(result.intValue());
        } catch (InterruptedException e) {
            //e.printStackTrace();
            sleepInterrupted = true;
        }
        // Whether the function was interrupted or completed.
        return sleepInterrupted ? RETURN_INTERRUPTED : RETURN_UNINTERRUPTED;
    }

}

4.3、定义函数描述类

需要为函数定义一个函数描述类。需要调用父构造函数的方法,以加载资源。

package org.pentaho.reporting.libraries.formula.function.others;

import org.pentaho.reporting.libraries.formula.function.AbstractFunctionDescription;
import org.pentaho.reporting.libraries.formula.function.FunctionCategory;
import org.pentaho.reporting.libraries.formula.function.information.InformationFunctionCategory;
import org.pentaho.reporting.libraries.formula.typing.Type;
import org.pentaho.reporting.libraries.formula.typing.coretypes.LogicalType;
import org.pentaho.reporting.libraries.formula.typing.coretypes.NumberType;

/**
 * Created by 陶鹏飞 on 2017/3/3.
 */
public class SleepFunctionDescription extends AbstractFunctionDescription {

    private static  final long serialVersionUID = 2368106667495213328L;

    public SleepFunctionDescription() {
        super("SLEEP", "org.pentaho.reporting.libraries.formula.function.others.Sleep-Function");
    }

    @Override
    public Type getValueType() {
        return LogicalType.TYPE;
    }

    @Override
    public FunctionCategory getCategory() {
        return OthersFunctionCategory.CATEGORY;
    }

    @Override
    public int getParameterCount() {
        return 1;       // 1 Parameter - number of milliseconds
    }

    /**
     * Returns the parameter type at the given position using the function metadata. The first parameter is at the
     * position 0;
     *
     * @param position The parameter index.
     * @return The parameter type.
     */
    @Override
    public Type getParameterType(int position) {
        return NumberType.GENERIC_NUMBER;
    }

    /**
     * Defines, whether the parameter at the given position is mandatory. A mandatory parameter must be filled in, while
     * optional parameters need not to be filled in.
     *
     * @param position
     * @return
     */
    @Override
    public boolean isParameterMandatory(int position) {
        return true;
    }
}

4.4、定义资源文件

需要定义一个资源文件用于界面显示。

  • Others分类资源文件
#
# Created by 陶鹏飞 on 2017/3/6.
#
display-name=Others
description=Contains some user-defined statistical functions.
  • 说明函数资源文件(国际化)
#
# Created by 陶鹏飞 on 2017/3/6.
#
display-name=SLEEP
description=Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds.
parameter.0.display-name=Number
parameter.0.description=The number of milliseconds to sleep (positive number only).

 

#
# Created by 陶鹏飞 on 2017/3/6.
#
display-name=SLEEP
description=\u4f7f\u5f53\u524d\u6267\u884c\u7684\u7ebf\u7a0b\u4e3a\u6307\u5b9a\u7684\u6beb\u79d2\u6570\u4f11\u7720\uff08\u6682\u65f6\u505c\u6b62\u6267\u884c\uff09.
parameter.0.display-name=Number
parameter.0.description=\u7761\u7720\u7684\u6beb\u79d2\u6570\uff08\u4ec5\u6b63\u6570).

4.5、注册函数

为了把函数注册到 libformula 模块,需要增加一个名为 libformula.properties 的属性文件,内容如下:

##
# Others functions
org.pentaho.reporting.libraries.formula.functions.others.Sleep.class=org.pentaho.reporting.libraries.formula.function.others.SleepFunction
org.pentaho.reporting.libraries.formula.functions.others.Sleep.description=org.pentaho.reporting.libraries.formula.function.others.SleepFunctionDescription

4.6、使用及效果

利用IDEA将libformula文件夹编译成libformula-6.1.0.1-196.jar包,并替换PRD报表开发工具目录D:\prd-ce-6.1.0.1-196\report-designer\lib下的libformula-6.1.0.1-196.jar包,重启 PRD。

效果展示:

 

四、在步骤三的基础上实现REGE()函数

        现在根据步骤三自定义一个REGE()函数。因为不需要访问报表状态和数据行,所以可以通过
实现 LibFormula 库 Function 接口的方式来自定义函数。我们将REGE()放置在Others分类下,所以不需要在创建定义函数分类的JAVA类了。

4.1、定义函数类

首先要实现一个函数类,这里是 RegexFunction。重点是实现 evaluate 方法。

package org.pentaho.reporting.libraries.formula.function.others;

import org.pentaho.reporting.libraries.formula.EvaluationException;
import org.pentaho.reporting.libraries.formula.FormulaContext;
import org.pentaho.reporting.libraries.formula.LibFormulaErrorValue;
import org.pentaho.reporting.libraries.formula.function.Function;
import org.pentaho.reporting.libraries.formula.function.ParameterCallback;
import org.pentaho.reporting.libraries.formula.lvalues.TypeValuePair;
import org.pentaho.reporting.libraries.formula.typing.TypeRegistry;
import org.pentaho.reporting.libraries.formula.typing.coretypes.TextType;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by 陶鹏飞 on 2017/3/3.
 */
public class RegexFunction implements Function{
    @Override
    public String getCanonicalName() {
        return "REGEX";
    }

    @Override
    public TypeValuePair evaluate(FormulaContext context, ParameterCallback parameters) throws EvaluationException {
        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) );
        try {
            final Pattern pattern = Pattern.compile(param1);
            final Matcher matcher = pattern.matcher(param2);
            matcher.find();
            return new TypeValuePair(TextType.TYPE, matcher.group(1));
        }catch (Exception e){
            return new TypeValuePair(TextType.TYPE,e.getMessage());
        }

    }
}

4.2、定义函数描述类

需要为函数定义一个函数描述类。需要调用父构造函数的方法,以加载资源。

package org.pentaho.reporting.libraries.formula.function.others;

import org.pentaho.reporting.libraries.formula.function.AbstractFunctionDescription;
import org.pentaho.reporting.libraries.formula.function.FunctionCategory;
import org.pentaho.reporting.libraries.formula.typing.Type;
import org.pentaho.reporting.libraries.formula.typing.coretypes.LogicalType;
import org.pentaho.reporting.libraries.formula.typing.coretypes.TextType;

/**
 * Created by 陶鹏飞 on 2017/3/3.
 */
public class RegexFunctionDescription extends AbstractFunctionDescription{

    public RegexFunctionDescription(){
        //确保调用父构造函数,带上函数名和函数的资源包名
        super("REGEX","org.pentaho.reporting.libraries.formula.function.others.Regex-Function");
    }

    //把函数放到Others分类中
    @Override
    public FunctionCategory getCategory() {
        return OthersFunctionCategory.CATEGORY;
    }

    //函数返回2个参数
    @Override
    public int getParameterCount() {
        return 2;
    }

    /**
     * Returns the parameter type at the given position using the function metadata. The first parameter is at the
     * position 0;
     *
     * @param position The parameter index.
     * @return The parameter type.
     */
    @Override
    public Type getParameterType(int position) {
        return TextType.TYPE;
    }

    @Override
    public Type getValueType() {
        return LogicalType.TYPE;
    }

    /**
     * Defines, whether the parameter at the given position is mandatory. A mandatory parameter must be filled in, while
     * optional parameters need not to be filled in.
     *
     * @param position
     * @return
     */
    @Override
    public boolean isParameterMandatory(int position) {
        return true;
    }
}

4.3、资源文件(国际化)

需要定义一个资源文件用于界面显示。这里叫 Regex-Function.properties。

#
# Created by 陶鹏飞 on 2017/3/6.
#
display-name=REGEX
description=Executes a regular expression on a string, returning the first found group
parameter.0.display-name=Regular Expression
parameter.0.description=A Java Regular Expression string, with a grouping defined within the string.
parameter.1.display-name=String Input
parameter.1.description=A string to parse.
#
# Created by 陶鹏飞 on 2017/3/6.
#
display-name=REGEX
description=\u5728\u5b57\u7b26\u4e32\u4e0a\u6267\u884c\u6b63\u5219\u8868\u8fbe\u5f0f\uff0c\u8fd4\u56de\u7b2c\u4e00\u4e2a\u5df2\u627e\u5230\u7684\u7ec4\u3002
parameter.0.display-name=Regular Expression
parameter.0.description=\u4e00\u4e2ajava\u6b63\u5219\u8868\u8fbe\u5f0f\u7684\u5b57\u7b26\u4e32\uff0c\u7528\u5b57\u7b26\u4e32\u4e2d\u5b9a\u4e49\u7684\u5206\u7ec4\u3002
parameter.1.display-name=String Input
parameter.1.description=\u89e3\u6790\u5b57\u7b26\u4e32\u3002

4.4、注册函数

为了把函数注册到 libformula 模块,需要增加一个名为 libformula.properties 的属性文件,内容如下:

##
# Others functions
org.pentaho.reporting.libraries.formula.functions.others.Sleep.class=org.pentaho.reporting.libraries.formula.function.others.SleepFunction
org.pentaho.reporting.libraries.formula.functions.others.Sleep.description=org.pentaho.reporting.libraries.formula.function.others.SleepFunctionDescription

org.pentaho.reporting.libraries.formula.functions.others.Regex.class=org.pentaho.reporting.libraries.formula.function.others.RegexFunction
org.pentaho.reporting.libraries.formula.functions.others.Regex.description=org.pentaho.reporting.libraries.formula.function.others.RegexFunctionDescription

4.5、使用及效果

134833_aLKT_2453090.png

134908_LSJT_2453090.png

134940_PTDC_2453090.png

五、在步骤三的基础上实现REGE()表达式

、、、

六、相关资源下载

转载于:https://my.oschina.net/TaoPengFeiBlog/blog/854017

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值