http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-cnfext/index.html#ibm-pcon
简介: CNF(Common Navigator Framework) 是一个通用的、可扩展的导航视图框架。通过 CNF,开发人员很容易开发一个 CNF 视图,或者对已有的 CNF 视图进行扩展。这篇文章主要介绍如何利用 CNF 框架扩展现有的 CNF 视图 Project Explorer,使其能够展示XML文件的文档结构,并且支持对 XML 元素的菜单操作。
通常,在开发 J2EE 应用程序的时候,用户会大量使用到 XML 文档,并对其进行查看、编辑和配置。XML 文档在 J2EE Perspective 中的 Project Explorer CNF 视图中是作为单个对象进行显示的(见图 1 的左侧),这种方法有一定的局限性,用户如果需要查看 XML 文档的结构,就必须打开 XML 文件并在 XML 编辑器中查看,或者在另外的视图中专门针对 XML 文档结构进行解析并展示。这对一些经常使用到 XML 的应用程序开发来说,增加了一定的复杂性,需要多个视图或编辑器配合使用才能够了解 XML 文档结构。有两种方法可以解决这个问题:
一是:构建自定义的视图,在这个视图中展现所需要的模型内容,包括 XML 文档结构,这种方法有一定的缺点:
- 引入了一个新的视图,要使用这个新的视图可能还需要定义一个 Perspective,这就不能和已有的 J2EE Perspective 集成。
- 重新开发一系列已有的 J2EE Perspective 中 Project Explorer 视图已有的功能,没有达到最大化可重用的效果
二是:扩展已有的 Project Explorer CNF 视图,使得这个视图能够支持 XML 文档结构的树型展示,并且支持对 XML 文档进行菜单操作。这种方法避免了上面方法的缺点,文章中选用的也是这种方法。
根据需求的不同,可以选用上面两种方法中的任一种,文章选用的是对已有的 CNF 视图进行扩展,但文章中用到的原理同样适用于创建一个新的 CNF 视图。扩展后的 Project Explorer CNF 视图支持 XML 文档结构的展示,以及一系列的菜单操作。其界面效果如 下图的右侧所示:
图 1. XML CNF 视图效果
CNF 框架原来是 RAD(IBM Rational Application Developer) v6.0 产品的一部分,后来贡献给开源社区,并在 Eclipse3.2 中使用了该框架。该框架通过 org.eclipse.ui.navigator 插件实现并引入,J2EE Perspective 中的 project explorer 视图就是基于该框架的一个 CNF 视图。通过该框架,允许开发者开发一个 CNF 视图或扩展一个已经存在的 CNF 视图,为一个 CNF 视图贡献内容(节点)、动作(菜单等)、过滤器、以及其他功能。CNF 框架有以下作用:
- 集成视图,该框架提供了一套通用机制和架构,允许开发人员很方便的进行 CNF 视图的开发、集成和扩展。
- 提高重用性,基于 CNF 框架可以扩展视图,这样就使得用户可以重用已有的 CNF 视图内容和操作,而不用进行重新的开发。另外用户可以开发自己的 CNF 视图,开发过程中可以重用大量已有的 CNF 视图内容,比如 Resource, Java 等视图中定义的内容,达到高重用性的目的。
- 很高的灵活性,根据 CNF 框架,不同软件版本功能上的改进可以采取更灵活的方式进行,新的功能、新的菜单操作、新的展示内容可以很方便的集成进已有的 CNF 视图中。
下面将一步一步介绍如何扩展已有的 Project Explorer CNF 视图以支持对 XML 模型结构的展现和操作。
扩展已有的 CNF 视图包括两个步骤,首先创建一个插件工程并配置依赖的插件,然后通过 CNF 扩展点扩展已存在的 CNF 视图。
在创建 CNF 视图之前,我们首先要创建插件工程,我们使用创建项目向导来创建插件工程:File > New > Project,选择 Plug-in Project,然后单击 Next。
输入项目名称为com.ibm.developerwork.xmlnavigator,Plugin id为:com.ibm.developerwork.xmlnavigator,然后单击Finish,成功创建项目。如下图所示:
图 2. 创建插件工程
创建完插件项目后需要设置插件依赖,用 plug-in manifest editor 编辑器打开 META-INF->MANIFEST.MF 文件,选择 Dependency 选项卡,并把下列必须用到的插件(plug-ins)加入到必须插件列表当中:
org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.ui.navigator,
org.eclipse.core.resources,
org.eclipse.wst.xml.core,
org.eclipse.wst.xml.ui,
org.eclipse.wst.sse.core,
org.eclipse.wst.sse.ui,
org.eclipse.core.expressions,
org.eclipse.ui.ide
其中 org.eclipse.ui.navigator 是 CNF 框架的插件,其他的插件是解析 XML 和一些 Eclpise 操作需要用到的插件。
如果用户是需要创建一个新的 CNF 视图,而不是想对已有的通用 CNF 视图 进行扩展,该用户需要创建新的 Eclipse 视图 ViewPart,下面给出自定义视图的步骤,供需要创建自定义 CNF 视图的用户参考。
创建视图,即扩展 Eclipse 的视图扩展点,具体步骤如下:
用 plug-in manifest editor 打开 META-INF->MANIFEST.MF 文件,在 Extensions 选项卡中:
- 单击 Add
- 在 Extension points 选项卡下面,选择 org.eclipse.ui.views
- 创建类别:
- 右键单击 org.eclipse.ui.views 扩展
- 选择 New > Category
- 将ID设为 com.ibm.developerwork.xmlnavigator.xmlnvcategory
- 将名称设为 xml navigator category
- 创建视图部件:
- 右键单击 org.eclipse.ui.views 扩展
- 选择 New > View
- 将 ID 设为com.ibm.developerwork.xmlnavigator.xmlnvview
- 将名称设为 XML Navigator View
- 将类设为 org.eclipse.ui.navigator.CommonNavigator
- 将类别设为 com.ibm.developerwork.xmlnavigator.xmlnvcategory
- 保存对项目的修改
如果用户只是扩展已有的 CNF 视图,只需要定义 CNF 视图的扩展点 ”org.eclipse.ui.navigator.viewer”,并把其中的 viewed 设置为要扩展的视图的插件 ID 即可。
创建自定义 CNF 视图和扩展已存在 CNF 视图的步骤一样,就是定义视图的配置扩展点:org.eclipse.ui.navigator.viewer。通过定义这个扩展点,表示该视图就是一个通用的 CNF 器 (Common Navigator),就具有通用 CNF 器的一系列特性。下面将介绍 org.eclipse.ui.navigator.viewer 扩展点。我们可以通过 Extensions 选项卡来创建此扩展点,最后创建的结果如下所示:
代码列表 1. org.eclipse.ui.navigator.viewer扩展
<extension point="org.eclipse.ui.navigator.viewer">
<viewer viewerId="org.eclipse.ui.navigator.ProjectExplorer"/>
</extension>
扩展点参数的解释:
这里 viewerId 填写的是 Project Explorer 的 view part id,意味着我们是对已有的 Project Explorer CNF 视图进行扩展。如果要建立新的 CNF 视图,viewerId 填写新创建的 view part 的 id。
要在一个CNF视图中展示XML模型内容,需要做两个步骤,首先定义XML模型内容扩展,该内容扩展的作用是展示XML文档结构内容;然后关联该扩展点到相应的通用 CNF视图,意味着XML文档结构的内容可以展现在被关联的通用 CNF视图中。
定义完 CNF视图扩展后,就需要定义内容扩展( content extension),并且把内容扩展和 CNF视图扩展点绑定,这样该视图扩展点对应的视图就可以显示相应的内容扩展中的内容。这一小节将介绍如何定义XML文件的内容扩展,该内容扩展允许在Project Explorer中展开一部分XML文件,并且显示其中的一些属性信息。这里只允许展开一部分XML文件是因为我们对XML文件作了一定的过滤,并不是所有的XML文件都可以被XML内容扩展调用,并能够展开内容,例子中只有符合一定条件的XML文件才可以被展开。下面是我们定义的XML内容扩展:
代码列表 2. XML内容扩
<extension point="org.eclipse.ui.navigator.navigatorContent">
<navigatorContent
activeByDefault="true"
contentProvider="com.ibm.developerwork.xmlnavigator.XMLViewContentProvider"
id="com.ibm.developerwork.xmlnavigator.xmlcontent"
labelProvider="com.ibm.developerwork.xmlnavigator.XMLViewLabelProvider"
name="XML Content"
icon="icons/sample.gif"
priority="normal">
<triggerPoints>
<and>
<instanceof value="org.eclipse.core.resources.IResource"/>
<test forcePluginActivation="true" property="com.ibm.developerwork.namespace"
value="http://www.ibm.com/developerwork"/>
</and>
</triggerPoints>
<possibleChildren>
<instanceof value="org.eclipse.wst.xml.core.internal.document.ElementImpl"/>
</possibleChildren>
</extension>
其中"org.eclipse.ui.examples.navigator.propertiesContent"是内容扩展的扩展点ID,这个内容扩展显示的名称为"XML Content",这个名称在Project Explorer->Customize View->Content列表中被使用,这个列表中列举了所有的可用的内容扩展,如下图所示,处于选中状态的正是本例中的XML内容扩展:
图 3. 内容扩展列表
不同的 <navigatorContent/> 扩展提供不同的内容扩展,下面是例子中定义的 XML 内容扩展的参数的含义:
- activeByDefault,表示在 eclipse 运行的时候,这个内容扩展是否默认是被激活的,也就是在上图的内容扩展列表中默认是处于选中状态的。XML 内容扩展设置为 true,表示默认情况下该内容扩展是激活的。
- Icon,设置显示在 Project Explorer->Customize View->Content 列表中的内容扩展对应的图标,如上图例子中 XML 内容扩展所示的 。
- Priority,该属性有多个用途,其中最重要的用途是决定在 CNF 视图中显示节点的相对位置。Priority 级别越高越靠近视图的顶部,级别越低就越靠近视图的底部。
- Id, 用于表示此内容扩展,这个 Id 将被 org.eclipse.ui.navigator.viewer 扩展点所引用,用于在 CNF 视图中显示这个Id代表的内容扩展的内容。
- contentProvider 和 lableProvider,这两个属性的类用于提供树型 CNF 视图的节点内容以及标题和图标,这里设置的值是 XML 模型的内容提供类和标题提供类。这两个类在下一节将进一步介绍。
接下来介绍定义了 XML 内容扩展后,这个内容扩展什么时候被 CNF 框架调用,然后显示 XML 结构、标题和图标。在内容扩展中,有两个非常重要的表达式:<triggerPoints /> and<possibleChildren />表达式。这两个表达式关系着一个内容扩展什么时候被调用,以及显示什么内容在树型视图中。
<triggerPoints />用于标志CNF视图树上的哪一类型的节点匹配这个扩展点,也就是说 CNF 框架在 CNF 视图中找到相应的这一类型的节点时,这个内容扩展才被调用,并且用于展开这一类节点。XML 内容扩展定义的 <triggerPoints /> 如下:
代码列表 3. triggerPoints 定义
<triggerPoints>
<and>
<instanceof value="org.eclipse.core.resources.IResource"/>
<test forcePluginActivation="true" property="com.ibm.developerwork.namespace"
value="http://www.ibm.com/developerwork"/>
</and>
</triggerPoints>
这里使用了 org.eclipse.core.expressions 机制,用于判断表达式的值。我们定义了”org.eclipse.core.expressions.propertyTesters”扩展点去验证该表达式的值,定义的扩展点如下:
代码列表 4. org.eclipse.core.expressions.propertyTesters 扩展
<extension point="org.eclipse.core.expressions.propertyTesters">
<propertyTester
id="org.eclipse.jdt.ui.IResourceTypeExtender"
type="org.eclipse.core.resources.IResource"
namespace="com.ibm.developerwork"
properties="namespace"
class="com.ibm.developerwork.xmlnavigator.XMLResourcePropertyTester">
</propertyTester>
</extension>
<triggerPoints> 中定义的表达式的属性 property 等于"com.ibm.developerwork.namespace",在”org.eclipse.core.expressions.propertyTesters”扩展点中,通过 namespace 和 properties 的值可以看出这个扩展点是用于验证 "com.ibm. developerwork.namespace" 属性表达式。所以当匹配 XML 内容扩展的时候,这个 PropertyTester 会被调用,用于验证相应表达式的返回值。这个扩展点的 type 等于"org.eclipse.core.resources.IResource",表示 Project Explorer CNF 视图中只有资源文件(IResource)节点才会被用于 XML 内容扩展的匹配验证。真正匹配验证的业务逻辑是在 XMLResourcePropertyTester 类中,该类中的 test 方法返回的值就是上面 <triggerPoints> 定义的表达式的结果。
如果返回结果为真,则表示相应节点会调用 XML 内容扩展,并且展示内容;
如果返回结果为假,则表示相应节点不会调用 XML 内容扩展,当然就不会展示文档内容了。
下面是验证的代码,我们可以看出,只有当资源文件的后缀为 XML 时,并且该 XML 文档的头元素的名称空间(namespace)等于 value=http://www.ibm.com/developerwork 时,这个 XML 内容扩展才会被 CNF 调用,并展开其文档内容:
public boolean test(Object receiver, String property, Object[] args, Object expectedValue)
{
//要验证的属性为"com.ibm.developerwork.namespace"
if (NAMESPACE.equals(property) && null != receiver && receiver instanceof IFile)
{
IFile resource = (IFile) receiver;
//被验证的资源文件的后缀为XML
if (resource.getType() == \
IFile.FILE && resource.getFileExtension().equalsIgnoreCase(XML_EXTENSION)) {
ResourcesPlugin.getWorkspace().addResourceChangeListener(
XMLResourceChangeListner.getInstance(), IResourceChangeEvent.POST_CHANGE);
//头元素的名称为http://www.ibm.com/developerwork时,返回真
return Helper.containDeveloperworkNamespace(resource);
}
}
return false;
}
通过上面的介绍可以看出,在创建或者扩展 CNF 视图时,通过<triggerPoints />可以对同一类型的节点做进一步的过滤,使得满足某一些条件的节点才会被内容扩展所调用,并且展示其结构子节点。
<possibleChildren/> 表示什么类型的节点才会被 XML 内容扩展作为子节点展示,并提供标题和图标。这个在需要和编辑器(Editor)进行关联的场景中会被用到,这时候要求 <possibleChildren /> 的表达式必须是正确并且全面,否则有可能关联不到相应的节点。下面是 XML 内容扩展的 <possibleChildren /> 定义:
代码列表 6. possibleChildren 定义
<possibleChildren>
<instanceof value="org.eclipse.wst.xml.core.internal.document.ElementImpl"/>
</possibleChildren>
该定义表示只有当 XML 文档的展示的孩子节点是 ElementImpl 模型时,才可以展示在 Project Explorer CNF 视图中。这里的 ElementImpl 是 XML 模型中的节点元素,是 XML 模型中的元素,在下一节中将会介绍。
定义完 XML 内容扩展后,我们必须绑定 XML 内容扩展到 CNF 视图,这样才能在 CNF 视图中展示该内容扩展。我们在上一节中定义的 CNF 视图扩展点加入 <viewerContentBinding/> 定义用于表示任何和 <viewerContentBinding/> 匹配的内容扩展都可以在 ”viewerId” 表示的视图中展示。下面是绑定后的结果:
代码列表7. 绑定 XML 内容扩展到 CNF 视图<extension point="org.eclipse.ui.navigator.viewer">
<viewer viewerId="org.eclipse.ui.navigator.ProjectExplorer"/>
<viewerContentBinding viewerId="org.eclipse.ui.navigator.ProjectExplorer">
<includes>
<contentExtension pattern=
"com.ibm.developerwork.transaction.based.tool.navigatorContent"/>
</includes>
</viewerContentBinding>
</extension>
到目前为止,我们已经定义完了 CNF 视图扩展点及 XML 内容扩展,但真正的执行 XML 文件结构展示的是 contentProvider 和 lableProvider 对应的类。下面一节中进行详细介绍。
在 Project Explorer 中展示 XML 文件的树型结构,也就是展示 XML 文档结构在 Project Explorer 的 TreeView 中,整个运行结构是一个模型-视图-控制器结构(MVC)。所以为了在树形结构中展示 XML 文档结构,需要一个 XML 模型,然后通过 ContentProvider 和 LableProvider 展示出来。ContentProvider 被 CNF 框架用来决定 CNF 视图 TreeView 中每一个节点的孩子节点。本文例子中 XML 模型使用 Eclipse WST XML 中的 XML 模型,这个模型只有在 ContentProvider 请求时才被装载。
我们的例子中 ContentProvider 是 XMLViewContentProvider,它继承了 JFaceNodeContentProvider 类,用于提供 XML 模型树结构的内容。这个类除了用于展示树型结构外,还用于监听资源变化,并且更新相应的模型和 CNF 视图,这些我们在第二篇中将详细介绍。
XMLViewContentProvider 类必须扩展 getElements(Object input),getChildren(Object parent),hasChildren(Object element) 和 getParent(Object element) 四个方法:
GetElements() 方法在得到 XML 文档节点根节点时会被调用,这里我们在 getElements() 方法中调用 getChildren() 方法来实现。
代码列表8. getElements() 方法
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
GetChildren() 方法接受的参数是一个对象(Object),该对象可能是一个 XML 的文件,也可能是一个 XML 模型的元素实例。在方法实现中,判断参数的类型,如果是 IFile 对象类型,则装载 XML 模型;如果不是 IFile 对象类型,则是 XML 模型元素实例,则返回该元素的子元素节点。
代码列表9. getChildren () 方法
public Object[] getChildren(Object parentElement) {
//如果是IFile文件实例,即XML文件的IFile实例
if (parentElement instanceof IFile) {
//装载XML模型
IDOMModel model = Helper.getModelForResource((IFile) parentElement);
EditorModelUtil.addFactoriesTo(model);
DocumentImpl ss = (DocumentImpl) model.getDocument();
Object firstElement = getElementImplFromObjectArray(getChildren(ss))[0];
if (firstElement != null) {
return getElementImplFromObjectArray(super.getChildren(firstElement));
}
}
return getElementImplFromObjectArray(super.getChildren(parentElement));
}
HasChildren() 方法接受的参数是一个对象(Object),该对象可能是一个 XML 的文件,也可能是一个 XML 模型的子节点实例。在方法实现中,判断参数的类型,如果是 IFile 对象类型,则装载 XML 模型,并判断该 XML 模型是否有子元素节点;如果不是 IFile 对象类型,则是 XML 模型元素实例,则返回该元素的是否有子元素节点。
代码列表10. hasChildren () 方法
public boolean hasChildren(Object element) {
//如果是IFile文件实例,即XML文件的IFile实例
if (element instanceof IFile) {
//装载XML模型
IDOMModel model = Helper.getModelForResource((IFile) element);
EditorModelUtil.addFactoriesTo(model);
DocumentImpl ss = (DocumentImpl) model.getDocument();
return super.hasChildren(ss);
}
return super.hasChildren(element);
}
GetParent() 方法也有两种不同的情况,如果传入的参数是文件 IFile 类型,则返回该文件的父节点。如果传入的参数是 XML 模型的元素节点,则返回该 XML 模型元素节点的父节点。
代码列表11. getParent () 方法
public Object getParent(Object element) {
//如果是IFile文件实例,即XML文件的IFile实例
if (element instanceof IFile)
return ((IResource) element).getParent();
return super.getParent(element);
}
例子中 LableProvider 是 XMLViewLabelProvider,它继承了 JFaceNodeLabelProvider 类,用于提供树结构的标题和图标。XMLViewLabelProvider 类必须扩展 getText() 和 getImage() 两个方法。
GetText() 方法显示 XML 模型元素的节点标题,显示规则为 value=element.attribute。如果元素属性中包含 id 属性,则显示 id 属性的值,如果不包含 id 属性,则显示该元素的第一个属性。
代码列表12. getText () 方法
public String getText(Object o)
{
//代码详看下载中例子
……
}
GetImage 方法用于显示 XML 模型元素的节点的图标。
代码列表13. getImage () 方法
public Image getImage(Object element)
{
if (element instanceof IFile) {
IDOMModel model = Helper.getModelForResource((IFile) element);
EditorModelUtil.addFactoriesTo(model);
DocumentImpl ss = (DocumentImpl) model.getDocument();
model.releaseFromRead();
return super.getImage(ss);
}
return super.getImage(element);
}
到这一步,我们已经定义了 XML 内容扩展,并且把该内容扩展绑定到 Project Explorer 通用 CNF 视图。运行查检,查看扩展后的 Project Explorer 的结果。
下图是 XML 文件的截图,图中灰色背影的是头元素的名称空间。该名称空间必须是 xmlns=http://www.ibm.com/developerwork 时,才能在 Project Explorer 中显示出 XML 文档的结构。
图 4. XML 文档内容
上面的 XML 文档,在 Project Explorer CNF 视图中展示结果如下所示:
图 5. 展示结果
通过这一篇文章,介绍了 CNF(Common Navigator Framework) 的基本功能以及扩展点,并通过介绍如何扩展 Project Explorer CNF 视图支持 XML 模型结构,作者一步一步演示了如何扩展一个基于 CNF 的 CNF 视图,每一步需要的技术细节。
扩展 project explorer 视图支持 XML 模型结构,第 2 部分http://blog.csdn.net/andywangcn/article/details/8267990