报表扩展类接口

在UniEAP Report现有的功能不能满足您的开发需求时,您可以在不改变系统结构的情况下,通过实现扩展类接口和配置文件来实现公式的扩展、系统变量的扩展。

1 接口简介

可扩展类接口如下:

  • 扩展自定义公式:com.neusoft.report.engine.formula.Formula.Executer接口;

  • 为系统变量赋值:com.neusoft.report.engine.formula.variable.Variant接口;

  • API数据源:com.neusoft.report.engine.dataset.executer.api.APIDataEntity接口;

2 实现步骤

从这个版本开始,报表支持以下三个配置文件的独立配置:report-formula.xml,report-system-variable.xml,report-engine-config.properties。

建议用户不再直接修改报表本身的配置文件,而是指定一个保存独立配置的目录,对以上三个文件中相关配置的的更改在该目录下进行。当报表服务启动时,会对配置文件进行合并,扩展的配置会覆盖报表本身的默认配置。

这样做的好处是升级时更方便,已改动的配置、自定义的公式、系统变量等不会被覆盖掉。例如,使用平台集成版本时,可以把独立配置目录建在平台的Patch工程中,升级报表时只需删除原有report patch工程,再导入新版工程即可,改动的配置会自动合并。

配置方式:在web.xml中修改上下文参数report.customconfigroot的值,指向独立的配置目录。该参数有一个默认设置:

<!—用户独立配置文件所在目录,也可改成其他已存在的磁盘目录,如C:/report/conf -->
<context-param>
    <param-name>report.customconfigroot</param-name>
    <param-value>WEB-INF/conf/unieap/report/ext/</param-value>
</context-param>

工程的WEB-INF/conf/unieap/report/ext/目录下预先放置了空的配置文件。如果不更改独立配置目录,扩展时直接修改这里的配置文件即可。

【注意】下文中使用“..ext”代表独立配置路径。

2.1 扩展自定义公式步骤

当UniEAP Report内置的公式不能满足您的需求时,UniEAP Report允许对公式扩展。增加自定义公式只要实现FormulaExecuter接口, 并在..ext/report-formula.xml中添加函数的声明。

接口的定义如下:

package com.neusoft.report.engine.formula;
import com.neusoft.report.engine.util.DataValue;
public interface FormulaExecuter {
public DataValue compute(Calculater calculater, DataValue[] parameters);
}

FormulaExecuter接口中只包含一个compute方法,该方法有两个参数:

其中Calculater是执行函数的句柄,是为了支持在某个函数中需要执行其它函数而设计的;第二个参数是使用公式时传入参数的数组,是DataValue类型,DataValue是UniEAP? Report对支持的数据类型的包装,可以是基本数据类型,也可以是复杂的数据结构类型。

下面以Sum公式为例详细介绍扩展公式的方法。

1.编写SumFunc类用来进行Sum的计算,代码如下:

package com.neusoft.report.engine.formula.function.aggrefunc;
import java.util.Iterator;
import com.neusoft.report.engine.formula.Calculater;
import com.neusoft.report.engine.formula.FormulaExecuter;
import com.neusoft.report.engine.util.DataValue;
import com.neusoft.report.engine.util.datavalue.CollectionDataValue;
import com.neusoft.report.engine.util.datavalue.NullDataValue;
public class SumFunc implements FormulaExecuter {
public DataValue compute(Calculater calculater, DataValue[] parameters) {
    int type = parameters[0].getType();
    if (type == DataValue.NULL) {
        return NullDataValue.newInstance();
    }
    CollectionDataValue colldv = (CollectionDataValue) parameters[0];
    Iterator iter = colldv.iteratorValue();
    DataValue dv = null;
    double v = 0.0;
    double value = 0;
    while (iter.hasNext()) {
        dv = (DataValue) iter.next();
        v = ((DecimalDataValue)dv.getDataValue()).doubleValue();
        value += v;
    }
    return new DecimalDataValue(value);
    }

2.配置..ext/report-formula.xml文件

report-formula.xml是UniEAP Report的函数配置文件,所有UniEAP Report支持的函数都在这里定义。下面是Sum函数的配置信息:

<Formula Category="集合函数"
ClassImpl="com.neusoft.report.engine.formula.function.aggrefunc.SumFunc"
Desc="统计数据集合中的所有值的和" 
ReturnType="数据集合的和" 
Name="Sum" 
Expression="Sum(collection_value)">
<Parameter 
Type="数字类型" 
Desc="一组数据的集合,集合中的值都是数字类型。" 
Index="0"
Name="Collection_value"/>
</Formula>

Formula节点是对公式的描述信息,Parameter是对公式中参数的描述。Formula中最关键的两个属性是Name和ClassImpl,其中Name是在定义报表时引用公式的名称(如Sum),由英文字母开头、英文字母和数字构成且大小写不敏感;ClassImpl是公式具体的实现类。其他的属性用于在报表设计器中使用,含义如下:

