第十四章 高級特性-海納百川:BIRT報表擴展點

如果僅僅只是前面章節提到的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 ToolsBIRT)擴展點模型,並在 BIRT V2.3.x 和 V2.5.x 中實際創建一個聚合擴展。在較早的 BIRT 版本中,創建聚合擴展的方式是擴展 org.eclipse.birt.data.aggregation 擴展點,這會在一個名爲 Total 的全局對象中添加一個小函數,您可以在整個報告的任何表達式中使用該函數,其工作原理類似於腳本函數擴展點。

但是,從 BIRT V2.2 到 V2.3,聚合擴展已經發生了變化。新的方式更加複雜,但可以在 Aggregation Widgets 下拉列表中得到一個不錯的聚合,還爲參數和表達式提供了漂亮的文本框。當創建此擴展時,可以在您的表中以列綁定的形式訪問結果。

從總體上看,新的聚合擴展點包含一個對象,該對象是 IAggregationFactory 接口的擴展。可以在此接口中重載方法來完成 項操作:

· 初始化您的工廠(在構造函數中)

· 提供由工廠提供的一組聚合(以列表形式提供,包含聚合對象的實際實例)

· 返回聚合對象的單一實例

聚合的每個實例都需要實現 IAggrFunction 接口。需要實現許多含義明顯的方法,比如 getNamegetDataType 和getParmaeterDefn,還需要實現其他含義不太明顯的方法。例如,getNumberOfPasses() 和 getType() 方法是相關的。getType() 方法制定此聚合器的執行方式和類型。有兩種聚合類型:SUMMARY_AGGR 表示只爲摘要計算該聚合(比如表的表頭或表尾),RUNNING_AGGR 表示爲表中的每一行或表尾計算聚合。getNumberOfPasses() 方法顯示獲得結果所需的 pass 數。所有基於評級的聚合器,比如 TopNpercentPercentSum 和 Percentile,都會返回值 2,其餘聚合器返回值 1

IAggrFunction 接口的實際實現必須返回其 newAccumulator() 方法中的 Accumulator 類的一個擴展。Accumulator 負責執行實際的計算。有一些默認方法需要重載,最重要的是 onRow(),表中的每一行都需要調用該方法。使用此方法,您可以解析函數的參數並執行計算。對於 SUM,可以添加到某個已存儲的數字;對於 ave,既可以保存到某個列表中進行存儲,也可以添加到一個累計總計並跟蹤調用次數。無論您如何執行計算,實際計算都需要在這裏完成。getValue() 獲取您的計算的最終值或當前值。所以,對於 SUM 操作,您將會返回總數/計數操作。在正在運行的聚合器中,將只返回正在計算的值。

本節的示例將展示如何創建一個簡單的 Word Count 聚合器。此聚合獲取一列中的所有句子並計算字數,返回一個包含該列的字數的整數值。對這種聚合的需求很少,所以還不存在這樣的聚合。對於本文中的練習,建議使用 Eclipse BIRT All-in-One 分發版


創建新聚合插件

要創建新聚合插件:

單擊 File > New > Other 創建一個新插件項目。展開 Plug-In Development 文件夾,然後單擊 Plug-in Project

在 New Plug-in Project 窗口中(如圖 所示),在 ID 字段中爲項目提供一個惟一名稱,使用合適的信息完成VersionName 和 Provider 字段。 

圖 1. 項目屬性


指定 Java™ 2 Platform, Standard Edition (J2SE) V1.5 作爲執行環境,以保持與 BIRT 基本兼容。

在 Options 區域,清除 This plug-in will make contributions to the UI 複選框。通過清除此複選框,可以限制可用模板的數量。我們不打算爲此項目使用模板。

在 Rich Client Application 區域,選擇 No,因爲這不是一個 Eclipse 富客戶端項目。單擊 Next

在 Templates 頁面上,清除 Create a plug-in using one of the templates 複選框,然後單擊 Finish。如果現在還未處於 Plug-In 開發透視圖中,系統將提示您打開它。

設置擴展點

