EMF介绍系列(六、自定义命令)

摘要:
在EMF生成的应用程序中,用户发出的每个命令都可以被撤销。例如,如果修改了产品的价格,则可以通过单击“撤消”按钮恢复原始价格。当然,新的价格可以通过重做来恢复。在EMF中,命令框架实际上可以分为两部分,一部分是独立于模型的通用命令,另一部分是编辑命令,后者基于前者。通用命令可以与EMF完全分离,也就是说,这个命令框架可以应用于任何需要命令框架的应用程序,包括非EMF应用程序。

EMF生成的应用程序里,用户的发出的每一条命令都是可以撤销(Undo)的,例如修改了产品的价格,按一下撤销按钮就能恢复原来的价格,当然还可 以通过重做(Redo)再回到新的价格。为了实现这个功能,应用程序里维护了一个用于存放命令的类似栈的数据结构(CommandStack),每一条执 行过的命令都被存放在那里,需要撤销时取出最近一条命令进行撤销。这个数据结构是由EditingDomain对象负责维护的, EditingDomain相当于编辑模型时的环境。

在EMF里命令框架实际上可以分为两大部分,一部分是与模型无关的通用命令,另一部分是.Edit命令,后者是建立在前者的基础之上的。EMF对模 型的任何修改都是通过命令完成的,例如当用户在属性视图里修改一个对象的属性时,会生成一个新的SetCommand实例,然后执行它的execute ()方法,这个方法里对模型进行修改(实际上是通过doExecute()方法),成功执行完成后这个命令被放入命令栈以便撤销时使用。

通用命令可以完全脱离EMF使用,也就是说,这个命令框架可以应用到任何需要命令框架的应用程序中,包括非EMF应用程序。它位于 org.eclipse.emf.common.command包里,其中Command接口定义了什么是“命令“,一个命令具有execute()、 undo()和redo()等方法,还有canExecute()和canUndo()方法用于判断命令是否可被执行或撤销(考虑到资源消耗,有些命令可 能设计为不可撤销更合理)。另一个重要的接口是前面提到过的CommandStack,它的作用是保存所有命令,可以通过 addCommandStackListener()方法注册监听器来观察CommandStack的状态变化。CompoundCommand接口可以 把多个命令按顺序包装成一个组合命令,它具有原子性,类似数据库里事务(Transaction)的概念,只有所有命令都可执行时这个组合命令才可执行, 撤销也是如此。

EMF在.Edit框架提供了针对EMF模型编辑所需要的一些命令(位于org.eclipse.emf.edit.command包),例如 SetCommand用于修改对象的属性,CreateChildCommand的作用是创建一个子元素,还有MoveCommand、 CopyCommand和CutToClipboardCommand等等。这些命令都实现Command接口,并且大部分继承自 AbstractOverrideableCommand这个抽象类,它给我们带来的影响是在Command接口里的方法名前面都加了一个do,比如 execute()变为doExecute()、canUndo()变为doCanUndo()等等,我们在扩展这些.Edit命令时要覆盖doXXX方 法。.Edit命令是通过反射的方式来修改模型的。

EMF提供的这些命令为我们完成基本的模型编辑功能,多数情况下直接使用它们就可以了,但有时通过自定义的命令可以实现一些特别的需求。举个例子来 说,在网上商店的例子里,假设要求产品的价格只精确到小数点后两位,那么我们要在用户输入新价格以后立刻对这个数值进行四舍五入处理,这个操作就可以利用 自定义命令完成。因为利用了.Edit提供的类,所以一般我们应该扩展.Edit命令,具体来说就是SetCommand。

首先通过继承SetCommand创建我们的SetPriceCommand,在这个方法里覆盖doExecute()方法,SetCommand 里有很多可供利用的环境变量,我们要用到的是owner和value这两项,前者是要修改的对象,在这里是产品对象,后者是属性的新值,在这里也就是新价 格。所以我们的SetPriceCommand可以像下面这样写(为了使代码最简,我们直接把EObject类型转换为Product类型,这样就不需要 用反射的方式了):

public class SetPriceCommand extends SetCommand {
    
public SetPriceCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value) {
        
super(domain, owner, feature, value);
    }
    