  • Category:公式的分类,表示公式属于哪类,如Sum属于集合公式;

  • Desc:对公式描述;

  • ReturnType:公式返回值的描述;

  • Expression:公式的写法。

    Parameter中的属性的含义如下:

  • (Type:参数的数据类型;

  • Desc:参数的描述;

  • Index:参数的索引,就是该参数是公式中的的第几个参数,从0开始;

  • Name:参数的名称。

    【注意】为了系统升级的方便,建议不要更改UniEAP Report提供的标准函数的函数名。

2.2 为系统变量赋值步骤

系统变量是用于支持不需要用户在创建报表时输入、但对创建报表有限定作用的信息,典型的系统变量如报表创建时间、当前登陆用户ID。系统变量是参数的扩展,通过实现变量接口返回变量值。

首先要实现Variant接口,接口的定义如下:

package com.neusoft.report.engine.formula.variable;
import java.util.Map;
import com.neusoft.report.engine.util.DataValue;
public interface Variant {
    public static final String CONTEXT_UID = "UID";
    public static final String CONTEXT_RID = "RID";
public static final String CONTEXT_HTTP_REQUEST = "HTTP_REQUEST";
        public DataValue getValue(Map context);
}

Variant接口中只包含一个方法,传入的是Map类型的集合对象,它是变量的上下文参数,包括当前用户ID、报表模板ID以及HttpServletRequest对象,如下:

  • CONTEXT_UID:当前用户ID;

  • CONTEXT_RID:报表模板ID;

  • CONTEXT_HTTP_REQUEST:javax.servlet.http.HttpServletRequest对象。

    【注意】javax.servlet.http.HttpServletRequest对象是UniEAP? Report 3.2.1版本后新增的上下文参数,使用这个对象可以简化一些系统变量的实现方式。但仅对使用ReportEngine接口public Report createReport(String rid, Map parameters, Map context)创建的报表有效,参见2.3.2节“关于ReportEngine接口的介绍”。

    下面以取得用户ID的实现为例介绍为系统变量赋值的步骤:

    1.实现Variant接口,代码如下:

import java.util.Map;
import com.neusoft.report.engine.util.DataValue;
import com.neusoft.report.engine.util.datavalue.StringDataValue;
public class UID implements Variant {
public DataValue getValue(Map context) {
    Object uid = context.get(Variant.CONTEXT_UID);
    return new StringDataValue((String)uid);
    }
}

2.配置..ext/report-system-variable.xml文件

增加一个变量,只需要在system_variable元素下增加一个variable元素,如下代码所示:

<system-variable>
<variable>
<name>UID</name>
<caption>用户id</caption>
<className>com.neusoft.report.engine.formula.variable.UID</className>
<dataType>1</dataType>
</variable>
……

Variable下节点说明如下:

  • name:表示这个系统变量的名称,用英文字母表示;

  • caption:表示这个系统变量的描述信息,可以是中文;

  • className:表示变量接口的实现类;

  • dataType:表示返回值的数据类型。dataType取值与返回值类型的对应关系如下:

    表格 5 dataType取值

返回值的数据类型 dataType值
String 1
Boolean 2
Integer 3
Decimal 4
DateTime 5
Picture 7
Collection 10

【注意】UniEAP Report中已经提供了三个默认系统变量 currentTime、UID、RID。

2.3 API数据源的实现步骤

API数据源即自定义数据源,是UniEAP Report产品中数据源的的扩展方式之一。用户通过实现约定的接口,自己写代码来获取或生成数据,理论上可以让任意来源的数据为报表所用。例如,编写查询数据库的代码,返回相应的数据,就可以替代SQL数据源、存储过程数据源。

API数据源的实现步骤如下:

1.实现com.neusoft.report.engine.dataset.executer.api. APIDataEntity接口,该接口又继承com.neusoft.report.engine.dataset.datasource. DataEntity接口。要实现的方法列表如下。

// 查询满足条件的数据
public void query(Map params, Map context);
// 如果数据实体中还有更多数据,则返回true,否则返回false。
public boolean hasNext();
// 返回数据实现中的下一个数据项
public Object[] next();
// 返回数据实体中数据项的个数
public int size();
// 返回数据实体的名称
public String getName();
// 返回对于数据实体的描述信息
public String getComments();
// 返回数据实体中所有域(字段)的名字集合
public String[] getFieldNames();
// 根据域名返回数据实体中的某个域
public Field getField(String name);
// 返回数据实体中所有域的集合
public Field[] getFields();
// 返回数据实体中域的个数
public int fieldSize();
// 返回数据实体的分类信息,例如直接返回”api”
public String getCategory();

几点说明:

a) com.neusoft.report.engine.dataset.datasource.Field,类似于关系型数据库中列的概念,是对列信息的描述,包含字段名称、字段类型和字段描述。本身是一个接口,其实现类的定义如下:

public class FieldImpl implements Serializable, Field {

