[Eclipse]GEF入門系列(八、使用EMF構造GEF的模型)

來源:互聯網
上載者:User

GEF的設計沒有對模型部分做任何限制,也就是說,我們可以任意構造自己的模型,唯一須要保證的就是模型具有某種訊息機制,以便在發生變化時能夠通
知GEF(通過EditPart)。在以前的幾個例子裡,我們都是利用java.beans包中的PropertyChangeSupport和
PropertyChangeListener來實現訊息機制的,這裡將介紹一下如何讓GEF利用EMF構造的模型(下載例子,可編輯.emfsubject檔案,請對比之前功能相同的非EMF例子),假設你對EMF是什麼已經有所瞭解。

EMF使用自己定義的Ecore作為元模型,在這個元模型裡定義了EPackage、EClassifier、EFeature等等概念,我們要定
義的模型都是使用這些概念來定義的。同時因為ecore中的所有概念都可以用本身的概念迴圈定義,所以ecore又是自己的元模型,也就是元元模型。關於
ecore的詳細概念,請參考EMF網站上的有關資料。

利用EMF為我們產生模型代碼可以有多種方式,例如通過XML
Schema、帶有注釋的Java介面、Rose的mdl檔案以及.ecore檔案等,EMF的代碼產生器需要一個副檔名為.genmodel的檔案提供
資訊,這個檔案可以通過上面說的幾種方式產生,我推薦使用Omondo公司的EclipseUML外掛程式來構造.ecore檔案,該外掛程式的免費版本可以從這裡下載。(也許需要使用國外代理才能訪問omondo網站)


圖1 樣本模型

為了節約篇幅和時間,我就不詳細描述構造EMF項目的步驟了,這裡主要把使用EMF與非EMF模型的區別做一個說明。圖1是例子中使用的模型,其中Dimension和Point是兩個外部java類型,由於EMF並不瞭解它們,所以定義為datatype類型。

使用兩個Plugins

為了讓模型與編輯器更好的分離,可以讓EMF模型單獨位於一個Plugin中(名為SubjectModel),而讓編輯器Plugin
(SubjectEditor)依賴於它。這樣做的另一個好處是,當修改模型後,如果你願意,可以很容易的刪除以前產生的程式碼,然後全部重建。

EditPart中的修改

在以前我們的EditPart是實現java.beans.PropertyChangeListener介面的,當模型改用EMF實現後,
EditPart應改為實現org.eclipse.emf.common.notify.Adapter介面,因為EMF的每個模型對象都是
Notifier,它維護了一個Adapter列表,可以把Adapter作為監聽器加入到模型的這個列表中。

實現Adapter介面時須要實現getTarget()和setTarget()方法,target代表發出訊息的那個模型對象。我的實現方式是在EditPart裡維護一個Notifier類型的target變數,這兩個方法分別返回和設定該變數即可。

還要實現isAdapterForType()方法,該方法返回一個布爾值,表示這個Adapter是否應響應指定類型的訊息,我的實現一律為"return type.equals(getModel().getClass());"。

另外,propertyChanged()方法的名稱應改為notifyChanged()方法,其實現的功能和以前是一樣的,但代碼有所不同,下面是NodePart中的實現,看一下就應該明白了:

 

public void notifyChanged(Notification notification) {
    int featureId = notification.getFeatureID(ModelPackage.class);
    switch (featureId) {
    case ModelPackage.NODE__LOCATION:
    case ModelPackage.NODE__SIZE:
        refreshVisuals();
        break;
    case ModelPackage.NODE__INCOMING_CONNECTIONS:
        refreshTargetConnections();
        break;
    case ModelPackage.NODE__OUTGOING_CONNECTIONS:
        refreshSourceConnections();
        break;
    }
}

還有active()/deactive()方法中的內容需要修改,作用還是把EditPart自己作為Adapter(不是
PropertyChangeListener了)加入模型的監聽器列表,下面是SubjectPart的實現,其中eAdapters()得到監聽器列
表:

 

public void activate() {
    super.activate();
    ((Subject)getModel().eAdapters()).add(this);
}

可以看到,我們對EditPart所做的修改實際是在兩種訊息機制之間的轉換,如果你對以前的那套機制很熟悉的話,這裡理解起來不應該有任何困難。

ElementFactory的修改

這個類的作用是根據template建立新的模型對象執行個體,以前的實現都是"new XXX()"這樣,用了EMF以後應改為"ModelFactory.eINSTANCE.createXXX()",EMF裡的每個模型對象執行個體都應該是使用工廠建立的。

 

public Object getNewObject() {
    if (template.equals(Diagram.class))
        return ModelFactory.eINSTANCE.createDiagram();
    else if (template.equals(Subject.class))
        return ModelFactory.eINSTANCE.createSubject();
    else if (template.equals(Attribute.class))
        return ModelFactory.eINSTANCE.createAttribute();
    else if (template.equals(Connection.class))
        return ModelFactory.eINSTANCE.createConnection();
    return null;
}

使用自訂CreationFactory代替SimpleFactory

在原先的PaletteFactory裡定義CreationEntry時都是指定SimpleFactory作為工廠,這個類是使用
Class.newInstance()建立新的對象執行個體,而用EMF作為模型後,建立執行個體的工作應該交給ModelFactory來完成,所以必須定義
自己的CreationFactory。(注意,範例程式碼裡沒有包含這個修改。)

處理自訂資料類型

我們的Node類裡有兩個非標準資料類型:Point和Dimension,要讓EMF能夠正確的將它們儲存,必須提供序列化和還原序列化它們的方
法。在EMF為我們產生的程式碼裡,找到ModelFactoryImpl類,這裡有形如convertXXXToString()和
createXXXFromString()的幾個方法,分別用來序列化和還原序列化這種外部資料類型。我們要把它的預設實現改為自己的方式,下面是我對
Point的實現方式:

 

public String convertPointToString(EDataType eDataType, Object instanceValue) {
    Point p = (Point) instanceValue;
    return p.x + "," + p.y;
}
public Point createPointFromString(EDataType eDataType, String initialValue) {
    Point p = new Point();
    String[] values = initialValue.split(",");
    p.x = Integer.parseInt(values[0]);
    p.y = Integer.parseInt(values[1]);
    return p;
}

注意,修改後要將方法前面的@generated注釋刪除,這樣在重建代碼時才不會被覆蓋掉。要設定使用這些類型的變數的預設值會有點問題(例
如設定Node類的location屬性的預設值),在EMF內建的Sample Ecore Model
Editor裡設定它的defaultValueLiteral為"100,100"(這是我們通過convertPointToString()方法定
義的序列化形式)會報一個錯,但不管它就可以了,在產生的程式碼裡會得到這個預設值。

儲存和載入模型

EMF通過Resource管理模型資料,幾個Resource放在一起稱為ResourceSet。前面說過,要想正常儲存模型,必須保證每個模
型對象都被包含在Resource裡,當然間接包含也是可以的。比如例子這個模型,Diagram是被包含在Resource裡的(建立新Diagram
時即被加入),而Diagram包含Subject,Subject包含Attribute,所以它們都在Resource裡。在圖1中可以看到,
Diagram和Connection之間存在一對多的內含項目關聯性,這個關係的主要作用就是確保在儲存模型時不會出現
DanglingHREFException,因為如果沒有這個內含項目關聯性,則Connection對象不會被包含在任何Resource裡。

在刪除一個對象的時候,一定要保證它不再包含在Resource裡,否則儲存後的檔案中會出現很多空元素。比較容易犯錯的地方是對
Connection的處理,在刪除串連的時候,只是從源節點和目標節點裡刪除對這個串連的引用是不夠的,因為這樣只是在介面上消除了兩個節點間的串連
線,而這個連線物件還是包含在Diagram裡的,所以還要調用從Diagram對象裡刪除它才對,DeleteConnectionCommand中的
代碼如下:

 

public void execute() {
    source.getOutgoingConnections().remove(connection);
    target.getIncomingConnections().remove(connection);
    connection.getDiagram().getConnections().remove(connection);
}

當然,建立串連時也不要忘記將串連添加在Diagram對象裡(代碼見CreateConnectionCommand)。儲存和載入模型的代碼請
看SubjectEditor的init()方法和doSave()方法,都是很標準的EMF訪問資源的方法,以下是載入的代碼(如果是新建立的檔案,則
在Resource中建立Diagram對象):  

 

public void init(IEditorSite site, IEditorInput input) throws PartInitException {
    super.init(site, input);
    IFile file = ((FileEditorInput) getEditorInput()).getFile();
    URI fileURI = URI.createPlatformResourceURI(file.getFullPath().toString());
    resource = new XMIResourceImpl(fileURI); //注意要區分XMIResource和XMLResource
    try {
        resource.load(null);
        diagram = (Diagram) resource.getContents().get(0);
    } catch (IOException e) {
        diagram = ModelFactory.eINSTANCE.createDiagram();
        resource.getContents().add(diagram);
    }
}

雖然到目前為止我還沒有機會體會EMF在模型互動引用方面的優勢,但經過進一步的瞭解和在這個例子的應用,我對EMF的印象已有所改觀。據我目前所知,使用EMF模型作為GEF的模型部分至少有以下幾個好處:

  1. 只需要定義一次模型,而不是類圖、設計文檔、Java代碼等等好幾處;
  2. EMF為模型提供了完整的訊息機制,不用我們手動實現了;
  3. EMF提供了預設的模型持久化功能(xmi),並且允許修改持久化方式;
  4. EMF的模型便於交叉引用,因為擁有足夠的元資訊,等等。

此外,EMF.Edit架構能夠為模型的編輯提供了很大的協助,由於我現在對它還不熟悉,所以例子裡也沒有用到,今後我會修改這個例子以利用EMF.Edit。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.