适配器类是通过EMF.Edit这个框架生成的,通过它生成的适配器类为EMF模型起到了编辑器的作用,包括:
1为Jface组件(如TableViewer、TreeViewer等)提供ContentProvider和LabelProvider
2提供常用命令操作(添加、删除、更新、undo、redo等)。
在Jface组件中,视图内容是通过ContentProvider接口来提供的,而标签和图标的显示是通过LabelProvider接口来实现的,
在EMF中,AdapterFactoryContentProvider间接实现了IContentProvider接口,而AdapterFactoryLabelProvider间接实现了ILabelProvider接口,因此,Jface组件便可以通过他们来获取组件的内容和标签的显示信息。
上一篇文章已经提到,视图不直接与EMF实体类交互,而是通过其适配器类的处理来完成EMF模型到视图的绑定过程。
这些适配器在EMF中都是以ItemProvider的形式存在的(如实体类Book的适配器类便是BookItemProvider类),并且由统一的工厂类来对其进行管理(LibraryItemProviderAdapterFactory),因此在构造AdapterFactoryContentProvider对象时,只要将该适配器工厂类以
构造参数的形式传递进去,AdapterFactoryContentProvider便可以通过适配器工厂将代码逻辑转交给相应的适配器类。
可通过一段代码来看一下适配器类的使用:
假设我们要将EMF模型应用到TreeViewer组建上,则需要文本提供器实现ITreeContentProvider接口,AdapterFactoryContentProvider实现了这个接口,当我们调用getChildren()获取子节点时,它会进行如下处理:
public Object [] getChildren(Object object) { //调用适配器工厂类来获取object的适配器类 ITreeItemContentProvider treeItemContentProvider = (ITreeItemContentProvider)adapterFactory.adapt(object, ITreeItemContentProviderClass); return (treeItemContentProvider != null ? treeItemContentProvider.getChildren(object) ://执行适配器类的getChildren()方法 Collections.EMPTY_LIST).toArray(); }
该方法首先会调用适配器工厂类的adapt方法来获取指定object的适配器类,
然后再调用适配器类的getChildren()方法,这样便将代码逻辑转交给适配器类去处理。
整个处理过程是这样的,但是这两个步骤我们也应该明确其实现细节。
1适配器工厂类(adapterFactory)如何通过指定object来获取其适配器类
我们可以看一下adapt()方法的实现(注:org.eclipse.emf.common.notify.impl.AdapterFactoryImpl类中声明)
public Object adapt(Object target, Object type){ if (target instanceof Notifier){//判断target对象是不是emf实体类 return adapt((Notifier)target, type);//如果是返回该实体类的适配器类 } else{ return target;//如果不是emf实体类则直接返回该对象 } }
方法首先会判断传入的target对象是不是emf实体类
(注:所有的emf实体类接口全部继承至EObject,而EObject接口又继承至Notifier,所以我们可以通过target instanceof Notifier来判断target是不是emf实体对象)
如果是emf实体,则通过adapterFactry返回其适配器类;
而如果不是emf实体,则直接返回了该对象。
代码这样设计其实是有以下好处的:
在使用EMF模型作为视图组件的提供器之后,我们可能还想将其他模型也整合到其中;换个角度说,视图的提供器可能是EMF模型和其他模型的混合体。那么adapt方法的处理逻辑便能满足这种需求。
比如,我们可以自己编写一个与EMF模型无关的类,让它实现ITreeItemContentProvider接口,当调用adapterFactory的adapt方法时,因为它不是EMF实体类,便会直接返回该对象,然后便可以将具体的业务操作转接给该对象进行处理。
该方法也从另一面说明了在EMF中,所有的ItemProvider都是适配器类,但并不是所有的适配器类都是ItemProvider。比如这里我们创建的对象,它也是适配器类,但不是作为ItemProvider的形式出现的。
2适配器类如何处理接下来的逻辑
如果该适配器类是EMF生成的(即以ItemPrvider形式出现的),那么它会继承org.eclipse.emf.edit.provider.ItemProviderAdapter这个模板类,然后调用模板类的相应方法去处理。如果该适配器类是我们手工生成的,则需要我们自己去编写方法的实现。
有了这些适配器类之后,构建Jface组件将会变得很容易,我们还没有手工编写任何代码,便具备了几乎所有构建组件的基本条件。
构建TreeViewer组件如下:
TreeViewer treeViewer=new TreeViewer(container); AdapterFactory adapterFactory=new LibraryItemProviderAdapterFactory();//创建适配器工厂类 treeViewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));//指定内容提供器 treeViewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));//指定标签提供其 //创建input数据源 LibraryFactory librayFactory=LibraryFactory.eINSTANCE; Library library=librayFactory.createLibrary(); library.setName("国家图书馆"); Book book=librayFactory.createBook(); book.setTitle("emf head first"); book.setCategory(Category.CATEGORY1); Writer writer=librayFactory.createWriter(); writer.setName("张三"); book.setWriter(writer); library.getBooks().add(book); library.getWriters().add(writer); treeViewer.setInput(library);
可以看到,我们将主要操作都放在了数据源input值的构造上,而其他操作通过构建EMF适配器工厂来进行处理。
构建TableViewer组件:
EMF为我们生成的模型并不直接支持TableViewer组件,需要稍加修改。
首先在适配器工厂类的构造函数中添加新类型支持。
如:在LibraryItemProviderAdapterFactory中添加对ITableItemLabelProvider类型的支持。
public LibraryItemProviderAdapterFactory() { supportedTypes.add(IEditingDomainItemProvider.class); supportedTypes.add(IStructuredItemContentProvider.class); supportedTypes.add(ITreeItemContentProvider.class); supportedTypes.add(IItemLabelProvider.class); supportedTypes.add(IItemPropertySource.class); supportedTypes.add(ITableItemLabelProvider.class);//新增table的 }
然后在每个适配器类所实现的接口中添加ITableItemLabelProvider接口
如:public class BookItemProvider
extends ItemProviderAdapter
implements
IEditingDomainItemProvider,
IStructuredItemContentProvider,
ITreeItemContentProvider,
IItemLabelProvider,
IItemPropertySource,
ITableItemLabelProvider//添加此接口
最后编写并覆盖getColumnText()方法和getColumnImage()方法。
接下来的构建步骤与TreeViewer相同。