【仅供内部供应商使用,不提供对外解答和培训】

Page tree

【仅供内部供应商使用,不提供对外解答和培训】

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

一、特殊名词介绍

低版本产品 : 下文中的低版本产品特指FineReport10.0以下的版本

报表预览:下文中特指cpt/frm模板的预览方式,但不包含移动端预览和H5预览方式。

二、背景、场景介绍

标准的帆软报表产品中只支持基于标准JDBC或JNDI的数据查询和几种基础的文件数据。对于非标JDBC数据库、WebService、REST、实时库、RPC等等的数据接口系统的对接需要依赖数据集接口接入。

以往开发者大多都接触过程序数据集的基本实现方法,程序数据集从功能上是可以满足开发者对数据的绝大部分诉求的,不过对于报表制作人员来说,程序数据集存在叫非常明显的短板就是,使用并不友好。制作人员需要记住大量的类名和业务的关联关系,或者需要手动添加大量的参数实现“配置化”,但是又无法解决参数配置化导致的参数引入问题(比如某些敏感配置只能在制作模板的时候指定,禁止从URL/参数面板等地方引入)。为了给报表制作人员提供更好的使用体验和安全体验,产品内设计了TableDataDefineProvider接口,让开发者得以用插件的形式向用户提供数据集扩展。

三、接口介绍

TableDataDefineProvider.java
package com.fr.design.fun;

import com.fr.base.TableData;
import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane;
import com.fr.stable.fun.mark.Mutable;
import com.fr.stable.fun.mark.Aftermath;

/**
 * @author : richie
 * @since : 7.1.1
 * 自定义报表数据集界面接口,单独存在的一个个数据集
 */
public interface TableDataDefineProvider extends Mutable, Aftermath {

    String XML_TAG = "TableDataDefineProvider";

    int CURRENT_LEVEL = 1;

    /**
     * 自定义的数据集设置界面所对应的数据集类型
     * @return 数据集的类型
     */
    Class<? extends TableData> classForTableData();

    /**
     * 自定义数据集设置界面所对应的初始化数据集类型,在一种数据集有多个实现的时候有效
     * @return 数据集类型
     */
    Class<? extends TableData> classForInitTableData();

    /**
     * 自定义的数据集设置界面所对应的界面类型
     * @return 数据集界面类型
     */
    Class<? extends AbstractTableDataPane> appearanceForTableData();

    /**
     * 自定义数据集设置界面在菜单上的现实名字
     * @return 名字
     */
    String nameForTableData();

    /**
     * 自定义数据集在新建的时候名字前缀
     * @return 名字前缀
     */
    String prefixForTableData();

    /**
     * 自定义数据集在菜单上现实的图标
     * @return 图标
     */
    String iconPathForTableData();
}
AbstractTableData.java
/**
 *
 */
package com.fr.base;

import com.fr.config.utils.UniqueKey;
import com.fr.data.TableDataSource;
import com.fr.data.impl.DataCacheKey;
import com.fr.data.impl.ParameterCacheKey;
import com.fr.general.data.DataModel;
import com.fr.script.Calculator;
import com.fr.stable.ParameterProvider;

import java.util.ArrayList;


/**
 * @date: 2014-11-24-上午11:30:22
 */
public abstract class AbstractTableData extends UniqueKey implements TableData {

    /**
     * 克隆
     *
     * @return 克隆的数据集
     * @throws CloneNotSupportedException
     * @date 2014-11-24-上午11:56:19
     */
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 获取当前tabledata所有的列名
     *
     * @param datasource 当前数据源
     * @return 所有列名的数组
     * @date 2014-11-24-上午11:49:07
     */
    public String[] getColumnNames(TableDataSource datasource) {
        return new String[0];
    }

    /**
     * 获取没有权限访问的数据集列表
     *
     * @param toBeRemoveList   没有权限的列表
     * @param nameWillBeRemove 需要移除的对象
     * @param name             当前数据集名
     * @date 2014-11-24-下午3:58:29
     */
    public void registerNoPrivilege(ArrayList<String> toBeRemoveList, String nameWillBeRemove, String name) {

    }


    /**
     * @param calculator     连接上下文计算的算子
     * @param timeoutSeconds 超时单位:秒
     * @return
     */
    public DataModel createDataModelWithTimeout(Calculator calculator, int timeoutSeconds) {
        return createDataModel(calculator);
    }

    public void setParameters(ParameterProvider[] paras) {

    }

    public void filterDataType(Class<?> type) {
    }

    @Override
    public DataCacheKey getDataCacheKey(Calculator calculator) {
        return new ParameterCacheKey(Calculator.processParameters(calculator, getParameters(calculator)));
    }
}
AbstractParameterTableData.java
/*
 * Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved.
 */
package com.fr.data;

import com.fr.base.AbstractTableData;
import com.fr.config.holder.factory.XmlHolders;
import com.fr.config.holder.impl.xml.XmlColConf;
import com.fr.general.ComparatorUtils;
import com.fr.general.data.DataModel;
import com.fr.script.Calculator;
import com.fr.stable.ArrayUtils;
import com.fr.stable.ParameterProvider;
import com.fr.stable.script.CalculatorKey;
import com.fr.stable.xml.StableXMLUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLableReader;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 带参数的数据集的抽象实现,如果需要设计一个带参数的数据集,可以继承此类以简化代码
 */
public abstract class AbstractParameterTableData extends AbstractTableData {

    private static final long serialVersionUID = 7017455818551821178L;
    public static final CalculatorKey ROW_COUNT = CalculatorKey.createKey("rowCount");
    //动态Parameter.
    //参数数组.
    protected XmlColConf<Collection<ParameterProvider>> parameters = XmlHolders.collection(new ArrayList<ParameterProvider>(),ParameterProvider.class);

    /**
     * 获取数据集所使用的参数
     *
     * @param c 算子
     * @return 参数数组
     */
    public ParameterProvider[] getParameters(Calculator c) {
        Collection<ParameterProvider> value = this.parameters.get();
        return value.size() == 0 ? processParameters(c) :  value.toArray(new ParameterProvider[value.size()]);
    }

    @Override
    public void setParameters(ParameterProvider[] parameters) {
        this.parameters.set(ArrayUtils.toList(parameters));
    }

    /**
     * 设置参数
     *
     * @param providers 参数
     */
    protected void setDefaultParameters(ParameterProvider[] providers) {
        this.parameters.set(ArrayUtils.toList(providers));
    }


    /**
     * 将数据集中的${p1}形式的参数解析成运行时的参数值
     *
     * @param calculator 连接上下文运算的算子
     * @return 解析后的参数数组,可以通过获取公式的结果获取解析后的值
     */
    protected ParameterProvider[] processParameters(Calculator calculator) {
        Collection<ParameterProvider> parameterProviders = this.parameters.get();
        return Calculator.processParameters(calculator, parameterProviders.toArray(new ParameterProvider[parameterProviders.size()]));
    }

    public void readXML(XMLableReader reader) {
        if (reader.isChildNode()) {
            String tmpName = reader.getTagName();
            if (ParameterProvider.ARRAY_XML_TAG.equals(tmpName)) {//读取Parameters.
                final List tmpParameterList = new ArrayList();

                reader.readXMLObject(new XMLReadable() {
                    public void readXML(XMLableReader reader) {
                        if (ParameterProvider.XML_TAG.equals(reader.getTagName())) {
                            tmpParameterList.add(StableXMLUtils.readParameter(reader));
                        }
                    }
                });

                //转换数组.
                if (!tmpParameterList.isEmpty()) {
                    this.parameters.set(tmpParameterList);
                }
            }
        }
    }


    public void writeXML(XMLPrintWriter writer) {
        //保存parameters.
        Collection<ParameterProvider> providers = this.parameters.get();
        StableXMLUtils.writeParameters(writer, providers.toArray(new ParameterProvider[providers.size()]));
    }

    public boolean equals(Object obj) {
        return obj instanceof AbstractParameterTableData
                && ComparatorUtils.equals(this.parameters, ((AbstractParameterTableData) obj).parameters);
    }


    /**
     * 克隆
     *
     * @return 克隆的数据集
     * @throws CloneNotSupportedException
     */
    @SuppressWarnings("unchecked")
    public Object clone() throws CloneNotSupportedException {
        AbstractParameterTableData cloned = (AbstractParameterTableData) super.clone();
        cloned.parameters = (XmlColConf<Collection<ParameterProvider>>) this.parameters.clone();
        return cloned;
    }


    /**
     * 创建数据集
     *
     * @param calculator 连接上下文计算的算子
     * @param name       数据集的名字
     * @return 返回数据集
     */
    public DataModel createDataModel(Calculator calculator, String name) {
        return createDataModel(calculator);
    }

    /**
     * 创建数据集
     *
     * @param calculator 连接上下文计算的算子
     * @param rowCount   要获取数据的行数
     * @return 返回数据集
     */
    public DataModel createDataModel(Calculator calculator, int rowCount) {
        calculator.setAttribute(AbstractParameterTableData.ROW_COUNT, rowCount);
        return createDataModel(calculator);
    }
}

四、支持版本

产品线

版本

支持情况

备注

FR8.0支持
FR9.0支持
FR10.0支持
BI3.6支持
BI4.0支持
BI5.1支持
BI5.1.2支持
BI5.1.3支持

五、插件注册

plugin.xml
<extra-designer>
        <TableDataDefineProvider class="your class name"/>
</extra-designer>

六、原理说明

当数据集类型选择列表被触发时,会调用TableDataCreatorProducer#createReportTableDataCreator方法。此时,会读取所有插件中申明的TableDataDefineProvider实例。因为数据集添加是在设计器上操作的,而实际数据集的计算是属于服务器的功能范畴,所以TableDataDefineProvider还有个作用就是解耦,对于一个具体的数据集最终只会把tabledata的class名保存到cpt/frm中。

七、特殊限制说明

iconPathForTableData :返回的图标路径(如:/com/demo/hg/resources/images/main.png)。需要注意,因为这个接口没有做图片的统一缩放,所以需要开发者自己准备好16*16(px)大小的图标。否则会显得很不协调。

classForTableData: 接口需要返回一个TableData接口类。实际开发时为了方便开发提高效率和稳定性,要求开发者全部继承AbstractParameterTableData

classForInitTableData:这个接口如果已经实现了classForTableData的话,目前没有什么实质性的作用,不过一般要求开发者保留性的与classForTableData返回相同的class。

在实现AbstractParameterTableData接口时,固定需要实现createDataModel方法。如果有其他自定义配置则需要额外实现readXML/writeXML两个接口方法,且自定义配置需要符合配置文件接口开发标准(必须是Conf对象,具体见demo示例)

八、常用链接

demo地址:demo-table-data-define

九、开源案例

免责声明:所有文档中的开源示例,均为开发者自行开发并提供。仅用于参考和学习使用,开发者和官方均无义务对开源案例所涉及的所有成果进行教学和指导。若作为商用一切后果责任由使用者自行承担。

demo-tabledata-redis


  • No labels