創建好新項目之後,就可以設置擴展點了。爲此,執行以下操作:

在打開的清單窗口中,單擊 Extensions 選項卡,然後單擊 Add

在 Extension Point Selection 窗口中(如圖 所示),清除 Show only extension points from the required plug-ins 複選框。 

圖 2. 創建新擴展


1 在 Extension Point filter 字段中,鍵入 org.eclipse.birt.data。將會出現聚合擴展點。當添加此擴展點時,系統將提示您添加依賴關係。

創建必要的聚合類

現在您已經添加了聚合擴展點,接下來需要添加一個新 AggregationFactory。爲此,右鍵單擊剛纔添加的聚合擴展點,指向 New,然後單擊 AggregationFactory,如圖 所示。注意,您並未添加聚合,聚合是以前的擴展方法,這種方法現在已被淘汰。AggregationFactory 是此插件的主要入口點,負責註冊可用的聚合類型,並在運行時創建這些聚合的實例。


圖 3. 創建新 AggregationFactory


添加了工廠的定義之後,您將獲得一個文本項,其中包含工廠的完全限定包和類名。從圖 可以看到,該工廠名爲 com.digiassn.blogspot.birt.aggregators.wordcount.WordCountFactory。請記住,此工廠可以註冊和創建多個聚合類型,但這需要在代碼體中操作。在 Extension Element Details 區域,鍵入或瀏覽到工廠類的名稱,然後單擊文本框旁邊的 class 超鏈接。


圖 4. 創建新工廠類


New Java Class 嚮導已經擁有了合適的包和類信息。確認 Java Class 頁面上的設置(如圖 所示),然後單擊 Finish。


圖 5. 工廠類屬性


打開類的源代碼,如圖 所示。如果無法找到 org.eclipse.birt.* imports,請返回並保存清單窗口中的更改。請記住,您需要爲類添加必要的繼承抽象方法。


圖 6. 工廠類框架


您的類包含 個函數:一個構造函數、一個 getAggregations() 方法(返回一個 IAggrFunction),以及一個 getAggregations() 方法(返回一個列表)。getAggregations() 方法向調用者返回一個 IAggrFunction 類型列表,使調用者知道此工廠可以生成的各個聚合的類型。調用者負責在列表上進行迭代,並調用 IAggrFunctions 方法來獲得描述。對於我們創建的工廠,我們不關心這些描述,工廠將負責返回和維護此列表。

向 getAggregation() 方法傳入聚合的名稱。該方法獲取一個名稱並提供一個 IAggrFunction 結果,清單 顯示了示例工廠。

清單 1. 完成之後的工廠類

  1. public class WordCountFactory implements IAggregationFactory {  
  2.     HashMap<String, IAggrFunction> aggregateMap;  
  3.       
  4.     public WordCountFactory() {  
  5.         aggregateMap = new HashMap<String, IAggrFunction>();  
  6.           
  7.         BasicWordcount wordCountAggregation = new BasicWordcount();  
  8.           
  9.         aggregateMap.put(wordCountAggregation.getName(), wordCountAggregation);  
  10.     }  
  11.   
  12.     public IAggrFunction getAggregation(String aggregationName) {  
  13.           
  14.         return aggregateMap.get(aggregationName);  
  15.     }  
  16.   
  17.     public List getAggregations() {  
  18.           
  19.         return new ArrayList<IAggrFunction>(aggregateMap.values());  
  20.     }  
  21.   
  22. }  

創建單獨的聚合描述類

接下來,在 src 文件夾中創建一個新包,將其命名爲 com.digiassn.blogspot.birt.aggregators.implIn。在這個新包之下,創建一個類BasicWordCount。在 Java Class 窗口中,選擇 Inherited abstract methods 複選框,以便此類可以繼承org.eclipse.birt.data.engine.api.aggregation.IAggrFunction 接口,如圖 所示。


圖 7. 創建新 IAggrFunction 實現


IAggrFunction 類具有兩項任務:它將自身描述爲工廠可以創建的聚合,它還會創建 Accumulator 類的實例來爲您的聚合執行實際工作。清單 給出了代碼。


清單 2. 完成後的聚合函數和 Accumulator 類

  1. public class BasicWordcount implements IAggrFunction {  
  2.       
  3.     private final static String sDescription =   
  4.         "This aggregation will count all words in a column and return the count.";  
  5.     private final static String sName = "Word Count";  
  6.     private final static String sDisplayName = "Basic Word Count Aggregator";  
  7.       
  8.     public int getDataType() {  
  9.         return DataType.INTEGER_TYPE;  
  10.     }  
  11.   
  12.     public Object getDefaultValue() {  
  13.         return new Integer(0);  
  14.     }  
  15.   
  16.     public String getDescription() {  
  17.         return this.sDescription;  
  18.     }  
  19.   
  20.     public String getDisplayName() {  
  21.         return this.sDisplayName;  
  22.     }  
  23.   
  24.     public String getName() {  
  25.         return this.sName;  
  26.     }  
  27.   
  28.     public int getNumberOfPasses() {  
  29.         return 1;  
  30.     }  
  31.   
  32.     public IParameterDefn[] getParameterDefn() {  
  33.         IParameterDefn paramDef = new IParameterDefn() {  
  34.             public boolean supportDataType(int paramType) {  
  35.                 if (paramType == DataType.STRING_TYPE)  
  36.                 {  
  37.                     return true;  
  38.                 }  
  39.                   
  40.                 return false;  
  41.             }  
  42.               
  43.             public boolean isOptional() {  
  44.                 return false;  
  45.             }  
  46.               
  47.             public boolean isDataField() {  
  48.                 return false;  
  49.             }  
  50.               
  51.             public String getName() {  
  52.                 return "StringColumn";  
  53.             }  
  54.               
  55.             public String getDisplayName() {  
  56.                 return "String Column";  
  57.             }  
  58.               
  59.             public String getDescription() {  
  60.                 return "A column expression that is a String";  
  61.             }  
  62.         };  
  63.           
  64.         IParameterDefn[] parameterDefinitionArray = new IParameterDefn[]   
  65.                                 {paramDef};  
  66.         return parameterDefinitionArray;  
  67.     }  
  68.   
  69.     public int getType() {  
  70.         return IAggrFunction.SUMMARY_AGGR;  
  71.     }  
  72.   
  73.     public boolean isDataOrderSensitive() {  
  74.         return false;  
  75.     }  
  76.   
  77.     public Accumulator newAccumulator() {  
  78.         return new Accumulator()  
  79.         {  
  80.             int sum;  
  81.       
  82.             @Override  
  83.             public Object getValue() throws DataException {  
  84.                 return new Integer(sum);  
  85.             }  
  86.   
  87.             @Override  
  88.             public void onRow(Object[] incomingStrings) throws DataException {  
  89.                 String localString = (String) incomingStrings[0];  
  90.                   
  91.                 sum += localString.split(" ").length;  
  92.             }  
  93.               
  94.         };  
  95.     }  
  96. }  

此類中的大部分方法只是簡單描述要在 BIRT Aggregation Widgets 下拉列表中顯示的聚合的各個方面,比如數據類型、標題和描述信息。這 個方面有必要進一步解釋一下。首先是 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. 測試插件


圖 顯示了一個簡單報告,可以根據該報告來測試插件。將一個聚合組件插入到此報告的表尾。

圖 9. 向報告添加聚合


在 Aggregation Widgets 下拉列表中,選擇 Basic Word Count 聚合器並輸入列表達式,以指向包含想要統計的句子的列,如圖 10 所示。


圖 10. Aggregation Builder


運行報告並驗證結果。圖 11 顯示了生成的報告。


圖 11. 報告結果



上面的例子是BIRT擴展點中相對簡單的情況,但實現擴展點的過程卻是一樣,都是需要添加工程類,繼承接口或者實現類,完成接口方法,添加插件依賴。後續的章節會講解實現BIRT-ODA數據源擴展,XML emitter擴展,chat擴展等等。

發佈了57 篇原創文章 · 獲贊 3 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章