GMF入門資料超全

最近發現一個比較全的GMF資料分享一下!

  
        上圖中紫色的橢圓結點在ecore模型中代表Method,在屬性視圖中可見它的一些屬性。如果要使TimeProperty屬性值爲"NONE" 時,TimeUnit和Value值分別變爲"NONE"和0.0,應該如下修改MethodItemProvider類中的 notifyChanged()方法(紫色的代碼是添加的,其他均爲GMF生成的):

    public void notifyChanged(Notification notification) {
        updateChildren(notification);

        switch (notification.getFeatureID(Method.class)) {
            case EventPackage.METHOD__PROPERTY_NAME:
         {
                //如果該項設置爲NONE,那麼TimeUnit和Value項應該分別設置爲NONE和0.0
                Method owner = (Method)notification.getNotifier();
                TimeProperty newValue = (TimeProperty)notification.getNewValue();
                if(newValue.equals(TimeProperty.NONE))
              {
                    owner.setTimeUnit(TimeUnit.NONE);
                    owner.setValue(0.0);            
                }
             }
            case EventPackage.METHOD__VALUE:
            case EventPackage.METHOD__TIME_UNIT:
            case EventPackage.METHOD__CLASS_NAME:
            case EventPackage.METHOD__METHOD_NAME:
            case EventPackage.METHOD__PARAMETERS:
            case EventPackage.METHOD__RETURN_TYPE:
                fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), false, true));
                return;
        }
        super.notifyChanged(notification);
    }

  MethodItemProvider類位於genmodel生成的edit項目中,它相當於Property View的ContentProvider。

  • 17:11
  • 評論 / 瀏覽 (0 / 610)
2008-07-09

GMF:基於Reference的連接

    博客分類:
  • GMF

GMF中的Link有兩個,分別是基於Type的和基於Reference的。對於前者,在ecore模型中有一個類對應這個連接,創建一個連接即創建了該類的一個實例,試想,如果一個模型比較複雜,有很多連接時,這種方法要在模型中要爲連接建立很多與業務無關的類,使得模型非常複雜;對於後者,沒有必要爲每個連接在ecore中建立一個類,而是設置該連接對應的Reference即可。下面用一個school的模型來說明基於Reference的連接的建立方法,這個模型非常簡單,如下圖(用gmf插件生成的對應school.ecore的school.ecore_diagram):一個學校裏邊有很多老師和學生,每個老師會指導多個學生,這裏的指導關係是Teacher類的一個Reference,名字爲tutoringStudents,我們打算在GMF編輯器中用Node來表示老師和學生,老師對學生的指導關係用Link來表示。

    該模型命名爲school.ecore, 然後生成school.genmodel,再按照常規方法建立school.gmfgraph, school.gmftool.
    最後建立最關鍵的school.gmfmap,首先爲mapping元素建立兩個Top Node Reference,分別對應Teacher和School,然後建立代表老師指導學生關係的link mapping,其屬性頁中各項按照下圖指定值:
   
    注意,Domain meta information中前三項都空着,只在最後一項中指定代表該連接的Reference即可。Validate無錯後,生成school.gmfgen,在生成Diagram code,最後運行效果圖如下:

      看一下保存semantic model的school文件: 

<?xml version="1.0" encoding="UTF-8"?>
<School:School xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:School="http://example.School">
  <teachers name="ProfessorLi" tutoringStudents="//@students.0 //@students.1"/>
  <students name="Tom"/>
  <students name="Mary"/>
</School:School>
  • 17:10
  • 評論 / 瀏覽 (0 / 375)

要讓GMF編輯器中的圖元更加生動,可以通過在gmfgraph中增加一些元素(比如前景色)來定製圖元的顯示形式,但是有些特徵(比如字體)無法僅僅通過gmfgraph來完成,這就需要我們修改GMF生成的Diagram Code來達到所需要的效果。如下圖所示:
 
   1.前景色:
      圖中紫色的橢圓代表Method類型的元素,起前景色可以通過gmfgraph來定製:
     
    2.線條寬度(2),線條種類(LINE_SOLID):
    
    也可以通過修改MethodEditPart中的內部類MethodFigure的構造函數來完成1和2兩個特徵的定製:

public MethodFigure() {
    this.setFill(true);
    this.setFillXOR(false);
    this.setOutline(true);
    this.setOutlineXOR(false);
    this.setLineWidth(2);
    this.setLineStyle(Graphics.LINE_SOLID);
    this.setForegroundColor(METHODFIGURE_FORE);
    createContents();
}


   3.字體: 要通過修改MethodNameEditPart的setLabelTextHelper方法來完成。
    

    protected void setLabelTextHelper(IFigure figure, String text) {
        if (figure instanceof WrapLabel) {
            ((WrapLabel) figure).setText(text);
            
            FontData fd = new FontData();
            fd.setStyle(SWT.BOLD);
            ((WrapLabel) figure).setFont(new Font(null, fd));

        } else {
            ((Label) figure).setText(text);
        }
    }

  
     4.結點默認大小:由於圖中兩個黃色的“BEGINING"和"ENDING"結點中的文字不需要修改,因此這兩個結點的默認大小可以根據字符串的寬度和高度來設置(GMF默認的高度和寬度是40,40)。修改BeginingEditPart中的createNodePlate方法(注:由於編輯器中的結點Shape是放在Plate上,Plate再放在畫布上的,因此Plate的大小決定了編輯器中圖元的大小,修改Shape沒用)。

    protected NodeFigure createNodePlate() {
        DefaultSizeNodeFigure result = new DefaultSizeNodeFigure(getMapMode()
                .DPtoLP(EventConstant.BEGINING_DEFAULT_WIDTH), getMapMode().DPtoLP(EventConstant.BEGINING_DEFAULT_HEIGHT));
        return result;
    }
  • 17:09
  • 評論 / 瀏覽 (1 / 540)
** 本文已發表在2006年12月《程序員》雜誌,請勿轉載。**

本文假設讀者有Eclipse平臺應用程序開發經驗,瞭解Eclipse平臺的插件機制,使用EMF和GEF開發過應用程序。在本文中,“Eclipse應用程序”等價於“Eclipse插件”。

Eclipse是一個開源的平臺,從一開始就被設計爲允許各種功能以插件(Plug-in)的形式自由組裝的結構,它凝聚了無數天才的設計,目前僅 Eclipse.org下的子項目就有數十種,全世界範圍內的各種插件則數以千計。隨着Eclipse開源社區影響力的不斷增強,國內外有越來越多的實際應用構造在Eclipse平臺上,同時有更多組織和個人加入到爲Eclipse貢獻力量的行列中,這樣的良性循環形成了以Eclipse平臺爲中心的巨大生態系統。

在衆多插件中,一些插件存在的目標是爲了簡化開發人員開發其他Eclipse插件的過程,接下來要介紹的GMF就是這樣一個插件。

一、GMF與EMF、GEF

開發過Eclipse插件的朋友們一定知道EMF和GEF這兩個插件項目。EMF (Eclipse Modeling Framework)可以幫助我們以模型驅動的方式開發Eclipse應用程序,具體來說,它定義了一套名爲Ecore的元模型(類似UML元模型,包括包、類型、屬性和方法等元素),用Ecore定義的模型可以被EMF轉換爲運行在Eclipse平臺上的Java代碼,這些代碼實現了一套消息通知機制,是構造一個可靠應用不可缺少的基礎。

絕大多數應用程序都有一個業務模型,不論以什麼方式描述,這個模型都是整個應用程序的基礎。利用EMF強大而完善的建模能力,我們不僅可以更直觀方便的構造業務模型,更重要的是,得益於EMF的代碼生成能力,在整個應用程序的開發週期裏,這個模型都能夠保持最新的版本,開發人員在任何時候都能很快的瞭解整個業務。

雖然對開發人員來說,業務邏輯是應用程序的關鍵,但直接和用戶打交道的還是程序的UI部分。我們經常說一幅圖勝過千言萬語,這句話在應用程序的UI 設計裏也同樣適用。試想如果我們熟悉的UML類圖是用文本來描述的,我們將多花費多少時間在理清類與類之間的關係上呢。GEF的出現,爲我們在 Eclipse平臺下開發圖形化的應用程序鋪平了道路。

GEF(Graphical Editing Framework)框架具有標準的MVC(Model-View-Control)結構。在模型部分,GEF沒有對開發人員進行任何限制,只要模型發生改變時能夠通知到控制器即可。GEF裏的控制器被稱爲EditPart,它們是應用程序的核心部分,負責將模型的改變反映到視圖,並將用戶在視圖上所做的操作應用到模型。在視圖方面,GEF使用Draw2D,一個輕量級的圖形系統。有了GEF的幫助,要在Eclipse裏開發出類似Visio的圖形化應用程序變得簡單多了。

隨着Eclipse平臺被更多開發人員接受,結合EMF和GEF逐漸成爲了構造GEF應用程序的主要模式,也就是在EMF生成的模型基礎上開發 GEF應用程序,這樣可以在模型部分節約不少的工作量。但也有一些問題,例如兩個框架各實現了一套命令機制來操作模型等等,它們又在一定程度上削弱了結合這兩種技術帶來的改善程度。

GMF(Graphical Modeling Framework)讓我們可以用更簡單的方法實現以前要同時用EMF和GEF開發的應用程序,而結合二者所帶來的的各種問題則不需要關心。同時,藉助 Eclipse的插件機制,GMF還提供了十分豐富的擴展性,便於開發適合特定需求的應用程序。

圖表 1 GMF的運行機制

我們不妨拿房子做比喻來說明他們在構造Eclipse應用程序中的地位。一個完整的圖形化應用程序就像一個漂亮舒適的家,用EMF構造了業務模型後就相當於買到了毛坯房,這時的房子甚至連門都不全,看起來就像EMF爲我們生成的缺省編輯器,十分的簡陋。所以接下來的工作就是裝修,也就是讓應用程序的界面更加漂亮。裝修的風格因人而異,這項花費有時甚至比房子本身的價值更高,在裝修過程中還有可能對房子本身做出一些修改,例如拆掉一面牆或是做一個樓梯等等,可以把GEF的部分比做對客廳的裝修,因爲客人來參觀時對整個房子的第一印象往往來自客廳,所以在客廳上多花些精力十分有必要,GEF幫我們實現的圖形化編輯器就是很大的亮點。然而,不可否認房子的主人每天最多時間是在臥室度過的,所以臥室的佈置也是決定房子是否舒適的重要因素,可以把應用程序裏各種視圖、嚮導、偏好、菜單和工具條等等比作對臥室的裝飾。

至於GMF呢,用它構造圖形化應用程序,就像買了一套“精裝修”的現房,按開發商的承諾您是可以立即入住的,不僅牆壁噴塗了,地板鋪好了,就連整套傢俱和家電也預備齊全。然而開發商給所有人的裝修都是差不多的,頂多提供幾套可選方案,不可能完全滿足你的全部個性化需求。同樣,用GMF生成的程序雖然比EMF生成的好看很多,但還有不少工作是需要自己動手的。

下面,爲了能讓大家對GMF能爲我們做什麼有一個直觀的印象,我們簡單演示一下如何用GMF開發一個流程編輯器。

二、用GMF實現圖形化流程編輯器

GMF是以EMF爲建模基礎的,理所當然業務模型也是一個ecore模型。開始一個GMF項目的第一步就是新建一個空白的EMF項目,在項目裏定義我們的業務模型,這個ecore模型裏,我們定義一個流程(Process)需要的各種元素。爲了節約篇幅,這裏的流程模型元素十分簡單,只包括開始節點(Beginning)、結束節點(Ending)、流程(Process)、活動(Activity)、條件(Condition)和關係連接(Relationship)這幾種類型。其中,Process是根節點,即一個流程文件裏只包含一個Process元素。Process下包含多個 Activity、多個Condition和多個Relationship,但只包含一個Beginning和一個Ending。

每個流程元素都具有一個字符串類型的名字屬性,我們爲Activity添加一個優先級(priority)屬性用來表示活動的緊急程度,這個屬性是枚舉類型,具有High、Normal和Low這三個值,在ecore裏定義枚舉類型是十分方便的;再爲Condition添加一個字符串類型的表達式(expression)屬性。

圖表 2 流程編輯器的ecore模型

現在從ecore模型生成genmodel模型(菜單File -> New -> EMF Model),並生成全部代碼(在genmodel模型根節點的右鍵菜單裏選Generate All命令),這個genmodel模型將被後面需要定義的一些模型引用,模型和.edit部分的代碼是GMF圖形項目編譯的前提條件,所以這一步是必要的。

到現在爲止的這些步驟和構造普通的EMF應用程序並沒有什麼不同,不要着急,下面要做的事情就進入GMF的領域了。

現在我們來定義圖形模型(Graph Model)、工具模型(Tooling Model)和映射模型(Mapping Model)這三個模型,然後利用它們生成名爲GMF生成模型的模型(GMFGen Model,相當於EMF裏的Gen Model),最後由生成代碼。爲了把將要建立的幾個GMF模型與已經定義好的EMF模型分開,我們可以新建一個Eclipse項目存放它們,以便在需要時更快的找到想要的模型。

首先來定義圖形模型,顧名思義,在這個模型裏我們要定義出流程編輯器裏每種元素的外觀,例如,用圓圈表示開始和結束節點,用方塊表示 Activity,用菱形表示Condition等等。GMF提供了名爲“GMFGraph Simple Model”的嚮導可以從剛纔定義好的ecore文件得到一個缺省的圖形模型,我們只要在嚮導裏指定ecore文件所在的位置即可。

接下來定義工具模型,這個模型指定流程編輯器的調色板(Palette)裏都可以有哪些工具,你還可以在這個模型裏對這些工具進行分組。同樣,使用“GMFTool Simple Model”嚮導可以很快的得到這個模型。

然後是映射模型,這個模型的作用是把ecore模型、圖形模型和工具模型聯繫起來,這樣每個業務元素就可以對應到一個外觀和一個工具,並且 ecore模型裏各元素之間的包含、引用關係也可以通過這個模型映射到圖元的包含或連線方式。GMF提供的“Guide GMFMap Creation”嚮導可以幫助我們建立這個模型的一部分內容,其餘部分還是需要在編輯器裏手動添加或修改。儘管現在這幾個模型的編輯界面還顯得十分簡陋,但在下一個版本(2.0)的GMF裏將會提供更多新的GUI以幫助編輯這幾個模型,例如提供可視化的模型編輯器。

有了映射模型,我們可以像在EMF裏從ecore模型生成genmodel模型那樣生成一個gmfgen模型了。生成步驟很簡單,只要在 Package Explorer裏的gmfmap文件上點右鍵,選擇“Create generator model”命令即可。生成以後,我們還可以對gmfgen模型進行微調,因爲其中有些信息是在前三個模型裏都沒有包含的,大部分時候我們只需要根據需要修改其中的少數幾個就夠了。常用的一個是可以修改Gen Compartment的List layout屬性來控制子圖形是否可以在父圖形裏隨意拖動。

最後,在gmfgen文件上點右鍵,選擇“Generate diagram code”命令,GMF就會爲我們生成完整的圖形編輯器代碼了。這些代碼會存放在名爲your.package.diagram的新工程裏,這個工程依賴 EMF幫你生成的兩個工程,所以前面要先生成EMF的工程,否則這個工程無法正確編譯。怎麼樣,這個界面和EMF生成的編輯器比起來夠不夠“精裝修”的標準:

圖表 3-1 GMF生成的流程編輯器


圖表 3-2 EMF缺省編輯器

當然,這個編輯器的外觀可能還不能讓你滿意,比如除了連接線外所有的圖元都是用矩形表示的,未免過於單調,而且每個圖元上只顯示了有限的文字信息。下面我們介紹一下如何通過修改GMF的幾個模型來定製流程編輯器。

首先,要改變各個圖形元素的缺省外觀,在缺省情況下,GMF建模嚮導裏都將節點元素映射爲矩形(Rectangle),連接元素映射爲折線(Polyline),當然這些都可以在gmfgraph裏修改,例如我們可以讓開始和結束節點顯示爲圓點,讓條件節點顯示爲菱形,將活動節點填充爲藍色,同時可以爲連接線的目標端增加箭頭圖形來表示連接的方向,這樣一來,熟悉UML的用戶就更容易理解流程圖的含義了。

從上面不難看出,gmfgraph模型的作用是定義圖形表現的方式,GMF映射模型負責關聯業務模型、gmfgraph模型和gmftool模型。任何在gmfgraph裏定義的元素如果沒有映射到業務模型也是沒有意義的,相對來講gmfmap模型是這三個模型中最複雜的一個,因爲幾乎每個元素都至少有三個屬性需要定義。通過修改gmfmap模型,我們可以規定圖形編輯器裏,各圖形元素的包含關係。在流程編輯器裏,可以通過添加一個標籤映射(Label Mapping)元素讓活動節點裏顯示優先級屬性。

圖表 4 定製後的流程編輯器界面

需要注意的是,在每次修改任意一個模型文件後,都要重新生成gmfgen文件,這樣重新生成的代碼才能反映最近的更新。

三、GMF Runtime介紹

前面已經說過,GMF的Runtime部分是GMF的核心,不依賴GMF幫助生成代碼,你也完全可以利用Runtime提供的功能寫出漂亮的圖形編輯器應用程序,但前提是你對GMF Runtime有充分的瞭解。

關於GMF Runtime提供了哪些實用功能,在Eclipse.org上有一篇文章“Introducing the GMF Runtime”已經介紹得很詳細了,其中主要包括可展開摺疊的compartment,Direct-Editing時的快速協助(Quick Assistant)、Pop-up工具條、連接手柄、通用工具項(如備註、文本、各種形狀等等)、通用菜單命令和工具條(如顏色、字體、選擇、排列、縮放,等等)、動態展示的縮放和自動佈局功能、通用屬性頁、打印和打印預覽支持、多種圖片格式輸出、系統剪貼板支持,等等。

GMF的Runtime相當於對GEF進行了一層包裝,如果不想利用GMF生成代碼的能力,只使用GMF Runtime也可以開發基於GMF的應用程序。與GEF相比,GMF Runtime充分利用了Eclipse的擴展功能,它提供了十分豐富的擴展點,不僅開發起來很方便(因爲各個功能都通過定義的擴展點劃分清楚了),同時還便於以後對應用程序的擴展。GMF提供的Logic例子就沒有使用代碼生成,而是直接寫成的,你對GMF Runtime的使用方法如果不清楚,這個例子是很好的參考。

相比起EMF來,GMF項目成立的時間要晚很多,如果想讓現有的GEF應用程序使用GMF Runtime帶來的新功能,最簡單的方法就是在原先代碼的基礎之上進行修改,而不是從頭開始以生成代碼的方式進行開發。

和EMF類似,GMF也有代碼生成的功能。除了上面提到的情況以外,最好利用GMF的代碼生成功能先得到應用程序的框架,再在此基礎之上進行開發。

在GEF或EMF+GEF構造的應用程序裏,一個讓人頭疼的問題就是在業務模型裏不得不包含一些與圖形有關的信息,例如每個元素在編輯器裏的位置、大小、顏色、字體等等,這可能讓本來清晰的業務模型變得難以理解。GMF通過引入記號模型(Notation Model)解決了這個問題。在GMF裏,EditPart不再與業務模型直接相連,而是連接到記號模型(GMF的 EditPart#getModel()方法得到的是記號模型),由記號模型維護到業務模型的引用。這個記號模型是GMF預先定義好的,它把圖形抽象爲畫布、節點和連接三大類元素,開發人員只要知道GMF的這個設計就可以了,而不需要在業務模型裏做任何特殊操作。GMF在持久化一個業務模型實例的時候(即用戶保存圖形編輯器時),缺省會得到兩個文件,一個是業務模型的持久化信息,另一個是記號模型的持久化信息,後者依賴前者的內容。當然,用戶也可以在 gmfgen裏設定參數讓GMF只生成一個文件包含這兩者的全部信息,但就失去了業務與圖形分離的好處。

前面說過,GMF應用程序裏的很多功能都是通過擴展點實現的,GMF裏這些擴展點被稱爲服務(Services),用戶通過在插件裏實現這些服務可以改變圖形編輯器的行爲。這些服務包括控制控制調色板(Palette)上有哪些工具,業務模型與EditPart的對應關係,EditPart上的 EditPolicy等等。實現GMF服務的方法和實現其他Eclipse平臺擴展點的方法是一樣的,用戶要實現的接口名稱一般以Provider結尾,例如EditPartProvider、ViewProvider和PaletteProvider,等等。

四、總結

GMF不僅在名稱上集合了EMF和GEF這兩個框架,它同時擁有EMF的代碼生成能力和GEF的圖形編輯能力。以往對EMF程序的定製主要通過修改代碼實現,在GMF裏,我們更多通過實現各種擴展點來改變應用程序的外觀和行爲,當然,必要的時候修改代碼也是不可避免的。

另外,GMF還利用了EMFT項目裏的不少成果,例如EMFT Transaction、EMFT Workspace和EMFT OCL等等,EMFT項目是對EMF項目的有益補充,它們讓GMF應用程序更加健壯,更好的和Eclipse平臺集成以及更好的用戶體驗,但也在一定程度上增加了GMF本身的入門門檻。

本文發出時,GMF 1.0版本已經相對比較穩定,而GMF 2.0也有了Milestone2版本。如果是做實際項目推薦選擇1.0版本,因爲雖然這兩個版本的模型略有區別,但2.0正式版按計劃要在2007年四月前後才能發出,這中間API很可能還會有多次修改。GMF 2.0的建模用戶界面更友好一些,至於Runtime增加的新功能我們拭目以待。

  • 11:16
  • 評論 / 瀏覽 (1 / 709)

假設GMF爲你生成的項目名稱爲com.example.diagram,現在要在右鍵菜單裏增加一個自定義命令,並關聯在名爲Activity的模型元素上,即只有在Activity類型的元素上點右鍵,彈出菜單裏纔有這個自定義命令。此命令的功能是簡單的把該Activity的Name屬性改爲“Modified Activity”。實現的步驟如下:

1、如果之前沒有創建過,則創建一個名爲com.example.diagram.custom的plugin項目(以下簡稱爲“custom項目”),新建這個項目的目的是把自己的定製與GMF生成的代碼分開;

2、在custom項目裏實現org.eclipse.ui.popupMenus擴展點,這樣會在右鍵菜單裏多出一個"Change"菜單項,下面有"Name"命令;

 
<extension
         point="org.eclipse.ui.popupMenus">
  <objectContribution
        adaptable="false"
        id="com.example.custom.objectContribution.ActivityEditPart"
        objectClass="com.example.diagram.edit.parts.ActivityEditPart">
     <menu
           id="BMAChange"
           label="&amp;Change"
           path="additions">
        <separator name="group1"/>
     </menu>
     <action
           class="com.example.diagram.popup.ChangeActivityNameAction"
           enablesFor="1"
           id="com.example.diagram.popup.ChangeActivityNameAction"
           label="&amp;Name"
           menubarPath="BMAChange/group1"/>
  </objectContribution>
</extension>
 

3、實現上一步裏定義的Action類ChangeActivityNameAction,這個類不僅要實現 IObjectActionDelegate(popupMenus擴展點的要求),還要繼承自AbstractActionDelegate這個類(GMF的要求)。我們要做的是實現doRun()方法,首先取得當前選中的editpart,然後創建一個SetRequest實例,它包含了改變屬性操作的所有信息,包括目標對象、屬性的名字和新屬性值。因爲GMF裏editpart的getModel()方法不是業務模型裏的元素了,而是View對象,要再調用View#getElement()才能得到業務模型裏的元素,所以代碼裏我們利用 ViewUtil#resolveSemanticElement()方法直接得到Activity對象。另外,GMF使用了EMFT的 Transaction項目來操作模型,所以editpart.getEditingDomain()方法得到的會是一個 TransactionalEditingDomain類型。

有了request,我們用它作爲構造參數創建一個SetValueCommand(),這是一個GMF命令(實現 org.eclipse.gmf.runtime.common.core.command.ICommand),用來改變屬性值。最後要執行這個命令,我們知道command是要用CommandStack來執行的,這樣才能undo/redo,但 editpart.getDiagramEditDomain().getDiagramCommandStack()得到的CommandStack只能執行GEF的命令(org.eclipse.gef.commands.Command),所以要把我們的command用 ICommandProxy()包裝一下,這樣就沒問題了。

public class ChangeActivityNameAction extends AbstractActionDelegate 
                implements IObjectActionDelegate {

    protected void doRun(IProgressMonitor progressMonitor) {

        // Get the selected edit part
        IStructuredSelection structuredSelection = getStructuredSelection();
        Object selection = structuredSelection.getFirstElement();
        IGraphicalEditPart editpart = (IGraphicalEditPart) selection;

        // Create a command to modify its property
        SetRequest request = new SetRequest(
                editpart.getEditingDomain(),
                ViewUtil.resolveSemanticElement((View) editpart.getModel()),//The semantic model 
                BmaPackage.Literals.ACTIVITY__NAME,//Name feature of activity
                "Modified Activity");//New name value
        SetValueCommand command = new SetValueCommand(request);

        //Do the work
        editpart.getDiagramEditDomain().getDiagramCommandStack().execute(new ICommandProxy(command));

    }
}

Update: 可以用IGraphicalEditPart#resolveSemanticElement()直接取得editpart對應的 EObject,IGraphicalEditPart#getNotationView()是得到View對象,和getModel()作用一樣。

運行效果如下,選擇修改名字命令後,Activity1的名字改爲Modified Activity,並且可以undo/redo:


參考:

  • GMF提供的Logic例子中CreateLogicElementActionDelegate.java文件
  • GMF Tips,Change Names Of Newly Created Elements小節
  • GMF Tutorial Part 3

Update(2007/07/17):似乎GMF更推薦使用IOperationHistory來修改模型,例如在Action的doRun()方法裏像下面這樣寫:

AbstractTransactionalCommand command = new AbstractTransactionalCommand(
        editingDomain,
        "Modifying the model", Collections.EMPTY_LIST) { 
    protected CommandResult doExecuteWithResult(
            IProgressMonitor monitor, IAdaptable info)
            throws ExecutionException {
        //在這裏修改模型
        return CommandResult.newOKCommandResult();
    }
};
try {
    OperationHistoryFactory.getOperationHistory().execute(command,
            new SubProgressMonitor(progressMonitor, 1), null);
} catch (ExecutionException e) {
    MindmapDiagramEditorPlugin.getInstance().logError(
            "Unable to create model and diagram", e); //$NON-NLS-1$
}

因爲在GMF新聞組裏提到過:"in a GMF application, you should probably never execute commands in a CommandStack, because it will not be worth the effort of coordinating the IUndoContexts applied to these commands and your GMF AbstractTransactionalCommands to ensure that the Undo/Redo menus make sense.",原文鏈接

  • 17:01
  • 評論 / 瀏覽 (0 / 821)

2006 年 11 月 27 日

本文介紹了 Graphical Modeling Framework(GMF)項目,說明了如何開發一個簡單的 Eclipse Modeling Framework(EMF)模型,並使用 GMF 的工具將其轉換成典型的圖形化編輯器。

背景

坦白說:過去在 Eclipse 裏使用 Graphical Editor Framework(GEF)創建圖形化編輯器 既慢又痛苦。這個過程包括理解複雜的框架和大量的冗餘代碼。但也說明 GEF 是創建圖形化編輯器的最佳框架,因爲它與模型無關。另一方面,與模型無關本身也有一些問題。

GMF 雜談

GMF 的運行時組件是 IBM® 爲使用 Eclipse Foundation 開發的,它以前還受 IBM Rational® 建模產品的支持。

GEF 是 Model-View-Controller(MVC)機制的精髓,它允許將您自己的模型引入表中。在使用 GEF 的早期,大多數人還使用自定義模型(考慮傳統 Java 對象 [Plain Old Java™ Object, POJO])。您會發現自定義模型帶來的問題是需要自己編寫通用代碼來支持模型,如序列化及偵聽模型更改的功能。

在 GEF 中使用模型的下一個邏輯步驟是要使用 Eclipse Modeling Framework(EMF),EMF 提供了以各種形式將模型序列化的工具和偵聽對模型默認值的更改的功能。

想要了解 EMF 的更多信息?

如果您覺得缺乏 Eclipse Modeling Framework(EMF)方面的基礎知識,或想要強化關於 EMF 的一般知識,建議您看書或者 developerWorks 中的一系列介紹性文章,這些都是非常好的參考資料。有關這些參考資料和其他 EMF 參考資料,請參閱 參考資料

但是,將 EMF 模型與 GEF 框架整合在一起有一定的技術難度(如不同的命令堆棧),導致 EMF 模型並沒有很快被基於 GEF 的編輯器接受。最終,GMF 項目在這種逆境中應運而生,並希望能夠引入一種快速生成圖形化編輯器的方法。通過類似的方法,EMF 生成適用於 EMF 模型的基本編輯器。

創建 EMF 模型

創建模型的第一步就是定義 EMF 模型的協作對象。我寫這篇文章的目的只是說明定義模型的過程,而不是深入講解 EMF 提供的模型處理工具的用法。本例中將要使用的模型是一個簡單的圖形化模型。我將通過一張圖來幫助我說明模型的外觀。


圖 1. 可視的圖形化模型
可視的圖形化模型

如您所見,使用模型是幫助我們理解各部分之間關係的一種簡單方法。這個模型由圖形、連接和圖形化圖表組成。

EMF 支持通過多種方法定義模型。爲簡單起見,我決定使用加註的 Java 技術。下面的代碼清單說明了如何使用 EMF 定義模型。第一個模型對象是一個有名稱屬性、源連接和目標連接(Connection 類型)的圖形。請注意,這是一個抽象的 EMF 類。


清單 1. Shape.java

/** * @model abstract="true" */public interface Shape {/** * @model */String getName();/** * @model type="com.ibm.model.shapes.model.Connection" containment="true" */List getSourceConnections();/** * @model type="com.ibm.model.shapes.model.Connection" */List getTargetConnections();} 

 

接下來定義囊括所有圖形的列表的圖形化圖表。


清單 2. ShapesDiagram.java

  /** * @model */public interface ShapesDiagram {/** * @model type="com.ibm.model.shapes.model.Shape" containment="true" */List getShapes();}

 

接下來定義一些特殊的圖形使模型更加生動。


清單 3. RectangularShape.java

  /** * @model */public interface RectangularShape extends Shape {}



清單 4. EllipticalShape.java

  /** * @model */public interface EllipticalShape extends Shape {}

 

最後,再添上連接就可以了,這樣就能將各種圖形真正連接在一起。


清單 5. Connection.java

  /**  * @model  */public interface Connection {/** @model */Shape getSource();/** @model */Shape getTarget();}

 

使用 Java 編程語言定義了模型後,請單擊 File > New > Eclipse Modeling Framework > EMF Model(參見圖 2)定義一個新的 EMF genmodel。注意:如果還沒有 EMF 項目,就先創建一個。


圖 2. EMF 加註的 Java importer
EMF 加註的 Java importer

生成 EMF 模型

如果在生成 EMF 模型遇到問題,請參閱名爲 "Generating an EMF Model" 的教程,它可以幫助您入門。請參閱 參考資料

創建了 EMF genmodel 後,請在文件上單擊鼠標右鍵,並確保生成 ModelEdit 組件(您只需選擇 Generate All 就可以輕鬆地完成)。

創建 GMF 模型

GMF 需要您先創建一組模型,然後生成圖形化編輯器。圖 3 顯示了創建這些模型所涉及的過程。我們需要使用的第一個模型是圖形化定義,它定義了編輯器生成後的視覺效果。接下來是工具定義,它包括與編輯器面板、菜單等相關的事務。最後,還需要一個模型就是映射定義,相信您猜得到,它用於定義業務邏輯(EMF 圖形化模型)與可視化模型(圖形化和工具定義)之間的映射。


圖 3. GMF 概覽(來自 GMF 維基)
GMF 概覽

GMF 有個叫 GMF 指示板(Window > Show View > Other > GMF Dashboard)的實用程序非常好。它可以幫助您用一種簡單的方法完成圖形化編輯器生成過程。在這個階段,您必須選定域模型和域 genmodel。


圖 4. GMF 指示板
GMF 指示板

GMF 圖形化定義模型

GMF 備忘單

GMF 有一個優秀的備忘單,它可以幫助您完成用 GMF 生成編輯器的創建過程。建議將此備忘單與 GMF 指示板結合使用。請通過菜單項 Help > Cheat Sheets... 訪問備忘單。

需要創建的第一個模型是圖形化定義模型(在指示板的 Graphical Def Model 下單擊創建超級鏈接)。確保選擇 Canvas 爲模型對象。這個模型很容易創建:

  1. 創建圖表中顯示的圖。方法是在編輯器中創建一個新的 Figure Gallery 條目,然後創建各種圖。
  2. 創建圖表中顯示的節點(矩形和橢圓形)。
  3. 在圖表中創建連接。
  4. 確保每個節點都與圖庫中創建的圖相匹配。

注意:如果您在執行此任務時遇到問題,您可以下載一個樣例插件,此插件爲您準備好了所有模型。


圖 5. GMF 圖解模型
GMF 圖解模型

GMF 工具定義模型

這個步驟需要定義工具定義模型,使用此模型可以定義圖形化編輯器的信息類面板和菜單。要定義工具定義模型,請打開 GMF 指示板,然後單擊 Create。我們的簡單模型只需定義一個面板和一些創建工具來輔助創建模型(參見圖 6)。


圖 6. GMF 工具模型
GMF 工具模型

GMF 映射定義模型

所有工作都從映射定義模型 開始。在此模型中,我們將可視化(圖形)模型映射到業務邏輯(域模型)中。GMF 有一組有序嚮導可以幫助您創建映射定義。請通過 File > New > Graphical Modeling Framework > Guide GMFMap Creation 調用這組嚮導。第一步要選擇所有 GMF 模型(參見圖 7)。


圖 7. GMFMap 嚮導 1
GMFMap 嚮導 1

接下來,嚮導將智能地提示我們選擇要使用哪個模型元素作爲圖表根元素。在我們舉的例子中,此模型元素爲 ShapesDiagram


圖 8. GMFMap 嚮導 2
GMFMap 嚮導 2

最後,GMF 會像變魔術一樣算出哪些模型元素必須映射到哪些可視化元素上。


圖 9. GMFMap 嚮導 3
GMFMap 嚮導 3

自定義 GMF 映射

可以使用基本編輯器編輯 GMF 映射定義文件來添加更多高級自定義功能。要勇敢地去嘗試!

必須注意的是:這些嚮導可能會隨着 GMF 的發展而改變。據說使用 GMF 自身提供的圖形化編輯器就可以幫助創建映射定義文件(和其他 GMF 模型)。

生成 GMF 編輯器

整個過程的最後一步也是最有趣的一步是生成圖形化編輯器。這需要您先通過映射定義模型創建一個 GMFGen 模型。然後,在映射定義文件上單擊鼠標右鍵並選擇 Create generator model...。這樣,一個包含使用圖形化編輯器所需的全部代碼的新項目就生成了。要使用圖形化編輯器,請啓動一個新的 Eclipse 運行時工作臺,然後轉到 File > New > Examples > Model Diagram(參見圖 10)。


圖 10. 模型嚮導
模型嚮導

模型文件創建後,您就應當能夠使用生成的編輯器了(參見圖 11)。很不錯,是吧?


圖 11. 圖形編輯器
圖形編輯器

自定義 GMFGen 模型

通過映射定義文件獲得的 GMFGen 模型是可以根據需要加以修改的。例如,GMFGen 模型的屬性就可以使用屬性視圖進行編輯。GMFGen 模型還有可以控制生成代碼的命名規則、生成的編輯器是否支持打印功能及很多其他自定義操作的屬性。試試這些選項,然後根據您的需求對其進行自定義。

GMF 特點

必須注意的是:我們構造並生成的編輯器僅用到了 GMF 功能的一小部分。您可以做很多改動來利用框架的高級功能。

例如,GMF 支持驗證。這句話的含義是:如果我們要限定圖形化模型,只許與每個模型元素有一個連接會怎樣?只允許相似的元素相互連接,還是對可用於圖形的名稱類型進行控制?GMF 完全能夠支持這些類型的驗證,甚至其他驗證。對於驗證,GMF 利用了 Eclipse Modeling Framework Technology(EMFT)來支持涉及使用 Java 代碼和對象約束語言(Object Constraint Language,OCL)來定義驗證器的情況。GMF 將給未通過驗證的文件安插錯誤標記,這類似於 Eclipse 對未編譯的 Java 文件的處理方法。要查看關於 GMF 支持內容的更多信息,請參閱 參考資料

 



回頁首


結束語

我的目的有兩個:一是展示支持模型驅動開發的 Eclipse Callisto 發行版中令人興奮的新的一面;二是讓您瞭解它有多棒,只需 15 分鐘就能在 Eclipse 中生成圖形化編輯器。

  • 15:07
  • 評論 / 瀏覽 (1 / 602)

2007 年 2 月 28 日

基於 EMF 和 GEF,Graphical Modeling Framework(GMF) 提供了圖形化編輯器的開發環境和運行時框架。本文首先簡單地闡述 GMF 框架的基本內容,然後結合具體實例 Zachman View 介紹瞭如何對 GMF 代碼框架進行高級的擴展和定製,以滿足複雜圖形化編輯器的開發需求。

前言

在 GMF 出現之前我們在做一個基於模型的圖形化工具時,通常用 EMF 來創建後臺模型, 用 GEF 來做用戶界面和後臺模型的同步,這需要花費相當的時間去了解 EMF 的模型設計方法和 GEF 的 MVC 框架。

爲了簡化開發過程,GMF 對 EMF 和 GEF 做了進一步的封裝,提供了一套圖形化的開發環境和運行時框架,開發人員可在圖形環境中輕鬆定製自己的領域模型,並基於該模型一步一步地進行設計開發。最後 GMF 根據用戶的定製情況,自動生成代碼框架。

本文以 zachman view 爲例子,主要介紹在用 GMF 生成一個 zachman 的 diagram 後,如何對代碼作進一步的高級擴展以滿足具體的要求,下面將在 5 個方面進行擴展:

1. 自定義佈局管理器和背景色

2. 定製上下文菜單

3. 動態加載 Palette Entry

4. 定製 EditPolicy 和 Command

5. 模型轉換




回頁首



GMF 簡介

GMF 框架主要包括兩個部分:運行時框架,圖形化開發環境。圖形開發環境依賴底層的運行時框架。


圖 1 GMF 組件
圖 1 GMF 組件

如圖 1 所示,GMF Editor 主要基於 GMF Runtime 來構造應用框架,但同時允許開發人員直接使用 EMF、GEF 和 Eclipse 系統接口來完成一些特定功能(GMF 不能很好支持的功能)。

GMF 的圖形化開發環境

GMF 編輯器的主要功能是用來定義一組用來生成代碼框架的模型,主要包括:graphical model, tool model 和 map model。


圖 2 GMF 模型
圖 2 GMF 模型

如圖 2,GMF 編輯器首先需要一個 domain model,通常是一個 *.ecore 文件,GMF 根據這個 domain model 做進一步的操作,這個 model 是實施 GMF 後續工作的基礎。

  • graphical model:定義了 ecore model 元素的圖形信息,如形狀,顏色,邊框等等。嚴格的說這個圖形信息是和 ecore model 無關的(以便於領域模型和用戶界面模型的映射),但是在具體的操作過程中我們都是根據 ecore model 來生成其所對應的 graphical model.
  • tool model:定義了在未來生成的圖形編輯器中 palette view 裏面的 palette entry 信息。 如名字,圖標。
  • map model:定義了 ecore model, graphical model 和 tool model 三者間的映射關係,通過 map model 來調整其他 3 個 model 的映射關係可以增強 model的可重用性和靈活性。
  • generator model:由 map model 結合其他三個 model 生成,最終 GMF Runtime 根據 generator model 來生成 diagram editor 的代碼框架。可以通過調整 generator model 的一些屬性來做一些個性化的工作。比如:默認 GMF 框架會把 ecore model 和 notation model(diagram model) 分開來保存,可以通過修改其屬性"Same File For Diagram And Model" 來使 model 和 view 保存在一個文件中,等等。




回頁首



GMF 進階

環境:

GMF 可以方便的爲開發人員生成模型的 diagram 框架,和一些基本的功能。本文以 zachman view 爲例子具體講述在用 GMF 生成一個 diagram 框架後,如何進一步對代碼進行擴展以滿足具體的要求。

圖 3 是 zachman view 的 ecore model, zachman 由行和列組成,每個列包含一些 CItem 對象,每個行引用一個或多個 CItem 對象,每個 CItem 關聯一個特定的行。每個 CItem 包含 0 個或多個屬性。


圖 3 zachman ecore model
圖 3 zachman ecore model

根據圖3的 ecore model,我們可以藉助 GMF 的圖形化開發環境快速地生成與之對應的 graphical model, tool model 和 map model,進而由 generator model 生成 diagram 的基本框架(詳細步驟可參考 15 分鐘學會 Eclipse GMFGMF Tutorial)。生成的 diagram 如下圖 4,


圖 4 生成的 Zachman view diagram
圖 4 生成的 Zachman view diagram

圖4 中,用戶必須自己調整每個元素的位置,其中的行和列對象不能像表格一樣自動調整佈局,Palette Viewer 上的 entry 項也沒有辦法進行定製,此外,彈出菜單上也有好多的無關項容易引起誤會,等等。

期望的 diagram 如下圖5


圖 5 擴展後的 Zachman view diagram
圖 5 擴展後的 Zachman view diagram

在圖 5 中,擴展了 Zachman 的佈局管理器,行和列將自動調整它們的佈局,修改了上下文菜單,去掉了無關的菜單項,同時增加了用戶自定義的 palette entry 項等等。下面的章節將對這些實現的細節進行詳細說明。

自定義佈局管理和背景色

在圖 4 中,每個行(列)的位置是由創建對象時鼠標的位置決定的,調整一個列的高度不會引起其他列的高度的同步改變,因此,爲了實現行和列的自動按表格佈局,行列不重疊,列同高,行同寬,需要對 Zachman view 的佈局管理器進行定製。

Zachman ecore model 中根元素 Zachman 和其中的每個元素生成對應的 EditPart,每個 EditPart 中定義了一個 Figure 對象用來表示元素的圖形化信息,這個 Figure 的屬性和外觀特點就是通過在 graphical model 裏的描述生成的。每個 Figure 有一個佈局管理器,由它來管理其中的子元素的佈局位置。通常根元素的所對應的佈局管理器是 FreeformLayout,該佈局管理器中,用戶可隨意指定子對象位置大小。下面將解決如何擴展 FreeformLayout 來定製根的子元素的佈局。

首先需要定一個佈局管理 ZachmanDiagramLayout,


清單 1 自定義佈局管理器

public class ZachmanDiagramLayout extends FreeformLayout {  ...   public void layout(IFigure parent) {    Iterator cit = parent.getChildren().iterator();    IFigure f;    ...    while(cit.hasNext()){      f = (IFigure)cit.next();      Rectangle bounds = (Rectangle) getConstraint(f);            //計算出f的bounds            f.setBounds(bounds);      setConstraint(f, bounds);      ...    }  }  ...}


其次,安裝 ZachmanDiagramLayout。根 ZachmanEditPart 元素擴展的是 DiagramEditPart,其定義了一個方法 protected IFigure createFigure() {…},由這個方法返回根元素 ZachmanEditPart 的圖形對象,只要給它設置 ZachmanDiagramLayout 就可以了。如下


清單 2 安裝佈局管理器

protected IFigure createFigure() {	IFigure f = super.createFigure();	ZachmanDiagramLayout layout = new ZachmanDiagramLayout();	f.setLayoutManager(layout);		return f;}


如果要定製某些對象的佈局,都可以按照上面兩個步驟通過給其父對象安裝佈局管理器來實現。

如果想要修改 ZachmanDiagramEditor 的背景色,可以通過 GMF editor 的 getGraphicalViewer().getControl() 獲取 editor 的畫布,調用其 setBackgraoundColor(…) 方法就可以給 editor 設置新的背景色了,如果加上調用畫布的 redraw() 方法,就可以隨需變換 editor 的背景色了。

定製上下文菜單

ZachmanDiagramEditor 自帶了很多系統的菜單項,如圖6


圖 6 上下文菜單
圖 6 上下文菜單

在實際的應用中,很多菜單項都是多餘的,如何把多餘的菜單項去掉並且添加自定義菜單呢?

GMF 提供了一個擴展點 org.eclipse.gmf.runtime.common.ui.services.action.contributionItemProviders。這個擴展點有一個子項 contributionItemProvider,它對應的 class 爲 com.ibm.eis.soaif.smm.model.diagram.providers.DiagramContributionItemProvider,由這個 DiagramContributionItemProvider 決定了 GMF Editor 上 contextMenu 的內容,所以要需要重寫 ContextMenu 的這個 provider 來定製菜單內容。


清單 3 定製上下文菜單

public class ZachmanContextMenuProvider extends DiagramContextMenuProvider {   public void buildContextMenu(IMenuManager menu) {     ...     // 添加用戶自定義菜單項     // ...     super.buildContextMenu(menu);   }// 過濾不需要的上下文菜單   public IContributionItem[] getItems() {     IContributionItem[] ic = super.getItems();     filterSystemPopMenu(ic);     return ic;    }}


通過 filterSystemPopMenu(),過濾掉不需要的系統菜單信息。一個 IContributionItem 對應一個菜單信息,通過設置其爲不可見,可以過濾掉相應的菜單項。

添加用戶自定義 Action 到上下文菜單,首先定義一個 Action


清單 4 自定義 Action

public class SampleAction extends Action { public SampleAction(String text) {     super(text);     setId("Zachman.sample");     setText("Sample"); } @Overridepublic void run() {    super.run();}}


添加 SampleAction 到上下文菜單中去


清單 5 添加自定義 Action 到上下文菜單

public void buildContextMenu(IMenuManager menu) {   SampleAction action = new SampleAction("Sample");   menu.add(action);   super.buildContextMenu(menu); }


最後,需要用 ZachmanContextMenuProvider 去替換 ZachmanDiagramEditor 原有的 DiagramContextMenuProvider,修改 ZachmanDiagramEditor 的 configureGraphicalViewer() 方法如下:


清單 6 爲 Editor 安裝 ContextMenuProvider

protected void configureGraphicalViewer() {  super.configureGraphicalViewer();  IDiagramGraphicalViewer viewer = getDiagramGraphicalViewer();  /* customize popup menu */  ContextMenuProvider provider = new ZachmanContextMenuProvider(this,viewer);  viewer.setContextMenu(provider);  getSite().registerContextMenu(ActionIds.DIAGRAM_EDITOR_CONTEXT_MENU	  ,provider	  ,viewer);    }


新的上下文菜單如下圖7


圖 7 用戶自定義菜單
圖 7 用戶自定義菜單

動態加載 Palette Entry

在圖 3 中,CItem 包含多個屬性項,根據屬性項的變化,我們希望能夠在 palette viewer上同步更新 palette viewer 上的 entry 項。

GMF 生成的 Palette View 上提供了用戶在 tool model 裏面定義的 palette entry, 但是它們是靜態的,如何像 palette view 上根據需要動態添加 palette entry 呢?

首先,每個 palette Viewer 如下


圖 8 palette Viewer
圖 8 palette Viewer

紅色區域是一個 PaletteContainer,它裏面有 Column, Row 和 CItem 三個 ToolEntry。每個 PaletteViewer 對應一個 PaletteRoot, PaletteRoot 是一個 PaletteContainer,它是 PaletteViewer 的根容器。下面介紹如何向 palette viewer 添加一個 Sample 屬性項。

1. 定義 Tool, 由 Tool 完成 ToolEntry 在接收到鼠標事件的具體動作。


清單 7 SampleCreationTool

public class SampleCreationTool extends SelectionTool {protected boolean handleButtonDown(int button)    {        if(button == 3){    	        //TODO right click event    	    }    	    if(button == 1){            //TODO left click event 		    /* set the default palette selection */getDomain().loadDefaultTool();}}


2. 定義 ToolEntry, ToolEntry 是 PaletteViewer 裏面可以顯示的一個 entry,如圖8中的 "Zoom"、"Column" 等。


清單 8 定義 ToolEntry

public class AttibuteCreationToolEntry extends ToolEntry{	public Tool createTool() {		SampleCreationTool tool = new SampleCreationTool();    tool.setUnloadWhenFinished(false);            return tool;	}       }


3. 歸類 ToolEntry 項。PaletteDrawer 是對 PaletteContainer 的一個擴展,這裏爲 CItem 創建一個 Sample 屬性來歸類屬於 Sample 的 ToolEntry

a. 創建一個 PaletteDrawer,名字爲 Sample。

b. 添加 ToolEntry 爲 Sample 的子項。如圖 9 的 new, quarter, half 項等等

c. 然後把這個 PaletteDrawer 添加到 PaletteRoot 中去,刷新 PaletteViewer 就可以看到新添加的 PaletteEntry 項如圖 9


圖 9 用戶自定義 PaletteViewer
圖 9 用戶自定義 PaletteViewer

Sample 是爲 CItem 新添加的屬性,通過單擊它可以顯示/隱藏它所包含的 ToolEntry("new","quarter","half","three-quarters","full")。

定製 EditPolicy 和 Command

GMF 沿用了 GEF 中定義的 EditPolicy 和 Command 機制來完成對應用模型的操作和用戶界面交互。這種設計提高了代碼的複用和靈活性,同一個 EditPolicy 可以被安裝到多個 EditPart 上,而同一個 Command 也可以被多個 EditPolicy 重用。

通常,每個 EditPart 都會安裝一些類型的 EditPolicy,每個 EditPolicy 對應一個 Role,用一個字符串常量來標示,發送到 EditPart 的請求都會被根據類型委託給合適的 EditPolicy,由 EditPolicy 根據請求的特點決定操作細節和否創建 Command 並執行。

GMF 除了支持 GEF 的 EditPolicy 外,還對增加了種類豐富的 EditPolicy,常用的如:

CreationEditPolicy: 用於元素的創建操作,模型的同步,顯示方式等。

DragDropEditPolicy: 對象拖放時的行爲控制,模型的同步,用戶界面交互等等。

SemanticEditPolicy: 處理語意相關的操作,如對模型元素的刪除操作。

在創建一個 CItem 對象時,要求出現一個插入線提示即將創建新對象的位置如圖 10,同時要求創建一個新 CItem 的同時,建立起它和 Row 對象的關聯。而這些都是自動生成的代碼所無法提供的。下面介紹如何通過 EditPolicy 和 Command 實現上面的兩個要求。


圖 10 自定義 Editpolicy 和 Command
圖 10 自定義 Editpolicy 和 Command

1.添加插入線。當選中 palette viewer 上的 CItem 在編輯器裏創建一個對象時,由安裝在 CItem 上的 CreationEditPolicy 來處理這個請求,這裏擴展一個新的 CItemCreationEditPolicy 來覆蓋掉 CItem 原來的 CreationEditPolicy,提供對插入線的控制,見清單 9。由 updateInsertLine(…) 來控制插入線的位置和長度。


清單 9 CItemCreationEditPolicy

public class CItemCreationEditPolicy extends CreationEditPolicy {	protected Command getCreateCommand(CreateViewRequest request) {		Command cc = super.getCreateCommand(request);				// update insert line 		updateInsertLine(request);		TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())|-------10--------20--------30--------40--------50--------60--------70--------80--------9||-------- XML error:  The previous line is longer than the max of 90 characters ---------|				.getEditingDomain();				CompoundCommand cm = new CompoundCommand();		cm.add(cc);				UpdateRefsCommand cmd1 = new UpdateRefsCommand(editingDomain,"Create Item",getDATable());		Command cmd2 = new ICommandProxy(cmd1);		cm.add(cmd2);				return new ICommandProxy(new CommandProxy(cm.unwrap())); 	}	private void updateInsertLine(CreateViewRequest creq) {				IGraphicalEditPart igp = (IGraphicalEditPart)getHost();		Point location = getPreferredLocation(creq);		int width = igp.getFigure().getBounds().width;				if(location == null){			ZachmanUtil.getCurrentDiagramEditor().bgLayer.eraseInsertedLine();			return ;		}				Point startP = location;		Point endP = new Point (startP.x+width,startP.y);				ZachmanUtil.getCurrentDiagramEditor().bgLayer.setLineToShow(startP, endP);		|-------10--------20--------30--------40--------50--------60--------70--------80--------9||-------- XML error:  The previous line is longer than the max of 90 characters ---------|		ZachmanUtil.getCurrentDiagramEditor().bgLayer.repaint();			}	}


2. 同步 CItem 和 Row 的關聯。清單 9 中,在創建完一個 CItem 對象後,在 CommandStack 裏面添加 UpdateRefsCommand 來建立 CItem 對象和 Row 的關聯,由 UpdateRefsCommand 來完成關聯的創建。


清單 10 UpdateRefsCommand

public class UpdateRefsCommand extends AbstractTransactionalCommand {		...		@Override	protected CommandResult doExecuteWithResult(IProgressMonitor monitor,			IAdaptable info) throws ExecutionException {		updateRefs(getCurrentItem());		return CommandResult.newOKCommandResult();	}		private updateRefs(CItem item){		Row row = getRow(item);		item.setRRow(row);		row.getRItem().add(item);	}	...	}


模型轉換

Zachman View 希望能把第三方的 zachman model 導入到圖 3 的 zachman view ecore model 來使用和管理,因此,這裏需要做模型的轉換與顯示。

GMF 的模型分兩部分,一部分是 ecore model,一個是用於顯示信息的 notation(diagram) model,也就是 diagram 的信息。在把一個模型轉換成 zachman view ecore model 並在 diagram 中顯示和管理,這裏需要處理兩件事情:

1. 把第三方 model 轉換成 zachman view 的 ecore model;

2. 需要根據生成的 zachman view 的 ecore model 生成對應的 notation model。

ecore model 間的轉換有很多方法,IBM 提供了一個很好的工具來做這種轉換:MTF(Model Transformation Framework),可參見Model Transformation with the IBM Model Transformation Framework來做 ecore model 的轉換。下面將重點介紹如何利用 GMF API 生成 notation model 的方法。

GMF 生成的 ZachmanDiagramEditor 中有一個 setDocumentProvider(IEditorInput input) 方法,這個方法爲 editor 設置了一個 DocumentProvider,由這個 provider 負責 model 的保存和讀取的操作。所以,這裏計劃在讀取 zachman model 時,動態生成其對應的 notation model。

1.當雙擊 zachman model 文件時,DocumentProvider 使用 setDocumentContent(IDocument document,IEditorInput editorInput) 方法來載入 model 文件,其調用 DiagramIOUtil 的 load(…) 方法來完成文件到 Resource 的載入。在載入過程中,load 方法會查找 model 中的 notation model,如果沒有查找到,會拋出一個 NO_DIAGRAM_IN_RESOUCE 異常。在由 zachman ecore model 生成 notation model 的過程中,我們捕獲這個異常,在其處理方法中完成對 notatoin model 的創建。

2. 爲 zachman ecore model 創建一個 diagram,如下,首先根據 ecore model 創建一個 diagram,然後根據 ecore model 爲 diagram 添加具體的 notation model 的信息。


清單 11 創建 diagram

private Diagram getDiagramForECoreModel(Zachman model) {		Diagram diagram = null;		diagram = ViewService.createDiagram(								(EObject) model,								"zachman",				ZachmanDiagramEditorPlugin.DIAGRAM_PREFERENCES_HINT);		// genarate diagram notation model		generateDiagramModel(model, diagram);		return diagram;}


3. 由 ecore model 生成 notation model,下面的代碼是創建 model 中 Column 元素對應的 View model 的代碼,這裏需要注意的是要根據不同的元素類型,創建相應的 CreateElementRequestAdapter,它包含了創建 View 的一些必須信息。


清單 12 爲每個 model 元素創建對應的 View

private void generateDiagramModel(Zachman model, Diagram diagram) {	boolean persisted = true;		// create column view model	Iterator dit = model.getColumns().iterator();	ColumnViewFactory dvf = new ColumnViewFactory();	CreateElementRequestAdapter dSemanticAdapter = dvf			.getCreateSemanticAdapter();	IElementType dElementType = ZachmanElementTypes.Column_1001;	String dSemanticHint = ((IHintedType) dElementType).getSemanticHint();	while (dit.hasNext()) {		Column column = (Column) dit.next();		dSemanticAdapter.setNewElement(column);		View view = dvf.createView(dSemanticAdapter, diagram, dSemanticHint, -1,				persisted, dvf.getColumnPreferencesHint());	}}


4. 設置 Document 的內容,在步驟1中的 setDocumentContent(…) 方法中的 document 參數有 setContect() 方法,對步驟3返回的 diagram,調用 document.setContent(diagram)。至此,view model 已經加入到 document 中,保存 model,再次打開 model 可見已經可以在 Zachman Diagram 中顯示 model 的圖形信息,並可進行相應的管理和操作。

這裏需要注意的是,因爲創建 notation model 是對 Resource 的修改,需要在一個事務中完成以上操作。




回頁首



總結

GMF 爲開發基於模型的圖形化編輯器提供的便利,它爲 EMF 和 GEF 間架起了一座溝通的橋樑,方便開發人員更好的集中精神在關鍵問題上,而不用考慮框架結構,界面一致性等情況。同時 GMF 允許開發人員直接使用 GEF 和 EMF 的接口去完成一些底層的複雜的功能。GMF 功能也在不斷的完善和增強,希望我們的在本文的嘗試會幫助大家解決在 GMF 開發過程可能會遇到的問題。

  • 10:18
  • 評論 / 瀏覽 (0 / 578)
本文在 GMF2.0 的基礎上,用一個自上而下的流程分析建模工具爲例,完整的描述了從如何建模,如何修改模型,以及如何客戶化生成的代碼框架的整個過程,主要涉及佈局,UI 外觀,模型操作以及對多個 Editor 的支持等等。

前言

GMF(Graphical Modeling Framework)是Eclipse的一個開源項目,它在結合了EMF和GEF的基礎上,爲基於模型的圖形化編輯器的開發提供了一個功能強大的框架,開發人員可以採用建模的方式很容易的生成高質量的代碼框架。

GMF主要由開發工具和運行時兩部分組成。開發工具負責基於GMF核心模型的設計和建模工作,包括:描述具體領域模型的 Graph 模型的表示,Tool 模型的表示,以及如何用 Mapping 模型對前面三個模型進行映射組合,從而根據定義好的 Mapping 模型生成 Gen 模型,並最終完成代碼框架的生成。三個獨立的模型設計大大提高了模型的可重用性,開發人員可以在各模型所關注的領域對其進行進一步的定製,這個將在深入瞭解GMF建模工具中有詳細介紹。GMF的運行時環境提供了豐富的組件(如Command框架,Services, Global Action, Layout等等)和強大的擴展機制,如 org.eclipse.gmf.runtime.emf.ui.modelingAssistantProviders, org.eclipse.gmf.runtime.common.ui.services.parserProviders,org.eclipse.gmf.runtime.diagram.core.viewProviders 等等,這些將在深入瞭解GMF運行時框架中詳細地介紹。

剛發佈的GMF2.0 在 GMF1.0 的基礎上進一步提高了其易用性,同時也加入了相當的新功能。比如:Diagram 內容導航的支持,對 RCP 應用的支持,對 Preference Pages 的支持,支持模型的合併等等。

本文主要基於 GMF2.0 的運行環境,介紹如何實現一個自上而下的 Process 分析工具。其主要功能是:自上而下的分解一個大的 Process 爲一批子 Process,每個 Process 可以由一個具體的 Flow 來描述,對於同一個模型,可以由多個Editor去編輯,如Process Editor, Flow Editor。期望的結果如下圖。


圖 1.Process Editor
Process Editor

圖 2.Flow Editor
Flow Editor

本文將從定義領域模型入手,一步一步地介紹實現的細節。




回頁首



GMF建模

GMF框架提供了對一個具體的Ecore模型進行圖形化建模的支持,以模型驅動的方式,一步一步完成功能描述,直至代碼的生成。參見(Graphical Model Framework進階)。本文的環境如下:

  • Eclipse3.3.0
  • EMF2.3.0
  • GEF3.3.0
  • GMF2.0

ECore模型

ECore模型是GMF建模的起點,通常ECore模型是一個領域模型,描述了要建模的領域特徵。其後的GMF借鑑了MVC的思想,Ecore模型就是MVC中的Model,詳情可參見(深入瞭解GMF建模工具)。

用EMF嚮導新建一個空的process.ecore文件,右鍵單擊該Ecore文件,選擇Initiallize ecore_diagram diagram file,將會打開一個Ecore Diagram編輯器,創建Process的ECore模型如下:


圖 3.Process的ECore模型
Process的ECore模型

當然,開發人員也可以直接用傳統的樹形EMF編輯器構上面的Ecore模型,或用RSA(Rational Software Architect)對模型進行UML圖的描述,再轉換成Ecore模型。

Graph模型

Graph模型是用來描述領域模型的顯示信息,在GMF的定義中,Graph模型原則上是一個獨立的模型,它描述了一個用於信息顯示的模型。從它的定義裏面就可以看出,它描述了Diagram, Node, Connection和Figure等圖形化信息。將其關聯到具體的領域模型上,就表示了該領域模型前端的顯示圖元。

選擇已經定義的領域模型,打開向導對話框,


圖 4.GMF模型嚮導
GMF模型嚮導

圖4中,GMF提供了一系列嚮導來輔助創建不同的GMF模型。值得一提的是,GMF2.0支持對已有的Graph模型和Tool模型的提供更新操作。對於一個已存在的模型,新的更新會被自動添加到模型中,而原有的部分不會影響,參見(GMF2.0新特性介紹)。在具體的實現上,GMF2.0添加了Reconcile Graphical Definition Model和Reconcile Tool Definition Model兩個嚮導來完成此功能,和GMF1.0相比,這個設計更人性化。

在具體的操作中,以上面定義的 ECore 模型爲基礎,利用 GMF 嚮導一步一步的生成對應的Graph模型如下圖5,操作細節不再贅述。當然,也可以不用ECore模型,直接編輯一個空的Graph模型來進行定製,因爲原則上Graph模型是獨立的,可以被多個 Ecore 模型和Mapping 模型公用的。GMF嚮導以 ECore 模型爲基礎,方便了開發人員進行更有效的開發。


圖 5.自定義Graph模型
自定義Graph模型

在Graph模型中,選擇Map元素爲根元素,這裏只是簡單的爲每個元素定義了圖元信息。關於圖元外觀的定製會在下面介紹。

Tool模型

圖形化建模工具裏有一個很重要的部分,就是Palette部分,它定義一系列的Tool Entry項,每個Tool Entry項對應工具所支持的某一類型對象的創建,如RSA Class Diagram的Palette裏的Package, Class, Interface 等Tool Entry項。

GMF提供了對Palette部分的功能建模。Tool模型提供了對將要在Diagram裏面出現的Tool Entry項的描述,包括名稱,描述等。同時,GMF Tool模型提供了對常用Tool Entry的支持,此外,還有一些標準的Tool Entry項,如放大/縮小,選擇,註釋等等。Tool模型也是一個獨立的模型,可以被多個ECore模型和Mapping模型共享。


圖 6.自定義GMF Tool模型
自定義GMF Tool模型

這裏使用GMF Tool模型創建嚮導爲ECore模型生成了一個Tool模型。可以在下面的屬性也裏面對Tool Entry做一些基本的配置,不過,上面的Default Image是不允許配置的,GMF自動爲每個Tool Entry生成了默認的圖標,如果要自己定義圖標,可以使用Bundle Image來指定特定的圖標。

Mapping模型

Mapping模型是一個組織者,針對前面提到的三個獨立的模型,由Mapping模型把這三個模型合理的組織起來。在Mapping模型裏,孤立的模型間有了相互的聯繫,領域模型的對象將會由特定的Graph模型裏的圖元來進行展示,同時,還在Tool模型裏爲其設定一個Tool Entry作爲Palette上的元素創建選項。

此外,GMF Mapping模型還提供了豐富靈活的配置選項,詳情參見(深入瞭解GMF建模工具)。


圖 7.Process的Mapping模型
Process的Mapping模型

上圖中,只提供了在一個Diagram Editor裏面創建一個Process節點的功能,實際需要每個Process節點下可以創建多個子節點。通常這種情況下,需要在Node Mapping下創建一個Child Reference和Compartment Mapping來定義節點所包含的子節點的信息,但是這樣一來,在Diagram的顯示上將會是一個Process節點圖形內部包含了其所有的子 Process節點,它們在前端圖形顯示上是一個包含關係,不能滿足我們所期望的層次關係的表示。所以這裏只是在Diagram定義了Process的節點的信息,其父子節點的層次關係將在代碼部分解決。

在這個例子中,我們需要在雙擊一個Process節點時,打開一個Flow Editor來對應Process的內部流程的編輯,因此,需要再定義一個Flow Editor,對應的Mapping模型如下圖8


圖 8.Flow Mapping模型
圖片示例

在這個Flow裏面關注3個元素,Task、Decision和表示它們間流程關係的Relationship。Flow Mapping模型共享了Process的Ecore模型和Graph模型。

Gen 模型

Gen模型是用來生成Diagram框架的模型,Gen 原模型描述了生成Diagram代碼框架所需要的一些元素。在GMF中,Gen模型由Mapping模型生成,一個Mapping模型對應一個Gen模型,當然也可以由一個Mapping模型生成多個自定義的Gen模型,這樣就可以生成多個Diagram Editor了。


圖 9.Process Gen模型
Process Gen模型

圖 10.Flow Gen模型
Flow Gen模型

Gen模型和Mapping模型相比,有更多關於代碼生成的屬性信息,開發人員可以自己定義生成代碼的一些細節信息。在GMF2.0裏,加入了內容導航元模型,在Gen模型裏會生成一個Gen Navigator項,其下的一系列子項詳細定義了導航欄和編輯器間作內容同步時的配置信息。其次,在Gen Diagram下面,還增加了對Preference Page的支持,開發人員可以自己配製和定製Diagram的Preference page頁面,GMF提供了預定義的一組標準的Preference page,如Appearance, Printing, Connections等,當然,開發人員也可以預定義自己的Preference Page的配置情況。GMF將會生成相應的代碼框架,開發人員只需把注意力集中在具體的應用邏輯上。值得一提的是,GMF Gen模型提供了對RCP應用在Gen模型的支持,通過對Gen Application項的定義和配置,可以很容易實現對RCP應用的設置,比如菜單項,工具條等等。

在一個GMF工程下面可以有多個Tool/Graph模型,同時也可以有多個Mapping模型和對應的Gen模型。事實上,本實例中就是創建了兩個Mapping模型和Gen模型,分別用於Process Diagram和Flow Diagram的建模。




回頁首



自定義編輯器的佈局

在生成的代碼框架中,GMF Diagram的默認佈局管理器是FreeFormLayoutEx,在此佈局管理器中,圖元採用XY佈局管理。這個和Process預期的自上而下的樹形佈局不相符,因此需要對Process Diagram的圖元佈局管理器進行一定的修改。

Node的佈局

Node的佈局是由它的父元素對應的圖元的佈局管理器來控制的,在Process Diagram裏Node就是Process圖元,它的父容器就是Diagram,這樣,通過修改Diagram的佈局管理器就規範了其子節點(Process節點)的佈局了。可參見(GMF進階-自定義佈局管理器和背景色

所不同的只是Layout()方法的實現,本示例中用樹形的佈局來組織Process節點的顯示。

Connection的佈局

Connection圖元是一個由至少兩個點組成的線/折線,並且有可能帶有一個箭頭指向目標節點。折線上點的位置和數量決定了折線的形狀和佈局。默認的兩個圖元之間的一個連接是一條帶箭頭的直線,要實現圖所示的Connection的折線型佈局,這裏需要修改Connection的佈局管理器。

首先自定一個佈局管理器,在這個佈局管理器裏會自動計算出折線的形狀。


清單 1. 自定義Connection的佈局

                  public class ConnectionLayout extends DelegatingLayout {    public void layout(IFigure parent) {  		...      Connection conn = (Connection)parent;      points = new PointList();      while(條件成立){      Point p[i] = //計算第i個節點座標  		  Points.add(p[i]);      }      ...       conn.setPoints(points);    }  }      


其次,和Node的佈局類似,把此佈局管理器安裝到Connection的圖元上,如下所示。


清單 2. 安裝Connection的佈局

                public class RelationshipConnection extends PolylineConnectionEx {  public RelationshipConnection() {    setLayoutManager(new ConnectionLayout());  }}      


這樣,在Connection進行佈局的時候就會把原來的直線通過計算變成折線形狀,照此方法,可以很容易的構造出水平或垂直方向的樹形佈局。

自定義Layout provider

在GMF生成的Diagram Editor裏,在編輯模型的時候,可以選中一些或全部模型,在選擇工具條上的link icon按鈕或右鍵菜單裏的Format->Arrange->All,就可以對選中的模型元素進行佈局。

事實上,GMF對此功能提供了一個擴展點 org.eclipse.gmf.runtime.diagram.ui.layoutProviders進行支持,由這個擴展點可以輕鬆實現上述功能。下面實現一個CustomerLayoutProvider類,


圖 11.自定義LayoutProvider
自定義LayoutProvider

這裏需要注意兩個方法

provides()方法,該方法檢查當前的Provider是否提供具體的方法,檢查選中的節點是否可以被佈局,並且驗證被傳遞的佈局類型是否是有效的。

layoutLayoutNodes()方法,該方法是真正計算所要佈局的節點的座標從而進行佈局的更新,和上面提到的做Node的佈局是類似的。所不同的是此方法返回一個Runnable對象,由這個Runnable對象的執行,完成真正的佈局功能的執行。具體的功能實現可參見GMF默認的LayoutProvider: org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider




回頁首



UI外觀的修改

GMF和GEF一樣,都是用Draw2D來完成模型前端的顯示工作,這裏提到的對UI外觀的修改主要是對Draw2D功能的一些應用。

在GMF中,每一個EditPart都會有一個Figure或PolylineConnection對象來做其前端的顯示,因此對UI的外觀的修改也就是對這些圖形屬性的修改。

設置漸變背景色

圖2中,要實現圖形背景的漸變色,需要對TaskEditPart對應的TaskFigure進行修改,在圖形進行重新繪製時設置其背景色。這裏Draw2D提供了fillGradient()方法來繪製漸變色。如下:


清單 3. 設置漸變的背景色

                  public void paintFigure(Graphics g) {    super.paintFigure(g);    Color oldForeground = g.getForegroundColor();    Color oldBackground = g.getBackgroundColor();    g.setForegroundColor(FlowUtil.FORE_COLOR);    g.setBackgroundColor(FlowUtil.TASK_BG_COLOR);    g.fillGradient(bounds, true);    g.setForegroundColor(oldForeground);    g.setBackgroundColor(oldBackground);  }      


設置子圖形位置

默認情況下,Process和Task節點的圖標和文本是放在同一行的,如何設置其爲上下佈局呢?修改它們對應的XXXNameEditPart的兩個方法如下,設置文本的在下,圖標在上:


清單 4. 設置文本和圖標的佈局

                  protected void setLabelTextHelper(IFigure figure, String text) {    if (figure instanceof WrapLabel) {      WrapLabel l = (WrapLabel)figure;      l.setText(text);      l.setTextPlacement(PositionConstants.SOUTH);      l.setTextWrap(true);    } else {      ((Label) figure).setText(text);    }  }  protected void setLabelIconHelper(IFigure figure, Image icon) {    if (figure instanceof WrapLabel) {      WrapLabel l = (WrapLabel)figure;      l.setIcon(icon);      l.setIconAlignment(PositionConstants.TOP);    } else {      ((Label) figure).setIcon(icon);    }  }      





回頁首



自定義UI操作

GMF工具生成了一個很好的代碼框架,但是爲了使其更加符合具體的應用,需要對生成的代碼進行一定的修改,尤其是對UI上的一些操作常常伴隨着模型的修改。由於GMF是基於事務的機制對模型進行管理,所以,對模型的修改或更新操作需要放在一個具體的事務中進行。GMF豐富的EditPolicy和 Command機制提供了對上述的支持。

創建

模型的創建是由CreationEditPolicy來處理的,如果一個EditPart需要創建其子元素,則需爲其安裝一個CreationEditPolicy來控制子元素的生成。通常爲


清單 5. 安裝自定義的CreationEditPolicy

                  installEditPolicy(EditPolicyRoles.CREATION_ROLE,	new XXXCreationEditPolicy());      


其中,XXXCreationEditPolicy是對CreationEditPolicy的一個擴展,本實例中有個限制,需要在創建一個Process節點的同時,創建一條和它的父節點的連線,也就是一個帶箭頭Connection連接,實現getCreateCommand()如下。


清單 6. 自定義CreationEditPolicy

                  protected Command getCreateCommand(CreateViewRequest request) {    TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())		                                                         .getEditingDomain();    CompositeTransactionalCommand cc = //Create Process command    ...    Command cmd = new ICommandProxy(cc.reduce());    CompoundCommand cm = new CompoundCommand();		    cm.add(cmd);        // add Create Connection command     if(request instanceof CreateViewAndElementRequest){		      CreateElementRequestAdapter requestAdapter =        ((CreateViewAndElementRequest)request)            .getViewAndElementDescriptor().getCreateElementRequestAdapter();      if(CBSDiagramUtil.getInstance().getSourceEP() instanceof DANode2EditPart){        CreateChildrenRelation4ProcessCommand cmd1 = createRalationCommand(                                     editingDomain,                                      (CreateViewAndElementRequest)request,                                          requestAdapter);        Command cmd2 = new ICommandProxy(cmd1);		  		        cm.add(cmd2);      }    }    return new ICommandProxy(new CommandProxy(cm.unwrap()));  }  private CreateChildrenRelation4ProcessCommand createRelationCommand(          TransactionalEditingDomain editingDomain, CreateViewAndElementRequest request,           CreateElementRequestAdapter requestAdapter) {    Diagram diagramView = (View)getHost().getModel()).getDiagram();	    IElementType type = ProcessElementTypes.Relation_3001;     CreateConnectionViewAndElementRequest req =           New CreateConnectionViewAndElementRequest(type,                   ((IHintedType) type).getSemanticHint(),                   request.getViewAndElementDescriptor().getPreferencesHint());    CreateChildrenRelation4ProcessCommand cmd =                new CreateChildrenRelation4ProcessCommand(editingDomain,requestAdapter,req,                                                      diagramView.getDiagram());		    return cmd ;  }      


在創建一個Create Process命令後,緊接着在生成一個創建Connection的命令,這樣就會在創建子Process後自動創建一條與其父節點的連接。

這裏需要說明的一點是,在創建一個Connection對應的模型信息是在對應EditPart的XXXItemSemanticEditPolicy生成的Command裏完成的,GMF2.0裏面這個Command是作爲一個獨立的Command放在command package下面的,和其他的CreateElementCommand類似。如果使用之前的GMF版本的話,這個Command是作爲XXXItemSemanticEditPolicy的一個內部類存在的。

生成一個Connection對應的模型後,需要在Diagram裏面創建一個其對應的Notation View的信息,也就是一個Edge對象,由這個Edge對象維護Connection的顯示信息,包括連線形狀,起始點和目標節點等。這和創建一個 Node的Notation View類似,需要指定CreateElementRequestAdapter, SemanticHint,然後由RelationshipViewFactoryCreateView()方法來生成一個Edge對象。所有的這些操作都是在CreateChildrenRelation4PorcessCommand類的doExecuteWithResult()方法裏面進行,具體操作由CreateRelationView()方法完成,其中containerView爲Diagram View,因爲所有的Process View都是Diagram View的子節點。


清單 7. 創建Relation View

                  private void createRelationView() {    DARelationViewFactory drvf = new DARelationViewFactory();    final int index = -1 ;    boolean persisted = true ;    final String semanticHint = connReq.getConnectionViewAndElementDescriptor()                                                            .getSemanticHint();		    ConnectionViewAndElementDescriptor ccd =                                         connReq.getConnectionViewAndElementDescriptor();    final CreateElementRequestAdapter rAdapter = ccd.getCreateElementRequestAdapter();    PreferencesHint preferencesHint = ccd.getPreferencesHint();				    Node srcNode = getSrcView();    Node tgtNode = getTgtView();		    if(srcNode == null || tgtNode == null){      return ;    }    View view = drvf.createView(rAdapter, containerView, semanticHint,                                                    index, persisted, preferencesHint);    Edge edge = (Edge)view;    edge.setSource(srcNode);    edge.setTarget(tgtNode);				  }      


刪除

在EditPolicy中,有一個SemanticEditPolicy,GMF生成的代碼框架中常會以此EditPolicy爲基礎,派生出XXXBaseItemSemanticEditPolicy,進而派生出其他EditPart所需的具體的XXXItemSemanticEditPolicy。正是這個EditPolicy控制着元素的刪除操作。安裝在EditPart上的SemanticEditPolicy控制此EditPart被刪除時所進行的操作。

在本實例中,要求在刪除一個Process節點時刪除它所關聯的子節點,如果此Process節點有Flow的信息,刪除其對應的Flow的信息,並關閉對應的Flow Editor(如果打開的話)。基於上述要求,首先需要自定義一個CustomizeDestroyElementCommand,由它來完成具體的刪除操作。


清單 8. 自定義DestroyElementCommand

                public class CustomizeDestroyElementCommand extends DestroyElementCommand {  …  @Override  protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)                                                          throws ExecutionException {    ProcessEditPart editpart = (ProcessEditPart)getHost();    if(root == null) {      root = (ProcessEditPart)editpart.getParent();    }		    // delete all descents and their relative process flow    List children = editpart.getSourceConnections();    int size = children.size();    for(int i=0; i<size; i++) {      delete((ProcessEditPart)((RelationshipsEditPart)children.get(i)).getTarget());    }		    // update parent's process flow    List targetConns = editpart.getTargetConnections();    if(targetConns.size() != 0) {      ProcessEditPart parent =                 (ProcessEditPart)((RelationshipsEditPart)targetConns.get(0)).getSource();      Diagram flowDiagram = getOrDeleteProcessFlowDiagram(parent, false);      if(flowDiagram != null) {        deleteFlow(flowDiagram, ((Node)editpart.getModel()).getElement());      }    }		    // delete self process flow and close flow editor if acitved    Diagram flowDiagram  = getOrDeleteProcessFlowDiagram(editpart, true);    if(flowDiagram != null) {      closeEditor(flowDiagram);    }		    return super.doExecuteWithResult(monitor, info);			  }	}      


其次需要把上面的Command安裝在正確的地方,如下,修改ProcessEditPart的XXXItemSemanticEditPolicy的方法如下,用CustomizeDestroryElementCommand替換掉原有的Command。


清單 9. 應用自定義的DestroyElementCommand

                  protected Command getDestroyElementCommand(DestroyElementRequest req) {    CompoundCommand cc = getDestroyEdgesCommand();    addDestroyShortcutsCommand(cc);    View view = (View) getHost().getModel();    if (view.getEAnnotation("Shortcut") != null) { //$NON-NLS-1$      req.setElementToDestroy(view);    }    cc.add(getGEFWrapper(new CustomizeDestroyElementCommand(req)));     return cc.unwrap();  }      





回頁首



多編輯器的支持

很明顯,這裏需要處理多個編輯器同時打開一個模型的情況,我們希望可以在編輯Process樹的時候,同時可以打開它的Flow Editor來編輯其具體的流程細節。但是如果用默認的方法給一個編輯器設置一個文件作爲輸入的話,這一點是絕對做不到的。因爲Eclipse本身在第二次打開同樣的文件時,如果發現它已經被一個Editor打開就不會再次打開它。

針對這種情況,一種比較直接的辦法就是把一個模型拆分成一些小的部分,分別用文件保存,每個Editor之打開其中的一個文件,像WBM就採用了類似的做法。

還有一種就是整個模型就是一個文件,而用非文件的方式去打開和編輯模型,像RSA就是這種作法,整個模型就是一個大的.emx文件。本文計劃採用一個模型文件的做法。

首先,需要一個EditingDomainManager來管理EditingDomain和文件所對應Resource。由這個Manager維護當前處於活躍狀態的EditingDomain和Resource,Flow Editor共享其對應的Process Editor的EditingDomain。


清單 10. EditingDomain Manager

                public class EditingDomainManager {	  public TransactionalEditingDomain getCurrentEditingDomain(final String id){  ...  return editingDomain ;  }	  public Diagram getDiagramOfElement(Resource gmfRes, String editorID, Process element) {	    ...    return  getDiagram(gmfRes, kind,element);  }}      


其次,需要用DiagramEditorInput來替換默認的以文件爲輸入的FileEditorInput。雙擊一個Process圖標的時,如果當前節點沒有對應的Diagram,則由initialFlowDiagram()方法生成一個空的Diagram對象,然後構造DiagramEditorInput來打開Flow Editor。


清單 11. 打開Flow Editor

                  private void openFlowEditor() {    ...    IWorkbenchPage page = PlatformUI.getWorkbench()                               .getActiveWorkbenchWindow().getActivePage();		    if(!isFlowDiagramExisted(node)){      initialFlowDiagram(node);    }		    Diagram diagram = getProcessNodeDiagram(node);    DiagramEditorInput input = new DiagramEditorInput(diagram);    try {      IEditorPart ep = page.openEditor(input, editorId, true);    } catch (PartInitException e) {			      e.printStackTrace();    }		  }      


爲了從DiagramEditorInput打開一個Editor,Flow Diagram Editor要修改它的getDiagram()方法,默認的getDiagram()方法沒有提供對DiagramEditorInput的處理。


清單 12. 修改Flow Editor的方法

                  @Override  public Diagram getDiagram() {    IEditorInput input = getEditorInput();		    if(input instanceof DiagramEditorInput){      DiagramEditorInput dinput = (DiagramEditorInput)input;      return dinput.getDiagram();    }		    return super.getDiagram();  }      


此外還需要對FlowDocumentProvider進行相應的修改,重載三個函數如下


清單 13. 修改DocumentProvider

                  @Override  public IEditorInput createInputWithEditingDomain(IEditorInput editorInput,                                              TransactionalEditingDomain domain) {    return editorInput;  }	  @Override  public IStatus getStatus(Object element) {    return STATUS_OK;  }  @Override  public boolean isModifiable(Object element) {    if(element instanceof DiagramEditorInput){      return true;    }    return super.isModifiable(element);  }      


這樣,就可以同時打開多個Process文件,並且編輯多個Flow Editor了,具體如圖1圖2所示。




回頁首



結束語

GMF2.0在前一個版本的基礎上,在可用性和功能上都有了很大的改進,本文用一個完整的例子介紹了GMF在從建模、模型修改到生成代碼框架以及對代碼定製的過程,對GMF的應用和開發做了一個系統的介紹。當然,GMF本身還在不斷的完善中,新的功能也會不斷的包含近來,希望能有越來越多的人學習和使用 GMF。

  • 16:05
  • 評論 / 瀏覽 (0 / 1489)

XML Schema學習筆記和註解-(轉)

1、複雜型和簡單型之間最根本的區別就是:複雜型的內容中可以包含其他元素,也可以帶有屬性(Attribute),但簡單型既不能包含子元素,也不能帶有任何屬性,但限制條件或擴展條件還是可以有的。

一個複雜類型例子:
<xsd:complexType >
    <xsd:sequence>
        <xsd:element name="name"type="xsd:string"/>
        <xsd:element type="xsd:string"/>
        <xsd:element type="xsd:string"/>
        <xsd:element type="xsd:decimal"/>
    </xsd:sequence>
    <xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
</xsd:complexType>
一個簡單類型例子:
<xsd:simpleType >
    <xsd:restriction base="xsd:integer">
        <xsd:minInclusive value="10000"/>
        <xsd:maxInclusive value="99999"/>
    </xsd:restriction>
</xsd:simpleType> 


2、element存在約束:element可以通過其minOccurs和maxOccurs兩個屬性來約束元素實例存在的個數,這兩個屬性的缺省值都是1,表示默認情況下此元素在XML實例文檔中必須出現一次。
例如:
<element minOccurs="1" maxOccurs="1" type="timeInstant"/>

3、attribute存在約束:元素屬性也可以通過attribute的use屬性來約束出現一次或根本不出現;use屬性的取值可以是required,optional,prohibited三個值,缺省(默認)值是optional. 


4、 element和attribute都有一個default和fixed屬性,不能同時存在。針對element來說,只有當element實例爲空時才採用此 default值,而attribute是當實例不提供此attribute時才採用此default值,因此對attribute而言,只有其use值是optional時default值纔有意義,而且對element和attribute來說fixed和default兩個屬性不能同時存在,否則會出現錯誤。 
fixed屬性要求element或attribute如果出現的話,就一定要是fixed屬性裏指定的值。


5、全局元素和全局屬性。直接定義在schema元素下,即schema元素的頂級子元素的element和attribute都是全局的,稱之爲全局元素和全局屬性,你在其他型定義中可以直接引用。 

  <xsd:annotation>    <xsd:documentation xml:lang="en">     Purchase order schema for Example.com.     Copyright 2000 Example.com. All rights reserved.    </xsd:documentation>  </xsd:annotation>  <xsd:element  type="PurchaseOrderType"/>  <xsd:element  type="xsd:string"/>  <xsd:complexType >    <xsd:sequence>      <xsd:element  type="USAddress"/>      <xsd:element  type="USAddress"/>      <xsd:element ref="comment" minOccurs="0"/>      <xsd:element   type="Items"/>    </xsd:sequence>    <xsd:attribute  type="xsd:date"/>  </xsd:complexType>  <xsd:complexType >    <xsd:sequence>      <xsd:element    type="xsd:string"/>      <xsd:element  type="xsd:string"/>      <xsd:element    type="xsd:string"/>      <xsd:element   type="xsd:string"/>      <xsd:element     type="xsd:decimal"/>    </xsd:sequence>    <xsd:attribute  type="xsd:NMTOKEN"                   fixed="US"/>  </xsd:complexType>  <xsd:complexType >    <xsd:sequence>      <xsd:element  minOccurs="0" maxOccurs="unbounded">        <xsd:complexType>          <xsd:sequence>            <xsd:element  type="xsd:string"/>            <xsd:element >              <xsd:simpleType>                <xsd:restriction base="xsd:positiveInteger">                  <xsd:maxExclusive value="100"/>                </xsd:restriction>              </xsd:simpleType>            </xsd:element>            <xsd:element   type="xsd:decimal"/>            <xsd:element ref="comment"   minOccurs="0"/>            <xsd:element  type="xsd:date" minOccurs="0"/>          </xsd:sequence>          <xsd:attribute  type="SKU" use="required"/>        </xsd:complexType>      </xsd:element>    </xsd:sequence>  </xsd:complexType>  <!-- Stock Keeping Unit, a code for identifying products -->  <xsd:simpleType >    <xsd:restriction base="xsd:string">      <xsd:pattern value="\d{3}-[A-Z]{2}"/>    </xsd:restriction>  </xsd:simpleType></xsd:schema>

從上面這個例子可以看出,purchaseOrder和comment是全局元素,可以被其他element用ref屬性引用,purchaseOrderType,USAddress,Items和SKU是全局屬性,可以被其他element用type屬性引用。

6、派生新型有兩種方式:第一種就是直接從其他型中擴展(繼承)而來,另外一種就是通過對已有型進行限定性約束而來。 
如:以下有三種通過限定性約束定義的新型: 
通過值範圍限定: 
<xsd:simpleType >
    <xsd:restriction base="xsd:integer">
        <xsd:minInclusive value="10000"/>
        <xsd:maxInclusive value="99999"/>
    </xsd:restriction>
</xsd:simpleType> 
使用模式匹配限定: 
<xsd:simpleType >
    <xsd:restriction base="xsd:string">
        <xsd:pattern value="\d{3}-[A-Z]{2}"/>
    </xsd:restriction>
</xsd:simpleType> 
使用枚舉方式限定: 
<xsd:simpleType >
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="BeiJing"/>
        <xsd:enumeration value="NanChang"/>
        <xsd:enumeration value="ShangHai"/>
    </xsd:restriction>
</xsd:simpleType> 

7、原子型(不可分割的型,象string,integer等系統內建的型)、列表型、聯合型合起來統一稱爲簡單型。在Schema中有NMTOKENS、IDREFS、ENTITIES三種內建的列表型,你也可以從已有的簡單型來創建list(列表)型,但你不能從已有的list型和複雜型來創建列表(list)型。 
如: 
<xsd:simpleType >
    <xsd:list itemType="myInteger"/>
</xsd:simpleType> 
在XML實例文檔中列表型的值是通過空格來進行分隔的,如果聲明瞭一個listOfMyIntType元素,其值可能是: 
<listOfMyInt>20003 15037 95977 95945</listOfMyInt> 

8、約束List類型。有幾個方面的元素可以應用於list型來進行約束,它們是:length、minLength、maxLength和enumeration,如: 
<xsd:simpleType >
    <xsd:list itemType="USState"/>
</xsd:simpleType>
<xsd:simpleType >
    <xsd:restriction base="USStateList">
        <xsd:length value="6"/>
    </xsd:restriction>
</xsd:simpleType> 
注:針對列表型要千萬注意成員是string型的,因爲string型中的空格和列表型的分割符空格會造成部分混淆。

9、對元素的定義可以採用通過指定其type屬性爲已定義的屬性的方式(基本類型或自定義的全局屬性),也可一採用匿名定義型的方式,如: 
採用型定義: 
<xsd:element name=”comment” type=”xsd:string”> 
採用匿名定義: 
<xsd:element
    <xsd:simpleType>
        <xsd:restriction base=”xsd:positiveInteger”>
            <xsd:maxExclusive value=”100” />
        </xsd:restriction>
    </xsd:simpleType>
</xsd:element> 

10、union(聯合)表示在XML實例文檔中的元素實例符合union型定義的成員型中的一種就可以了(合法),這一點和C++中的聯合型有似的概念,如: 
<xsd:simpleType >
    <xsd:union memberTypes="xsd:string integer"/>
</xsd:simpleType> 

11、複雜型一般可以分爲三
第一是包含字符內容和屬性但不包含子元素,針對第一可以通過simpleContent來實現;
第二是包含屬性和子元素但不包含字符數據(字符數據包含在子元素中),通過complexContent來做到;
第三是即包含屬性和字符內容又包含子元素的,只需要將complexType的屬性mixed設爲true就可以了;
具體的例子如下: 

第一種型(從一個簡單型擴展而來,增加了屬性): 
<xsd:element >
    <xsd:complexType>
        <xsd:simpleContent>
            <xsd:extension base="xsd:decimal">
                <xsd:attribute type="xsd:string"/>
            </xsd:extension>
        </xsd:simpleContent>
    </xsd:complexType>
</xsd:element> 

第二種型(有一個element和兩個attribute構成): 
<xsd:element >
    <xsd:complexType>
        <xsd:complexContent>
            <xsd:element ?type=”xsd:string” />
            <xsd:attribute type="xsd:string"/>
            <xsd:attribute ?type="xsd:decimal"/>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:element> 
注意:在這裏由於默認情況下缺省是complexContent,所以在這裏簡略的寫法是: 
<xsd:element >
    <xsd:complexType>
        <xsd:element />
        <xsd:attribute type="xsd:string"/>
        <xsd:attribute ?type="xsd:decimal"/>
    </xsd:complexType>
</xsd:element> 

第三種型: 
<xsd:element >
    <xsd:complexType mixed="true">
        <xsd:sequence>
            <xsd:element >
                <xsd:complexType mixed="true">
                    <xsd:sequence>
                        <xsd:element type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element type="xsd:positiveInteger"/>
            <xsd:element type="xsd:string"/>
            <xsd:element type="xsd:date" minOccurs="0"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element> 
第三種型的實例可能如下: 
<letterBody>
    <salutation>Dear Mr.
    <name>Robert Smith</name>.
    </salutation>
    Your order of
    <quantity>1</quantity>
    <productName>Baby Monitor</productName>
    shipped from our warehouse on
    <shipDate>1999-05-21</shipDate>
</letterBody>

12、根據11的描述那麼要定義一個空內容的元素,也就是說定義一個只包含屬性的元素,只要在complexContent中不包含任何子元素,就可以了,如(注:complexContent在complexType外面被省略了):
<xsd:element >
    <xsd:complexType>
        <xsd:attribute type="xsd:string"/>
        <xsd:attribute type="xsd:decimal"/>
    </xsd:complexType>
</xsd:element>

13、anyType是所有Schema型的基型,和Java中的Object似。因此,以下定義: 
<xsd:element name="anything" type="xsd:anyType"/> 
可以寫成: 
<xsd:element name="anything"/> 

14、Schema中用annotation、document、appInfo三個元素來進行註釋,其中appI和document都是作爲annotation的子元素來處理的。並且annotation一般是作爲schema的頂層子元素、element的構造、型定義的頂層子元素的。 
如: 
<xsd:element >
    <xsd:annotation>
        <xsd:documentation xml:lang="en">
            element declared with anonymous type
        </xsd:documentation>
    </xsd:annotation>
    <xsd:complexType>
        <xsd:annotation>
            <xsd:documentation xml:lang="en">
                empty anonymous type with 2 attributes
            </xsd:documentation>
        </xsd:annotation>
        <xsd:complexContent>
            <xsd:restriction base="xsd:anyType">
                <xsd:attribute type="xsd:string"/>
                <xsd:attribute type="xsd:decimal"/>
            </xsd:restriction>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:element> 

15、 choice,all
choice僅允許在實例文檔中使用其中一個子元素;在all中的所有元素都可以出現一次或一次都不出現,並且其中元素實例是沒有順序約束的,而且all 必須放在任何內容模型的最頂層,爲了說明這個問題,下面先列出一個合法的,然後列出一個不合法的以供對照說明: 
<xsd:complexType >
    <xsd:all>
        <xsd:element type="USAddress"/>
        <xsd:element type="USAddress"/>
        <xsd:element ref="comment" minOccurs="0"/>
        <xsd:element type="Items"/>
    </xsd:all>
    <xsd:attribute type="xsd:date"/>
</xsd:complexType> 
下面是一個不合法的: 
<xsd:complexType >
    <xsd:sequence>
        <xsd:all>
            <xsd:element type="USAddress"/>
            <xsd:element type="USAddress"/>
            <xsd:element type="Items"/>
        </xsd:all>
        <xsd:sequence>
            <xsd:element ref="comment" />
        </xsd:sequence>
    </xsd:sequence>
    <xsd:attribute type="xsd:date"/>
</xsd:complexType>

 

16、attributeGroup屬性組。在存在很多型中都有幾個相同的型的情況,可以採用屬性組的方式來進行重用,屬性組定義的格式是: 
<xsd:attributeGroup
    <xsd:attribute type=”xsd:string” />
    <xsd:attribute type=”xsd:string” />
    …
</xsd:attributeGroup> 

使用可以採用以下方式: 
<xsd:element
    <xsd:comlexType>
        <xsd:element />
        <xsd:attributeGroup ref=”attrGroupName” />
    </xsd:complexType>
</xsd:element> 

17、關於include的用法 
include元素可以將外部的定義和聲明引入到文檔中,並且作爲新Schema文檔的一部分,但必須注意的一點是,被包含成員的目標命名空間TargetNamespace必須和包含的目標命名空間schemaLocation一樣。具體寫法例子是: 
<include schemaLocation=“http://www.example.com/schemas/address.xsd” /> 

18、extension。如果一個型是從另一個型擴展而來的,那麼定義爲父型的element,在實例文檔中,可以通過符合子型的element實例來代替,但必須通過xsi:type指明此元素的具體型。例如: 
<xsd:complexType
    <xsd:sequence>
        <xsd:element type=”xsd:string” />
        <xsd:element type=”xsd:string” />
    </xsd:sequence>
</xsd:complexType> 
<!-- 擴展型定義 --> 
<xsd:complexType
    <complexContent>
        <xsd:extension base=”Person”>
            <xsd:sequence>
            <xsd:element
                <xsd:restriction base=”string”>
                    <xsd:enumeration value=”male” />
                </xsd:restriction>
            </xsd:element>
            </xsd:sequence>
        </xsd:extension>
    </complexContent>
</xsd:complexType>
<!-- 型的聲明 --> 
<xsd:element name=”human” type=”Person” /> 
在XML實例文檔中針對human可以是這樣的(和麪向對象有似的概念): 
<human xsi:type=”Father”>
    <name>xiaogen</name>
    <gender>male</gender>
</human> 
19、關於置換組 substituionGroup
XML Schema 提供了一種機制叫置換組,允許原先定義好的元素被其他元素所替換。更明確的,這個置換組包含了一系列的元素,這個置換組中的每一個元素都被定義爲可以替換一個指定的元素,這個指定的元素稱爲頭元素(Head Element),需要注意的是頭元素必須作爲全局元素聲明,注意,當一個實例文檔包含置換元素時替換元素的型時從它們的頭元素那裏派生的,此時並不需要使用我們前面所說的xsi:type來識別這些被派生的型,當定義了置換組之後,並非意味着不能使用頭元素,而只能使用這個置換組中的元素,它只是提供了一個允許元素可替換使用的機制。例如: 
<xsd:schema>
    <xsd:element type=”xsd:string”/>
    <xsd:element type=”xsd:string” substitutionGroup=”comment” />
    <xsd:element type=”xsd:string” substituionGroup=”comment” />
    <xsd:element
        <xsd:complexType>
            <xsd:element type=”xsd:string” />
            <xsd:element type=”xsd:decimal” />
            <xsd:element ref=”comment” />
            <xsd:element type=”xsd:date” />
        </xsd:complexType>
    </xsd:element>
</xsd:schema> 
下面是實例文檔的一個例子: 
<order>
    <productName>Lapis necklace</productName>
    <price>999</price>
    <shipComment>Use gold wrap if possible</shipComment>
    <customerComment>Want this for the holidays!</customerComment>
    <shipDate>2004-08-15</shipDate>
</order> 

20、抽象元素和抽象型 

當一個元素或者型被聲明爲“abstract”時,那麼它就不能在實例文檔中使用。當一個元素被聲明爲”abstract”的時候,元素的置換組的成員必須出現在實例文檔中。當一個元素相應的型被定義聲明爲"abstract"時,所有關聯該元素的實例必須使用"xsi:type"來指明一個型,這個型必須是非抽象的,同時是在定義中聲明的抽象型的派生型。 
一、如將上面的comment元素的聲明更改成: 
<xsd:element name=”comment” type=”xsd:string” abstract=”true” /> 
那麼上面的order實例中就只能包含customerComment和shipComment纔是有效的。 
二、如果有下面的型定義: 
<schema xmlns="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://cars.example.com/schema"  xmlns:target="http://cars.example.com/schema"> 
    <complexType abstract="true"/> 
    <complexType > 
        <complexContent>
            <extension base="target:Vehicle"/>
        </complexContent>
    </complexType>
    <complexType > 
        <complexContent>
            <extension base="target:Vehicle"/>
        </complexContent>
    </complexType>
    <element type="target:Vehicle"/> 
</schema> 
根據以上的定義和聲明,下面的實例片斷就是不能通過驗證的: 
<transport xmlns="http://cars.example.com/schema" /> 
下面經過修改的就可以通過驗證了。 
<transport xmlns=“http://cars.example.com/schema”  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Car"/> 

21、Final。爲了阻止型被派生(包括通過限制派生和通過擴展派生),可以使用final來設置(有點和java中的final的概念似),其值有三個:restriction,extension,#all。在模式文件的根元素schema元素中有一個可選的finalDefault屬性,它的值能夠取爲final屬性所允許的幾個值之一。指定finalDefault屬性的值的效果等於在模式文檔中每個型定義和元素聲明中指定final屬性,同時其值爲finalDefault屬性的值。如果想阻止前面的Person被限制派生,我們需要修改定義爲如下: 
<xsd:complexType final=”restriction”>
    <xsd:sequence>
        <xsd:element type=”xsd:string” />
        <xsd:element type=”xsd:string” />
    </xsd:sequence>
</xsd:complexType> 

22、block。因爲在實例文檔中與父型關聯的元素(也就是聲明爲父型的元素)可以通過派生的子型來替代,這在2中已經有詳細的說明和例子。同時,XML Schema也提供了一種機制來控制派生型以及置換組在實例文檔中使用的機制。這種機制是通過型的block屬性來控制的,該屬性可以取值爲:restriction,extension,#all。和final屬性一樣,在模式文檔的根元素 schema元素裏有一個可選的屬性blockDefault,它的值爲block屬性所允許的值中的一個。指定blockDefault屬性的作用等價於在模式文檔中爲每個型定義和元素聲明指定block屬性。如在前面例子中我們想阻止在使用Father來替代Person的話我們需要修改Person的定義如下: 
<xsd:complexType block=”extension”>
    <xsd:sequence>
        <xsd:element type=”xsd:string” />
        <xsd:element type=”xsd:string” />
    </xsd:sequence>
</xsd:complexType> 
取值爲“extension”的block屬性將阻止在實例文檔中使用通過擴展的派生型來替換Person(即可以阻止Father來替換Person),然而它不會阻止通過約束的派生來替換Person。 

23、用fixed禁止派生。另外一種派生型的控制機制是應用於簡單型方面的派生。當定義一個簡單型的時候,我們可以使用fixed屬性對它的所有定義的參數進行修飾,以阻止這些參數在型派生中被修改,例如: 
<xsd:simpleType
    <xsd:restriction base=”xsd:string”>
        < xsd:length value=”7” fixed=”true” />
    </xsd:restriction>
</xsd:simpleType> 
當這個簡單型被定義之後,我們能夠派生出一個新的郵編型,在其中我們使用了一個沒有在基本型中固定的參數: 
<xsd:simpleType >
    <xsd:restriction base=" Postcode">
        <xsd:pattern value="[A-Z]{2}\d\s\d[A-Z]{2}"/>
    </xsd:restriction>
</xsd:simpleType> 
然而,我們不能購派生出一個這樣的新的郵編型:在其中我們重新定義了任何在基型中已經被固定(fixed)的參數: 
<xsd:simpleType >
    <xsd:restriction base="ipo:Postcode">
        <xsd:pattern value="[A-Z]{2}\d\d[A-Z]{2}"/>
        <!-- illegal attempt to modify facet fixed in base type --> 
        <xsd:length value="6" fixed="true"/>
    </xsd:restriction>
</xsd:simpleType>

  • 11:20
  • 評論 / 瀏覽 (0 / 438)

剛剛接觸eclipse的這些概念,着實有點頭暈

現在總算對這一部分有了一點具體的認識了,寫出來,順便理一下思路:


emf 用於創建模型
gef是eclipse中表示可編輯圖形界面的非常強大的一種工具,把具體的工作劃分爲MVC三層模式.

所以在通常的開發中,一般是將兩種技術合起來用 , 簡稱GMF .

emf的建模
       emf建模可以採用多種方式,也有不少現成的工具,比如:eclipse uml ,下載個人版的就基本夠用了,但是不能於小組開發(對cvs有限制),當然要是誠心一點也可以找到破解版.....(呵呵,個人言論,沒用過,也不推薦),建模部分唯一要注意的是對其它類的引用(比如說emf.draw2d.Rectangle),其實是可以直接引用的.

       三下兩下建模完畢了,如果是用  .xsd 格式寫的( 辛苦了......) 那麼在eclipse裝完emf的插件後,新建emf項目,導入xsd文件,就可以生成模型的代碼了,要是熟悉了以後,建模還是很方便的.當然 eclipse裏也提供rose, ecore, 以及 uml  類圖的直接導入.
   
       模型部分通常一定會建立control的監聽,我的做法是在寫類圖的時候讓AbstractModel繼承一個Adaptor,這樣生成後的模型就會自動加入監聽了.

       emf提供了非常良好的數據的串行化和反串行化,EMF 能夠處理多重模型源,例如XML模型,所以指定使用哪些資源來實現(反)串行化你的數據也是很重要的。通常,當你調用 ResourceSet.createResource(URI)方法時,它查詢Resource.Factory.Registry來查找一個工廠-該工廠是爲該URI而註冊的並且使用它來創建一個適當的資源實現。因此,在你(反)串行化你的數據以前,請確保你已註冊了適當的資源工廠實現。EMF提供若干Resource.Factory實現:

  ·對於XML數據,使用org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl。

  ·對於XMI數據,使用org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl。

  ·對於Ecore模型,使用org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl。

    對於剛入門的人來說(比如說我啦),對調用那些什麼工廠之類嫌太麻煩的話,只接用xmlEncoder 進行編碼,解碼也是一種不錯的方式,貼一段代碼:
    //   保存  

    XMLEncoder encoder=null;
        try {
            encoder = new XMLEncoder(
                    new BufferedOutputStream(
                            new FileOutputStream("d://Sample.xml")));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        encoder.writeObject(contentsModel);
        encoder.close();

//解析
    XMLDecoder decoder = null;
      
        try {
            decoder = new XMLDecoder(
                        new BufferedInputStream(
                          new FileInputStream("d://Sample.xml")));
                contentsModel = (ContentsModel)decoder.readObject();
                decoder.close();
        } catch (FileNotFoundException e) {
            contentsModel = new ContentsModel();
        }

       最後保存成標準的xml,  它也有自已的dtd; 沒寫出來,但是解析器都能夠識別的
        其實還是相當方便的,要記住了:
        放入的對象必須是標準的pojo,不然的話.......你自已看看保存的 xml就知道了!

      
       最重要的部分,就應該是把生成的模型,與gef無縫集成了,不過涉及到的東西太多,思路倒是有,具體怎麼做......會在下一章節,詳細介紹的;

  • 11:18
  • 評論 / 瀏覽 (0 / 613)
用gmf實現業務模型編輯器
過去用Graphical Editor Framework(GEF)實現業務模型編輯器既慢又痛苦,光是理解複雜的GEF框架,就要花費很長時間,GEF是個典型的MVC框架,用戶可以自定義模型,只要當模型的屬性發生變化時,通知模型的監聽器就可以了,GEF中模型的監聽器被稱爲控制器,它在MVC框架中處於核心地位,是連接模型和視圖的橋樑,它負責將模型的變化反映到視圖上,同時把用戶在視圖上所作的操作反映到模型上,在這過程中還涉及到命令和策略等一些概念,這裏我們就不細說,畢竟不是介紹GEF的。
當然我們可以藉助於Eclipse Modeling Framework(EMF)來實現模型,因爲用EMF生成的模型,就已經實現了消息通知機制,省去了自定義模型中很多工作量,如果EMF和GEF真能很好的結合,估計也不會有GMF的產生了,由於EMF和GEF採用不同的命令堆棧,給EMF和GEF要實現無縫結合帶來了很大的技術問題,這個時候GMF就應運而生了(“時勢造英雄”),它解決了GEF和EMF相結合中遇到的技術問題,當然了我並不是說有了Graphical Modeling Framework (GMF),GEF和EMF就沒有用武之地了,深刻理解GEF和EMF對學習GMF有很大的幫助。
我們還是進入正題吧!這段時間由於工作的原因,對GMF作了一些研究,雖然只是瞭解了一些皮毛,但也想寫出來和大家一起分享,同時也懇請這方面的大蝦們給點意見。
下面,我就用GMF實現了一個簡單的業務模型編輯器,以後可以在這基礎上擴展。
我們知道GMF是以EMF建模爲基礎了,所以需要一個ecore模型,這裏我們的ecore模型是從mdl轉化過來的,我們用Rose設計一個mdl文件,如圖:
在此模型中,有IPackage和IClass類(以後可以增加IAttribute,IAssociation類),它們之間是聚合關係。
構造ecore模型
1.打開Eclipse,在導航器視圖右鍵,New-->Project,,新建一個GMF項目etm;
2.mdlàecore,在導航器視圖右鍵,New-->Other,,新建一個EMF Model:etm.genmodel
 在Select Model Importer嚮導頁中選擇“Rose class model”,在Rose model Import嚮導頁,選擇我們前面新建的etm.mdl文件,點擊完成,這樣就產生了ecore模型和genmodel。
產生Model和Edit Code
1.       打開剛纔產生genmodel,在根節點右鍵,點擊Generate Model Code和Generate Model Code(產生的兩個項目會被以後產生的業務模型編輯器項目引用)。
 創建其他模型
GMF有個非常實用的工具,如圖:
它可以幫助你如何一步步地生成編輯器。
1.從Domain Model開始,選擇select,在彈出的對話框中,選擇我們前面產生的etm.ecore;
2.在Domain Gen Model,選擇select,在彈出的對話框中,選擇我們前面產生的etm.genmodel;
3.選擇Domain Model上面的Derive,將彈出嚮導對話框,這個嚮導對話框用來產生Graphical Def Model,熟悉GEF的人就知道,這些Model對應圖形化編輯器中的圖形模型。在這個嚮導對話框第一個嚮導頁可以指定產生Graphical Def Model的文件名和存放路徑,這裏我們接受默認值etm.gmfgraph,點“下一步”,在第二個嚮導頁中,可以指定對應的ecore模型,這裏我們接受默認值etm.ecore,點“下一步”,在第三個嚮導頁中,只要指定Diagram Element爲IPackage,即指定畫布對應的模型,其它接受默認值,點完成即可。
4.選擇Domain Model下面的Derive,將彈出嚮導對話框,這個嚮導對話框用來產生Graphical Tool Model,熟悉GEF的人就知道,這些Model對應編輯器調色板中的圖形模型。在這個嚮導對話框第一個嚮導頁可以指定產生Graphical Tool Model的文件名和存放路徑,這裏我們接受默認值etm.gmftool,點“下一步”,在第二個嚮導頁中,可以指定對應的ecore模型,這裏我們接受默認值etm.ecore,點“下一步”,在第三個嚮導頁中,只要指定Diagram Element爲IPackage,其它接受默認值,點完成即可。
5.選擇Domain Model右邊的Combine,將彈出嚮導對話框,這個嚮導對話框用來產生emt.ecore,etm.gmfgraph,etm.gmftool三者的Mapping Model。在這個嚮導對話框第一個嚮導頁可以指定產生GMFMap Model的文件名和存放路徑,這裏我們接受默認值etm.gmfmap,點“下一步”,在第二個嚮導頁中,可以指定對應的ecore模型,gmfgraph模型,gmftool模型,這裏我們接受默認值,點“下一步”,在第三個嚮導頁中,只要指定Diagram Root Element爲IPackage,點“下一步”,在第四個嚮導頁中,可以指定節點(Node)和連接弧(Link)對應的類,這裏我們接受默認值,點完成即可。這兒我們還必須修改生成的etm.gmfmap文件,否則下面的操作就會出錯。給Mapping\Top
 Node Reference\Node Mapping\Label Mapping的Diagram Label屬性指定一個值,這裏我們指定Diagram Label IElementName,即在節點圖形上顯示節點的name屬性。
6選擇Mapping Model右面的Transform,將彈出對話框,這裏我們接受默認值etm.gmfgen,點完成,接下來,又彈出彈出對話框,點“Yes”即可。
7選擇Diagram Gen Model裏面的Generator Diagram Editor,就可以生成圖形化編輯器了。
我們重新啓動一個Eclipse 工作臺,File\New\Examples\Etm Diagram如圖:
  • 10:15
  • 評論 / 瀏覽 (0 / 961)

前言:本文源自Eclipse網站GMF子項目的同名實例文檔。雖然本文絕大部分是原文的翻譯,但是我仍然更願意稱其爲“編譯”而非“翻譯”。原因是在讀這個系列文檔的同時,我也在學習GMF相關技術,而學習就會有些心得或想法以及時間操作中遇到的問題,所以本文的內容就不會僅侷限於原文中所包含的內容。我會盡量將其中語焉不詳或不夠具體的部分加以細化,但也會對於某些我覺得冗長的部分也會予以簡化,這種非完全“字字對譯”的方法或許只能體現我的主觀意願,但我相信也不會離題太遠。我記得有位著名的翻譯家也說過,翻譯其實也是一種再創作,如何將作者的本意用自己熟悉的語言表述出來是“字字對譯”所不能達到的。再說原汁原味也未必適合我們中國人的習慣,要想吃原味大餐最好還是看原文。這個系列的教程一共有4個,也不知道我有沒有時間翻譯完,隨遇而安吧,呵呵。

原文前言

本文旨在介紹圖形化的模型框架[Graphical Modeling Framework (GMF)],該框架是一個Eclipse建模項目的子項目,其目標是爲Eclipse建模框架[Eclipse Modeling Framework (EMF)]和圖形化編輯框架[Graphical Editing Framework (GEF)]提供一個統一的橋樑。

在本文中,我們將開發一個稱爲mindmap的應用程序。本文所描述的有關GMF所提供的功能都是基於2.0M4版本的。隨着GMF的不斷演化,本文也會及時地更新內容以便可以覆蓋GMF提供的最新功能。我們已經將例子工程的源碼及其相關的內容打包,有需要的話可以到http://www.eclipse.org/gmf/tutorial/mindmap_tutorial_1.zip下載該示例工程。

GMF的系統需求

Eclipse3.3啓動設置:我家的“老爺機”配置如下:P41.5/256M內存/40G硬盤。由於運行本文的例子需要以及網上的朋友都贊3.3版的內存使用率比以前有很大提高並且不容易出現在3.2版中不明不白死掉的狀況,於是到官網下了一個,但只要一運行就會出現如下的對話框,然後Eclipse就自動終止:

忙活了好一陣之後發現,只要將eclipse.ini文件中的XX:MaxPermSize和Xmx改小一點就可以了,例如以我的機器來說只要設置成XX:MaxPermSize=80和Xmx=512,Eclipse即可正常啓動。如果你用過3.2版的話,可以看到在那個版本中啓動參數沒有XX:MaxPermSize,並且-Xms和-Xmx分別是40M和256M,可見3.3版對內存的要求還是提高了。當然,如果你的機器有N個G內存的話,自然不會有任何煩惱,直接掠過這段就好J。

1.   GMF簡介

目前,聯合使用EMF和GEF來構建基於Eclipse的圖形化應用程序已經越來越普遍。下面的許多參考資料都給出瞭如何將這兩個框架整合使用的方法,其中的一些方法甚至需要深入到GMF工程內部。在進入GMF工程之前,讓我們首先了解一下GMF是如何通過一個通用的方式來使用EMF和GEF來完成既定任務的。另一篇關於GMF 的runtime部分的文章可以在這裏找到:http://www.eclipse.org/articles/Article-Introducing-GMF/article.html

GMF工程使用術語“toolsmith”來指代那些使用GMF來構建插件的開發者,而“user”通常指那些利用這些插件並同時也是開發者的人。在一個可用的透視圖中,GMF以內部方式構建出來的“模型”的數量和種類應該在很大程度上是隱藏的。然而,大多數toolsmith都對臺面之下正在發生什麼感興趣(注:好奇心重是天性,呵呵),所以在本文中我們將對每一個模型都給出一個詳細的描述。

下圖展示了在進行基於GMF應用開發時所需要使用的主要構件和模型。GMF的核心是一個概念模型(concept of a graphical definition model)。該模型包含了那些將會出現在基於GEF的runtime中,且與圖形元素相關聯的各種信息,而這些信息是與那些提供描述和編輯功能的領域模型(domain model)沒有直接關聯的。工具定義模型(tooling definition model)是一個可選組件,它可以用來設計palette(注:就是我們用VE時常常會看到的選擇組件用的面板)以及其他實用組件(菜單,工具欄,等等)。

我們希望圖形化或工具組件的定義可以在面對不同的領域時仍然能正常工作。例如,UML的類圖有很多counterpart,而所有這些conterpart的基本外觀和結構都是十分相似的。GMF的一個目標就是允許一個圖形化定義可以被很多領域複用。通過使用一個獨立的映射模型來將圖形定義和工具定義連接到所選擇的領域模型,GMF漂亮的完成了這一目標。

一旦定義了合適的映射,GMF就會提供一個生成器模型(generator model)來幫助用戶確定在生成階段(generation phase)需要定義的各種實現細節。一個基於生成器模型編輯器插件將會作爲這一過程的產生品,並將導致最終的模型[final model]的產生。最終模型也稱爲圖形運行時[diagram runtime](或“符號”)模型。當一個用戶工作在一個圖形上時,該runtime將會把符號和領域模型橋接起來,並同時提供持久化和序列化功能(注:以上提到的這幾種模型都有實物對應,現在先不必細究)。這個runtime的一個很重要的方面是它提供了一個基於服務的方法來使用EMF和GEF,並且能通過non-generated式的應用程序來進行調整。

在瞭解了GMF的一些基本概念之後,接下來我們要將GMF應用到一個某一特殊領域的圖形化編輯層的開發當中去。首先,你可能會需要安裝GMF及其相關組件。

2.   GMF組件的安裝

本文所用的GMF版本爲GMF(2.0M4),請注意在build頁上標明的GMF運行的先決條件,必須在將這些組件安裝完之後才能安裝GMF。安裝方式很簡單,或通過其下載頁面,或通過更新管理器進行。

由於要安裝的組件種類繁多,而且缺一不可,而且組件之間的安裝也有一定的順序,所以不推薦自己費時費力地一個一個到Eclipse的下載頁面尋找。本文推薦的方法是利用Eclipse的更新管理器到Europa的更新站點進行自動升級,又輕鬆又安全。具體方法是,點擊“Help->Updates->Find and Install….”,然後選“Serach for new features for install->Europa Discovery Site”。然後從“Models and Model Develop”列表中選擇Graphical Modeling Framework(Europa Edition),然後點擊“select Required”按鈕來選擇GMF所需要的相關組件。很簡單吧?

但其實還有更簡單的方法,在GMF的下載頁面的中第一個連接是:Consolidated requirements zip,也就是說,開發人員已經把包打好了,該放的東西也放進去了,於是我們解壓縮就可以直接用了。比起用上面的方法訪問那個超慢的更新網站,這個方法顯然更加方便。

除了上面所說的那個例子之外,各位還可以下載一個名爲TaiPan的例子,它是使用最新版本的GMF構建而成,專門用來展示GMF最新功能的例子。所以,即便本文過時了,你也可以通過下載這個例子來獲取最新的知識。不過這個例子需要使用CVS--速度超級慢,下載要有耐心才行。

2.1. TaiPan的下載與運行

如果你急切地想要看看GMF到底有什麼值得你關注的地方,你就應該通過CVS下載Taipan示例工程到你的工作空間。否則的話,請直接閱讀下一部分。

通過CVS下載資源之前,首先需要建立一個CVS Repository Location,具體方法是點擊new->other,在CVS目錄窗格下選擇“CVS Repository Location”就會看到如下圖的界面。

按照如下圖所示的內容進行填寫,完成後訪問:/HEAD/org.eclipse.gmf/examples,並選擇org.eclipse.gmf.examples.taipan.*模塊(這裏面好東西不少,希望多學一些的可以多下載些例子),右鍵點擊並選擇check out就可以下載了。如果你沒有使用最新版本的GMF的先決組件,你總是可以通過檢查與你正在使用的版本相對應的日期來下載對應版本的Taipan示例工程。很重要的一點是你必須使GMF SDK的版本與Taipan示例工程的版本保持一致才行。爲了做到這一點,在校驗完畢之後,你可以右鍵單擊該工程,並選擇“Team | Switch to Another Branch or Version...”,然後選擇“Select the tag from the following list”並使用底部的“add Date…”按鈕來輸入GMF2.0M4 release的日期(03 January 2007)。最後點擊Finish。

切換到插件開發透視圖,並打開org.eclipse.gmf.examples.taipan工程下的model文件夾。查看每一個可用模型,特別是taipan.gmfgraph和taipan.gmfmap模型以及他們的元素屬性。你會發現有很多rcp版本的Taipan例子可供學習。

在本文中,我們將會按順序查看每一個模型,但僅僅是驗證一下你的配置,你需要在一個運行時工作空間(runtime workspace)裏自己運行這個例子(建立一個EclipseApplication運行配置,並簡單的接收默認值即可)。在一個獨立的工作空間中,建立一個空的工程和一個新的“TaiPan Diagram”(在一個新的運行時工作空間中,點擊new機會發現建立TaiPan Diagram的選項),給他起個名字然後點擊Finish。生成的圖標編輯器會爲你打開這個新生成的圖形。學習時我們特別需要注意以下部分:

·   工具選項板和overview[tool palette and overview]

·   佈局和選擇工具[layout and selection tools]

·   圖形圖像的輸出[diagram image export (svg, bmp, jpeg, gif) ]

·   層疊的屬性視圖[tabbed properties view]

·   選中元素的字體和顏色選項[font and color options for selected element]

·   連接路由和風格選項[link routing and style options]

·   彈出欄和連接柄[pop-up bars and connection handles]

·   短箋和幾何圖形[notes and geometric shapes]

·   即時縮放和佈局[animated zoom and layout]

建立一個命名爲TaiPanApplication新的啓動配置,並在插件選項板中僅選擇Taipan *.rcp插件及其依賴插件,然後以一個獨立RCP圖形編輯器的方式來運行Taipan。

以上是本文的一個概覽,在下面的部分我會帶領大家深入查看上面在談到mindmap模型層所提及的每一個模型的細節。(編譯者注:對於Taipan工程,如果有時間的話,我會在下一篇文章中詳細介紹)

2.2. 一個新的工程

在我們開始新的工程之前,你需要先定製一下你的工作空間(或至少定製一下你的GMF工程)的編譯器爲JDK1.5編譯器。具體位置在Window->Preference->Java->Compiler選項,然後選擇支持JDK1.5的編譯器。

一個典型的GMF的使用場景(usage scenarios)包括爲mindmap應用程序產生一個圖形層,最終補充進一個可供選擇的、用於展示臨時信息的視圖(例如甘特圖)。本節將會展示GMF在這方面的能力,並將隨着工程的成熟而不斷演化。如果你更喜歡使用那些包含了完整解決方案的工程的話,你可以通過用上面的方法通過查閱org.eclipse.gmf/examples模塊來獲取完整的工程。

GMF自帶了一個“Tutorial Cheat Sheet”,你可以通過“Help | Cheat Sheets”來讓它“顯形”(主意,如果你已經將GMF安裝到一個不含SDK的平臺上,例如,使用Europa的更新管理器升級得到的GMF,你將需要從Window | Show View… | Other菜單打開Cheat Sheets的視圖,然後從視圖菜單中打開GMF Tutorial Cheat Sheet)。如果你打開了這個cheat sheet並遵照其提示的每一個步驟,你就能獨立完成本文第一個階段的大部分內容,並同時能獲得一些以後需要的技能和知識。現在就試着建立你的新project吧。

作爲可選項,你可以建立一個“New GMF Project”(使用Ctrl+N快捷鍵探出的窗口裏找到“Graphical Modeling Framework”)作爲學習的起點。將其命名爲“org.eclipse.gmf.examples.mindmap”,並在工程根目錄下建立一個名爲“model”的新文件夾。

3.   領域模型的定義(Domain Model Definition)

雖然看起來以領域模型作爲起點是必須的,但在GMF中卻不是這樣,因爲圖形的定義是與領域相分離的。然而和大多數教程一樣,本文也將從領域模型開始,然後通過使用下一節要講解的、由嚮導生成的領域模型來轉入到我們的圖形定義當中。在原文所給出的連接中,我們可以找到一個屬於mindmap的ecore類型的基本領域模型。此外,你還可以使用mindmap模型的XSD版(在這種情況下,你可能需要安裝EMF的XSD feature)。將這個文件複製到你的工程根目錄下的‘model’文件夾,如果你願意,可以隨意進行一些你想要進行的測試或檢查。作爲標準EMF生成的編輯器的補充,GMF也提供了一個圖形化的編輯器。並將其打包在SDK中。如果想用這個編輯器對mindmap.ecore(或任何 *.ecore模型)進行渲染(render),只需要簡單地右鍵單擊ecore文件,並在右鍵菜單中選擇“Initialize ecore_diagram diagram file”即可。

接下來,使用New->Eclipse Modeling Framework > EMF Model wizard,從mindmap.ecore文件創建一個新的mindmap.genmodel。你也許會想要把genmodel的“base package”屬性從“Mindmap”改成org.eclipse.emf.examples,以便使你所生成的包名與工程名保持一致。

通過右鍵單擊生成器模型(generator model)的根所彈出的菜單,我們就可以生成模型並編輯代碼了。雖然創建一個編輯器或進行測試不是必需的步驟,但是我建議各位還是實際操作一下。至此,我們已經爲創建一個圖形和有關mindmap應用程序的映射的定義(mapping definitions)作好了準備。

4.   圖形的定義(Graphical Definition)

圖形定義模型被用來定義形狀,節點,鏈接(link)等等這些將會顯示在你的圖形理的元素。接着cheat sheet的下一步,我們將要創建一個新的圖形定義模型。Cheat cheet將會啓動一個簡單的圖形定義模型嚮導,我們同樣也可以在“新建”對話框的GMF文件夾下找到它。在你的org.eclipse.gmf.example.mindmap工程下的‘model’文件夾中,選擇mindmap.gmfgraph模型,在嚮導的下一頁使用‘Browse’來定位你的mindmap.ecore文件。選擇我們的Map類作爲圖形元素(diagram element)。

如下圖所示,在嚮導的最後一頁,爲我們的Topic類選擇一個元素,鏈接和標籤選項的最小集合。稍後,你可以親自驗證一下這個嚮導並觀察一下它的輸出。現在,我們已經獲得了啓動應用程序所需模型的最小集合。點擊finish來結束該向導。

提示:在GMF中包含了很多以複用爲目的的圖形庫。通過使用“load resource1…”並輸入“platform:/plugin/org.eclipse.gmf.graphdef/models/basic.gmfgraph”作爲資源URL,你就可以將這些圖形庫載入到你的*.gmfgraph模型(或*.gmfmap模型)中。其他可用的資源包括classDiagram.gmfgraph和stateDiagram.gmfgraph。

5.   工具定義(Tooling Definition)

正如前面所描述的那樣,工具定義模型被用來定義工具板,創建工具(creation tools),動作(actions)等等。Cheat sheet將會通過一個非常簡單的過程來引導你掌握我們的這個簡單的工具定義模型。事實上,這兩個過程是十分類似的,因爲mindmap領域模型是由其可能用到的工具所載入並檢查的。我們將會簡單地選擇和圖形定義模型相同的選項,爲Topic名字的標籤保存一個tool,然後爲我們的Topic元素啓動一個簡單的palette。

通過提供給我們的模型,我們可以看到在一個Palette中存在着一個頂層“工具註冊表(Tool Registry)”元素。Palette包含一個帶有創建工具元素(Creation Tool element)的工具組,這些創建工具元素是專門爲Topic節點和那些用嚮導定義的subtopic元素的鏈接而設立的。我們將會在未來對他們進行一些小改動,但是現在,我們將接受默認值並且將注意力轉移到映射定義。你可以瀏覽一下這個模型並實地查看一下它們的各個屬性來熟悉一下工具定義。

6.  

  • 09:41
  • 評論 / 瀏覽 (0 / 3260)

在GMF中當使用XYLayout時經常會遇到這樣的問題:

1。在create一個新figure時,figure會跑到它的parent figure的外面。

2。在move或者resize時跑到parent figure的外面。

如下圖logic example中的問題:

一般的情況下,用戶會challenge這個現象,如果在resize的時候,即便把figure的邊界拖出parent,figure可以自動保持在parent的內部就比較好。

做到這個也不難,重載XYLayoutEditPolicy中的public Object getConstraintFor(Rectangle r) 方法就行了。參數r是gmf xylayout默認返回的figure的constrain,你可以根據自己的需要改變r中的width, height, x,y等參數,具體的算法就因需求而異了。

看一下XYLayoutEditPolicy還有他的父類中有許多同名不同參數的getConstrainFor(...)方法,有可以重載來定製用戶的resize或move figure的行爲。就不多介紹了,看源代碼吧!

  • 09:32
  • 評論 / 瀏覽 (0 / 566)
2008-06-19

GMF常見問題

    博客分類:
  • GMF
1、問題:連接線旁邊沒有文字標籤和箭頭

文字標籤:在gmfmap裏的Connection Mappping下增加Label Mapping元素;箭頭:在gmfgraph裏爲Polyline Connection指定一個Polyline Decorator作爲source/target decoration,要爲這個Decorator創建一些Template Point來決定箭頭的形狀,例如指定(-1,-1), (0,0), (-1,1)。

2、讓一個圖形可以在另一個圖形裏隨意改變位置

在gmfgen裏把作爲容器的那個圖形的Gen Compartment裏把Listlayout屬性改爲false。

3、隱藏圖形標籤文字前的小圖標

在gmfgen裏把相應的Gen Node Label元素的Element Icon屬性改爲false(但重新生成gmfgen時這個屬性會被覆蓋)

4、讓標籤裏同時顯示和編輯多個屬性

在gmfmap裏把相應的Label Mapping元素的View Pattern屬性改爲類似“屬性1:{0},屬性2:{1}”的形式。

5、問題:跨Compartment進行連線操作時會創建兩條連線

GMF的bug,見https://bugs.eclipse.org/bugs/show_bug.cgi?id=148021,在你的XXXDiagramCanonicalEditPolicy裏覆蓋方法:

protected boolean shouldHandleNotificationEvent(Notification event) {
    return false;
}

6、讓Label出現在圖元外面

在gmfgraph裏定義這個Figure時把Label定義在外面,而非定義爲Figure的子元素。

7、在gmfgraph裏設置一個Figure使用GridLayout後生成的代碼無法正確編譯

GMF的bug,見https://bugs.eclipse.org/bugs/show_bug.cgi?id=133279

8、改變Figure的缺省大小

在gmfgraph裏爲Figure增加Preferred Size子元素;若想讓圖形尺寸小於40x40象素,要覆蓋XXXEditPart裏的createNodePlate()方法。在GMF2.0裏,使用DefaultSizeFacet,見http://dev.eclipse.org/newslists/news.eclipse.modeling.gmf/msg01546.html

9、禁止用戶修改圖元的尺寸

在gmfgraph裏將此Node的Resize Constraint屬性值改爲“NONE”(但size-on-drop功能仍存在,也就是用戶仍然可以在創建時指定尺寸)。

10、讓Compartment在容納不下子圖形時自動顯示滾動箭頭

在genmodel的GenDiagram元素裏改Units屬性爲“himetric”(經測試對GMF1.0不起作用),見https://bugs.eclipse.org/bugs/show_bug.cgi?id=140789

11、爲畫布Canvas指定Layout

GMF1.0不支持,需要手工改代碼,見https://bugs.eclipse.org/bugs/show_bug.cgi?id=139951

12、Border Item

Border Item是指只能緊貼其他圖元運動的圖形,GMF1.0可通過打patch實現這個功能,見https://bugs.eclipse.org/bugs/show_bug.cgi?id=124826;GMF2.0開始支持。

13、規定連接線的約束,例如規定source和target不能是同一對象

在gmfmap裏定義,在Link Mapping元素下定義Link Constraint元素,缺省使用OCL,見教程http://wiki.eclipse.org/index.php /GMF_Tutorial_Part_2#Link_Constraints;不論使用OCL或是Java,在 XXXBaseItemSemanticEditPolicy裏會生成LinkConstraint類,在生成command前檢查是否滿足這些 constraint。

14、Audit

定義的constraint出現在com.your.diagram項目的plugin.xml裏,作爲constraintProvider擴展;爲了讓這些constraint生效,要在gmfgen的Gen Diagram元素裏設定Validate Enabled/Decorator屬性值爲true,並將優先級(Validation Provider Priority, Validation Decorator Provider Priority)設定爲medium(非lowest)纔會在Diagram菜單裏出現Validate命令。

若是在gmfmap裏選擇使用Java驗證,則在gmfmap裏指定的是一個Java方法名,生成代碼後,應在XXXValidationProvider類裏應實現這個方法。

15、GMF裏從EditPart得到Semantic Model

因爲GMF裏EditPart#getModel()方法得到的是Notation Model裏的對象,如Node或Edge,所以可以使用這樣的方法得到真正的業務對象: ((org.eclipse.gmf.runtime.notation.View) EditPart.getModel()).getElement()或ViewUtil.resolveSemanticElement(view)

16、問題:從gmfgen生成代碼時產生java.lang.ClassCastException: org.eclipse.jdt.internal.core.jdom.DOMMethod

刪除原先生成的代碼中無法編譯的類,重新生成。

17、在gmfgraph裏定義Polyline的圖形

在Rectangle上畫Polyline,注意是固定大小的

18、問題:每次重新生成代碼後,在plugin.xml裏的修改會丟失

GMF1.0裏生成代碼時不能保留plugin.xml裏的任何修改,從GMF2.0開始用戶可以在plugin.xml裏標記不要覆蓋的區域

19、問題:Outline樹視圖裏的節點沒有圖標

在plugin.xml裏找到org.eclipse.gmf.runtime.emf.type.core.elementTypes擴展點,在下面相應的元素裏指定icon屬性,見http://dev.eclipse.org/newslists/news.eclipse.modeling.gmf/msg00341.html,但我在GMF1.0裏測試不起作用,何況每次生成代碼時這個文件都會被覆蓋。

20、問題:提示java.lang.IllegalStateException: Cannot modify resource set without a write transaction異常

在GMF裏修改Model要通過在TransactionalEditingDomain裏執行命令完成,GMF提供的RecordingCommand是不錯的選擇,它爲我們提供了Undo支持,我們只要實現執行部分的代碼就可以了,下面是一個例子:

TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(myElement);
domain.getCommandStack().execute(new RecordingCommand(domain) {
    @Override
    protected void doExecute() {
        //Do anything
    }
});

若是在EditPolicy裏需要返回一個Command,用下面的代碼:

AbstractTransactionalCommand command = new AbstractTransactionalCommand(TransactionUtil
        .getEditingDomain(myElement), "Command Name", Collections.EMPTY_LIST) {
    @Override
    protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)
            throws ExecutionException {
        //Any modification to the model
        return CommandResult.newOKCommandResult();
    }
};

21、問題:創建圖元時提示異常“java.lang.IllegalArgumentException: Figure must be a child”

Workaround:註釋掉產生異常的setRatio()方法裏的全部內容。

22、問題:在AbstractBorderItemEditPart子類的getPrimaryDragEditPolicy()方法裏提示ClassCastException異常

在gmfgraph裏檢查作爲BorderItem的那個Node的Resize Constraint屬性是否改過,若爲缺省的NSEW則對應的editpart不會生成這個方法,對BorderItem(即Affixed Node Side屬性不爲NONE的Node)來說這個屬性雖然設置爲NSEW也無法改變大小。相關鏈接:https://bugs.eclipse.org/bugs/show_bug.cgi?id=155698

23、如何禁用PopupBar和ConnectionHandler功能(鼠標停止在圖形上時出現的連線符號)

在需要禁用該功能的EditPart的createDefaultEditPolicies()方法的最後加下面的語句:

//禁用PopupBar
removeEditPolicy(EditPolicyRoles.POPUPBAR_ROLE);
//禁用ConnectionHandler
removeEditPolicy(EditPolicyRoles.CONNECTION_HANDLES_ROLE);

24、使用ConnectionHandler連接到canvas上已存在的圖形或創建新的圖形

覆蓋XXXModelingAssistantProvider裏的幾個get方法,要連接到已存在的圖形覆蓋 getRelTypesOnSourceAndTarget()方法,創建新的作爲源的圖形覆蓋getRelTypesOnSource()和 getTypesForTarget()方法,創建新的作爲目標的圖形應覆蓋getRelTypesOnTarget()和 getTypesForSource()方法。具體代碼可參考LogicModelingAssistantProvider裏的實現。

25、給畫布加背景圖

http://www.cnblogs.com/bjzhanghao/archive/2007/03/13/673273.html
BTW, 以上所有問題只針對GMF1.0,GMF2.0的gmfmap模型和gmfgen模型與前一版本有所不同,一些問題可能也在GMF2.0裏不存在了。

26、使用Label作爲一個editpart的figure

在.gmfgraph裏不用創建Node,只用Diagram Label即可;在.gmfmap裏,Node Mapping的Diagram Node屬性指定爲這個Diagram Label,下面的Feature Label Mapping的Diagram Label屬性也是這個Diagram Label。在parent使用ListLayout的時候這個方法比較有用。GMF的mindmap例子裏的ThreadItem就是這樣一個例子。

27、若類A包含B和C,且C繼承B,則試圖讓A的圖形同時包含B和C會造成運行時異常,異常信息是無法創建C的View,可能是GMF目前版本的bug。解決辦法是建立抽象類D,讓B和C都繼承D,並且讓A包含D。(update 2007/7/23: 有一點像這個bug,異常信息差不多)

28、用漸變色填充非矩形圖形

覆蓋圖形的fillShape()方法,利用swt的Path,但draw2d的graphics對它的支持似乎不好。http://dev.eclipse.org/newslists/news.eclipse.tools.gef/msg13928.html

29、(這條實際是關於EMF的,anyway)爲TableViewer增加Drag and Drop支持

非常簡單,見下面的代碼(tv是TreeViewer的一個實例)

int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
tv.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(tv));
tv.addDropSupport(dndOperations, transfers, new EditingDomainViewerDropAdapter(editingDomain, tv));

30、從EObject得到TransactionalEditingDomain

TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(eobject);

31、讓Label換行

.gmfgraph裏無法指定Label是否換行,要修改生成的代碼:

fFigureXXXFigure = new WrapLabel();
fFigureXXXFigure.setTextWrap(true);//add this line
fFigureXXXFigure.setText("<>");

另外可以在.gmfgraph裏指定需要的佈局以便讓換行Label更好的顯示。給Label設置Margin Border會有問題(Label被推向右側),可以給Parent圖形設置Margin Border,或建一個RectangleFigure來實現設置文字邊距的需求。

32、定製Project Exploerer裏顯示的內容

修改.gmfgen裏Gen Navigator節點下面的元素,見http://wiki.eclipse.org/index.php/GMF_Tutorial_Part_4#Project_Navigator

33、可縮放的多邊形

在.gmfgraph裏定義爲Scalable Polygon,和普通Polygon一樣要定義template points,每個點的座標絕對值不是關鍵,但它們之間的位置關係要保證。我發現絕對值定義得大一些時,得到的結果會更精確。下面是一個可縮放菱形的定義:
<descriptors name="ConditionFigure">
 
  <actualFigure xsi:type="gmfgraph:ScalablePolygon" name="MyDiamondFigure">
    <template x="200" y="0"/>
    <template x="0" y="200"/>
    <template x="200" y="400"/>
    <template x="400" y="200"/>
    <template x="200" y="0"/>
  </actualFigure>
</descriptors>

34、生成的RCP應用裏,保存操作後經常提示“the file has been modifying on the file system...”信息。

GMF太“聰明”了,每次save後都要記錄文件修改的timestamp,一旦發現不符則認爲有其他程序修改了這個文件。要讓RCP應用不檢查當前編輯的文件是否被其他程序修改,可覆蓋XXXDocumentProvider的isSynchronized()方法,讓它直接“return super.isSynchronized(element);”。(但要小心,這有可能造成用戶的修改無法被保存的情況。)

35、生成的GMF應用程序裏,打印功能是禁用狀態。
打開.gmfgen文件,修改Gen Plugin的"Printing Enabled"屬性爲true,再重新生成代碼。這樣除了 Print變爲可用外,GMF還會生成一個XXXContributionItemProvider類在主菜單上添加Print Preview選項。 http://dev.eclipse.org/newslists/news.eclipse.modeling.gmf/msg02207.html

36、(實際是Eclipse OCL問題)脫離Eclipse環境使用OCL時,報異常java.lang.NoClassDefFoundError: lpg/lpgjavaruntime/RuleAction

Eclipse OCL依賴lpg庫(LALR parser generator,使用EPL協議),在RCP裏使用OCL需要把net.sourceforge.lpg.lpgjavaruntime這個插件加在dependencies列表裏。參考鏈接

37、(還是OCL問題)Eclipse OCL實現裏,OCL語句裏各集合類型與ecore裏集合類型的映射:
Collection Type isUnique isOrdered
Bag N N
Sequence N Y
Set Y N
OrderedSet Y Y

所以,如果一個EList在ecore裏定義爲Unique且Ordered(即缺省定義)時,在OCL裏應該用OrderedSet類型,例如:XXX->allInstances()->asOrderedSet()或OrderedSet{object1, object2},等等。

38、在畫布上創建一個元素(包括連接)後根據當前模型狀態自動設置某屬性值:

(GMF允許通過多種語言如ocl、regexp和java來實現初始值的設置,這裏以java爲例)在xxx.gmfmap文件裏,找到這個元素對應的Mapping節點(如Node Mapping或Link Mapping),點右鍵新建一個Feature Seq Initializer元素,在這個元素上點右鍵再新建一個Feature Value Spec元素,設置後者的Feature爲想要設置的類型,語言選java,在Body屬性裏輸入一個方法名,例如 “initialMyFeature”。重新生成.gmfgen和代碼,GMF會在名爲ElementInitializers.java的文件裏生成 initialMyFeature()這個空殼方法,實現它即可。

39、新建嚮導結束後,生成一個非空的模型文件。(Customize新創建的模型文件)

修改XXXDiagramEditorUtil#createInitialModel()方法。

40、在partition diagram裏,從shortcut到一個正常節點間的連線在關閉editor後再次打開時丟失(2008.1.4)

原因不明,暫時的解決方法是註釋掉XXXCanonicalEditPolicy#refreshConnections()方法裏的deleteViews(existingLinks.iterator()),其中XXX代表link元素的父元素,例如Diagram。(這個解決方法有嚴重問題,會造成Initialize Diagram時丟失全部連接)。新探索出來的解決方法如下,覆蓋XXXCanonicalEditPolicy#sholdDeleteView()方法:
/** *//**
* @generated NOT
*/
protected boolean shouldDeleteView(View view) {
    if(view instanceof Edge){
        Edge edge = (Edge)view;
        View sourceView = edge.getSource();
        View targetView = edge.getTarget();
        if(sourceView.getEAnnotation("Shortcut")!=null
                || targetView.getEAnnotation("Shortcut")!=null){
            return false;
        }
    }
    return true;
}

41、刪除右鍵菜單裏不需要的菜單項

在plugin.xml裏聲明contributionItemProviders擴展點,在popupContribution下指定如下元素:
<popupPredefinedItem id="autoSizeAction"remove="true"/>

一些GMF Runtime定義的ID:deleteFromModelAction, navigateGroup, fileMenu, toolbarArrangeAllAction, addGeoShapesGroup, addGeoShapes2Group

詳見org.eclipse.gmf.runtime.diagram.ui.actions.ActionIds

42、在單獨的編輯窗口裏編輯子圖(Diagram Partitioning)

http://wiki.eclipse.org/Diagram_Partitioning

43、在單獨的項目(非GMF生成的xxx.diagram項目)裏擴展DiagramEditor能識別的adapter類型:

在單獨項目的Activator的start()方法裏用類似下面的代碼,這樣就不需要直接修改生成的XXXDiagramEditor#getAdapter()方法了(因爲我們希望把定製的內容儘量放在生成的項目以外):
//Register adapters for ERM reports and charts
Platform.getAdapterManager().registerAdapters(new IAdapterFactory() {

    /** *//**
     * @see org.eclipse.core.runtime.IAdapterFactory
     */
    public Object getAdapter(Object adaptableObject, Class adapterType) {
        ErmDiagramEditor editor = (ErmDiagramEditor) adaptableObject;
        Process process = (Process) editor.getDiagram().getElement();
        if (adapterType == IRiskImportancePage.class) {
            return new RiskImportancePage(process);
        } else if (adapterType == IRiskDrillDownPage.class) {
            return new RiskDrillDownPage(process);
        }

        return null;
    }

    /** *//**
     * @see org.eclipse.core.runtime.IAdapterFactory
     */
    public Class[] getAdapterList() {
        return new Class[] { IRiskImportancePage.class, IRiskDrillDownPage.class };
    }

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