您的位置:首页 > 其它

流程设计器开发一(模型部分)

2007-03-14 10:35 239 查看
我自从进入公司后,一直从事有关gef方面的开发工作,在这期间,走过不少弯路,仅仅是把GEF框架弄明白,就费了很大力气,所以,现在想写一点东西出来,供初学者阅读。
GEF(Graphical Editing Framework)是图形化编辑器开发的工具,比较典型的应用就是IBM 的Rose,它是一个模型驱动的MVC框架,控制器(EditPart)作为模型的侦听器,侦听模型的变化,如果模型的属性发生变化,它会通知控制器,控制器就会刷新模型对应的视图(Figure)。可以看出模型和视图之间没有直接联系,它们通过控制器而间接联系,可见控制器在gef框架中有着很重要的重要。
下面我们将从最基本的开始,介绍如何用GEF框架开发出一个流程设计器(开发工具Eclipse3.2.1包含插件包gef3.2.1和draw2d3.2.1)。
我们首先从模型开始,流程设计器顶层模型是流程(WorkflowProcess),流程有活动和链接这些活动的转移组成,其中活动又可以分为开始活动,普通活动,结束活动。理解了模型之间的组成关系,我们就可以设计模型对应的类了。由于上面提到,模型的属性变化了,必须通知控制器,由它来刷新模型对应的视图,所以控制器必须注册为模型的侦听器。由于每个模型都有相应的控制器侦听器侦听它属性的变化,我们把这方面的功能都放在父类中,定义一个ModelElement父类,具体代码如下:
[align=left]package com.example.workflow.model;[/align]
[align=left] [/align]
[align=left]import java.beans.PropertyChangeListener;[/align]
[align=left]import java.beans.PropertyChangeSupport;[/align]
[align=left]import java.io.IOException;[/align]
[align=left]import java.io.ObjectInputStream;[/align]
[align=left]import java.io.Serializable;[/align]
[align=left]publicclass ModelElement implements Serializable{ [/align]
[align=left] privatestaticfinallongserialVersionUID = -5117340723140888394L; [/align]
[align=left] privatetransient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport(this);[/align]
[align=left] [/align]
[align=left] publicsynchronizedvoid addPropertyChangeListener(PropertyChangeListener l) {[/align]
[align=left] if (l == null) {[/align]
[align=left] thrownew IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] pcsDelegate.addPropertyChangeListener(l);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] protectedvoid firePropertyChange(String property, Object oldValue, Object newValue) {[/align]
[align=left] if (pcsDelegate.hasListeners(property)) {[/align]
[align=left] pcsDelegate.firePropertyChange(property, oldValue, newValue);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] privatevoid readObject(ObjectInputStream in) [/align]
[align=left] throws IOException, ClassNotFoundException {[/align]
[align=left] in.defaultReadObject();[/align]
[align=left] pcsDelegate = new PropertyChangeSupport(this);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] publicsynchronizedvoid removePropertyChangeListener(PropertyChangeListener l) {[/align]
[align=left] if (l != null) {[/align]
[align=left] pcsDelegate.removePropertyChangeListener(l);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left]}[/align]

接下来我们定义流程,活动,转移模型,让这些模型都继承这个父类ModelElement,我们注意到活动由开始活动,普通活动,结束活动组成,这三类活动由很多相同的属性,例如活动的位置,名称,大小等等,所以给这三类活动进行抽象,定义一个父类AbstractActivity,把这些公共属性都放在这个父类中,父类的代码如下:
[align=left]package com.example.workflow.model;[/align]
[align=left][/align]
[align=left]import java.util.ArrayList;[/align]
[align=left]import java.util.List;[/align]
[align=left][/align]
[align=left]import org.eclipse.draw2d.geometry.Dimension;[/align]
[align=left]import org.eclipse.draw2d.geometry.Point;[/align]
[align=left][/align]
[align=left]/**[/align]
[align=left] * Abstract prototype of a Activity.[/align]
[align=left] * Has a size (width and height), a location (x and y position) and a list of incoming[/align]
[align=left] * and outgoing connections. Use subclasses to instantiate a specific Activity.[/align]
[align=left] * @see com.example.workflow.model.Activity[/align]
[align=left] * @see com.example.workflow.model.StartActivity[/align]
[align=left] * @see com.example.workflow.model.EndActivity[/align]
[align=left] */[/align]
[align=left]public class AbstractActivity extends ModelElement{[/align]
[align=left][/align]
[align=left] private static final long serialVersionUID = 3023802629976246906L; [/align]
[align=left] /** Property ID to use when the location of this Activity is modified. */[/align]
[align=left] public static final String LOCATION_PROP = "Activity.Location";[/align]
[align=left] /** Property ID to use then the size of this Activity is modified. */[/align]
[align=left] public static final String SIZE_PROP = "Activity.Size";[/align]
[align=left] /** ID for the Name property value (used for by the corresponding property descriptor). */[/align]
[align=left] public static final String NAME_PROP = "Activity.Name";[/align]
[align=left][/align]
[align=left] /** Property ID to use when the list of outgoing transitions is modified. */[/align]
[align=left] public static final String SOURCE_TRANSITIONS_PROP = "Activity.SourceTran";[/align]
[align=left] /** Property ID to use when the list of incoming transitions is modified. */[/align]
[align=left] public static final String TARGET_TRANSITIONS_PROP = "Activity.TargetTran";[/align]
[align=left] /** ID for the Width property value (used for by the corresponding property descriptor). */[/align]
[align=left] private static final String WIDTH_PROP = "Activity.Width";[/align]
[align=left] /** ID for the X property value (used for by the corresponding property descriptor). */[/align]
[align=left] private static final String XPOS_PROP = "Activity.xPos";[/align]
[align=left] /** ID for the Y property value (used for by the corresponding property descriptor). */[/align]
[align=left] private static final String YPOS_PROP = "Activity.yPos";[/align]
[align=left][/align]
[align=left][/align]
[align=left] /** Name of this Activity. */[/align]
[align=left] private String name = new String("");[/align]
[align=left] /** Location of this Activity. */[/align]
[align=left] private Point location = new Point(0, 0);[/align]
[align=left] /** Size of this Activity. */[/align]
[align=left] private Dimension size = new Dimension(50, 50);[/align]
[align=left] /** List of outgoing Transitions. */[/align]
[align=left] private List sourceTransitions = new ArrayList();[/align]
[align=left] /** List of incoming Transitions. */[/align]
[align=left] private List targetTransitions = new ArrayList();[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Add an incoming or outgoing connection to this Activity.[/align]
[align=left] * @param conn a non-null Transition instance[/align]
[align=left] * @throws IllegalArgumentException if the Transition is null or has not distinct endpoints[/align]
[align=left] */[/align]
[align=left] void addTransition(Transition tran) {[/align]
[align=left] if (tran == null || tran.getSource() == tran.getTarget()) {[/align]
[align=left] throw new IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] if (tran.getSource() == this) {[/align]
[align=left] sourceTransitions.add(tran);[/align]
[align=left] firePropertyChange(SOURCE_TRANSITIONS_PROP, null, tran);[/align]
[align=left] } else if (tran.getTarget() == this) {[/align]
[align=left] targetTransitions.add(tran);[/align]
[align=left] firePropertyChange(TARGET_TRANSITIONS_PROP, null, tran);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Return the Name of this Activity.[/align]
[align=left] * @return name[/align]
[align=left] */[/align]
[align=left] public String getName() {[/align]
[align=left] return name;[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Return the Location of this Activity.[/align]
[align=left] * @return a non-null location instance[/align]
[align=left] */[/align]
[align=left] public Point getLocation() {[/align]
[align=left] return location.getCopy();[/align]
[align=left] } [/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Return the Size of this Activity.[/align]
[align=left] * @return a non-null Dimension instance[/align]
[align=left] */[/align]
[align=left] public Dimension getSize() {[/align]
[align=left] return size.getCopy();[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Return a List of outgoing Transitions.[/align]
[align=left] */[/align]
[align=left] public List getSourceTransitions() {[/align]
[align=left] return new ArrayList(sourceTransitions);[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Return a List of incoming Transitions.[/align]
[align=left] */[/align]
[align=left] public List getTargetTransitions() {[/align]
[align=left] return new ArrayList(targetTransitions);[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Remove an incoming or outgoing Transition from this Activity.[/align]
[align=left] * @param conn a non-null Transition instance[/align]
[align=left] * @throws IllegalArgumentException if the parameter is null[/align]
[align=left] */[/align]
[align=left] void removeTransition(Transition tran) {[/align]
[align=left] if (tran == null) {[/align]
[align=left] throw new IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] if (tran.getSource() == this) {[/align]
[align=left] sourceTransitions.remove(tran);[/align]
[align=left] firePropertyChange(SOURCE_TRANSITIONS_PROP, null, tran);[/align]
[align=left] } else if (tran.getTarget() == this) {[/align]
[align=left] targetTransitions.remove(tran);[/align]
[align=left] firePropertyChange(TARGET_TRANSITIONS_PROP, null, tran);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Set the Name of this Activity.[/align]
[align=left] * @param newName[/align]
[align=left] * @throws IllegalArgumentException if the parameter is null[/align]
[align=left] */[/align]
[align=left] public void setName(String newName) {[/align]
[align=left] if (newName == null) {[/align]
[align=left] throw new IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] this.name = newName;[/align]
[align=left] firePropertyChange(LOCATION_PROP, null, name);[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Set the Location of this Activity.[/align]
[align=left] * @param newLocation a non-null Point instance[/align]
[align=left] * @throws IllegalArgumentException if the parameter is null[/align]
[align=left] */[/align]
[align=left] public void setLocation(Point newLocation) {[/align]
[align=left] if (newLocation == null) {[/align]
[align=left] throw new IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] location.setLocation(newLocation);[/align]
[align=left] firePropertyChange(LOCATION_PROP, null, location);[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] /**[/align]
[align=left] * Set the Size of this Activity.[/align]
[align=left] * Will not modify the size if newSize is null.[/align]
[align=left] * @param newSize a non-null Dimension instance or null[/align]
[align=left] */[/align]
[align=left] public void setSize(Dimension newSize) {[/align]
[align=left] if (newSize != null) {[/align]
[align=left] size.setSize(newSize);[/align]
[align=left] firePropertyChange(SIZE_PROP, null, size);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left][/align]
[align=left]}[/align]

在这个类中,我们定义两个List对象,分别对应该活动的输入转移和输出转移,因为对于一个完整的流程来说,每个活动都会转移的(或者有输入转移,或者有输出转移,或者两者都有),在这个类中,我们还注意到,每个改变对象属性的方法中,都会调用firePropertyChange方法,这个方面就是通知控制器,模型的属性发生发生变化了,让控制器根据相应的属性来刷新视图。
定义了活动的父类之后,我们就可以分别来定义开始活动,普通活动,结束活动对应的类了,具体代码如下:
开始活动:
[align=left]package com.example.workflow.model;[/align]
[align=left] [/align]
[align=left]publicclass StartActivity extends AbstractActivity{[/align]
[align=left] [/align]
[align=left] privatestaticfinallongserialVersionUID = 4639994300421360712L;[/align]
[align=left] privatestaticfinal String STARTACTIVITY_NAME = "START";[/align]
[align=left] [/align]
[align=left] public String getName() { [/align]
[align=left] returnSTARTACTIVITY_NAME;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public String toString() {[/align]
[align=left] return"StartActivity " + hashCode();[/align]
[align=left] }[/align]
}

普通活动:
[align=left]package com.example.workflow.model;[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]publicclass Activity extends AbstractActivity{[/align]
[align=left] [/align]
[align=left] privatestaticfinallongserialVersionUID = 3023802629976246906L; [/align]
[align=left] privatestaticfinal String ACTIVITY_NAME = "ACTIVITY";[/align]
[align=left] [/align]
[align=left] public String getName() { [/align]
[align=left] returnACTIVITY_NAME;[/align]
[align=left] }[/align]
[align=left] public String toString() {[/align]
[align=left] return"Activity " + hashCode();[/align]
[align=left] }[/align]
[align=left] [/align]
}

结束活动:
[align=left]package com.example.workflow.model;[/align]
[align=left] [/align]
[align=left]publicclass EndActivity extends AbstractActivity{ [/align]
[align=left] [/align]
[align=left] privatestaticfinallongserialVersionUID = 316984190041034535L; [/align]
[align=left] privatestaticfinal String ENDACTIVITY_NAME = "END";[/align]
[align=left] [/align]
[align=left] public String getName() { [/align]
[align=left] returnENDACTIVITY_NAME;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public String toString() {[/align]
[align=left] return"EndActivity " + hashCode();[/align]
[align=left] }[/align]
[align=left] [/align]
}

定义完这些活动之后,我们来定义流程模型,由于流程中包含多个活动,所以里面应该有个列表来维护这些对象,同样,流程中还包含多个转移,由于在活动模型中,已经维护了转移对象,所以这里就不维护这些转移对象了,具体代码如下:
package com.example.workflow.model;

import java.util.ArrayList;
import java.util.List;

/**
* 流程模型,可以包含多个活动和转移模型
* @author Administrator
*
*/
public class WorkflowProcess extends ModelElement{

private static final long serialVersionUID = -5478693636480758659L;
/** Property ID to use when a child is added to this WorkflowProcess. */
public static final String CHILD_ADDED_PROP = "WorkflowProcess.ChildAdded";
/** Property ID to use when a child is removed from this WorkflowProcess. */
public static final String CHILD_REMOVED_PROP = "WorkflowProcess.ChildRemoved";
private List activities = new ArrayList();

/**
* Add a Activity to this WorkflowProcess.
* @param s a non-null Activity instance
* @return true, if the Activity was added, false otherwise
*/
public boolean addChild(Activity a) {
if (a != null && activities.add(a)) {
firePropertyChange(CHILD_ADDED_PROP, null, a);
return true;
}
return false;
}

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

/**
* Remove a Activity from this WorkflowProcess.
* @param s a non-null Activity instance;
* @return true, if the Activity was removed, false otherwise
*/
public boolean removeChild(Activity a) {
if (a != null && activities.remove(a)) {
firePropertyChange(CHILD_REMOVED_PROP, null, a);
return true;
}
return false;
}
}

最后我们来定义转移模型,我们知道转移模型是链接两个活动的,所以在转移模型中,应该有个转移的源活动和目标活动,同时如果两个活动之间已经有转移连接时,就不能再在两者之间建立转移了,所以在两个活动之间建立转移时,必须先判断两者之间是否已经建立转移,所以转移模型具体代码如下:
[align=left]package com.example.workflow.model;[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] *ATransitionbetweentwodistinctactivities.[/align]
[align=left] */[/align]
[align=left]publicclass Transition extends ModelElement{[/align]
[align=left] [/align]
[align=left] privatestaticfinallongserialVersionUID = 516473924757575767L;[/align]
[align=left] /**True,ifthetransitionisattachedtoitsendpoints.*/[/align]
[align=left] privatebooleanisConnected;[/align]
[align=left] /**Transition'ssourceendpoint.*/[/align]
[align=left] private AbstractActivity source;[/align]
[align=left] /**Transition'stargetendpoint.*/[/align]
[align=left] private AbstractActivity target;[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *CreateaTransitionbetweentwodistinctactivities.[/align]
[align=left] *@paramsourceasourceendpointforthisTransition(nonnull)[/align]
[align=left] *@paramtargetatargetendpointforthisTransition(nonnull)[/align]
[align=left] *@throwsIllegalArgumentExceptionifanyoftheparametersarenullorsource==target[/align]
[align=left] *@see#setLineStyle(int)[/align]
[align=left] */[/align]
[align=left] public Transition(AbstractActivity source, AbstractActivity target) {[/align]
[align=left] reconnect(source, target);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *Disconnectthisconnectionfromtheactivitiesitisattachedto.[/align]
[align=left] */[/align]
[align=left] publicvoid disconnect() {[/align]
[align=left] if (isConnected) {[/align]
[align=left] source.removeTransition (this);[/align]
[align=left] target.removeTransition (this);[/align]
[align=left] isConnected = false;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *ReturnsthesourceendpointofthisTransition.[/align]
[align=left] *@returnanon-nullActivityinstance[/align]
[align=left] */[/align]
[align=left] public AbstractActivity getSource() {[/align]
[align=left] returnsource;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *ReturnsthetargetendpointofthisTransition.[/align]
[align=left] *@returnanon-nullActivityinstance[/align]
[align=left] */[/align]
[align=left] public AbstractActivity getTarget() {[/align]
[align=left] returntarget;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *ReconnectthisTransition.[/align]
[align=left] *TheTransitionwillreconnectwiththeactivitiesitwaspreviouslyattachedto.[/align]
[align=left] */ [/align]
[align=left] publicvoid reconnect() {[/align]
[align=left] if (!isConnected) {[/align]
[align=left] source.addTransition (this);[/align]
[align=left] target.addTransition (this);[/align]
[align=left] isConnected = true;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] /**[/align]
[align=left] *Reconnecttoadifferentsourceand/ortargetActivity.[/align]
[align=left] *Theconnectionwilldisconnectfromitscurrentattachmentsandreconnectto[/align]
[align=left] *thenewsourceandtarget.[/align]
[align=left] *@paramnewSourceanewsourceendpointforthisTransition(nonnull)[/align]
[align=left] *@paramnewTargetanewtargetendpointforthisTransition(nonnull)[/align]
[align=left] *@throwsIllegalArgumentExceptionifanyoftheparamersarenullornewSource==newTarget[/align]
[align=left] */[/align]
[align=left] publicvoid reconnect(AbstractActivity newSource, AbstractActivity newTarget) {[/align]
[align=left] if (newSource == null || newTarget == null || newSource == newTarget) {[/align]
[align=left] thrownew IllegalArgumentException();[/align]
[align=left] }[/align]
[align=left] disconnect();[/align]
[align=left] this.source = newSource;[/align]
[align=left] this.target = newTarget;[/align]
[align=left] reconnect();[/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left]到这儿,模型的定义已经全部完成,下一节我们将定义GEF框架中最重要的部分,也是最复杂的部分,控制器。[/align]
[align=left] [/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: