posts - 36, comments - 30, trackbacks - 0, articles - 3

          流程設計器開發一(模型部分)

          Posted on 2008-01-02 15:32 笑看人生 閱讀(3532) 評論(5)  編輯  收藏 所屬分類: Java插件開發
              我自從進入公司后,一直從事有關gef方面的開發工作,在這期間,走過不少彎路,僅僅是把GEF框架弄明白,就費了很大力氣,所以,現在想寫一點東西出來,供初學者閱讀。

              GEF(Graphical Editing Framework)是圖形化編輯器開發的工具,比較典型的應用就是IBM 的Rose,它是一個模型驅動的MVC框架,控制器(EditPart)作為模型的偵聽器,偵聽模型的變化,如果模型的屬性發生變化,它會通知控制器,控制器就會刷新模型對應的視圖(Figure)。可以看出模型和視圖之間沒有直接聯系,它們通過控制器而間接聯系,可見控制器在gef框架中有著很重要的重要。

              下面我們將從最基本的開始,介紹如何用GEF框架開發出一個流程設計器(開發工具Eclipse3.2.1包含插件包gef3.2.1和draw2d3.2.1)。

              我們首先從模型開始,流程設計器頂層模型是流程(WorkflowProcess),流程有活動和鏈接這些活動的轉移組成,其中活動又可以分為開始活動,普通活動,結束活動。理解了模型之間的組成關系,我們就可以設計模型對應的類了。由于上面提到,模型的屬性變化了,必須通知控制器,由它來刷新模型對應的視圖,所以控制器必須注冊為模型的偵聽器。由于每個模型都有相應的控制器偵聽器偵聽它屬性的變化,我們把這方面的功能都放在父類中,定義一個ModelElement父類,具體代碼如下:

           1package com.example.workflow.model;
           2 
           3import java.beans.PropertyChangeListener;
           4import java.beans.PropertyChangeSupport;
           5import java.io.IOException;
           6import java.io.ObjectInputStream;
           7import java.io.Serializable;
           8publicclass ModelElement implements Serializable{   
           9    privatestaticfinallongserialVersionUID = -5117340723140888394L;   
          10    privatetransient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport(this);
          11       
          12    publicsynchronizedvoid addPropertyChangeListener(PropertyChangeListener l) {
          13       if (l == null{
          14           thrownew IllegalArgumentException();
          15       }

          16       pcsDelegate.addPropertyChangeListener(l);
          17    }

          18       
          19    protectedvoid firePropertyChange(String property, Object oldValue, Object newValue) {
          20       if (pcsDelegate.hasListeners(property)) {
          21           pcsDelegate.firePropertyChange(property, oldValue, newValue);
          22       }

          23    }

          24       
          25    privatevoid readObject(ObjectInputStream in) 
          26    throws IOException, ClassNotFoundException {
          27       in.defaultReadObject();
          28       pcsDelegate = new PropertyChangeSupport(this);
          29    }

          30    
          31    publicsynchronizedvoid removePropertyChangeListener(PropertyChangeListener l) {
          32       if (l != null{
          33           pcsDelegate.removePropertyChangeListener(l);
          34       }

          35    }

          36 
          37}

          38

          接下來我們定義流程,活動,轉移模型,讓這些模型都繼承這個父類ModelElement,我們注意到活動由開始活動,普通活動,結束活動組成,這三類活動由很多相同的屬性,例如活動的位置,名稱,大小等等,所以給這三類活動進行抽象,定義一個父類AbstractActivity,把這些公共屬性都放在這個父類中,父類的代碼如下:

            1package com.example.workflow.model;
            2 
            3import java.util.ArrayList;
            4import java.util.List;
            5 
            6import org.eclipse.draw2d.geometry.Dimension;
            7import org.eclipse.draw2d.geometry.Point;
            8 
            9/**
           10 * Abstract prototype of a Activity.
           11 * Has a size (width and height), a location (x and y position) and a list of incoming
           12 * and outgoing connections. Use subclasses to instantiate a specific Activity.
           13 * @see com.example.workflow.model.Activity
           14 * @see com.example.workflow.model.StartActivity
           15 * @see com.example.workflow.model.EndActivity
           16 */

           17public class AbstractActivity extends ModelElement{
           18    
           19    private static final long serialVersionUID = 3023802629976246906L
           20    /** Property ID to use when the location of this Activity is modified. */
           21    public static final String LOCATION_PROP = "Activity.Location";
           22    /** Property ID to use then the size of this Activity is modified. */
           23    public static final String SIZE_PROP = "Activity.Size";
           24    /** ID for the Name property value (used for by the corresponding property descriptor). */
           25    public static final String NAME_PROP = "Activity.Name";
           26    
           27    /** Property ID to use when the list of outgoing transitions is modified. */
           28    public static final String SOURCE_TRANSITIONS_PROP = "Activity.SourceTran";
           29    /** Property ID to use when the list of incoming transitions is modified. */
           30    public static final String TARGET_TRANSITIONS_PROP = "Activity.TargetTran";
           31    /** ID for the Width property value (used for by the corresponding property descriptor). */
           32    private static final String WIDTH_PROP = "Activity.Width";
           33    /** ID for the X property value (used for by the corresponding property descriptor). */
           34    private static final String XPOS_PROP = "Activity.xPos";
           35    /** ID for the Y property value (used for by the corresponding property descriptor). */
           36    private static final String YPOS_PROP = "Activity.yPos";
           37    
           38    
           39    /** Name of this Activity. */
           40    private String name = new String("");
           41    /** Location of this Activity. */
           42    private Point location = new Point(00);
           43    /** Size of this Activity. */
           44    private Dimension size = new Dimension(5050);
           45    /** List of outgoing Transitions. */
           46    private List sourceTransitions = new ArrayList();
           47    /** List of incoming Transitions. */
           48    private List targetTransitions = new ArrayList();
           49    
           50    /**
           51     * Add an incoming or outgoing connection to this Activity.
           52     * @param conn a non-null Transition instance
           53     * @throws IllegalArgumentException if the Transition is null or has not distinct endpoints
           54     */

           55    void addTransition(Transition tran) {
           56       if (tran == null || tran.getSource() == tran.getTarget()) {
           57           throw new IllegalArgumentException();
           58       }

           59       if (tran.getSource() == this{
           60           sourceTransitions.add(tran);
           61           firePropertyChange(SOURCE_TRANSITIONS_PROP, null, tran);
           62       }
           else if (tran.getTarget() == this{
           63           targetTransitions.add(tran);
           64           firePropertyChange(TARGET_TRANSITIONS_PROP, null, tran);
           65       }

           66    }

           67    
           68    /**
           69     * Return the Name of this Activity.
           70     * @return name
           71     */

           72    public String getName() {
           73       return name;
           74    }

           75    
           76    /**
           77     * Return the Location of this Activity.
           78     * @return a non-null location instance
           79     */

           80    public Point getLocation() {
           81       return location.getCopy();
           82    }
             
           83    
           84    /**
           85     * Return the Size of this Activity.
           86     * @return a non-null Dimension instance
           87     */

           88    public Dimension getSize() {
           89       return size.getCopy();
           90    }

           91 
           92    /**
           93     * Return a List of outgoing Transitions.
           94     */

           95    public List getSourceTransitions() {
           96       return new ArrayList(sourceTransitions);
           97    }

           98 
           99    /**
          100     * Return a List of incoming Transitions.
          101     */

          102    public List getTargetTransitions() {
          103       return new ArrayList(targetTransitions);
          104    }

          105    
          106    /**
          107     * Remove an incoming or outgoing Transition from this Activity.
          108     * @param conn a non-null Transition instance
          109     * @throws IllegalArgumentException if the parameter is null
          110     */

          111    void removeTransition(Transition tran) {
          112       if (tran == null{
          113           throw new IllegalArgumentException();
          114       }

          115       if (tran.getSource() == this{
          116           sourceTransitions.remove(tran);
          117           firePropertyChange(SOURCE_TRANSITIONS_PROP, null, tran);
          118       }
           else if (tran.getTarget() == this{
          119           targetTransitions.remove(tran);
          120           firePropertyChange(TARGET_TRANSITIONS_PROP, null, tran);
          121       }

          122    }

          123    
          124    /**
          125     * Set the Name of this Activity.
          126     * @param newName
          127     * @throws IllegalArgumentException if the parameter is null
          128     */

          129    public void setName(String newName) {
          130       if (newName == null{
          131           throw new IllegalArgumentException();
          132       }

          133       this.name = newName;
          134       firePropertyChange(LOCATION_PROP, null, name);
          135    }

          136 
          137    /**
          138     * Set the Location of this Activity.
          139     * @param newLocation a non-null Point instance
          140     * @throws IllegalArgumentException if the parameter is null
          141     */

          142    public void setLocation(Point newLocation) {
          143       if (newLocation == null{
          144           throw new IllegalArgumentException();
          145       }

          146       location.setLocation(newLocation);
          147       firePropertyChange(LOCATION_PROP, null, location);
          148    }

          149    
          150    /**
          151     * Set the Size of this Activity.
          152     * Will not modify the size if newSize is null.
          153     * @param newSize a non-null Dimension instance or null
          154     */

          155    public void setSize(Dimension newSize) {
          156       if (newSize != null{
          157           size.setSize(newSize);
          158           firePropertyChange(SIZE_PROP, null, size);
          159       }

          160    }

          161 
          162}

          163 

          在這個類中,我們定義兩個List對象,分別對應該活動的輸入轉移和輸出轉移,因為對于一個完整的流程來說,每個活動都會轉移的(或者有輸入轉移,或者有輸出轉移,或者兩者都有),在這個類中,我們還注意到,每個改變對象屬性的方法中,都會調用firePropertyChange方法,這個方面就是通知控制器,模型的屬性發生發生變化了,讓控制器根據相應的屬性來刷新視圖。
          定義了活動的父類之后,我們就可以分別來定義開始活動,普通活動,結束活動對應的類了,具體代碼如下:
          開始活動:

           1package com.example.workflow.model;
           2 
           3public class StartActivity extends AbstractActivity{
           4    
           5    private staticfinallongserialVersionUID = 4639994300421360712L;
           6    private staticfinal String STARTACTIVITY_NAME = "START";
           7    
           8    public String getName() {       
           9       returnSTARTACTIVITY_NAME;
          10    }

          11 
          12    public String toString() {
          13       return"StartActivity " + hashCode();
          14    }

          15}

          普通活動:

           1package com.example.workflow.model;
           2 
           3 
           4public class Activity extends AbstractActivity{
           5    
           6    private staticfinallongserialVersionUID = 3023802629976246906L
           7    private staticfinal String ACTIVITY_NAME = "ACTIVITY";
           8    
           9    public String getName() {       
          10       returnACTIVITY_NAME;
          11    }

          12    public String toString() {
          13       return"Activity " + hashCode();
          14    }

          15 
          16}

          17 

          結束活動:
           1package com.example.workflow.model;
           2 
           3public class EndActivity extends AbstractActivity{   
           4    
           5    private static final long serialVersionUID = 316984190041034535L
           6    privates tatic final String ENDACTIVITY_NAME = "END";
           7    
           8    public String getName() {       
           9       returnENDACTIVITY_NAME;
          10    }

          11 
          12    public String toString() {
          13       return"EndActivity " + hashCode();
          14    }

          15 
          16}

          定義完這些活動之后,我們來定義流程模型,由于流程中包含多個活動,所以里面應該有個列表來維護這些對象,同樣,流程中還包含多個轉移,由于在活動模型中,已經維護了轉移對象,所以這里就不維護這些轉移對象了,具體代碼如下:

           1package com.example.workflow.model;
           2 
           3import java.util.ArrayList;
           4import java.util.List;
           5 
           6/**
           7 * 流程模型,可以包含多個活動和轉移模型
           8 * @author Administrator
           9 *
          10 */

          11public class WorkflowProcess extends ModelElement{       
          12       
          13       private static final long serialVersionUID = -5478693636480758659L;
          14       /** Property ID to use when a child is added to this WorkflowProcess. */
          15       public static final String CHILD_ADDED_PROP = "WorkflowProcess.ChildAdded";
          16       /** Property ID to use when a child is removed from this WorkflowProcess. */
          17       public static final String CHILD_REMOVED_PROP = "WorkflowProcess.ChildRemoved";     
          18       private List activities = new ArrayList();
          19       
          20       /** 
          21        * Add a Activity to this WorkflowProcess.
          22        * @param s a non-null Activity instance
          23        * @return true, if the Activity was added, false otherwise
          24        */

          25       public boolean addChild(Activity a) {
          26              if (a != null && activities.add(a)) {
          27                     firePropertyChange(CHILD_ADDED_PROP, null, a);
          28                     return true;
          29              }

          30              return false;
          31       }

          32 
          33       /** Return a List of Activities in this WorkflowProcess. The returned List should not be modified. */
          34       public List getChildren() {
          35              return activities;
          36       }

          37       
          38       /**
          39        * Remove a Activity from this WorkflowProcess.
          40        * @param s a non-null Activity instance;
          41        * @return true, if the Activity was removed, false otherwise
          42        */

          43       public boolean removeChild(Activity a) {
          44              if (a != null && activities.remove(a)) {
          45                     firePropertyChange(CHILD_REMOVED_PROP, null, a);
          46                     return true;
          47              }

          48              return false;
          49       }

          50}

          最后我們來定義轉移模型,我們知道轉移模型是鏈接兩個活動的,所以在轉移模型中,應該有個轉移的源活動和目標活動,同時如果兩個活動之間已經有轉移連接時,就不能再在兩者之間建立轉移了,所以在兩個活動之間建立轉移時,必須先判斷兩者之間是否已經建立轉移,所以轉移模型具體代碼如下:

           1package com.example.workflow.model;
           2 
           3/**
           4 *ATransitionbetweentwodistinctactivities.
           5 */

           6public class Transition extends ModelElement{
           7    
           8    private static final long serialVersionUID = 516473924757575767L;
           9    /**True,ifthetransitionisattachedtoitsendpoints.*/
          10    private boolean isConnected;
          11    /**Transition'ssourceendpoint.*/
          12    private AbstractActivity source;
          13    /**Transition'stargetendpoint.*/
          14    private AbstractActivity target;
          15    
          16    /**
          17     *CreateaTransitionbetweentwodistinctactivities.
          18     *@paramsourceasourceendpointforthisTransition(nonnull)
          19     *@paramtargetatargetendpointforthisTransition(nonnull)
          20     *@throwsIllegalArgumentExceptionifanyoftheparametersarenullorsource==target
          21     *@see#setLineStyle(int)
          22     */

          23    public Transition(AbstractActivity source, AbstractActivity target) {
          24       reconnect(source, target);
          25    }

          26 
          27    /**
          28     *Disconnectthisconnectionfromtheactivitiesitisattachedto.
          29     */

          30    publicvoid disconnect() {
          31       if (isConnected) {
          32           source.removeTransition (this);
          33           target.removeTransition (this);
          34           isConnected = false;
          35       }

          36    }

          37    
          38    /**
          39     *ReturnsthesourceendpointofthisTransition.
          40     *@returnanon-nullActivityinstance
          41     */

          42    public AbstractActivity getSource() {
          43       returnsource;
          44    }

          45 
          46    /**
          47     *ReturnsthetargetendpointofthisTransition.
          48     *@returnanon-nullActivityinstance
          49     */

          50    public AbstractActivity getTarget() {
          51       returntarget;
          52    }

          53 
          54    /**
          55     *ReconnectthisTransition.
          56     *TheTransitionwillreconnectwiththeactivitiesitwaspreviouslyattachedto.
          57     */
           
          58    public void reconnect() {
          59       if (!isConnected) {
          60           source.addTransition (this);
          61           target.addTransition (this);
          62           isConnected = true;
          63       }

          64    }

          65 
          66    /**
          67     *Reconnecttoadifferentsourceand/ortargetActivity.
          68     *Theconnectionwilldisconnectfromitscurrentattachmentsandreconnectto
          69     *thenewsourceandtarget.
          70     *@paramnewSourceanewsourceendpointforthisTransition(nonnull)
          71     *@paramnewTargetanewtargetendpointforthisTransition(nonnull)
          72     *@throwsIllegalArgumentExceptionifanyoftheparamersarenullornewSource==newTarget
          73     */

          74    public void reconnect(AbstractActivity newSource, AbstractActivity newTarget) {
          75       if (newSource == null || newTarget == null || newSource == newTarget) {
          76           thrownew IllegalArgumentException();
          77       }

          78       disconnect();
          79       this.source = newSource;
          80       this.target = newTarget;
          81       reconnect();
          82    }

          83}

          到這兒,模型的定義已經全部完成,下一節我們將定義GEF框架中最重要的部分,也是最復雜的部分,控制器。

          Feedback

          # re: 流程設計器開發一(模型部分)  回復  更多評論   

          2008-01-03 09:35 by yuan1129
          寫的非常好,希望能夠提供源代碼下載,謝謝。

          # re: 流程設計器開發一(模型部分)  回復  更多評論   

          2008-01-03 09:51 by 玩轉Java
          在流程設計器三中可以下載代碼。

          # re: 流程設計器開發一(模型部分)  回復  更多評論   

          2008-07-29 17:08 by zhang li guo
          知道你是誰了。 :)

          還記得你剛進公司的第一天談話。那些天,做引擎的單元測試,感覺做的一般。
          后來就讓你全心做設計器了。 :)

          三年很快過完了。

          說句實話,我非常懷念那段開發產品的日子,充滿激情和夢想……
          也很有幸,后來我們臨別前在投標項目中又合作一把。

          最近我業余時間,在和一個老外合作一個eclipse插件,有空來看看
          http://code.google.com/p/camel-route-viewer/

          離你的eclipse水平差遠了。

          :)

          # re: 流程設計器開發一(模型部分)  回復  更多評論   

          2008-07-29 17:09 by zhang li guo
          MSN:catflame AT msn.com
          加我

          # re: 流程設計器開發一(模型部分)  回復  更多評論   

          2010-03-22 21:24 by 楊志斌
          很強大
          主站蜘蛛池模板: 富蕴县| 平遥县| 罗城| 宜丰县| 巫山县| 辛集市| 邵武市| 个旧市| 海兴县| 济宁市| 夹江县| 麻栗坡县| 江津市| 胶州市| 盐城市| 贵溪市| 石狮市| 东乌珠穆沁旗| 西平县| 达拉特旗| 大荔县| 海盐县| 务川| 舞阳县| 那曲县| 永吉县| 巴东县| 大厂| 南靖县| 广元市| 泸水县| 溧阳市| 东阿县| 吴川市| 剑河县| 南江县| 博罗县| 浮梁县| 望奎县| 根河市| 乐都县|