八进制写的关于GEF以及EMF的文章,对我当时的工作帮助很大。
我最初看的是他的关于GEF的文章,但是我的工作需要将GEF与EMF结合,本文使用的例子就是根据八进制的Blog中gefpractice改编而成。
一 定义模型 自从EMF出现以后,出现了一种新的说法:模型驱动的软件开发(例如Marlin),经过这段时间的研究开发,发现使用GEF与EMF确实是这样,一旦模型定义好了,这个项目就基本定型了。
Ok,废话留着以后慢慢说,进入正题先。
首先,定义模型。在gefpractice这个例子中,模型很简单,只有三类元素:Diagram、Node和Connection。他们之间的关系,用EMF的模型文件(ecore)定义出来就是这样的:
见 图1
在做模型定义的时候,需要注意他们之间的包含关系。打开Properties视图,有一个Containment属性需要格外注意,因为它将决定该接点是一个属性还是一个子节点。以上图为例,这个属性设置为true,那么xml文件的格式是
<Diagram>
<Node/>
</Diagram>
假如是false,那么就是
<Diagram nodes=”//node.0”/>
<Node />
见图2
由于有这个问题,在定义Node的时候就一定要注意sourceConnections和targetConnections的这个属性,一个是true,另一个是false。(自己想想,为什么呢?^-^)
当然模型的定义并不是只有这种方式,只要你将逻辑表述对了,这个模型就没有问题。
二 创建工程 用这个模型创建一个EMF Project。操作过程是New -> Project -> EMF Project -> 工程名称是eclipse.my.gefpractice -> 选择Ecore Model -> 选择定义好的nodeemf.ecore并load -> 其他的按照默认选择。这样,你的工作区就创建了这个工程。
这个新建的工程有个model文件夹,里面有nodeemf.ecore和nodeemf.genmodel两个文件。用当前默认的编辑器打开nodeemf.genmodel文件,更改根节点(Nodeemf)的子节点(nodeemf)的属性(从属性视图中改)
见图3
接着,右键选择上图中的根节点,选择Generate Model Code。这样,模型的代码就全部生成了。
三 事件监听 一般,完全使用GEF的情况下,模型的监听是通过模型实现PropertySource来实现的。详细的请参看八进制的相关文章。但是本文中使用EMF来定义模型,那么原则上就不修改生成的代码。那么,这里利用Adapter来给模型“安装”属性。
这里给出一段示例代码
public class NodePropertySource extends AbstractPropertySource {
private static final String ID_X = "X";
private static final String ID_Y = "Y";
private static final String ID_NAME = "Name";
private static final String ID_VISIBLE = "Visible";
public NodePropertySource(Object model) {
super(model);
}
private Node getNode() {
return (Node) getModel();
}
public void createPropertyDescriptors(List descriptors) {
descriptors.add(new TextPropertyDescriptor(ID_X, ID_X));
descriptors.add(new TextPropertyDescriptor(ID_Y, ID_Y));
descriptors.add(new TextPropertyDescriptor(ID_NAME, ID_NAME));
descriptors.add(new TextPropertyDescriptor(ID_VISIBLE, ID_VISIBLE));
}
public Object getPropertyValue(Object id) {
if(id == ID_X) {
return Integer.toString(getNode().getX());
}
if(id == ID_Y) {
return Integer.toString(getNode().getY());
}
if(id == ID_NAME) {
return getNode().getName();
}
if(id == ID_VISIBLE) {
return Boolean.toString(getNode().isVisible());
}
return null;
}
public void setPropertyValue(Object id, Object value) {
if (id == ID_X) {
getNode().setX(Integer.parseInt(value.toString()));
} else if (id == ID_Y) {
getNode().setY(Integer.parseInt(value.toString()));
} else if (id == ID_NAME) {
getNode().setName(value.toString());
} else if(id == ID_VISIBLE) {
getNode().setVisible(Boolean.getBoolean(value.toString()));
}
}
}
在这个模型对应的EditPart中安装这个PropertySource:
public Object getAdapter(Class key) {
if (IPropertySource.class == key)
return new NodePropertySource(getModel());
return super.getAdapter(key);
}
由于用EMF定义的model本身就实现了notifier,故在本例中采用了EMF的事件监听机制。(采用GEF的事件监听机制也是可行的)
public void notifyChanged(Notification notification) {
int type = notification.getEventType();
int featureId = notification.getFeatureID(ModelPackage.class);
switch(type) {
case Notification.SET:
switch(featureId) {
case ModelPackage.NODE__X:
case ModelPackage.NODE__Y:
case ModelPackage.NODE__NAME:
case ModelPackage.NODE__VISIBLE:
refreshVisuals();
break;
}
case Notification.ADD:
case Notification.REMOVE:
switch(featureId) {
case ModelPackage.NODE__INPUTS:
refreshSourceConnections();
break;
case ModelPackage.NODE__OUTPUTS:
refreshTargetConnections();
break;
}
}
}
四 编辑器
编辑器的其他重要组成部分,如Command,policy根原来的基本上没区别,这里就不再叙述了。
五 其他
本例中不能从左边的Explorer视图中打开这个编辑器,而是用了Open Action和New Action。原因是本插件项目打包后可以加载到Rcp项目中。
六 本例的运行结果
见图4
除了上图中的问题外,还有一些小bug,呵呵,我比较懒,基本不用它,所以就没有改(Eclipse的懒加载法则)。这是我的一个面试考题,做这个例子,共用了1.5天。
七 源码
点击下载(见最后一个附件)
八 环境
JDK1.4
Eclipse 3.1
GEF
EMF
图片: