簡介: 結合目前比較受歡迎的 EMF(Eclipse Modeling Framework)和 GEF(Graphical Editing Framework)技術,提出了一種在圖形化開發中創建連接線時普遍適用的驗證方法. 最後給出了一個創建 連接線時動態 Schema 驗證的例子,並根據 Schema 的定義列出創建連接線的真值表。針對該真值表的每 項進行驗證,保證不符合 Schema 定義規則的模型對應的圖形之間不能創建連接線,降低了 Schema 驗證 和修改的復雜性。
一種基於 EMF 模型的在 GEF 中判斷創建連接線有效性的方法
在 Java 中進行圖形化工具的開發傳統的方法是直接使用 AWT 或者 Swing,這種方法在處理圖形化開 發的細節上(圖形的創建/刪除、放大/縮小、拖放和撤銷等)一般比較復雜;圖形化設計和後台模型的對 應關系都需要開發者自己定義,沒有統一的標准,這樣很難進行維護;AWT 或者 Swing 開發的圖形界面 跟 Windows 界面的風格不一致,讓一直使用 Windows 的用戶很難接受。 EMF 和 GEF 技術作為 Eclipse 工程的一部分,分別用來進行模型的代碼生成和圖形化工具的開發。其中 GEF 簡化了對圖形的處理,開 發人員不必把主要精力放在圖形的處理上;提供了一個基於 MVC(Model-View-Controller)結構的圖形 化開發框架,有效地維持了圖形和模型之間的對應關系;基於SWT,圖形的風格跟Windows的一樣。因此, 采用 GEF 進行圖形化開發可以提高開發效率的同時保證了可擴展性。
EMF 和 GEF 介紹
EMF
EMF 是一套 Java 的框架,可以用於創建基於結構化模型的工具和其他的應用程序。對於引入的面向 對象的模型,EMF 可以幫助你快速地將你的模型轉換為高效、正確和容易定制的 Java 代碼。 EMF 使用 的模型定義的標准格式是 XMI(XML Metadata Interchange),有四種方式可以將你自己創建的模型轉換 為XMI格式:
使用文本或 XML 編輯器直接創建 XMI 文檔;
將使用 Rational Rose 等建模工具創建的模型導出,生成 XMI 文檔;
使用帶有模型特征注釋的 Java 接口;
使用 XML Schema 來描述模型的格式。
GEF
GEF 可以方便開發者從一個現成的模型來創建一個功能豐富的圖形化編輯器,提供了一個基本框架來 構建多種應用,比如說:狀態圖、GUI 編輯器、類圖編輯器和狀態機等。 GEF 包含了兩個插件: org.eclipse.draw2d 和org.eclipse.gef。 其中 org.eclipse.draw2d 為顯示的圖形提供了布局和描述 的工具集;org.eclipse.gef 使用了 MVC 的架構,提供了控制器(比如: EditPart )來操作各個模型和 視圖。
GEF工作原理
GEF 采用的是 MVC 架構,其架構圖如圖1所示。GEF一般和 EMF 結合使用,EMF 負責生成模型。MVC 的各個部分都是樹狀結構的,並且是一一對應關系。其中 EditPart 充當控制器的角色,裡面可以使用各 種 EditPolicy,而 EditPolicy 的具體實現有時需要調用 Command 工具集類;而通過 draw2d 繪制的圖 形充當視圖的角色。 具體的工作流程為:
EditPartFactory 會針對每個模型創建一個對應的EditPart;
在每個 EditPart 內部會創建與模型對應的視圖,即顯示的圖形;
當模型的屬性改變後,會通知 EditPart 它的屬性變化情況;
EditPart 會根據模型屬性的變化更新視圖的顯示;
當需要在模型間建立某種關聯關系時,用 Command 在兩個圖形間建立一條連接線。該連接線也是一個 模型,所以針對該連接線的一些操作可以重復以上1)—4)步。
當圖形化設計完後進行保存,後台調用的是 EMF 的保存機制,將各個模型和它們之間的關系保存成 EMF 自定義的 XMI 格式(各個模型保存時是並列的結構,它們之間的關系是通過屬性來標識,具體的關 系要在設計時確定)。 這個 XMI 文件一般作為一種中間格式來保存,當我們想要按照自己的格式來保存 各個模型和它們之間的關系時(比如說樹狀結構),最好的辦法就是對 EMF 保存的 XMI 文件進行二次 XML 處理。
圖 1. GEF 的 MVC 架構圖
連接線創建時的驗證方法
在使用圖形化來設計工作流的時候,為了把具體的流程和邏輯表現清楚,需要在不同的模型之間建立 關聯關系,通常的方式就是在模型對應的圖形之間創建一條連接線(連接線本身也是一個模型,代表了某 個特定的關聯關系)。關聯關系可以是多種多樣的,比如說一對一、一對多、多對多等。 如圖2所示,模 型 A 同時跟三個模型 B1、B2 和 B3 建立了連接(假定均為一對一的關系)。每條連接線都有起始端和 目的端,當創建一條連接線時,連接線兩端對應的模型都要進行添加連接線的操作。在建模的時候,對於 每個模型都允許從其引出一條連接線或者作為連接線的終點,而連接線是保存在模型內部的一個集合中。 連接線的創建是在一個 Command 裡面實現的。 Command 是 GEF 裡包含的一個基類,提供了重新執行、 撤銷執行和執行條件判斷等操作。
圖 2. 一般的連接線創建
由 EMF 生成的模型對應的 Java 代碼有著特定格式的。 假設有模型 A、B1、B2 和 B3,模型A 和 B1 、B2、B3 的關系分別為一對一、一對多和多對多。 每個模型分別對應著兩個類(一個接口、一個接口實 現類)。 比如模型 A對應著 interface ModelA 和 class ModelAImpl(此處用的是假設的類名)。 圖3 為 EMF 生成模型的類圖。可以看出,當模型間的關系不同時,其對應類的成員方法是有規律可循的。A 和 B1是一對一的,對應的 ModelA 類就含有ModelB1 getModelB1( ) 和void setModelB1(ModelB1)方法 ;而 A 和 B2 是一對多的,ModelA 類就含有 EList getModelB2( ) 方法。
圖 3. EMF 生成模型的類圖
在創建連接線之前先定義好不同的連接線分別代表的關聯關系(一對一、一對多和多對多)。在創建 某種連接線時,根據該連接線代表的關聯關系,分別對連接線兩端模型對應的類進行上面所述的方法驗證 ,如果符合的話,就允許創建該連接線,否則,撤銷剛剛建立的連接線。
一個 Schema 驗證的例子
該部分給出了一個通過 Schema 定義來驗證連接線有效性的例子。 可以直接使用 EMF 導入Schema 文 件,Schema 文件中定義的各個復雜類型和其元素都被生成對應的模型,模型間的關系和 Schema 文件中 定義的一樣。圖4是一個定義復雜類型 A 的 Schema 文件,可以看出類型 A 裡面包含 B1、B2、 sourceConn 和 targetConn 元素和 a 屬性。 當編寫一個 xml 文檔用到 A 標簽時,它的後繼標簽只能 是 B1、B2、sourceConn 和 targetConn。同時通過 Schema 文件裡對各個元素最大出現次數屬性 maxOccurs 的賦值可知:B1 的出現次數最多只能是一次,而其它標簽可以出現任意多次。 如果 A 的後 繼標簽中出現了 B3 之類的標簽時,將不能通過 Schema 的驗證,說明該 xml 文檔是非法的。
圖 4. 復雜類型 A 的 Schema 文件內容
我們定義連接線代表的關聯關系是:連接線發起端和目的端的模型分別對應的標簽符合 Schema 中的 定義。 假如現在有四個圖形分別對應著模型 A、B1、B2 和 B3。 如果在它們之間建立連接線的時候,想 讓連接線目的端模型 B1 作為發起端模型 A 的子模型,即後台保存的 XMI 文檔中,B1 對應的標簽是作 為子標簽嵌套在 A 對應標簽裡的,這時就需要用Schema 驗證該 XMI 文檔的合法性。現在想在創建連接 線的時候進行動態的驗證,即如果連接線發現目的端和發起端對應的模型不符合 Schema 中的定義時,該 連接線就不被創建。由於B3 不是 A 的元素,所以在 A 和 B3 之間創建連接線時該連接線就不能創建, 如圖5所示。
圖 5. A 和 B3 不允許建立連接
圖4 Schema 文件中復雜類型 A 對應的模型生成的 Java 類中有 interface AType 和 class ATypeImpl(此處為真實的類名)。當規定元素 B1 的最大出現次數為1的話,則在 interface AType 和 class ATypeImpl 中會有相應的 void setB1(B1Type) 和 B1 getB1( ) 方法;若元素B2 的最大出現次數 不限的話,則在 interface AType 和 class ATypeImpl 中會有相應的 EList getB2( )方法。
另外,類型 A 裡面包含了元素 sourceConn 和 targetConn。 在 interface AType 和 class ATypeImpl 中會有 EList getSourceConn( ) 和 EList getTargetConn( ) 方法。這兩個方法是為了維護 從 A 出發或者結束到 A 的連接線。
表1中列出了 A 和 B 之間是否允許創建連接線的真值表,可以很清楚地看出滿足什麼條件下 A 和 B 之間才可以建立連接線。 本文的最後給出了對連接線進行 Schema 驗證的詳細步驟。
表 1. 創建連接線真值表
首先建一個 ConnectionCreateCommand 類,內部創建一個成員方法 boolean validateConnection( ) ,該類繼承了 GEF 的 Command 類。根據以上的真值表,把驗證過程分為三個部分:判斷 A 是否包含 B 、判斷 A 和 B之間是否存在連接線、判斷 B 在 A 中出現的次數限制。
判斷 A 是否包含 B
獲取連接線兩端的模型對應的Java類,即 ATypeImpl 和 BTypeImpl;
獲取 ATypeImpl 類(包括其父類)的所有方法名中包含 “set” 或者 “get” 字符串的成員方法集 合,並循環判斷方法集合中是否含有 “void setB(B)” 或者 “EList getB( )” 方法;
如果2)中的條件成立則說明 A 包含 B,否則說明 A 不包含 B。
判斷 A和 B之間是否存在連接線
在 ATypeImpl 中調用 getSourceConn( ) 方法返回一個從 A 出發的連接線的集合;
獲取該集合中的每個元素(連接線),判斷其目的端對應類的名稱是否為 BTypeImpl;
如果2)中的條件成立則說明 A 和 B 之間已經存在連接線,否則說明不存在連接線。
判斷 B在 A中出現的次數限制
同 “判斷 A是否包含 B” 中的一樣,獲取 ATypeImpl 類(包括其父類)的所有方法名中包含 “set ” 或者 “get” 字符串的成員方法集合;
循環判斷方法集合中是否含有 “void setB(B)” 或者 “EList getB( )” 方法;
如果含有 “void setB(B)” 方法,說明 B 在 A 中最多出現一次;
如果含有 “EList getB( )” 方法,說明 B 在 A 中可以出現多次。
由以上三個判斷,並且參照表1,可以確定 A 和 B 之間是否可以創建連接線,即確定 validateConnection( ) 方法的返回值。 在 ConnectionCreateCommand 的回調函數 execute( ) 中判斷 validateConnection( ) 方法的返回值,如果為 false 的話,則不創建該連接線;反之如果為 true 的 話,則創建該連接線,並且
在 ATypeImpl 中調用 getSourceConn( ) 方法返回一個從 A 出發的連接線的集合,然後向該集合中 添加一個剛才創建的連接線;
在 BTypeImpl 中調用 getTargetConn( ) 方法返回一個目的點是 B 的連接線的集合,然後向該集合 中添加一個剛才創建的連接線。
這樣整個驗證過程就結束了,保證了後台生成的 XML 文件的規范性。
結束語
本文在 GEF 和 EMF 來進行建模和圖形化開發的基礎上,提出了對創建的連接線有效性驗證的方法。 將其用於業務流程設計中能提高規范化設計的效率。 由於本文中所討論的模型都是由 EMF 導入的,具體 的驗證方法也都是依賴於 EMF 生成的 Java 代碼的特定格式,因此如果模型對應的 Java 代碼采用其它 的格式時,該驗證方法就不能適用。如何提出一種通用的連接線驗證機制有待於更進一步的研究。