    private static final long serialVersionUID = 1308755359669685961L;

    /** 字段名称 */
    private String name;

    /** 字段类型 */
    private byte type;    

    /** 字段Comments */
    private String comments;

    public FieldImpl(String name, byte type, String comments ){
        this.name = name;
        this.type = type;
        this.comments = comments;
    }

    public String getName() {
        return this.name;
    }

    public byte getType() {
        return this.type;
    }

    public String getComments(){
        return this.comments;
    }

}

type字段的取值可以使用com.neusoft.report.commonbase.datavalue.DataValue中的常量,以下为几种常用类型:

public static final byte STRING    = 1;
public static final byte BOOLEAN   = 2;
public static final byte INTEGER   = 3;
public static final byte DECIMAL   = 4;
public static final byte DATETIME  = 5;

b) next()方法的返回值Object数组。相当于关系型数据库表中的一行,数组中的每个值分别对应一个字段(Field)。c) query()方法,它的params参数包含了各报表参数、系统变量的值,context参数包含了一些上下文信息,如报表id、用户id、本次访问报表的HTTP请求等。

在query()方法中获取报表参数的值,可以有两种方法。

(1) 通过params参数获取:

// 获取整型参数type的值
int type = DataValueUtil.intValue(((DataValue) params.get("type")));
// 获取字符串参数id的值
String id = DataValueUtil.stringValue(((DataValue) params.get("id")));

(2) 通过context获取HTTP请求对象,再取参数值。除报表参数外,这种方法也可用于获取其他的请求参数:

// 获取HTTP请求对象
HttpServletRequest req = (HttpServletRequest) context.get("HTTP_REQUEST");
// 获取整型参数type的值
int type = Integer.valueOf(req.getParameter("type"));

2.在\WEB-INF\conf\unieap\report\datasource.xml文件中添加API数据源的配置,例如:

<Category Name="api" Caption="自定义数据源"
 SourceClass="com.neusoft.unieap.dataset.datasource.impl.APIDataSource"
QuerierClass="com.neusoft.unieap.dataset.executer.api.APIDataSetExecuter">
    <DataSource Name="sample API" Caption="API 样例库">
        <DataEntity Name="APIDataSetSample" Caption="products">
            <Parameters>
                <Param Name="classname" Value='com.neusoft.unieap.dataset.executer.api.APIDataSetSample />
            </Parameters>
        </DataEntity>
    </DataSource>
</Category>

其中APIDataSetSample是自定义数据源的实现类,DataEntity的Caption属性可以取任意有一定意义的值。

2.4 新图表主题映射的实现步骤

在设计器端进行新图表制作时,可以在图表公共属性设置页面选择主题,系统会有默认的主题映射过程,将当前图表应用所选主题。如果默认的主题应用不满足用户的需求,用户可以实现ThemeMapping接口,来实现自定义映射。

新图表主题映射的实现步骤如下:

1.实现com.neusoft.unieap.workshop.report.editor.grid. parts.editor.dataviz.util. ThemeMapping接口,该接口要实现的如下方法:

/**
     * 自定义主题映射需要实现本接口
     * @param theme 主题
     * @param graphJSON 图表
     * @return void
     */
public void setTheme(JSONObject theme, JSONObject graphJSON);

2.5 JavaBean数据源的实现步骤

从V5.0版本开始,UniEAP Report支持JavaBean数据源,即使用JavaBean对象作为数据来源。与API数据源类似,用户通过实现特定的接口,可以自由决定数据的获取或生成方式,让报表使用的数据可以有任意的来源,例如Web Service、其他应用程序、NoSQL数据库等等。

相比于API数据源,JavaBean数据源的实现要简单不少,主要是因为要实现的接口方法大大减少了。但要求是作为数据源的对象必须是标准的JavaBean,具体来说,要满足以下几点要求:

  • 类必须是具体的和公共的。

  • 具有一个无参数的构造方法。

  • 属性必须私有化。

  • 私有属性必须通过公共的get和set方法暴露给其他程序,并且方法命名要遵守一定的命名规范。

2.5.1 实现步骤

下面举例说明JavaBean数据源的实现步骤:

1.提供一个JavaBean的实现类,注意要满足上面提供的四点要求,该类的每个成员属性会成为数据源中的一个字段。例如下面的Customer类:

