本书的第二章介绍了EMF产生的Model部分的代码,你可以使用EMF Edit Framework来为model构建功能强大的视图和编辑器。在EMF为你生成的编辑器里,可以编辑(复制,粘贴,拖拽等)、显示model,还可以无限的redo/undo。如果这个编辑器不能完全满足你的需求,你可以在此基础上做一些修改,肯定比从头开始写节约时间。
Eclipse通过JFace中的一些Viewer来显示结构化的数据,这些Viewer不直接从model中获取要显示的数据,而是通过ContentProvider和LabelProvider来取得要显示的内容、文本以及图标等信息。不同的Viewer需要不同的ContentProvider和LabelProvider:
其中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()的实现如下:
如果我们要用TableViewer显示订单(模型中对应PurchaseOrder类),那么应该修改PurchaseOrderItemProvider,使它实现ITableItemLabelProvider,同时实现方法getColumnText和getColumnImage。还要在为该模型生成的POItemProviderAdapterFactory类的构造函数中添加对ITableItemLabelProvider的支持:
(2) Item Property Source
当打开PropertySheet时,AdapterFactoryContentProvider还是通过adapterFactory找到相应的实现了IItemPropertySource接口的item Provider。EMF.Edit中的PropertySource类实现了这一接口,因此AdapterFactoryContentProvider返回PropertySource的一个新创建的实例(作为被选中的item provider的wrapper)给PropertySheet,PropertySource把很多方法的实现分发给了itemPropertySource。其代码如下:
(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留到下一部分介绍。
Eclipse通过JFace中的一些Viewer来显示结构化的数据,这些Viewer不直接从model中获取要显示的数据,而是通过ContentProvider和LabelProvider来取得要显示的内容、文本以及图标等信息。不同的Viewer需要不同的ContentProvider和LabelProvider:
Viewer | ContentProvider | LabelProvider |
ListViewer |
IStructureContentProvider |
ILabelProvider |
TreeViewer | ITreeContentProvider |
ILabelProvider |
TableViewer | IStructureContentProvider |
ITableLabelProvider |
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()的实现如下:
public
boolean
hasChildren(Object object)
{
// 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)是支持这一功能的.
{
// 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);
}
如果我们要用TableViewer显示订单(模型中对应PurchaseOrder类),那么应该修改PurchaseOrderItemProvider,使它实现ITableItemLabelProvider,同时实现方法getColumnText和getColumnImage。还要在为该模型生成的POItemProviderAdapterFactory类的构造函数中添加对ITableItemLabelProvider的支持:
...
supportedTypes.add(ITableItemLabelProvider. class );
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。其代码如下:
public
IPropertySource getPropertySource(Object object)
{
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中显示的内容列表。
{
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);
}
(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留到下一部分介绍。