如果僅僅只是前面章節提到的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 是此插件的主要入口點,負責註冊可用的聚合類型,並在運行時創建這些聚合的實例。
添加了工廠的定義之後,您將獲得一個文本項,其中包含工廠的完全限定包和類名。從圖 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擴展等等。