public void doExecute() {
        Product product 
= (Product) owner;
        
double newPrice = ((Double) value).doubleValue();
        newPrice 
= Math.max(0, newPrice);//New price value must >= 0
        newPrice=Math.round(newPrice*100)/100d;//Max fraction digits is 2    
        product.setPrice(newPrice);
    }
}

要让这个自定义命令生效,必须在ProductItemProvider里覆盖createSetCommand()方法,因为这个方法缺省是返回SetCommand的,我们要在这里做一个判断:如果修改的是价格属性,就返回我们自定义的这个命令,如下所示:

protected Command createSetCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value,
        
int index) {
    
if (feature == ShopPackage.eINSTANCE.getProduct_Price())
        
return new SetPriceCommand(domain, owner, feature, value);
    
return super.createSetCommand(domain, owner, feature, value, index);
}

这样当用户在属性页里改价格属性时,就会调用我们的SetPriceCommand了。顺便说一句,在GEF里 也有类似的EditDomain和Command,只是GEF里的Command一般通过EditPolicy的createXXXCommand()方 法来创建。因为GEF和EMF的两套Command机制没有实现统一的接口,所以结合GEF和EMF的时候常会遇到一些问题,需要额外的代码帮助解决,请 参考这两处讨论(讨论1,讨论2)。

最后要说一句,CreateChildCommand有点特殊,它是.Edit命令但不继承 AbstractOverrideableCommand,而且如果想在创建子元素时自动完成一些工作,不应该通过扩展这个类完成,而应该在 XXXItemProvider的collectNewChildDescriptors()方法里处理,这个方法决定每个对象可以创建哪些子元素,你可 以修改它的代码以对新建的元素做一些处理。

参考资料: Eclipse Modeling Framework A Developers Guide,第3.3节、第14.1节。

点击下载源代码

免责声明:文章转载自《EMF介绍系列(六、自定义命令)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Openfire验证机制的修改(整合自定义用户表)多线程系列之生产者和消费者问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

(4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)

Opensslrsa是RSA对称密钥的处理工具。opensslpkey是一种通用的非对称密钥处理工具。它们的用法基本相同,因此只有opensslrssa通过示例进行了说明。-Outfilename:默认情况下,opensslrss用于读取文件中的公钥或私钥并将其显示到stdout,此选项用于将读取的内容输出到指定的文件-Passonarg:传递解密密钥文件的...

20、EVE-NG华丽登场Openstack镜像,体验私有云!

EVE-NG华丽登场Openstack镜像,体验私有云!为了推动Cloud技术在国内更快速的发展,本人决定为EVE-NG定制一个Openstack私有云系统的镜像,来提升工程师个人的技术实力,横向拓宽技术知识面,以应对IT技术高速迭代,快速发展的现状。当然,您可以将Openstack设备连接到其他拓补中,但设备所在的网络必须是10.0.0.0/24,并且网关...

buildroot使用介绍【转】

整个Buildroot由Makefile脚本和Kconfig配置文件组成。就像编译Linux内核一样,您可以编译一个完整的Linux系统软件,该软件可以通过buildroot配置和menuconfig修改直接写入机器。使用buildroot构建基于qemu的虚拟开发平台。请参阅通过buildroot+qemu构建ARM Linux虚拟开发环境。工具链--˃配...

微信分享之分享图片/分享图标不能显示

微信分享的分享图标/图片无法显示,主要是由于以下几个问题:1.确保分享界面调用成功,分享路径正确。2.确保共享图片的路径不使用中文或全半角字符。3.确保副本不包含敏感字符,如红包和收据。当共享接口未能成功加载时,将发生错误。在页面的前面使用隐藏的div来放置要制作缩略图的图片。记住,不能直接隐藏图片。style=“display:noen”,如果没有,则使用...

allure报告实现保存失败用例截图功能

allure中可以保存日志信息和截图日志allure能够自动识别。截图需要自己在添加allure方法。...

微信小程序生成带参数的二维码(小程序码)独家asp.net的服务端c#完整代码

1) 我第一次使用wx。小程序端请求调用API,发现这是一个坑!@-_~Page:'pages/index/index',//在此处填写要跳转到的小程序页面。你不能在它前面添加/oh。发布后必须为1024页//小程序代码的边长,以像素为单位,范围[2801280]},标头:{'content-type':“application/json;charset=U...