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

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网站)

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中的实现,看一下就应该明白了:

 

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

还有active()/deactive()方法中的内容需要修改,作用还是把EditPart自己作为Adapter(不是 PropertyChangeListener了)加入模型的监听器列表,下面是SubjectPart的实现,其中eAdapters()得到监听器列 表:

 

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

可以看到,我们对EditPart所做的修改实际是在两种消息机制之间的转换,如果你对以前的那套机制很熟悉的话,这里理解起来不应该有任何困难。

ElementFactory的修改

这个类的作用是根据template创建新的模型对象实例,以前的实现都是"new XXX()"这样,用了EMF以后应改为"ModelFactory.eINSTANCE.createXXX()",EMF里的每个模型对象实例都应该是使用工厂创建的。

 

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

使用自定义CreationFactory代替SimpleFactory

在原先的PaletteFactory里定义CreationEntry时都是指定SimpleFactory作为工厂,这个类是使用 Class.newInstance()创建新的对象实例,而用EMF作为模型后,创建实例的工作应该交给ModelFactory来完成,所以必须定义 自己的CreationFactory。(注意,示例代码里没有包含这个修改。)

处理自定义数据类型

我们的Node类里有两个非标准数据类型:Point和Dimension,要让EMF能够正确的将它们保存,必须提供序列化和反序列化它们的方 法。在EMF为我们生成的代码里,找到ModelFactoryImpl类,这里有形如convertXXXToString()和 createXXXFromString()的几个方法,分别用来序列化和反序列化这种外部数据类型。我们要把它的缺省实现改为自己的方式,下面是我对 Point的实现方式:

 

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

注意,修改后要将方法前面的@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中的 代码如下:

 

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

当然,新建连接时也不要忘记将连接添加在Diagram对象里(代码见CreateConnectionCommand)。保存和载入模型的代码请 看SubjectEditor的init()方法和doSave()方法,都是很标准的EMF访问资源的方法,以下是载入的代码(如果是新创建的文件,则 在Resource中新建Diagram对象):   

 

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

虽然到目前为止我还没有机会体会EMF在模型交互引用方面的优势,但经过进一步的了解和在这个例子的应用,我对EMF的印象已有所改观。据我目前所知,使用EMF模型作为GEF的模型部分至少有以下几个好处:

  1. 只需要定义一次模型,而不是类图、设计文档、Java代码等等好几处;
  2. EMF为模型提供了完整的消息机制,不用我们手动实现了;
  3. EMF提供了缺省的模型持久化功能(xmi),并且允许修改持久化方式;
  4. EMF的模型便于交叉引用,因为拥有足够的元信息,等等。

此外,EMF.Edit框架能够为模型的编辑提供了很大的帮助,由于我现在对它还不熟悉,所以例子里也没有用到,今后我会修改这个例子以利用EMF.Edit。

本文转自博客园八进制的博客,原文链接:[Eclipse]GEF入门系列(八、使用EMF构造GEF的模型),如需转载请自行联系原博主。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好了,准备工作完成后我们就开始安装了,大家看我是怎么安装的,我的eclipse是安装在D:\Eclipse目录下的,我在Eclipse文件夹下面建了三个文件夹,分别命名问EMFGEF,VE_SDK,现在我们要做的就是把我们刚刚下载下来的三个文件分别解压缩到这三个文件里面,即把emf-sdo-runtime-2.2.0解压到EMF里面,依次类推。现在打开我们新建的三个文件夹看看,是不是每个文件夹里面都多了一个eclipse文件夹,如果有的话就证明你已经接近成功了,注:(没有的话也不要紧,自己手动在三个文件夹里分别新建三个eclipse文件夹,注意文件夹必须以eclipse命名!!再把自己解压出来的东西中的features和plugins两个文件夹依次一一对应的移动到我们新建的三个eclipse文件夹里面。) 接下来我们在我们的eclipse的安装目录下D:\Eclipse\eclipse下面新建一个文件夹,命名为links,注意文件名一定要为links,接下来我们在links文件夹下面新建三个后缀为.link文本文件,我建的是EMF.link,GEF.link,和VE_SDK.link,在三个文本文件里分别出入如下语句: path=D:\\Eclipse\\EMF path=D:\\Eclipse\\GMF path=D:\\Eclipse\\VE_SDK 保存即可! 接下来启动eclipse,新建一个java工程,完成后点击菜单栏上的新建按钮,看,下拉菜单中是不是多了Visual Editor选项!这个你也可以在工程文件上面单击右键,看,弹出的菜单里边是不是也有这个选项呢? 大功告成! 安装插件后在窗口— > 首选项 中看不到 ① 把 eclipse\configuration\org.eclipse.update 删除掉。出现这种情况的原因是在你安装新的插件以前你启动过 eclipse ,在 org.eclipse.update 文件夹下记录了插件的历史更新情况,它只记忆了以前的插件更新情况,而你新安装的插件它并不记录,所以删除掉这个文件夹就可以解决这个问题了,不过删除掉这个文件夹后, eclipse 会重新扫描所有的插件,此时再重新启动 eclipse 时可能会比刚才稍微慢点。 ② 创建一个 Eclipse 快捷启动方式,在目标栏中加入一个“ 空格— clean ”参数,注意不要把— clean 前面的空格漏掉。或者在菜单【开始】—【运行】中启动你的 eclipse 时加上— clean 参数,如我的可以在“运行”中输入: F:\hongjun\eclipse\eclipse.exe –clean 如果启动 eclipse 后找到你所安装的新插件后,在下次启动之前把参数 clean 去掉就可以了。 ③ 如果 Eclipse 启动找不到插件的话,解决办法是在 eclipse\configuration 目录下的 config.ini 文件中加入一行 : osgi.checkConfiguration=true 这样它就会寻找并安装插件 , 找到插件后可以把该行注释掉 ( 去掉 ), 这样以后每次启动就不会因为寻找插件而显得慢了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值