报表扩展类接口
在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 |