扩展 project explorer 视图支持 XML 模型结构,第 1 部分

来源:互联网 发布:算法设计举例 编辑:程序博客网 时间:2024/05/16 05:57

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 文档结构,这种方法有一定的缺点:

  1. 引入了一个新的视图,要使用这个新的视图可能还需要定义一个 Perspective,这就不能和已有的 J2EE Perspective 集成。
  2. 重新开发一系列已有的 J2EE Perspective 中 Project Explorer 视图已有的功能,没有达到最大化可重用的效果

二是:扩展已有的 Project Explorer CNF 视图,使得这个视图能够支持 XML 文档结构的树型展示,并且支持对 XML 文档进行菜单操作。这种方法避免了上面方法的缺点,文章中选用的也是这种方法。

根据需求的不同,可以选用上面两种方法中的任一种,文章选用的是对已有的 CNF 视图进行扩展,但文章中用到的原理同样适用于创建一个新的 CNF 视图。扩展后的 Project Explorer CNF 视图支持 XML 文档结构的展示,以及一系列的菜单操作。其界面效果如 下图的右侧所示:


图 1. XML CNF 视图效果
XML CNF 视图效果

CNF 背景

CNF 框架原来是 RAD(IBM Rational Application Developer) v6.0 产品的一部分,后来贡献给开源社区,并在 Eclipse3.2 中使用了该框架。该框架通过 org.eclipse.ui.navigator 插件实现并引入,J2EE Perspective 中的 project explorer 视图就是基于该框架的一个 CNF 视图。通过该框架,允许开发者开发一个 CNF 视图或扩展一个已经存在的 CNF 视图,为一个 CNF 视图贡献内容(节点)、动作(菜单等)、过滤器、以及其他功能。CNF 框架有以下作用:

  1. 集成视图,该框架提供了一套通用机制和架构,允许开发人员很方便的进行 CNF 视图的开发、集成和扩展。
  2. 提高重用性,基于 CNF 框架可以扩展视图,这样就使得用户可以重用已有的 CNF 视图内容和操作,而不用进行重新的开发。另外用户可以开发自己的 CNF 视图,开发过程中可以重用大量已有的 CNF 视图内容,比如 Resource, Java 等视图中定义的内容,达到高重用性的目的。
  3. 很高的灵活性,根据 CNF 框架,不同软件版本功能上的改进可以采取更灵活的方式进行,新的功能、新的菜单操作、新的展示内容可以很方便的集成进已有的 CNF 视图中。

下面将一步一步介绍如何扩展已有的 Project Explorer CNF 视图以支持对 XML 模型结构的展现和操作。

扩展 Project Explorer CNF 视图

扩展已有的 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 操作需要用到的插件。

扩展 Project Explorer CNF 视图

如果用户是需要创建一个新的 CNF 视图,而不是想对已有的通用 CNF 视图 进行扩展,该用户需要创建新的 Eclipse 视图 ViewPart,下面给出自定义视图的步骤,供需要创建自定义 CNF 视图的用户参考。

创建视图,即扩展 Eclipse 的视图扩展点,具体步骤如下:

用 plug-in manifest editor 打开 META-INF->MANIFEST.MF 文件,在 Extensions 选项卡中:

  • 单击 Add
  • 在 Extension points 选项卡下面,选择 org.eclipse.ui.views
  • 创建类别:
    1. 右键单击 org.eclipse.ui.views 扩展
    2. 选择 New > Category
    3. 将ID设为 com.ibm.developerwork.xmlnavigator.xmlnvcategory
    4. 将名称设为 xml navigator category
  • 创建视图部件:
    1. 右键单击 org.eclipse.ui.views 扩展
    2. 选择 New > View
    3. 将 ID 设为com.ibm.developerwork.xmlnavigator.xmlnvview
    4. 将名称设为 XML Navigator View
    5. 将类设为 org.eclipse.ui.navigator.CommonNavigator
    6. 将类别设为 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。

定义 XML 模型内容扩展并关联 CNF 视图

要在一个CNF视图中展示XML模型内容,需要做两个步骤,首先定义XML模型内容扩展,该内容扩展的作用是展示XML文档结构内容;然后关联该扩展点到相应的通用 CNF视图,意味着XML文档结构的内容可以展现在被关联的通用 CNF视图中。

定义XML模型内容扩展

定义完 CNF视图扩展后,就需要定义内容扩展( content extension),并且把内容扩展和 CNF视图扩展点绑定,这样该视图扩展点对应的视图就可以显示相应的内容扩展中的内容。这一小节将介绍如何定义XML文件的内容扩展,该内容扩展允许在Project Explorer中展开一部分XML文件,并且显示其中的一些属性信息。这里只允许展开一部分XML文件是因为我们对XML文件作了一定的过滤,并不是所有的XML文件都可以被XML内容扩展调用,并能够展开内容,例子中只有符合一定条件的XML文件才可以被展开。下面是我们定义的XML内容扩展:


代码列表 2. XML内容扩
                <extension point="org.eclipse.ui.navigator.navigatorContent"><navigatorContentactiveByDefault="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 内容扩展的参数的含义:

  1. activeByDefault,表示在 eclipse 运行的时候,这个内容扩展是否默认是被激活的,也就是在上图的内容扩展列表中默认是处于选中状态的。XML 内容扩展设置为 true,表示默认情况下该内容扩展是激活的。
  2. Icon,设置显示在 Project Explorer->Customize View->Content 列表中的内容扩展对应的图标,如上图例子中 XML 内容扩展所示的 。
  3. Priority,该属性有多个用途,其中最重要的用途是决定在 CNF 视图中显示节点的相对位置。Priority 级别越高越靠近视图的顶部,级别越低就越靠近视图的底部。
  4. Id, 用于表示此内容扩展,这个 Id 将被 org.eclipse.ui.navigator.viewer 扩展点所引用,用于在 CNF 视图中显示这个Id代表的内容扩展的内容。
  5. 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"><propertyTesterid="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 调用,并展开其文档内容:


代码列表5. 验证逻辑

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;//被验证的资源文件的后缀为XMLif (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 内容扩展到相应的 CNF 视图展

定义完 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 对应的类。下面一节中进行详细介绍。

XML 文档结构

在 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 文档内容

上面的 XML 文档,在 Project Explorer CNF 视图中展示结果如下所示:


图 5. 展示结果
展示结果

小结

通过这一篇文章,介绍了 CNF(Common Navigator Framework) 的基本功能以及扩展点,并通过介绍如何扩展 Project Explorer CNF 视图支持 XML 模型结构,作者一步一步演示了如何扩展一个基于 CNF 的 CNF 视图,每一步需要的技术细节。


参考资料

  • Eclipse 网站

  • http://scribbledideas.blogspot.com/

  • 构建 Eclipse 插件在 EMF 模型中浏览内容

扩展 project explorer 视图支持 XML 模型结构,第 2 部分
http://blog.csdn.net/andywangcn/article/details/8267990
	
				
		
原创粉丝点击