论坛首页 Java企业应用论坛

Command Framework如何实现EMF Model的编辑

浏览 3211 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-02-10  
源于《EMF.Edit Framework Programmgering's Guide》

EMF.Edit里面有几个类比较绕,很容易被搞得晕头转向,所以需要澄清以下:
★AdapterFactoryContentProvider, ItemProviderAdapterFactory和ItemProviderAdapter之间的关系,ItemProviderAdapterFactory用来创建各种Adapter以及将各种notifier跟这些adapter关联起来,AdapterFactoryContentProvider包装了一个ItemProviderAdapterFactory(AdapterFactory),它用来将JFace需要的content provider代理到item content provider上,对content provider各种方法的调用将调用到相应的item content provider上,对org.eclipse.jface.viewers.IStructuredContentProvider的调用将代理到IStructuredItemContentProvider上,对ITreeContentProvider的调用将代理到ITreeItemContentProvider上,而对IPropertySourceProvider的调用将代理到IItemPropertySource,而ItemProviderAdapter是所有ItemProvider的基类,AdapterFactoryLabelProvider和ItemProviderAdapter之间也存在类似的关系

★为了显示model内容,我们需要使用content provider和label provider,而编辑model内容则需要使用到editing domain,AdapterFactoryEditingDomain是一个和AdapterFactoryContentProvider、AdapterFactoryLabelProvider类似的东东

★editing domain主要有两个功能:一个是作为command的factory(所以它的实现类是AdapterFactoryEditingDomain);另一个对EMF Model(ResourceSet,因此提供了getResource()方法)进行管理

★EditingDomain,AdapterFactoryEditingDomain,EditingDomainItemProvider和Command之间的关系,AdapterFactoryEditingDomain实现了EditingDomain接口,AdapterFactoryEditingDomain和AdapterFactoryContentProvider一样,也是用来将EditingDomain的方法代理到EditingDomainItemProvider上去。

从一般的操作说起,比如从一个company对象上删除一个department对象,通常我们的做法是:
 
java 代码
  1. Department d = ...  
  2.   Company c = ...  
  3.   c.getDepartments().remove(d);  

但是如果是使用command,则会这样做:
 
java 代码
 
  1. Department d = ...  
  2.   Company c = ...  
  3.   EditingDomain ed = ...  
  4.   RemoveCommand cmd =  
  5.     new RemoveCommand(ed, c, CompanyPackage.eINSTANCE.getCompany_Departments(), d);  
  6.   ed.getCommandStack().execute(cmd);  

不过这个做法有一个问题,就是不是很通用,因为所有的删除操作基本上都差不多,所以还需要继续抽象,这时就必须引入EditingDomain.
EditingDomain的接口定义如下:
java 代码
 
  1. public interface EditingDomain  
  2.   {  
  3.     ...  
  4.     Command createCommand(Class commandClass, CommandParameter commandParameter);  
  5.     ...  
  6.   }  

为了创建一个Command对象,我们需要构造一个CommandParameter对象。在createCommand方法里面会调用指定的Command的静态create方法来创建指定的Command对象,通过使用create方法,我们可以对上面的操作做进一步的改写:
java 代码
  1. Department d = ...  
  2. EditingDomain ed = ...  
  3. Command cmd = RemoveCommand.create(ed, d);  
  4. ed.getCommandStack().execute(cmd);  

通过上面的改写,差不多实现了一个通用的删除操作流程
接下来我们可以看看一个command的创建过程,首先是调用指定command的静态create方法,该方法将调用EditingDomain的createCommand方法,AdapterFactoryEditingDomain作为EditingDomain的实现类,又将command的创建过程代理到EditingDomainItemProvider上,在Itemprovider(实现了EditingDomainItemProvider接口)中,最终使用new创建指定的Command实例
我们可以采用多种方式对command定制,第一种就是复写generated的EditingDomainItemProvider实现类的createCommand方法:
java 代码
 
  1. public class CompanyItemProvider ...  
  2. {  
  3.   ...  
  4.   
  5.   public Command createCommand(final Object object, ...)  
  6.   {  
  7.     if (commandClass == RemoveCommand.class)  
  8.     {  
  9.       return new RemoveDepartmentCommand(...);  
  10.     }  
  11.     return super.createCommand(...);  
  12.   }  
  13. }  

这里的RemoveDepartmentCommand 就是我们自己实现的删除操作。
第二种方式就是复写createRemoveCommand()来实现定制:
java 代码
 
  1. protected Command createRemoveCommand(...)  
  2.   {  
  3.     return new RemoveDepartmentCommand(...);  
  4.   }  


通知的处理
在创建AdapterFactoryContentProvider的时候会将其作为一个listener注册到AdapterFactory里面,这个AdapterFactory实现了IChangeNotifier接口,而AdapterFactory在创建每一个ItemProvider的时候又会把自己传递过去,从而使得AdapterFactory成为model的消息分发中心,在AdapterFactoryContentProvider又会记录所有需要接受通知的viewer(也就是为其提供了content provider的viewer)。
当model被改变之后,将触发和该model相关的adapter的notifyChanged()方法(这里面的adapter就包括itemprovider),当然这里还有一个过滤的过程,只把那些跟viewer相关的notification才会发送给viewer。为了将notification继续传递,会使用ViewerNotification这样一个对象来对notifation以及其他的信息进行封装,因此它继承了Notification,除了Notification相关的信息之外,还封装了要更新的viewer的相关元素,IViewerNotification 的定义如下:
 
java 代码
 
  1. public interface IViewerNotification extends Notification  
  2.   {  
  3.     Object getElement();  
  4.     boolean isContentRefresh();  
  5.     boolean isLabelUpdate();  
  6.   }  

对于消息的传递还会进行分类,这个是在notifyChanged这个方法里面做的,如下面的代码:
java 代码
 
  1. public void notifyChanged(Notification notification)  
  2.   {  
  3.     ...  
  4.     switch (notification.getFeatureID(Company.class))  
  5.     {  
  6.       case CompanyPackage.COMPANY__NAME:  
  7.     //ViewerNotification(Notification decoratedNotification, Object element,
  8. boolean contentRefresh, boolean labelUpdate)  
  9.         fireNotifyChanged(new ViewerNotification(notification, ..., falsetrue));  
  10.         return;  
  11.       case CompanyPackage.COMPANY__DEPARTMENT:  
  12.         fireNotifyChanged(new ViewerNotification(notification, ..., truefalse));  
  13.         return;  
  14.     }  
  15.     super.notifyChanged(notification);  
  16.   }  

可以看出,如果是attribute,那么会对label进行更新,如果是reference,那么需要更新content了,否则什么都不做。fireNotifyChanged方法是在ItemProviderAdapter(就是所有ItemProvider的基类)里面定义的,它会把notifaction传给adapter factory,前面我们说过adapter factory是notification的分发器,因此它会将notification发送给所有注册的listener,我们前面也说过AdapterFactory实现IChangeNotifier接口,并作为listener注册到adapter factory中去了,因此在最后会调用adapter factory的fireNotifyChanged方法,当然了adapter factory也会将notification代理别的对象(可能是tree或者table的content/label provider,当然在emf中就是itemprovider了)上去,最后viewer被更新了。
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics