sharky的點滴積累

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            56 隨筆 :: 104 文章 :: 10 評論 :: 0 Trackbacks

          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文件,該插件的免費版本可以從這里下載。

          subject_ecd.gif
          圖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 XMLResourceImpl(fileURI);
              
          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。

          posted on 2005-04-15 18:13 八進制 閱讀(2262) 評論(9)  編輯 收藏 收藏至365Key 所屬分類: Eclipse

          評論

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-04-18 16:49 tinachen
          請問:“EMF提供了缺省的模型持久化功能(xmi),并且允許修改持久化方式”持久化功能有什么用呀?
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-04-19 11:12 八進制
          就是把模型從內存保存到硬盤等持久化媒體上的功能,比如存為文件或者存到數據庫。如果沒有持久化功能,你編輯的模型在退出程序以后就丟失了。
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-05-18 12:57 blunwen
          例子中emf構造的模型一定要繼承一個包含位置和大小信息的Node類嗎?如果單純的用emf構造一個模型而不含有任何的除模型外的信息,但是又想使用gef來畫模型的圖,應該怎么入手好呢?
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-05-18 14:39 八進制
          目前我是通過繼承的方式把大小位置信息放在父類里。
          因為保存下來的本地文件里必須包含這些信息(否則不可能復原),所以嚴格來說它們也是模型的一部分。
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-05-22 14:32 blunwen
          有一個辦法可以使模型是模型。既模型不需要繼承具有大小位置信息的Node類,而是在Node類里面有一個指向模型的屬性。在save的時候,把Node類對應的模型保存在另外一個文件就ok了。這一點經過實現證明是可行的
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-06-06 21:12 whatisnew
          blunwen真是無所不在呀,哈哈哈
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-07-08 23:36 小小笨鳥
          我想從xsd出發,構造EMF模型。可是導入XML schema時,總是出這樣的錯:

          * a null pointer exception, or
          * the error message "Specify a valid XML Schema and try loading again".

          eclipse的站點上說,這是個bug,

          "To avoid this, use the following to control the JAXP implementation:

          <eclipse-install-dir>eclipse.exe -vmargs
          -Djava.endorsed.dirs=<path-to-your-xerces-jars-folder>"

          我不明白這樣的解釋。我下載了xerces 2.7.0,可是不明白怎樣用。有誰可以幫我嗎?還有有必要做xerces integrity verification嗎?md5,pgp又是怎樣用的呢?

          郁悶中。。。:(

          急切期待回音!謝過各位大俠了先。 :)
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-07-09 11:20 八進制
          可能是啟動eclipse時要用“eclipse.exe -vmargs -Djava.endorsed.dirs=<你下載的xerces所在目錄>”,也就是加一個參數讓eclipse使用你下載的新的xerces。
            

          # re: [Eclipse]GEF入門系列(八、使用EMF構造GEF的模型) 2005-07-11 17:08 小小笨鳥
          呵呵,bingle!找到了,原來只是引號的問題。我也一直在想它只是一條命令,可是怎樣試都不行,差點就絕望了。ouf!終于解決了,好開心!多謝八進制l了! :)
            
          posted on 2005-08-26 17:13 sharky的點滴積累 閱讀(1091) 評論(1)  編輯  收藏

          評論

          # re: GEF入門系列(八、使用EMF構造GEF的模型) 2016-05-02 19:19 正正糖糖
          如何設置point這種類型的默認值,在哪里設置  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 常宁市| 泰安市| 辛集市| 重庆市| 离岛区| 望城县| 木里| 昌图县| 祥云县| 巧家县| 宝兴县| 哈密市| 呈贡县| 拉萨市| 蕉岭县| 弥勒县| 个旧市| 靖西县| 曲阜市| 济源市| 乐亭县| 日土县| 新宁县| 北票市| 盐边县| 兴化市| 绵竹市| 江源县| 八宿县| 阳城县| 彭泽县| 临夏县| 藁城市| 松溪县| 丰都县| 图片| 黔南| 桐庐县| 哈尔滨市| 龙游县| 荣成市|