内容


使用 XML

使用 Eclipse 为 XM 构建用户界面

根据经验和用户反馈进行改进

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 使用 XML

敬请期待该系列的后续内容。

此内容是该系列的一部分:使用 XML

敬请期待该系列的后续内容。

在前一篇专栏文章中结束了对 XI 的讨论后,我觉得是重新讨论 XM 的时候了,这是一个简单的 XML 内容管理和发布解决方案,本专栏的第一篇文章就是对它的介绍。在去年,我已将 XM 用于了多个项目,而且也收到了读者的反馈。在那一年中,我把关于哪些地方应当改进的建议收集起来。在本文中,我将向您演示如何为 XM 创建用户界面。我选择使用 Eclipse(它是一个开发源码项目)来构建一个可修改的编程环境。相信对于任何想要成为 Eclipse 开发人员的读者,即使他们对 XML 不感兴趣,本文都会为他们提供有用的信息。

工作区

以下是我对 XM 的理解以及应如何开发它:用户界面是最紧急的问题。正如上个月的专栏文章中提到的,我意识到我犯了一个错误:XM 在发行时没有某种图形用户界面。这个决定被证明不符合用户友好特性(如无需配置文件)。

目录遍历也需要改进。一次处理一个文件的简单解决方案限制了管理链接的能力,并使内容生成(当 XM 自动编译文件列表时)比实际需要的更复杂。我对那个方面的改进也有想法。

其它有用的改进将包括以下能力:

  • 发布 JSP 或 ASP 页面(生成包括 Java 或 Visual Basic 脚本的页面比较容易,但对文件扩展名还没有相应的解决方案)
  • 分割或分组文件(最近,我在发布一个图片库时偶然碰到了这个局限性)
  • 自动 FTP

最后也是最重要一点:用户抱怨缺少用户文档。尽管我无法在一篇文章中解决所有这些问题,但通过一次解决一个问题,我将尽力把它们都解决好。在接下来的几篇文章中,我将继续解决用户界面问题。

是否采用 IDE

起初,我考虑过使用上个月为 XI 引入的用户界面的变体:一个具有拖放能力的简单窗口。然而,我意识到同时提供编辑文件的能力会更有价值。随着特性列表的增加,我最终决定采用集成开发环境(IDE)。

从头构建 IDE 是一个庞大的工作,幸运的是,有两个 Java 开放源码项目可以帮忙 ― netBeans 和 Eclipse。这两个项目都旨在开发灵活的 Java IDE,而且两个项目都从普遍的业界支持中受益。它们最明显的区别在于:netBeans 基于 Swing,而 Eclipse 则构建在 Standard Widget Toolkit(SWT)之上。如果您阅读过本专栏以前的文章,您就知道我不是 Swing 的支持者。

因为我具有一些使用 SWT 的经验,所以我知道 Swing 的表现很好。的确,Swing 和 SWT 都旨在提供一组丰富的、可移植的 Java 窗口小控件。Swing 通过用 Java 描绘所有的窗口小控件(包括按钮、下拉列表和树型视图)取得可移植性。这个表面上很棒的想法保证了不受限制的可移植性,但实际上它并没有实现。Swing 窗口小控件的行为不象其本机同类,而且,在我的经验中,这会使用户感到困惑。

相反,SWT 尽可能地依靠本机窗口小控件。按钮和下拉列表是平台本机的。仅当没有本机窗口小控件时,SWT 才可能用 Java 描绘它们。例如,SWT 在 Windows 平台上使用本机的树型视图,但 Unix 平台没有本机的树型视图。因此,SWT 在 Unix 上描绘自己的树型视图。结果是鱼和熊掌兼得:良好的可移植性(除了 Macintosh,SWT 已经被移植到那些最流行的平台 ― 而且 Mac 移植正在开发中)、良好的性能和用户满意度。我只希望 SWT 能被合并到核心 Java 平台中。

Eclipse 插件

概括地说,Eclipse 是 Emacs(流行的程序员的编辑器)的现代版本。Emacs 之所以流行是因为:它几乎针对世界上所有的编程语言进行了定制。然而,尽管 Emacs 确实名副其实,但它的用户界面是从过去从未适应 GUI 的时代遗留下来的。Eclipse 的目标是与 Emacs 一样灵活,同时提供更现代的用户界面。图 1 显示了主 Eclipse 窗口。

图 1. 主 Eclipse 窗口
Eclipse
Eclipse

另外,Eclipse 不止是一个编辑器。这种高度模块化和可扩展的 IDE 能够集成开发人员需要的所有工具。它提供项目向导、一个集成的编译器、一个调试器和其它工具。标准分发版专为 Java 定制,但通过插件也可以支持其它语言(如 C/C++ 甚至 COBOL),这些插件保证了可移植性。(事实上,Eclipse IDE 使用插件 ― 包括 Java 编辑器、编译器、项目向导和类浏览器 ― 来创建您看到的大部分事物。我可以轻松地为 XM 编写类似的插件来组成一个 XM IDE。)

理解 Eclipse

Eclipse 是我所用过的最有雄心壮志的编辑器 API。如果您以前使用过定制的程序员编辑器,那么 Eclipse API 的应用之广可能会让您大吃一惊。遗憾的是,我发现可用的文档并非总是与极佳的工具相称。尽管有几篇好的文章描述过如何为 Eclipse 编写代码,但大多数文章都假定读者对这个平台很熟悉。特别是,它们没有对潜在的缺陷提出警告。幸运的是,本文(以及随后的文章)会帮助解决这个问题。

我将 API 大致分为两个部分:平台 API 和编辑器 API。平台 API 处理插件。插件是 Java 库,平台在响应用户请求(例如,当用户打开一个 Java 类文件时,运行时装入 Java 编辑器)或为其它插件提供服务(例如,SWT ― 窗口小控件工具箱 ― 就是一个插件)时装入插件。

插件实现编辑器的大部分(即您在屏幕上看到的大部分),这意味着:

  • 平台的任何方面都可以通过插件来定制
  • 编辑器 API 是一组插件

要再次注意的是,这个结构与操作系统类似:内核提供最基本的服务,而通过库来实现其它大多数服务。

平台 API

让我们从平台 API 开始。对于 Eclipse,插件由一个 Java 库和一个描述该插件的 清单组成。清单是一个名为 plugin.xml 的 XML 文件。

在启动时,平台装入所有已安装的插件的清单,然后编译一个注册表。在需要某些插件(如当用户编辑文件时)之前,它并不装入插件本身。已安装但未使用的插件对性能的影响最小。例如,在启动时装入核心用户界面插件以描绘主窗口。如果用户装入某个 Java 类,平台会在那时(也只在那时)装入 Java 编辑器插件。

如果插件中的某个类继承 Plugin 抽象类,则运行时在装入和卸载插件时会调用它的 startup()shutdown() 方法。此时,平台可以初始化插件或释放资源。大多数插件不需要这样的类。

第二个重要的概念是 扩展点(extension point)。可以把扩展点看作引发插件被装入的事件。插件将重要的扩展点注册到它的清单中。当事件触发(再次说明:它们不是真正的事件,只是我觉得将它们看作事件会有帮助)时,平台装入插件来处理它。例如,当有 Java 文档装入时,Java 编辑器插件注册扩展点。

编辑器 API

除了平台以外,Eclipse 的大部分是作为插件分发的。您可以把这些插件称为 系统插件,因为它们是 Eclipse 的操作所必需的。这些系统插件有自己的 API,常规插件(例如您可能编写的插件)可以调用它们。例如,在下一节中,我编写了一个用于显示信息的插件。为了在屏幕上描绘自身以及与其它窗口相互作用,我的插件会调用系统插件。(这些概念在下一节将变得更清楚。)

标识符

Eclipse API 的最后一个方面(给我造成了许多问题): 标识符。插件和扩展点有标识符,平台用它们来管理插件和扩展点。标识符是唯一的字符串。到目前为止,一切正常。之所以出现混淆是因为:Eclipse 用与构造 Java 包名称相同的规则构造它的标识符。

例如,编辑器定义一个名为 org.eclipse.ui.views 的扩展点(我将在下一节用到它)。因为 Eclipse 是用 Java 编写的,所以扩展点是通过名为 org.eclipse.ui.IViewPart 的接口实现的。我承认我花了较长的时间才弄清一个是标识符,另一个是接口。在本文中有几个以 org.eclipse.ui.views 开头的包,它们会使您更加迷惑!

为了避免这种困惑,请记住这一点:标识符出现在清单( plugin.xml )中。正如其名称所表达的含义,运行时使用它们来标识元素。然而,Java 类(或接口,其名称可能与标识符相似)实现了大多数值得注意的概念。

编写插件

我通过试验取得的学习效果最好,因此,为了更好地理解 Eclipse 平台(和前两节中讨论到的方面),我编写了一个插件。正如您将看到的那样,它没有很多功能,但它对 Eclipse 文档的解释被证明是非常重要的。请参阅 参考资料以下载该代码的可用版本。

View 插件

我创建的 View 插件(如图 2 所示)用 Eclipse 行话定义了一个视图。视图是显示某些信息的窗口。在本例中,插件显示了在导航窗口中选中的文件名。坦白说,它除了作为学习演练之外别无他用,但它确实值得略加关注 ― 它是我花了几天时间搜寻文档的成果!

图 2. Eclipse 中的插件
插件
插件

Java 接口中的视图

象 Eclipse 中任何其它事物一样,视图是用系统插件声明的。我的插件通过 org.eclipse.ui.views 扩展点注册它的视图。如前所述,请注意 org.eclipse.ui.views 是一个标识符而不是 Java 类。相应的 Java 接口是 org.eclipse.ui.IViewPart

清单 1 包含 TestView 类中的视图实现,它继承了 ViewPartViewPart 类代表 IViewPart 接口的缺省实现。我本可以实现 IViewPart ,但 ViewPart 节省了一些编码工作。

IViewPart 中定义的 createPartControl() 方法创建了该视图的用户界面。当屏幕上出现窗口时,编辑器就调用该方法。对于 TestView ,用户界面只是一个标号。

TestView 还实现了 ISelectionListener 接口。该接口也由 Eclipse 编辑器定义,负责侦听选择事件。当用户选择另一个文档时,导航视图( 图 2中左边的屏幕上)触发该事件。当它拦截事件时,插件检索文件名并更改其标号。

清单 1. ViewTest.java
package org.ananas.test.plugin;
import org.eclipse.ui.*;
import org.eclipse.swt.*;
import org.eclipse.ui.part.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.ui.texteditor.*;
import org.eclipse.core.resources.*;
/**
 * @author Benoit Marchal
 */
public class TestView
   
        
        extends ViewPart
   implements ISelectionListener
        
        
{
   private Label label;
   public TestView()
   {
   	  super();
   }
   public void createPartControl(Composite parent)
   {
      label = new Label(parent,SWT.WRAP);
      label.setText("None.");
      getViewSite().getPage().addSelectionListener(this);
      selectionChanged(null,getSite().getPage().getSelection());
   }
   public void setFocus()
   {
   	  label.setFocus();   // not very useful...
   }
   public void selectionChanged(IWorkbenchPart part,
                                ISelection selection)
   {
      if(selection != null
         && selection instanceof IStructuredSelection)
      {
         IStructuredSelection structured =
            (IStructuredSelection)selection;
         Object o = structured.getFirstElement();
         if(o != null
            && o instanceof IFile)
         {
         	IFile file = (IFile)o;
            label.setText("From navigator:" + file.getName());
            return;
         }
      }
      label.setText("None.");
   }
}

这个类的少许附加注释: Label 是一个 SWT 窗口小控件,它实现一个不可编辑的文本区。编辑器 API 还定义了 ISelectionListenerIWorkbenchPartISelection 和其它“I”接口。请注意这些接口和扩展点之间的区别。扩展点控制插件装入的时机。相反, ISelectionListener 和其它接口在装入插件 之后才开始活动。

清单

清单 2 是清单,也称为 plugin.xml 。让我们逐行查看它。 plugin 标记声明插件本身,并带有以下属性:

  • id 是插件标识符。此外,这不是一个类名,但象构造 Java 软件包名称一样构造它以保证唯一性。
  • name 是便于人们记忆的名称。
  • version 是插件版本。
  • provider 是插件的开发人员。
  • class 是插件类。不要把这个类与视图混淆。平台调用这个类来执行初始化和清理工作。大多数插件不需要插件类,而且清单 2 中确实没有这个类。
清单 2. plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin id="org.ananas.eclipse.test"
        name="Test Plugin"
        version="1.0.0"
        provider-name="ananas.org"
        class="">
   <runtime>
      <library name="test-plugin.jar"/>
   </runtime>
   <requires>
      <import plugin="org.eclipse.core.boot"/>
      <import plugin="org.eclipse.core.runtime"/>
      <import plugin="org.eclipse.core.resources"/>
      <import plugin="org.eclipse.swt"/>
      <import plugin="org.eclipse.ui"/>
   </requires>
   <extension point="org.eclipse.ui.views">
      <category name="ananas.org"
                id="org.ananas.eclipse.view">
      </category>
      <view
            name="Name"
            icon="icons/default.gif"
            category="org.ananas.eclipse.view"
            class="org.ananas.test.plugin.TestView"
            id="org.ananas.eclipse.view.test">
      </view>
   </extension>
</plugin>

接下来是 runtime 标记,它指向一个或多个 JAR 文件。 runtime 之后是带有 import 语句的 requires 标记。这些是在该插件之前必须装入到内存的插件。清单 2 引用不同的系统插件,如 SWT、基本用户界面和其它插件。

最后,清单声明了它关注的扩展点。清单 2 注册视图,声明一个名为 ananas.org 的新的视图类别,然后注册我们的视图。

测试以及将来的工作

要测试该插件,请下载 Eclipse 2.01(我没有用过 Eclipse 的其它版本对插件进行测试),然后下载测试插件或编译在本文中呈现的代码。将一切东西都放在 Eclipse 下的 plugins 目录中,然后完成以下步骤:

  • 启动 Eclipse
  • 从 Window 菜单选择 Show View
  • 选择 Other
  • 展开 ananas.org类别(我们在清单中定义的类别),然后选择 Name(我们的插件,清单中有定义)。平台触发这个扩展点,这会装入我们的插件并打开一个新的 Name窗口。

如您所见,这只是开始。我相信 Eclipse 提供了一个可在其上构建 UI 的、有前景的框架。由于它目前缺少文档,所以学习它花的时间比我期望的要多。在即将面世的文章中,我将为 XM 编写更有用的插件。

我希望本系列文章被证明不仅对 XML 开发人员有帮助,而且对任何对 Eclipse 平台感兴趣的读者都有帮助。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=22263
ArticleTitle=使用 XML: 使用 Eclipse 为 XM 构建用户界面
publish-date=10012002