如果仅仅只是前面章节提到的BIRT的设计器的便捷性和BIRT引擎提供的用户自定义BIRT报表展示器等自定义特性,只是表现在BIRT报表作为工具的优势,或者作为服务提供者为系统集成做出的贡献,但这还不足以让BIRT成为eclipse开源社区排名前六的基础插件平台。
众所周知,大凡伟大的IT作品,皆是以开放为基础。ios的app store,android的app market,eclipse的plug-in,maven的osgi,microsoft的windows虽然闭源,但提供了visual studio应用开发平台,而visual studio是一个很优秀的平台;
BIRT同样也提供了扩展点,为它的数据源,数据集,数据操作函数,数据展示,数据导出,图表等等提供多数据源,多种形式的展示,多种格式的导出等等扩展。比如Oracle给multimedia CLOB提供的新特性,只要实现BIRT的数据源,数据集和数据展示,数据导出的新特性,同样可以用BIRT报表进行展示;任何第三方,无论是数据库厂商,还是数据提供商,例如医院,教育机构,航天航空机构等等,都可以实现这些扩展点中提供的接口和实现类,来完成报表的功能。
下面先从一个最简单的扩展例子——扩展BIRT报表的聚合函数,开始着手详细讲解BIRT的扩展点。感谢IBM开源社区提供这个古老的案例。
本节介绍商业智能和报告工具(Business Intelligence and Reporting Tools,BIRT)扩展点模型,并在 BIRT V2.3.x 和 V2.5.x 中实际创建一个聚合扩展。在较早的 BIRT 版本中,创建聚合扩展的方式是扩展 org.eclipse.birt.data.aggregation 扩展点,这会在一个名为 Total 的全局对象中添加一个小函数,您可以在整个报告的任何表达式中使用该函数,其工作原理类似于脚本函数扩展点。
但是,从 BIRT V2.2 到 V2.3,聚合扩展已经发生了变化。新的方式更加复杂,但可以在 Aggregation Widgets 下拉列表中得到一个不错的聚合,还为参数和表达式提供了漂亮的文本框。当创建此扩展时,可以在您的表中以列绑定的形式访问结果。
从总体上看,新的聚合扩展点包含一个对象,该对象是 IAggregationFactory 接口的扩展。可以在此接口中重载方法来完成 3 项操作:
· 初始化您的工厂(在构造函数中)
· 提供由工厂提供的一组聚合(以列表形式提供,包含聚合对象的实际实例)
· 返回聚合对象的单一实例
聚合的每个实例都需要实现 IAggrFunction 接口。需要实现许多含义明显的方法,比如 getName、getDataType 和getParmaeterDefn,还需要实现其他含义不太明显的方法。例如,getNumberOfPasses() 和 getType() 方法是相关的。getType() 方法制定此聚合器的执行方式和类型。有两种聚合类型:SUMMARY_AGGR 表示只为摘要计算该聚合(比如表的表头或表尾),RUNNING_AGGR 表示为表中的每一行或表尾计算聚合。getNumberOfPasses() 方法显示获得结果所需的 pass 数。所有基于评级的聚合器,比如 TopNpercent、PercentSum 和 Percentile,都会返回值 2,其余聚合器返回值 1。
IAggrFunction 接口的实际实现必须返回其 newAccumulator() 方法中的 Accumulator 类的一个扩展。Accumulator 负责执行实际的计算。有一些默认方法需要重载,最重要的是 onRow(),表中的每一行都需要调用该方法。使用此方法,您可以解析函数的参数并执行计算。对于 SUM,可以添加到某个已存储的数字;对于 ave,既可以保存到某个列表中进行存储,也可以添加到一个累计总计并跟踪调用次数。无论您如何执行计算,实际计算都需要在这里完成。getValue() 获取您的计算的最终值或当前值。所以,对于 SUM 操作,您将会返回总数/计数操作。在正在运行的聚合器中,将只返回正在计算的值。
本节的示例将展示如何创建一个简单的 Word Count 聚合器。此聚合获取一列中的所有句子并计算字数,返回一个包含该列的字数的整数值。对这种聚合的需求很少,所以还不存在这样的聚合。对于本文中的练习,建议使用 Eclipse BIRT All-in-One 分发版
创建新聚合插件
要创建新聚合插件:
1 单击 File > New > Other 创建一个新插件项目。展开 Plug-In Development 文件夹,然后单击 Plug-in Project。
在 New Plug-in Project 窗口中(如图 1 所示),在 ID 字段中为项目提供一个惟一名称,使用合适的信息完成Version、Name 和 Provider 字段。
图 1. 项目属性
1 指定 Java? 2 Platform, Standard Edition (J2SE) V1.5 作为执行环境,以保持与 BIRT 基本兼容。
2 在 Options 区域,清除 This plug-in will make contributions to the UI 复选框。通过清除此复选框,可以限制可用模板的数量。我们不打算为此项目使用模板。
3 在 Rich Client Application 区域,选择 No,因为这不是一个 Eclipse 富客户端项目。单击 Next。
4 在 Templates 页面上,清除 Create a plug-in using one of the templates 复选框,然后单击 Finish。如果现在还未处于 Plug-In 开发透视图中,系统将提示您打开它。
设置扩展点
创建好新项目之后,就可以设置扩展点了。为此,执行以下操作:
1 在打开的清单窗口中,单击 Extensions 选项卡,然后单击 Add。
在 Extension Point Selection 窗口中(如图 2 所示),清除 Show only extension points from the required plug-ins 复选框。
图 2. 创建新扩展
1 在 Extension Point filter 字段中,键入 org.eclipse.birt.data。将会出现聚合扩展点。当添加此扩展点时,系统将提示您添加依赖关系。
创建必要的聚合类
现在您已经添加了聚合扩展点,接下来需要添加一个新 AggregationFactory。为此,右键单击刚才添加的聚合扩展点,指向 New,然后单击 AggregationFactory,如图 3 所示。注意,您并未添加聚合,聚合是以前的扩展方法,这种方法现在已被淘汰。AggregationFactory 是此插件的主要入口点,负责注册可用的聚合类型,并在运行时创建这些聚合的实例。
图 3. 创建新 AggregationFactory
添加了工厂的定义之后,您将获得一个文本项,其中包含工厂的完全限定包和类名。从图 4 可以看到,该工厂名为 com.digiassn.blogspot.birt.aggregators.wordcount.WordCountFactory。请记住,此工厂可以注册和创建多个聚合类型,但这需要在代码体中操作。在 Extension Element Details 区域,键入或浏览到工厂类的名称,然后单击文本框旁边的 class 超链接。
图 4. 创建新工厂类
New Java Class 向导已经拥有了合适的包和类信息。确认 Java Class 页面上的设置(如图 5 所示),然后单击 Finish。
图 5. 工厂类属性
打开类的源代码,如图 6 所示。如果无法找到 org.eclipse.birt.* imports,请返回并保存清单窗口中的更改。请记住,您需要为类添加必要的继承抽象方法。
图 6. 工厂类框架
您的类包含 3 个函数:一个构造函数、一个 getAggregations() 方法(返回一个 IAggrFunction),以及一个 getAggregations() 方法(返回一个列表)。getAggregations() 方法向调用者返回一个 IAggrFunction 类型列表,使调用者知道此工厂可以生成的各个聚合的类型。调用者负责在列表上进行迭代,并调用 IAggrFunctions 方法来获得描述。对于我们创建的工厂,我们不关心这些描述,工厂将负责返回和维护此列表。
向 getAggregation() 方法传入聚合的名称。该方法获取一个名称并提供一个 IAggrFunction 结果,清单 1 显示了示例工厂。
清单 1. 完成之后的工厂类
public class WordCountFactory implements IAggregationFactory { HashMap<String, IAggrFunction> aggregateMap; public WordCountFactory() { aggregateMap = new HashMap<String, IAggrFunction>(); BasicWordcount wordCountAggregation = new BasicWordcount(); aggregateMap.put(wordCountAggregation.getName(), wordCountAggregation); } public IAggrFunction getAggregation(String aggregationName) { return aggregateMap.get(aggregationName); } public List getAggregations() { return new ArrayList<IAggrFunction>(aggregateMap.values()); }}
创建单独的聚合描述类
接下来,在 src 文件夹中创建一个新包,将其命名为 com.digiassn.blogspot.birt.aggregators.implIn。在这个新包之下,创建一个类BasicWordCount。在 Java Class 窗口中,选择 Inherited abstract methods 复选框,以便此类可以继承org.eclipse.birt.data.engine.api.aggregation.IAggrFunction 接口,如图 7 所示。
图 7. 创建新 IAggrFunction 实现
IAggrFunction 类具有两项任务:它将自身描述为工厂可以创建的聚合,它还会创建 Accumulator 类的实例来为您的聚合执行实际工作。清单 2 给出了代码。
清单 2. 完成后的聚合函数和 Accumulator 类
public class BasicWordcount implements IAggrFunction { private final static String sDescription = "This aggregation will count all words in a column and return the count."; private final static String sName = "Word Count"; private final static String sDisplayName = "Basic Word Count Aggregator"; public int getDataType() { return DataType.INTEGER_TYPE; } public Object getDefaultValue() { return new Integer(0); } public String getDescription() { return this.sDescription; } public String getDisplayName() { return this.sDisplayName; } public String getName() { return this.sName; } public int getNumberOfPasses() { return 1; } public IParameterDefn[] getParameterDefn() { IParameterDefn paramDef = new IParameterDefn() { public boolean supportDataType(int paramType) { if (paramType == DataType.STRING_TYPE) { return true; } return false; } public boolean isOptional() { return false; } public boolean isDataField() { return false; } public String getName() { return "StringColumn"; } public String getDisplayName() { return "String Column"; } public String getDescription() { return "A column expression that is a String"; } }; IParameterDefn[] parameterDefinitionArray = new IParameterDefn[] {paramDef}; return parameterDefinitionArray; } public int getType() { return IAggrFunction.SUMMARY_AGGR; } public boolean isDataOrderSensitive() { return false; } public Accumulator newAccumulator() { return new Accumulator() { int sum; @Override public Object getValue() throws DataException { return new Integer(sum); } @Override public void onRow(Object[] incomingStrings) throws DataException { String localString = (String) incomingStrings[0]; sum += localString.split(" ").length; } }; }}
此类中的大部分方法只是简单描述要在 BIRT Aggregation Widgets 下拉列表中显示的聚合的各个方面,比如数据类型、标题和描述信息。这 3 个方面有必要进一步解释一下。首先是 getParameterDefn() 方法,它返回一个 IParameterDefn 对象数组,可以在该数组中定义聚合需要的参数。一个聚合可以有多个参数,这就是为什么要以数组的形式返回参数。此方法向 BIRT 引擎简单描述这些参数及其类型。在本例中,仅有一个参数(列表达式),它将是一个字符串。
那么,如果参数在 getParameterDefn() 方法中进行描述,要将它们传入到何处来执行实际工作呢?IAggrFunction 对象还充当着Accumulator 类的工厂,该对象应该在 newAccumulator 方法中创建。Accumulator 是在聚合中执行实际工作的类。它有两个方法需要重载:getValue() 和 onRow()。对于在 BIRT 中处理的每一行,如果使用此聚合,数据绑定将调用 onRow()。作为一个参数,onRow 接收一个包含聚合参数的数组,这些参数由 getParameterDefn() 描述。在更加健壮的方案中,可以调用 getParameterDefn() 并测试传入 onRow() 的参数是否与定义匹配。但是,本文中的简单示例跳过了这一步。onRow() 方法还负责执行处理工作。在上面的聚合代码示例中,它仅用于增加正在计算的字符串总字数。当报告准备好显示值时,它调用 getValue() 方法。
需要进一步说明的另一个元素是 IAggrFunctions getType() 方法。两种聚合类型(SUMMARY_AGGR 和 RUNNING_AGGR)定义该方法属于哪种聚合类型;它出现在报告表的表头或表尾,还是显示在每一行中;它在计算时显示的是累计总计,还是平均值。
测试插件
可以轻松测试此插件,无需象测试任何 Eclipse 插件一样进行部署:只需启动另一个 Eclipse 实例即可。启动另一个实例的最简单方式是从 plugin.xml/manifest 窗口的 Overview 选项卡上启动。在此选项卡上,在 Testing 区域,单击 Launch an Eclipse application 链接,如图 8 所示。
图 8. 测试插件
图 9 显示了一个简单报告,可以根据该报告来测试插件。将一个聚合组件插入到此报告的表尾。
图 9. 向报告添加聚合
在 Aggregation Widgets 下拉列表中,选择 Basic Word Count 聚合器并输入列表达式,以指向包含想要统计的句子的列,如图 10 所示。
图 10. Aggregation Builder
运行报告并验证结果。图 11 显示了生成的报告。
图 11. 报告结果
上面的例子是BIRT扩展点中相对简单的情况,但实现扩展点的过程却是一样,都是需要添加工程类,继承接口或者实现类,完成接口方法,添加插件依赖。后续的章节会讲解实现BIRT-ODA数据源扩展,XML emitter扩展,chat扩展等等。