本书的第二章介绍了EMF产生的Model部分的代码,你可以使用EMF Edit
Framework来为model构建功能强大的视图和编辑器。在EMF为你生成的编辑器里,可以编辑(复制,粘贴,拖拽等)、显示model,还可以无
限的redo/undo。如果这个编辑器不能完全满足你的需求,你可以在此基础上做一些修改,肯定比从头开始写节约时间。
Eclipse通过JFace中的一些Viewer来显示结构化的数据,这些Viewer不直接从model中获取要显示的数据,而是通过
ContentProvider和LabelProvider来取得要显示的内容、文本以及图标等信息。不同的Viewer需要不同的
ContentProvider和LabelProvider:
Viewer | ContentProvider | LabelProvider |
ListViewer | IStructureContentProvider | ILabelProvider |
TreeViewer | ITreeContentProvider | ILabelProvider |
TableViewer | IStructureContentProvider | ITableLabelProvider |
其中ILabelProvider有getText(Object)和getImage(Object)两个方
法,ITableLabelProvider有getColumnText(Object,
int)和getColumnImage(Object,int)两个方法,它们之间没有继承关系。IStructureContentProvider
有getElements(Object)方法,返回一个Object数组,而ITreeContentProvider是其子类,另有自己的方法
getChildren(Object),getParent(Object)和hasChildren(Objecct)三个方法。
EMF Edit是把Eclipse UI Framework(JFace)和EMF
Framework连接起来的桥梁,包括两部分,其中org.eclipse.emf.edit是与UI无关的部
分,org.eclipse.emf.edit.ui是与UI有关的部分。前一部分主要包括ItemProvider和Commmand。
1.Item Providers
item
provider,这个名字来源于它为模型中各个可编辑的"items"(对象)"provides"功能。它通常是EMF
Adapters,但是也可以不是。They are used to adapt model objects so the model
object can provide all of the interfaces that it needs to be viewed and
edited.
item provider主要有四个功能:作为content和label provider;
为EMF对象提供property source; 担当Command Factory; 将EMF模型的变化通知发送给Viewer。每个item
provider可以实现上述全部功能或者部分功能,通常情况下,item provider通过继承EMF Edit的基类ItemProviderAdapter
来实现全部的功能。下面分别介绍这四个功能各自需要哪些方法的支持:
(1)Content and label item providers
EMF.Edit分别通过AdapterFactoryContentProvider和AdapterFactcoryLabelProvider来提
供content provider和label provider的功能,这两个类的构造函数都需要一个adapter
factory(EMF生成的edit部分代码中的**ItemProviderAdapterFactory,负责为特定的类型创建或者定位
adapter),它们的很多方法都通过分发到相应的Item Provider来实现。比如getChildren()的实现如下:
{
// Get the adapter from the factory.
ITreeItemContentProvider treeItemContentProvider =
(ITreeItemContentProvider)adapterFactory.adapt(object, ITreeItemContentProviderClass);
// Either delegate the call or return nothing.
return
treeItemContentProvider != null &&
treeItemContentProvider.hasChildren(object);
}
EMF.Edit为模型中的每个类生成一个**ItemProvider类,负责为UI的显示提供具体的实现。每个类在默认情况下都实现了5个接口,其中
的三个IStructuredItemContentProvider(为ListViewer和TableViewer提供
contentProvider),
ITreeItemContentProvider(为TreeViewer提供
contentProvider),IItemLabelProvider(为TreeViewer和ListViewer提供
labelProvider)是支持这一功能的.
如果我们要用TableViewer显示订单(模型中对应PurchaseOrder类),那么应该修改
PurchaseOrderItemProvider,使它实现ITableItemLabelProvider,同时实现方法
getColumnText和getColumnImage。还要在为该模型生成的POItemProviderAdapterFactory类的构造函
数中添加对ITableItemLabelProvider的支持:
supportedTypes.add(ITableItemLabelProvider. class );
(2)Item Property Source
当打开PropertySheet时,AdapterFactoryContentProvider还是通过adapterFactory找到相应的实现
了IItemPropertySource接口的item
Provider。EMF.Edit中的PropertySource类实现了这一接口,因此
AdapterFactoryContentProvider返回PropertySource的一个新创建的实例(作为被选中的item
provider的wrapper)给PropertySheet,PropertySource把很多方法的实现分发给了
itemPropertySource。其代码如下:
{
if (object instanceof IPropertySource)
{
return (IPropertySource)object;
}
else
{
IItemPropertySource itemPropertySource =
(IItemPropertySource)
(object instanceof EObject && ((EObject)object).eClass() == null ?
null :
adapterFactory.adapt(object, IItemPropertySourceClass));
return
itemPropertySource != null ? createPropertySource(object, itemPropertySource) : null ;
}
}
protected IPropertySource createPropertySource(Object object, IItemPropertySource itemPropertySource)
{
return new PropertySource(object, itemPropertySource);
}
自动生成的**ItemProvider实现的5个接口中的IItemPropertySource就是支持这一功能的, 其中getPropertyDescriptors()方法返回的列表决定了PropertySheet中显示的内容列表。
(3)Command Factory
EMF.Edit提供了修改EMF对象(可以undo)的整套机制,并且提供了一些通用的命令。自动生成的**ItemProvider实现的5个接口中
还没提到的IEditingDomainItemProvider就是支持这一功能的。EMF.Edit的Command
Framework需要EditngDomain接口,其中类AdapterFactoryEditingDomain如同前面提到的
AdapterFactoryContentProvider和AdapterFactoryLabelProvider,它负责把相应的方法(比如
getChildren(),getParent()等)分发给实现了IEditingDomainItemProvider的Item
Provider。
(4)Change Notification
当**ItemProvider所adapt的对象状态发生变化时,**ItemProvider会收到Notification,其
NotifyChanged()会被调用。ItemProvider作为Observer负责过滤不相关的事件,然后把相关的事件传递给模型的
central change
notifier,通常是实现了IChangeNotifier接口的**ItemProviderAdapterFactory。
当我们在PropertySheet中修改Item(订单项,在模型中对应Item类)的名字时,首先ItemPropertyDescriptor的
setPropertyValue()方法被调用,它会通过SetCommand修改名字或者直接调用Item的eSet(),总之这两种方法都会调用到
ItemImpl的setName()方法,该方法中判断是否需要发送通知,如果需要,则向所有监听item对象的adapter发送通知(即调用它们的
notifyChanged()方法),这些adapter包括ItemItemProvide,它的notifyChanged()方法调用
POItemProviderAdapterFactory(IChangeNotifier类型)的fireNotifyChanged()方法,然后
再调用所有注册的listener的fireNotifyChanged()。这些listener可能是PropertySheet,Outline视
图中的TreeViewer等,收到通知后,更新视图以反映model的变化。当有事件发生需要通知JFace的Viewer时,真正的listener
并不是viewer本身,而是它的content provider。
EMF.Edit的另一部分重要内容Command Framework留到下一部分介绍。