天天看點

EMF介紹系列(七、.Edit初步)

EMF除了生成模型部分的接口和實作類(不妨稱作“核心模型”)以外,還生成一個名稱以.Edit結尾的項目,包含一些與核心模型和編輯器關系都十 分緊密的代碼。這部分代碼經過了精心設計,可重用的程度是相當的高。它們不僅在EMF生成的編輯器項目裡大量被用到,我們自己在擴充編輯器的時候也應該充 分利用。

線上商店的例子裡,com.my.shop.edit項目裡包含一個ItemProviderAdapterFactory類和一組 ItemProviderAdapter的子類,後者是和核心模型的接口一一對應的,例如核心模型的Shop、Category和Product分别對應 ShopItemProvider、CategoryItemProvider和ProductItemProvider。這篇文章主要介紹一下這些 ItemProvider,而關于ItemProviderAdapterFactory的内容将在以後的文章裡專門介紹,其實顧名思義, ItemProviderAdapterFactory的作用主要就是生成ItemProvider。事實上在構造EMF應用程式時,我們經常要修改 ItemProvider裡的代碼,而ItemProviderAdapterFactory則很少改動。

圖1 EMF生成的.Edit項目

注意:.Edit項目裡ItemProviderAdapter的子類名稱裡省略了Adapter這個單詞,例如 CategoryItemProvider而非CategoryItemProviderAdapter,你心裡應該清楚它是一個Adapter,因為它 确實實作了Adapter接口。EMF裡另外專門有一個ItemProvider類是為非Adapter類型準備的,在這篇裡說的 ItemProvider不是指它,而是指XXXItemProvider,也就是ItemProviderAdapter的子類。

從圖1中不難看出,ItemProvider構成了.Edit項目的主要部分,這些ItemProvider具有以下幾個作用。

一、實作了JFace中ContentProvider和LabelProvider的功能

JFace檢視器(Viewer)是對swt中控件的一種包裝,例如TableViewer是對Table的包裝,TreeViewer是對Tree的包 裝,等等,通過這種方式可以将控件與顯示在控件中的資料在一定程度上分離,進而友善資料顯示的更新。相當多的Eclipse應用程式都是通過JFace查 看器顯示資料的,與檢視器關聯的ContentProvider和LabelProvider分别控制檢視器中顯示的哪些資料以及每條資料的顯示方式。

以TreeViewer的ContentProvider為例,在JFace裡應該實作ITreeContentProvider接口,這個接口定 義了getParent()、hasChildren()和getChildren()這三個方法;在EMF裡有 ITreeItemContentProvider接口與之對應,這個接口同樣具有這三個方法,.Edit部分的每個ItemProvider都實作了這 個接口,因為EMF已經完全知道我們的模型結構,是以這三個方法在ItemProviderAdapter類裡已經實作好了。不過 ITreeItemContentProvider畢竟不能直接交給JFace的TreeViewer來使用,是以EMF提供了一個 AdapterFactoryContentProvider來做适配工作,你可以在編輯器的代碼裡看到如何使用它。

二、提供了關聯對象的屬性表

每個ItemProvider的getPropertyDescriptors()方法傳回在屬性視圖裡顯示的屬性清單,清單裡的每個元素是一個 ItemPropertyDescriptor對象,它決定了每個屬性的标簽、描述、圖示以及是否可編輯。EMF為生成的代碼會幫我們把模型定義裡的每個 屬性都顯示在屬性清單裡,如果希望隐藏某些屬性,可以通過修改這個方法移除之。

以Product為例,ProductItemProvider的getPropertyDescriptors()方法裡包含這樣六條語句,分别代表産品名稱、價格、描述、是否有貨、評價以及顔色這六個屬性,如果你想讓顔色屬性在屬性清單裡消失,隻要删除最後一句即可。

addNamePropertyDescriptor(object);

addPricePropertyDescriptor(object);

addDescriptionPropertyDescriptor(object);

addAvaiablePropertyDescriptor(object);

addScorePropertyDescriptor(object);

addBackgroundPropertyDescriptor(object);

三、生成編輯模型的各種指令

在ItemProviderAdapter基類裡有很多createXXXCommand()方法,如果你用過GEF應該對這些名稱不陌生,因為在 EditPolicy裡也有類似的方法。我們知道,為了實作Undo/Redo功能,對模型的每個改變都應該使用Command實作,然後把 Command儲存在Command棧裡,每個Command對象儲存Undo/Redo自己的資訊。ItemProviderAdapter相當于一個 生産這些Command的工廠,使用者對模型編輯的請求都将通過它轉換為對應的Command,例如使用者在屬性視圖裡修改了一個屬性的值,當按下回車後,會 調用該對象關聯的ItemProvider類的createSetCommand()方法生成一個SetCommand對象。

注意:在createCommand()方法裡會調用getChildrenFeatures()方法,而在實作ContentProvider的getChildren()時也需要這個方法,是以這個方法的傳回結果同時影響ItemProvider的這兩項功能。

四、将模型的改變通知到負責顯示模型的視圖

在一個Eclipse應用程式裡經常會有很多個檢視器顯示模型,無論使用者怎樣修改模型,要讓這些檢視器裡顯示的内容總是目前的模型,最好的辦法是讓檢視器能夠響應模型的變化。ItemProvider作為監聽器可以很好的完成這個任務。

模型發生改變時,與被修改的對象相關聯的ItemProvider的notifyChanged()方法被調用,事件立即被通知給 ItemProviderAdapterFactory,後者是整個模型的事件處理機構,所有的ItemProvider都是通過 ItemProviderAdapterFactory建立并注冊為監聽器的,是以ItemProviderAdapterFactory可以把事件通過 fireNotifyChanged()通知給所有這些監聽器的notifyChanged()方法去消化。圖2展示了這個通知過程,此圖來自 《Eclipse Modeling Framework: A Developer's Guide》第3.2.4節中的圖3.10。

圖2 ItemProvider的通知流程

最後,ItemProvider還有一個collectNewChildDescriptors()方法,這個方法決定了在編輯器裡,模型裡對應的 那個對象可以建立哪些子元素。例如線上商店模型裡,Category對象的子元素是Category和Product,那麼使用者在編輯器裡右鍵點選一個 Category對象選擇“New Child”時,就會出現“Category”和“Product”這兩個選項。有些場合我們想隐藏其中一些選項時,就可以修改這裡的代碼。

繼續閱讀