public class Customer {
    private long customerId;
    private String name;
    // 0: 男,1: 女
    private byte sex;
    private Date birthday;
    public Customer(long customerId, String name, byte sex, Date birthday) {
        this.customerId = customerId;
        this.name = name;
        this.sex = sex;
        this.birthday = birthday;
    }
    public long getCustomerId() {
        return customerId;
    }
    public void setCustomerId(long customerId) {
        this.customerId = customerId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public byte getSex() {
        return sex;
    }
    public void setSex(byte sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

2.提供一个用于获取数据(JavaBean对象集合)的类,该类必须实现接口com.neusoft.report.dataset.executer.javabean.JavaBeanDataQuerier,该接口只有一个要实现的方法,用于返回数据,定义如下:

public interface JavaBeanDataQuerier {
    /**
     * 返回数据
     * 
     * @param params 报表参数集合
     * @param context 生成报表的上下文
     * @return 指定类型的bean列表
     */
    public List<?> query(Map params, Map context);
}

注意这里没有使用泛型,而是可以返回的是任意类型的列表,目的是允许用户使用同一个实现类返回不同类型的JavaBean对象。

如下是返回Customer列表的实现类:

public class CustomerQuerier implements JavaBeanDataQuerier {
    public List<?> query(Map params, Map context) {
        /** 获取报表参数的两种方法 **/
        // 直接从map中获取
        // int type = DataValueUtil.intValue(((DataValue) params.get("type")));
        // 通过请求对象获取
        // HttpServletRequest r = (HttpServletRequest) context.get("HTTP_REQUEST");
        // int type2 = Integer.valueOf(r.getParameter("type"));

        // 使用上面取到的报表参数值,从数据库、其他应用程序等获取数据。这里为了简单,直接使用固定的数据
        List<Customer> customers = new ArrayList<Customer>();
        customers.add(new Customer(1, "王先生", (byte) 0, new Date()));
        customers.add(new Customer(2, "林女士", (byte) 1, new Date()));
        customers.add(new Customer(3, "方先生", (byte) 0, new Date()));
        customers.add(new Customer(4, "刘小姐", (byte) 1, new Date()));
        return customers;
    }
}

这里为了简单,只是直接返回固定的数据。另外演示了通过入参获取要用到的值的方法。

3.在\WEB-INF\conf\unieap\report\datasource.xml文件中添加JavaBean数据源的配置,例如:

<Category Name="javabean" Caption="JavaBean 数据源"     SourceClass="com.neusoft.report.dataset.datasource.impl.JavaBeanDataSource"
    QuerierClass="com.neusoft.report.dataset.executer.javabean.JavaBeanDataSetExecuter">
    <DataSource Name="JavaBean数据源示例" Caption="JavaBean数据源示例">
        <Parameters>
            <Param Name="beanClassname" Value='com.neusoft.report.sample.dataset.javabean.Customer'/>
            <Param Name="dataClassname" Value='com.neusoft.report.sample.dataset.javabean.CustomerQuerier'/>
        </Parameters>
    </DataSource>
</Category>
  <Category Name="javabean" Caption="JavaBean 数据源"     SourceClass="com.neusoft.report.dataset.datasource.impl.JavaBeanDataSource"
    QuerierClass="com.neusoft.report.dataset.executer.javabean.JavaBeanDataSetExecuter">
    <DataSource Name="JavaBean数据源示例" Caption="JavaBean数据源示例">
        <Parameters>
            <Param Name="beanClassname" Value='com.neusoft.report.sample.dataset.javabean.Customer'/>
            <Param Name="dataClassname" Value='com.neusoft.report.sample.dataset.javabean.CustomerQuerier'/>
        </Parameters>
    </DataSource>
</Category>
其中每个DataSource对应一个数据源,不再有DataEntity的配置,DataSource有两个必须的参数,含义如下:
beanClassname:指定数据源中包含哪些字段的JavaBean实现类,例如Customer。
dataClassname:指定为报表提供数据的查询者实现类,例如CustomerQuerier。

2.5.2 字段类型限制

由数据源生成报表可用的数据集之前,需要先将数据源中的字段类型(对于JavaBean来说就是类的成员属性的Java类型)转换为报表支持的类型(在com.neusoft.report.commonbase.datavalue.DataValue中定义了常量),并不是所有的Java类型都被支持,目前支持的类型、及报表引擎中与之对应的常量见下表:

Java类型 DataValue类中对应的常量
String DataValue.STRING
Double, Float, double, float DataValue.DECIMAL
Integer, Long, Byte, Short, Character,int, long, byte, short, char DataValue.INTEGER
Boolean, boolean DataValue.BOOLEAN
java.util.Date DataValue.DATETIME

results matching ""

    No results matching ""

    results matching ""

      No results matching ""