FROM:http://www.rcp.org.cn/bbs_topic.do?forumID=8&postID=6
Item providers是EMF.Edit工程里最重要的一部分,事实上该工程里除了plugin.java外就只有Item providers和Item provider factory。Item provider决定了不同类型的对象如何被显示以及他们如何响应用户的操作。我们将首先弄明白生成的代码如何做到的然后我们将探讨如何改变生成代码以满足我们的需要。
缺省情况下,core model里的每一个class都将会生成一个Item provider。Item provider的继承关系将遵循model里的继承关系。例如class B继承class A,那么B的Item provider也将继承A的Item provider。你也可以在genmodel文件中修改生成方式,比如将A的Provider Type选项改为None,那么将不会有A的Item provider被生成,B的Item provider也将继承A的父类的Item provider。
就像所有的Model类都继承自EObjectImpl一样,所有的Item provider都继承自一个框架类ItemProviderAdapter,它里面有大部分Item provider方法的缺省实现。
我们将以‘主机’类的Item provider为例来看看它和它的父类是如何完成Item provider的四种角色:
1. 实现Content和label provider的功能。
2. 作为EMF对象的property source将EMF对象的属性值显示在property sheet上。
3. 作为EMF对象的command factory,维护对EMF对象的增删改command。
4. 将EMF对象的notifications传递给viewers,以便于viewer将EMF对象的变化及时刷新显示出来。
我们从主机Item provider类的声明开始:
- public class 主机ItemProvider
- extends ItemProviderAdapter
- implements
- IEditingDomainItemProvider,
- IStructuredItemContentProvider,
- ITreeItemContentProvider,
- IItemLabelProvider,
- IItemPropertySource
继承的父类是ItemProviderAdapter,其他的五个接口都是用来帮助实现Item provider的那些角色,下面我们将按照角色来分别介绍Item provider中生成的方法。
Content and Label Provider
Eclipse中,不同的Viewer要求的content和label provider不同,所以Item provider实现了ITreeItemContentProvider, IStructuredItemContentProvider, IItemLabelProvider, 和 ITableItemLabelProvider以支持不同的viewer。
我们从ITreeItemContentProvider开始,这个接口要回答一个重要的问题是:我们希望什么将作为这个对象的children?下面这个方法给出了答案:
- public Collection getChildrenFeatures(Object object) {
- if (childrenFeatures == null) {
- super.getChildrenFeatures(object);
- childrenFeatures.add(MobilesPackage.eINSTANCE.get主机_功能());
- childrenFeatures.add(MobilesPackage.eINSTANCE.get主机_配件());
- }
- return childrenFeatures;
- }
一个主机将会包含配件和功能,生成代码将配件和功能的Ereference加入到它的childrenReferences list中。缺省情况下Model中该类的EReference都不会被作为其Child,只有当你修改genmodel中EReference的Children属性才可以。
与标准的content provider不同的是Item provider并没有实现getParent(), getChildren(), hasChildren(), 和 getElements()方法。事实上这些方法被Item provider的父类ItemProviderAdapter实现了,因为它们的实现方法是通用的,它将直接调用EObject的方法:
· getParent() 将返回该对象的container,假如该对象直接被resouce包含,那么返回resource。
· getChildren() 将遍历childrenReferences list,将其内容作为一个collection返回。
· hasChildren() 返回childrenReferences list是否为空。
· getElements() 调用getChildren()并返回其结果。
最后的这个方法,getElements()继承自IStructuredItemContentProvider。作为该接口的唯一方法,getElements()根据input对象返回它所包含的对象,返回值将作为table中的rows,list中的items,tree的最顶级的nodes而input对象就代表着table,list和tree。
你可以通过重写getParent()和getChildren()方法来提供一些不同的功能,例如让同一个对象在不同的视图中显示不同的children。
接下来我们来看IItemLabelProvider接口,它包括两个方法:getImage()和getText(),一个返回特定类型对象的图标另一个返回文本标识。下面是主机Item provider中的生成代码:
- public Object getImage(Object object) {
- return getResourceLocator().getImage("full/obj16/主机");
- }
- public String getText(Object object) {
- String label = ((主机)object).getName();
- return label == null || label.length() == 0 ?
- getString("_UI_主机_type") :
- getString("_UI_主机_type") + " " + label;
- }
调用getResourceLocator()将返回一个实现了ResouceLocator的类,这个类负责提供文本和图片资源。它由ItemProviderAdapter类定义并提供了个缺省实现,返回EMF.Edit plug-in class,在主机Item provider被重写为:
- public ResourceLocator getResourceLocator() {
- return MobilesEditPlugin.INSTANCE;
- }
它返回生成的plug-in class,由它访问我们的model定制的文本和图片资源。这个类将从Icons/subdirectory下搜索被请求的图片资源,例如这里的full/obj16/主机,缺省情况下会有一个生成的图片被返回。
getString()方法将通过指定的key去配置文件中寻找相应的值并返回。EMF采用了java的ResourceBundle机制来提供从配置文件中取文本值,通常这样做的目的是为了国际化。每一个plug-in都有一个配置文件,plugin.properties,那里面是一些key-value对,在我们的edit plug-in配置文件中包含着这样的一行:
_UI_\u4e3b\u673a_type = \u4e3b\u673a
其实它本来的值应该是 _UI_主机_type = 主机,ResourceBundle要求所有的字符都以ascii码来表示,所以中文“主机”被翻译成“\u4e3b\u673a”。下面简单的介绍一下实现国际化的方法:
缺省生成的配置文件中,好多属性名称都是英文的,这是我们的model里设定的,但假如我们想要在中国区域的系统上显示中文,或者日语系统上显示日文,我们只需要再写两个配置文件。比如支持简体中文的就将plugin.properties拷贝一份,命名为plugin_zh_CN.properties,然后将里面的英文属性名改为中文ascii码就可以:
将
_UI_\u4e3b\u673a_name_feature = Name
改为:
_UI_\u4e3b\u673a_name_feature = \u540d\u5b57
将系统的location设为中国,然后启动运行时ECLIPSE,可以看到原来属性页中的英文属性名name变成了中文。
在这里我们可以通过修改配置文件plugin.properties里面的value来定制我们需要的文本,也可以替换生成的图标文件定制节点图标。直接修改getText()和getImage()方法也可以,但是不要忘了将方法注释里面的@generated去掉,这样下次生成代码的时候就不会覆盖掉你的修改。