構建 Eclipse 插件在 EMF 模型中瀏覽內容

http://www.ibm.com/developerworks/cn/opensource/os-eclipse-emf/

代碼見outlook郵件

使用 Common Navigator Framework 和 Eclipse Modeling Framework 操作和瀏覽基於 EMF 的模型中的內容

簡介: 通過本文,瞭解如何使用 EMF.Edit 和 Common Navigator Framework (CNF) 創建基於樹形查看器的模型導航插件。構建一個 Eclipse 插件,使用戶能夠操作和瀏覽基於 Eclipse Modeling Framework (EMF) 的模型的內容。文中對開發插件提供了分步指導,實現了可通過 EMF 編輯框架提取模型內容的適當結構,並在基於 CNF 的視圖部件顯示內容。

通常,EMF 項目資源 — 如 EMF Ecore 模型 — 作爲查看器中的單個對象進行顯示(見 圖 1 的左側)。這種顯示方法的限制是,我們在研究模型時必須要打開相應的編輯器。這一問題在依賴域模型進行開發時顯得尤爲突出。有一種解決方法是,構建自定義的視圖,使我們能夠訪問所需的域模型內容。我們可以從頭開始構建此插件,也可以使用現有的框架減輕開發負擔。下面將分別對創建此類插件的過程進行概要以及詳細的介紹。在本文最後,我們將擁有一個查看器插件,用於瀏覽 Ecore 模型(見圖 1 右側)。


圖 1. 導航器的類型
導航器的類型

背景

我們已經瞭解了此 ModelNavigator 的用途,但在開始開發之前,需要對即將使用到的 Eclipse 組件有一個基本的瞭解。我們的任務是,構建一個樹形的查看器用以顯示模型的層次結構。樹形查看器的詳細信息不在本文的討論範圍之內;請參閱參考資料 瞭解更多細節。一個需要注意的重要方面是,樹形查看器通過稱爲內容提供程序的適配器訪問模型對象,並使用標籤提供程序確定顯示對象的方式。下面的章節詳細介紹瞭如何訪問內容和標籤提供程序的數據,以及如何顯示模型導航器。

EMF.Edit 框架

EMF.Edit 框架通常用於爲 EMF 模型構建編輯器。爲構建這些編輯器,框架提供了命令的代碼生成功能和一些其他類,用於提供對模型的程序化訪問。此框架所公開的另外一組功能 — 也是對我們而言最重要的一組功能 — 是它提供的一些類,使用這些類可以方便地在查看器中顯示 EMF 模型。框架提供這種訪問的方式是使用通用內容和標籤提供程序,它們使用了適配器爲特定類型的 EMF 對象顯示模型。這些類有AdapterFactoryContentProviderAdapterFactoryLabelProvider 類,這些類通過委託瞭解如何瀏覽 EMF 模型的項目提供程序適配器,爲 EMF 對象提供查看器的對象、標籤和圖像。此方案在圖 2 中進行了闡釋。這種方案對我們的項目極其有用,因爲它讓我們不必去了解如何調整模型以適應視圖。我們只需委託這些通用提供程序就可以了。


圖 2. 從 Eclipse 幫助改編的 EMF.Edit
從 Eclipse 幫助改編的 EMF.Edit

框架包含了用於各種 EMF 模型類型的項目提供程序。但是,並非大多數人都希望爲每種模型類型構建單獨的導航器。我們希望從同一個導航器中訪問所有的模型內容。此時就出現了組合適配器工廠。它讓我們能夠顯示來自多個 EMF 模型的對象,具體做法是,提供一個可從多個模型中適配對象集合的適配器工廠。

ComposedAdapterFactory 類是另一個 EMF.Edit 便利類,用作其他適配器工廠的通用接口。我們即將瞭解到,這個類的優勢在於,導航器只需將我們所需要的模型類型的項目提供程序作爲組合適配器工廠的一部分包括進來。然後,組合適配器工廠直接將實現委託給這些其他的提供程序。例如,我們可以讓導航器顯示生成器模型、Ecore 模型、UML 模型等。當查看器試圖顯示這些模型時,內容和標籤提供程序將直接委託給適配器工廠,後者接着委託給適當的項目提供程序,從而使我們的開發更加輕鬆,並擴展 ModelNavigator 以支持多個域模型。

Common Navigator Framework

關於 Common Navigator Framework 的更多信息

Michael Elder 的博客(請參閱 參考資料)中提供了大量有關 Common Navigator Framework (CNF) 的功能和用法的教程。我強烈建議對 CMF 感興趣的開發人員學習一下這些教程。

我們已經擁有了在 ModelNavigator 中訪問模型內容的整個框架,現在所需的另一個組件就是放置導航器的實際的查看器。您可以擴展視圖插件並實現自己的以樹形視圖顯示所需內容的視圖部件。但是,現有的框架就可以將各個域中的內容組合到一個視圖中,使用戶能夠在查看器中操作和瀏覽編輯器模型。這種框架在 Eclipse V3.2 中作爲 CNForg.eclipse.ui.navigator 引入,並允許開發者爲一個導航器貢獻內容、標籤、動作、過濾器,以及其他功能。這就提供了一種集成導航的查看器的方式,並提供了統一的用戶體驗。

CNF 支持用於所有編輯器模型集成程序的查看器,支持非資源驅動的模型內容,並允許用戶選擇希望在集成的查看器中顯示的內容。org.eclipse.ui.navigator.resources 插件就是使用此框架的一個示例,它使用 Project Explorer 視圖的形式。 它爲IResource 模型提供了聲明性的查看器擴展。此框架爲我們提供了構建查看器的最快速方法,它處理了查看器實現的細節,並且只需要我們實現模型的內容和標籤提供程序。它還爲將插件擴展至其他模型內容提供了空間,以及在查看器中使用更高級的框架功能(例如,分類器、過濾器、拖放等。)。

現在可以開始查看這些組件的運作,並開始創建模型導航插件。

簡單的導航器

創建 ModelNavigator 分爲兩個步驟進行。首先,我們將設置插件的基本結構並定義其行爲。然後擴展這個簡單的插件以顯示 EMF 模型內容。

ModelNavigator 項目

創建 ModelNavigator 的第一步是創建插件項目。爲此,我們使用了新的項目嚮導:File > New > Project,選擇Plug-in Project,然後單擊 Next

我們將插件項目命名爲 ModelNavigator,設置其 ID 爲 com.ibm.navigator.example.modelnavigator,激活程序爲com.ibm.navigator.example.modelnavigator.ModelNavigatorPlugin,然後單擊 Finish


圖 3. 創建插件項目
創建插件項目

創建視圖

現在開始開發插件。第一步是創建放置導航器的視圖。爲此,我們對插件進行必要的擴展以創建視圖部件和類別,視圖即顯示在該類別下。在插件的 xml 文件中,我們將添加一個視圖擴展。

在 Extensions 選項卡中:

  • 單擊 Add
  • Extension points 選項卡下面,選擇 org.eclipse.ui.views
  • 創建類別:
    • 右鍵單擊 org.eclipse.ui.views 擴展
    • 選擇 New > Category
    • 將名稱設爲 com.ibm.navigator.example.modelnavigator.mncategory
    • 將 ID 設爲 Model Navigator
  • 創建視圖部件:
    • 右鍵單擊 org.eclipse.ui.views 擴展
    • 選擇 New > View
    • 將 ID 設爲 com.ibm.navigator.example.modelnavigator.mnview
    • 將名稱設爲 Model Navigator View
    • 將類設爲 org.eclipse.ui.navigator.CommonNavigator
    • 並類別設爲 com.ibm.navigator.example.modelnavigator.mncategory
  • 保存對項目的修改

如前所述,我們將一些實現工作轉移給了 CNF。關於設置視圖的方式需要注意的一點是,我們並沒有創建自己的類來擴展視圖的 ViewPart,而是以視圖使用通用導航器類的方式來實現。這也意味着我們在類字段中指向的通用導航器類需要被添加到插件的類路徑中。因此,繼續操作並將org.eclipse.ui.navigator 添加到 Dependencies 選項卡上所需的插件中。

設置默認的行爲併合並 CNF

下一步是配置插件的 CNF 屬性,該屬性用於指定導航器的默認行爲。

首先,我們將視圖與通用導航器查看器關聯。在 Extensions 選項卡中:

  • 單擊 Add
  • Extension points 選項卡下面,選擇 org.eclipse.ui.navigator.viewer 並單擊Finish
  • 創建查看器:
    • 右鍵單擊 org.eclipse.ui.navigator.viewer 擴展
    • 選擇 New > viewer
    • viewerid 設爲我們前面定義的視圖 ID(com.ibm.navigator.example.modelnavigator.mnview

導航器插件的行爲由我們給它綁定的內容、動作,以及使用 CNF(過濾器、分類器等)時插件中設置的其他選項進行管理。在插件中,包含元素選擇哪些擴展對查看器可見。內容擴展告訴框架如何在視圖中顯示內容,而動作擴展告訴框架哪些選項對視圖(上下文菜單等)可用。爲使這個粗略的導航器正常運作,需要添加一些IResource 模型中的內容和動作綁定,爲插件提供 Package Explorer 所熟悉的風格。爲此,我們爲導航器創建了 viewerContentBinding 以添加資源內容擴展,還創建了 viewerActionBinding 以添加資源操作擴展。

要添加 IResource 內容擴展,在 Extensions 選項卡中:

  • 右鍵單擊 org.eclipse.ui.navigator.viewer 擴展
  • 選擇 New > viewerContentBinding
  • viewerid 設爲插件的視圖部件 ID(com.ibm.navigator.example.modelnavigator.mnview
  • 添加內容綁定:
    • 右鍵單擊先前創建的 viewerContentBinding
    • 選擇 New > includes
    • 右鍵單擊先前創建的包含元素
    • 選擇 New > contentExtension
    • 將模式設爲 org.eclipse.ui.navigator.resourceContent

要綁定 IResource 動作擴展,在 Extensions 選項卡中:

  • 右鍵單擊 org.eclipse.ui.navigator.viewer 擴展
  • 選擇 New > viewerActionBinding
  • viewerid 設爲插件的視圖部件 ID(com.ibm.navigator.example.modelnavigator.mnview
  • 添加動作綁定:
    • 右鍵單擊先前創建的 viewerContentBinding
    • 選擇 New > includes
    • 右鍵單擊先前創建的包含元素
    • 選擇 New > actionExtension
    • 將模式設爲 org.eclipse.ui.navigator.resources.*

此時,我們已經將導航器的結構設置完畢。我們瞭解瞭如何告知導航器框架我們希望顯示的內容類型,以及希望導航器如何運作。雖然我們並非一定要添加這些擴展,但是添加 IResource 模型內容和動作後,我們的視圖會更有用些。現在我們還了解了如何將內容綁定到查看器,還可以擴展它以添加自定義或預定義的過濾器。請參閱參考資料 以瞭解有關這些功能的更多信息。這個導航器仍然不是我們所需要的,因此我們要添加在查看器中顯示 EMF 模型的功能。

添加 EMF 域內容

要在導航器中顯示 EMF 域模型的內容,我們需要告知框架需要哪個模型,如何獲取此模型中的信息,以及顯示信息的時機/方式。這裏涉及創建一個內容擴展,此擴展可被導航器內容服務用於顯示 EMF 域模型。通過創建org.eclipse.ui.navigator.navigatorContent 插件的擴展可完成此工作。

導航器內容擴展定義了內容提供程序和標籤提供程序,可以使用這些程序爲 EMF 模型的元素提供子對象和父對象。該擴展還定義了何時調用此擴展提供子對象 triggerPoints 或父對象 possibleChildren。我們還可以修改導航器內容的很多其他屬性以更改插件的行爲,如動作提供程序、通用嚮導、過濾器等。但這些屬於附加特性,對於我們創建 ModelNavigator 插件而言不是必需的。

首先,我們需要創建新的內容擴展,在其中指定通用導航器使用的內容和標籤提供程序,稍後我們將實現這些類。

要添加 navigatorContent 擴展,在 Extensions 選項卡中:

  • 單擊 Add
  • Extension points 選項卡下面,選擇 org.eclipse.ui.navigator.navigatorContent 並單擊Finish
  • 創建導航器內容:
    • 右鍵單擊 org.eclipse.ui.navigator.navigatorContent 擴展
    • 選擇 New > navigatorContent
    • 將 ID 設爲 com.ibm.navigator.example.modelnavigator.emfModelContent
    • 將名稱設爲 EMF Model Content
    • 將內容提供程序設爲 com.ibm.navigator.example.modelnavigator.MNViewContentProvider
    • 將標籤提供程序設爲 com.ibm.navigator.example.modelnavigator.MNViewLabelProvider
    • 將優先級設爲 normal
    • 將 activeByDefault 設爲 true

我們所實現的類接下來將會用作此導航器內容的內容提供程序和標籤提供程序。

組合的適配器工廠

我們將使用 EMF.Edit 框架執行內容提供程序和標籤提供程序的實際工作。因此,需要創建的內容提供程序類和標籤提供程序類應爲 EMF 編輯框架中適配器工廠的子類。在創建內容提供程序類和標籤提供程序類 —— 它們分別委託給AdapterFactoryContentProviderAdapterFactoryLabelProvider —— 之前,我們應記住,這些適配器工廠是使用一系列的項目提供程序爲不同的 EMF 模型所實例化的。這意味着我們需要首先創建這些提供程序作爲組合的適配器工廠。創建一個新類(File > New > ClassMNComposedAdapterFactory.


圖 4. 創建實現類
創建實現類

我們還需要將 org.eclipse.emf.codegen.ecore.ui 添加到 Dependencies 選項卡上所需的插件中。這使插件可以使用不同 EMF 模型類型的ComposedAdapterFactory 項目提供程序和其他的 EMF.Edit 便利類。然後,MNComposedAdapterFactory 類的實現將如清單 1 所示。


清單 1. ComposedAdapterFactory 類
                
...
public class MNComposedAdapterFactory
{
    private static ComposedAdapterFactory mnCompAdapterFactory;

    public final static ComposedAdapterFactory getAdapterFactory()
    {
        if (mnCompAdapterFactory == null)
            mnCompAdapterFactory = new ComposedAdapterFactory(createFactoryList());
        return mnCompAdapterFactory;
    }

    public final static ArrayList<AdapterFactory> createFactoryList()
    {
        ArrayList<AdapterFactory> factories = new ArrayList<AdapterFactory>();
        factories.add(new ResourceItemProviderAdapterFactory());
        factories.add(new EcoreItemProviderAdapterFactory());
        factories.add(new ReflectiveItemProviderAdapterFactory());
        return factories;
    }
}

需要注意的重要一點是,我們創建了一個靜態方法用於創建一系列的項目提供程序。在這些提供程序中,我們希望適配器工廠委託給 EcoreItemProviderAdapterFactory,以便爲 Ecore 模型提供內容和標籤。

內容和標籤提供程序

使用類似的方法創建我們在 navigatorContent 擴展詳細信息中指定的內容和標籤提供程序類。

從內容提供程序開始(MNViewContentProvider),單擊擴展詳細信息面板中提供程序名旁邊的鏈接創建類,或使用 File > New > Class 對話框創建類。我們沒有爲內容提供程序實現 ITreeContentProvider 接口,而是使用 EMF.Edit 類。爲此,更改MNViewContentProvider 類,使其擴展 AdapterFactoryContentProvider 類。AdapterFactoryContentProvider 類已經知道如何實現ITreeContentProvider 接口,實際上,它的實現方式是委託給一個適當的提供查看器內容的項目提供程序。

AdapterFactoryContentProvider 類的構造函數中需要一個適配器工廠。因此我們不能對內容提供程序使用隱式的構造函數。我們必須顯式地聲明內容提供程序構造函數並使用所需的參數調用超類構造函數。聲明構造函數後,將super(MNComposedAdapterFactory.getAdapterFactory()); 添加到其中。這個類中我們需要重點關注的其他方法有getChildrengetParenthasChildrengetElements

查看器需要顯示域對象的子元素時將調用 getChildren 方法。它將返回一組域對象,這些域對象是參數元素的子元素。與此類似,調用getElements 方法可以獲得參數元素的域對象。這二者的運作原理類似;只是調用的情形不同。要實現 getChildren 方法,我們只需要求AdapterFactoryContentProvider 返回給定父元素 URI 的子元素。getChildren 方法如清單 2 所示。對於getElements 方法,我們只需返回對這個 getChildren 方法的調用。


清單 2. getChildren 實現
                
...
public Object[] getChildren(Object parentElement) 
{
    if (parentElement instanceof IFile)
    {
        String path = ((IFile)parentElement).getFullPath().toString();
        URI uri = URI.createPlatformResourceURI(path, true);
        parentElement = resourceSet.getResource(uri, true);
    }
    return super.getChildren(parentElement);
}
...
	

爲在樹形視圖中顯示內容,下面兩個重要步驟將確定視圖中的對象何時擁有下述條件的子對象:需要進行顯示並且能夠將一組子對象關聯到父對象。這就使查看器能夠控制域對象的狀態,而不用考慮域對象是否展開或是被破壞。在本文的例子中,可將它轉換爲擁有一組子包的 Ecore 模型。這些包中的類對象與相同的父包相關聯。我們在清單 3 中實現了這一功能。


清單 3. getParent 實現
                
...
public Object getParent(Object element)
{
    if (element instanceof IFile)
        return ((IResource)element).getParent();
    return super.getParent(element);
}

public boolean hasChildren(Object element) 
{
    if (element instanceof IFile)
        return true;
    return super.hasChildren(element);
}
...

這裏,我們可能看見一些錯誤,即,無法解析當前使用的資源。爲了解決這個問題,將 org.eclipse.core.resources 添加到 Dependencies 選項卡上所需的插件中。另外,爲了使內容提供程序獲取視圖中文件資源的 URI,我們需要使用平臺中的資源集。我們創建了單個的靜態資源集實現,使用該實現將private static ResourceSetImplresourceSet = new ResourceSetImpl(); 添加到MNViewContentProvider 類中可獲得這些資源。

標籤提供程序與內容提供程序具有相同的格式,因此我們直接將工作委託給 EMF.Edit 框架中的 AdapterFactoryLabelProvider 類。與內容提供程序相同,標籤提供程序接受域對象作爲它的參數,但是後者所返回的ImageString 應與查看器中的這個對象相關聯。如果要對查看器再做些定製,可以通過編程控制何時希望委託獲取名稱和圖標的任務,以及何時希望爲對象提供自己的圖表。出於本文討論的目的,我們不做定製。我們只需要 EMF 模型所提供的圖像和文本。要實現標籤提供程序,我們使用爲內容提供程序準備的項目提供程序列表調用超類,然後將所有對對象的圖像或描述的調用委託給AdapterFactoryLabelProvider 超類。


清單 4. 標籤提供程序
                
...
public MNViewLabelProvider() 
{
    super(MNComposedAdapterFactory.getAdapterFactory());
}

public Image getImage(Object element) 
{
    return super.getImage(element);
}

public String getText(Object element) 
{
    return super.getText(element);
}
...

將各個部分組合起來

此時,我們已經在插件中完全實現了內容擴展。讓視圖使用內容擴展需要執行另外兩個步驟。首先,我們必須定義事件以通知使用這個特殊的內容擴展。其次,我們必須將導航器內容綁定到視圖。

通過將 <possibleChildren /><triggerPoints /> 元素添加到導航器內容擴展中,當能夠顯示類中所描述的域模型時,我們可以向 CNF 發出通知。這兩個元素可以很容易地定義爲插件 xml 文件中的 Eclipse 核心表達式。在本文的例子中,當查看器包含有作爲 EMF 模型的實例的對象時,將調用導航器內容。因爲目前我們只希望瀏覽 Ecore 模型,所以可以將其處理爲一個簡單的表達式,用於檢查資源的擴展是否爲 Ecore。可能得到的子元素規定了擴展何時爲查看器中的對象提供父對象,在本文的例子中,這些對象指的是 EMF 模型對象或資源的實例。


清單 5. 觸發點和可能的子元素
                
<triggerPoints> 
    <or>
        <and> 
            <instanceof value="org.eclipse.core.resources.IResource"/> 
            <test 
                forcePluginActivation="true" 
                property="org.eclipse.core.resources.extension" 
                value="ecore"/> 
        </and> 
    </or>
</triggerPoints> 
<possibleChildren> 
    <or> 
        <instanceof value="org.eclipse.emf.ecore.resource.Resource"/>
        <instanceof value="org.eclipse.emf.ecore.EObject"/>  
    </or> 
</possibleChildren>
	

最後,將我們創建的導航器內容綁定到視圖,我們將使用先前同樣的方法將 IResource 模型內容擴展包含到查看器內容綁定。

要將導航器內容 com.ibm.navigator.example.modelnavigator.emfModelContent 綁定到實際視圖,在 Extensions 選項卡中執行以下操作:

  • 擴展 org.eclipse.ui.navigator.viewer 元素
  • 擴展 viewerContentBinding 元素
  • 創建新的內容擴展:
    • 右鍵單擊 includes 元素
    • 選擇 New > contentExtension
    • 將模式設爲先前定義的 navigatorContentcom.ibm.navigator.example.modelnavigator.emfModelContent
  • 單擊 Save

現在,將 EMF 模型內容添加到通用導航器中的步驟已基本結束。我們現在擁有的 ModelNavigator 插件應該可以顯示來自 IResource 模型和 EMF Ecore 模型中的對象。

測試導航器

測試 EMF 項目

要運行此測試,您可以選擇將現有的 EMF 項目導入到運行時 Eclipse 工作區,或者創建新項目。EMF Developers Guide 教程向您展示瞭如何創建導入 UML 模型的新項目,可以選擇下載並安裝。

我們需要運行對象並在新視圖中顯示 Ecore 模型以確保 ModelNavigator 可以正常工作。對於本測試,我們需要創建在 Model 導航器中顯示的 EMF 對象。我們將使用基於SchoolLibrary UML 文件的示例(請參閱 參考資料)。

將 ModelNavigator 項目作爲 Eclipse 應用程序運行,然後:

  • 在新的工作臺中,單擊 Window > Show View > Other 顯示 ModelNavigator 視圖
  • 找到 Model Navigator 類別並單擊 Model Navigator View
  • 單擊 OK

要在運行時工作區中創建 EMF 項目,請執行以下步驟:

  • 單擊 File > New > Project 創建新項目
  • 擴展 Eclipse Modeling Framework 元素
  • 單擊 EMF project 並選擇 Next
  • 將項目命名爲 SchoolLibrary 並選擇 Next
  • 選擇 Rose class model 作爲模型導入程序並選擇 Next
  • 對於模型 URI,瀏覽到下載 schoollibrary.mdl 文件的位置
  • 單擊 Next
  • 選擇 librarySchoolLibrary 包,如圖 5 所示。
  • 單擊 Finish

圖 5. 創建 EMF 測試項目
創建 EMF 測試項目

如果一切按預想進行,我們應該可以在 Model Navigator 視圖中看見新的 SchoolLibrary 項目。更重要的是,如果導航器按預期情況運行,我們就能夠擴展項目的模型目錄,並且能夠瀏覽導航器視圖中的librarySchoolLibrary Ecore 文件的內容。導航器應類似於圖 6 所示。


圖 6. 測試 ModelNavigator
測試 ModelNavigator

更新同步化

我們已經完成了設定的任務:在查看器中顯示 EMF 模型內容。但是,當我們真正地在編輯器中打開 Ecore 文件並開始進行更改時會出現什麼情況?就目前而言,什麼也不會發生。ModelNavigator 對於針對其當前顯示的模型做出的更改沒有反應,也不會刷新。要讓它刷新當前顯示的模型,我們必須讓查看器意識到編輯器中做出了更改,並強制刷新。實現這種同步的一種方法是將資源更改偵聽程序添加到內容提供程序中。下面的章節簡要介紹了所需的步驟。

資源更改偵聽程序

我們讓內容提供程序響應模型中的更改的方法是,允許其偵聽模型文件資源中的更改(本例中指的是 Ecore 文件)。爲此,我們需要在 MNViewContentProvider 類中實現IResourceChangeListenerIResourceDeltaVisitor 接口。

資源發生更改時調用 resourceChanged(IResourceChangeEvent event) 方法,並傳遞描述資源更改的一組事件。這組更改爲我們提供了在不同時間點資源樹的差別。我們可以使用它查看資源樹中的更改,並根據已更改的資源類型確定所需的操作。我們實現resourceChanged(IResourceChangeEvent event) 方法,如清單 6 所示,它爲我們提供了已更改的資源集,並允許我們訪問資源的變化。


清單 6. 資源更改的方法
                
...
try 
{
    IResourceDelta delta = event.getDelta();
    delta.accept(this);
} 
catch (CoreException e) 
{
...

更改被訪問後,調用 visit(IResourceDelta delta) 方法。對於我們的插件,我們只關注已更改的資源是擁有 Ecore 擴展的 IResource 文件的情形。如果更改的資源是 Ecore 模型,那麼需要刷新 ModelNavigator 以反映這些更改。爲此,我們提取更改後的文件,獲取其資源,然後重新加載此資源所表示的模型。


清單 7. 訪問方法
                
...
IResource changedResource = delta.getResource();
if (changedResource.getType() == IResource.FILE 
   && changedResource.getFileExtension().equals("ecore"))
{
    try
    {
        String path = ((IFile)changedResource).getFullPath().toString();
        URI uri = URI.createPlatformResourceURI(path, true);
        Resource res = resourceSet.getResource(uri, true);
        res.unload();
        res.load(resourceSet.getLoadOptions());
    }
    catch(IOException ie)
    {
        System.out.println("Error reloading resource - " + ie.toString());
    }	
    return false;
}
return true;
...

要使用此資源更改偵聽程序,我們需要將其添加到內容提供程序中,在適當的時候刪除。在 MNContentProvider 類中,通過在構造函數中插入ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); 添加偵聽程序。另外,將ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); 插入到類的處理方法中。雖然這樣應該可以處理對資源更改的更新,但是請記住這是一種處理更改的簡單方法。通過只更新內容提供程序中受影響的模型,我們將不查看此模型的依賴項以更新這些元素。這是保持導航器更新的一種基本方法。

測試模型更新

要測試 Ecore 模型中的更改在查看器中得到了反映,我們將只需重新運行應用程序並開始做出更改即可。

將 ModelNavigator project 作爲 Eclipse 應用程序運行,然後:

  • 瀏覽到模型目錄下的 schoollibrary.ecore
  • 在編輯器中打開 schoollibrary.ecore 文件
  • 展開 schoollibrary.ecore 節點
  • 右鍵單擊 SchoolLibrary 包節點
  • 選擇 New Child > EClass
  • Properties 選項卡上,將此新類命名爲(Test
  • 保存文件

此項更改應觸發了資源更改偵聽程序代碼的執行並強制 ModelNavigator 更新模型。我們應該看見 ModelNavigator 視圖重新加載自身。如果我們擴展樹並瀏覽到 schoollibrary.ecore 文件,我們應該看見所做的更改,如圖 7 所示。


圖 7. 進行資源更新的 ModelNavigator
進行資源更新的 ModelNavigator

後續步驟

對 ModelNavigator 實現的最後一個 — 也是非常簡單的一個 — 擴展是使用 Composed Adapter Factories。我們所創建的導航器將顯示 Ecore 模型,但是其他類型的 EMF 模型怎麼辦?這實際上是一項非常簡單的任務,得益於 EMF.Edit 框架的實現方式。我們可以以最小的更改添加對更多域模型的支持,具體做法是,將合適的適配器工廠添加到ComposedAdapterFactory 併爲這些新模型設置觸發器。

擴展組合的適配器工廠

例如,要將 genmodel 對象添加到導航器中,我們只需將生成器模型項目提供程序添加到內容提供程序和標籤提供程序所使用的適配器工廠列表中。在MNComposedAdapterFactory 類中,將 factories.add(newGenModelItemProviderAdapterFactory()); 添加到createFactoryList() 方法中。模型導航器需要更改的惟一一處是告訴導航器內容何時觸發此內容提供程序。我們使用與觸發點相同的資源擴展檢查策略,如下所示。


清單 8. genmodel 的觸發點
                
<and> 
    <instanceof value="org.eclipse.core.resources.IResource"/> 
    <test 
        forcePluginActivation="true" 
        property="org.eclipse.core.resources.extension" 
        value="genmodel"/> 
</and> 
		

通過將這個新觸發器表達式添加到當前觸發器的正下方,我們在這些情況下使導航器內容相關:對象是 IResource 的實例且擁有一個 Ecore 擴展,或者對象是IResource 的實例且擁有一個 genmodel 擴展。如果我按上面介紹的方法重新運行此項目,我們將發現現在可以瀏覽 Ecore 和 Generator EMF 模型(見圖 8)。但是,這種功能在我們的簡單資源更新方案中並沒有提供,因此更新在 ModelNavigator 視圖中的不同模型之間並不同步。


圖 8. 具有多個模型的 ModelNavigator
具有多個模型的 ModelNavigator

結束語

我們已經完成創建插件以便在查看器中顯示 EMF 模型內容的任務。我們通過使用 Eclipse 提供的框架展示瞭如何簡化此過程,並進行了進一步的探討,即,將一些簡單的特性添加到插件中。本文只研究了這些框架所公開的功能中的一部分,我強烈建議所有讀者都深入瞭解一下這些 Eclipse 組件。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章