内容


使用 XML

用 Eclipse 和 XM 构建项目

深入研究增量构建器

Comments

系列内容:

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

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

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

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

本文继续有关 Eclipse 和 XM 问题的讨论。如果您尚未阅读 以前的专栏文章,那么下面是对迄今为止所有专栏文章的概括:XM 最初是为“使用 XML”专栏而开发的一个简单内容管理解决方案。设计它的初衷是将它用于批处理,但是用它发布了几个网站并收到了来自用户的众多反馈之后,我意识到它需要一个更友好的界面。

我不是从头开始设计一个集成开发环境(IDE),而是求助于 Eclipse。Eclipse 是由 IBM 发起的一个开放源码项目,其目的是用 Java 编程语言构建一个 可扩展的IDE,从而能够通过插件向该 IDE 添加新的语言和功能部件。本系列文章的目的是为 XM 准备这样一个插件。

项目构建器

在前一篇专栏文章(请参阅 参考资料)中,我向 XM 插件添加了一个 New Project 向导。该向导初始化 XM 项目,包括源代码、规则以及发布目录。它还向项目添加了样本 XML 和 XSLT 文件。

此外,向导还将项目性质注册为 org.ananas.xm.eclipse.xmnature ,而该项目性质同样标识 XM 项目本身。接下来,Eclipse 将菜单项及其它界面窗口小部件(widget)关联到项目性质,然后插件将 XM 批处理注册为项目的构建器(本质上是编译器)。

在前一篇专栏文章中,我无法讨论这一构建器,因此我现在将介绍它。由于我已修复了早期版本中的错误,因此您最好下载代码的最新版本 — 即便您在构建器最初发布时下载了构建器(请参阅 参考资料以获取到最新发行版的链接)。

我还要提醒您的是,我对构建器的这一实现并不十分满意。更确切地说,它受困于当前(以批处理为中心的)XM 实现的一些限制。在本文中,我将向您演示如何能够最终克服这些限制。

IncrementalProjectBuilder

正如 Eclipse 平台的其它各方面一样,构建器是通过扩展点定义的。如同在前面几篇专栏文章(尤其是“使用 Eclipse 为 XM 构建用户界面”这篇文章(请参阅 参考资料))中所见到的那样,必须在插件清单中声明扩展点,还要提供实现类。对于构建器而言,要声明的扩展点是 org.eclipse.core.resources.builders ,而实现类则必须继承 org.eclipse.core.resources.IncrementalProjectBuilder 类。

类的名称强调了 Eclipse 期待其构建器应该具有的最重要功能之一:构建器应该能够以 增量方式重新构建项目。增量构建意味着:只有自上一次调用构建器以来被修改的文件才会被处理。显然,这样做的目的是为了加快速度。

有些 IDE 只有在开发人员请求时(如当他单击 RunDebug按钮时)才会构建项目。在这些环境中,似乎要花很多时间才能启动项目的测试版本。Eclipse 却不这样。它在后台不断地重新构建项目,试图将负载分摊到整个开发期间。只有在保存文件时它才会略微降低速度,但是,当您真正单击 RunDebug时,它的速度却很快。

可能更为重要的是,Eclipse 有助于及早捕获输入错误和其它错误。当用户保存文件时,用户会立即收到关于可能的错误的反馈。如果构建器的速度够快的话,那么这是一项极好的功能。(在使用插件时,我发现与我用过的其它一些 IDE 相比,即时反馈为我节省了不少时间。)显然,由于速度至关重要,因此构建器应该尽可能地快,免得降低用户的速度。

为了提高速度,优秀的构建器应该只重新编译用户更改的部分 — 这倒并非一定是件容易的任务。例如,当更改 Java 代码中的某个类时,所做的更改可能会影响其它类(例如,按照优化方法,当某个 final 变量的值改变时,编译器可能必须更新其它引用该变量的类)。另一个示例是 C/C++ 编译器,它需要保存预先编译的头文件以加速新类的构建。

通过持续地跟踪项目,并报告程序员对构建器所做的更改,该平台试图为重新编译这项工作提供了帮助。从理论上说,构建器应该使用这一信息来确定要重新编译哪些文件。

就 XM 而言,它有优点也有不足。其优点在于 XM 总是提供增量构建。 DirectoryWalker 是负责构建站点的类,它确保只处理那些自上一次运行以来被更改的文件。

其不足在于: DirectoryWalker 根本不是设计或用来与另一个应用程序进行集成的。目前,它只编译自己的更改列表,这个列表实际上复制了 Eclipse 的项目管理。当然,理想的解决方案应该是修改 DirectoryWalker 以利用 Eclipse 平台提供的信息。然而,我选择了一个略显逊色的解决方案。几个月来我一直都在考虑对 DirectoryWalker 更新,我决定:在弄清楚应该添加的全部功能之前,暂缓进行任何重大修改。在 本文的增量管理一节中,我将重新讨论这一主题(还将探讨 Eclipse 如何向构建器报告更改)。

这一折衷方案工作得相当好。虽然 XM 的速度并没有达到预期速度,但对于大多数正常的操作,这已经够快了。

XMBuilder

该构建器是在 XMBuilder 类中实现的,如 清单 1中所示。显然,它继承了 IncrementalProjectBuilder 。最重要的方法是 build() ,每当应该重新构建项目时,Eclipse 就会调用该方法。该方法带有三个参数:

  1. kind — 整数,取下列值之一:
    • AUTO_BUILD :如果平台检测到项目已经改变,那么构建器应该执行增量构建。
    • FULL_BUILD :如果用户(例如,通过菜单)已请求完整的重新构建,那么就实现完整的构建。
    • INCREMENTAL_BUILD :如果用户请求进行增量构建,那么就实现增量构建(但我承认:我还没有搞清楚如何让平台发送这后一种代码)。
  2. args — 构建器的一组参数。
  3. monitor — 供构建器更新用户的进度监视器。该监视器控制一个进度条和一个停止按钮。由于构建过程可能会很慢,因此构建器应该测试用户是否已取消了该操作。

您可能想知道有关(我之前讨论过的)资源更改的情况。Eclipse 有另一个接口来报告这些情况,稍后我将在 增量管理一节对此加以讨论。

build() 方法返回一个项目数组。该平台将试图跟踪这些项目中的变化,以便于构建器可以注册对子项目或任意其它项目(构建器认为该项目可能会影响自己的构建过程)感兴趣的内容。平台只是试图跟踪这些项目,尽管可能并不会成功(例如,为了节省内存,它可能会废弃一些更改)。

清单 1. XMBuilder.java
package org.ananas.xm.eclipse;
import java.io.*;
import java.util.Map;
import org.ananas.xm.*;
import org.eclipse.ui.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.resources.*;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.widgets.Display;
public class XMBuilder
extends IncrementalProjectBuilder
{
private IWorkbench workbench;
public XMBuilder()
{
workbench = PlatformUI.getWorkbench();
}
protected IProject[] build(int kind,Map args,IProgressMonitor _monitor)
{
try
{
IProject project = getProject();
if(project != null && project.isAccessible())
{
if(_monitor == null)
_monitor = new NullProgressMonitor();
saveDirtyEditors();
MessengerProxy proxy = new MessengerProxy();
MessengerView console = MessengerView.showConsole(workbench);
if(console != null)
{
console.clean();
proxy.addMessenger(console);
}
MessengerMonitor monitor =
new MessengerMonitor(_monitor,"Publish XML documents");
proxy.addMessenger(monitor);
IResource publish = runXM(proxy,kind == FULL_BUILD);
publish.refreshLocal(IResource.DEPTH_INFINITE,_monitor);
monitor.done();
}
}
catch(Exception x)
{
ErrorDialog.openError(workbench.getActiveWorkbenchWindow().getShell(),
Resources.getString("eclipse.dialogtitle"),
Resources.getString("eclipse.builderror"),
PluginTools.makeStatus(x));
}
return new IProject[0];
}
private IResource runXM(Messenger messenger,boolean build)
throws CoreException, IOException, XMException
{
IProject project = getProject();
String rulesPath =
project.getPersistentProperty(PluginConstants.RULES_PROPERTY_NAME),
sourcePath =
project.getPersistentProperty(PluginConstants.SOURCE_PROPERTY_NAME),
publishPath =
project.getPersistentProperty(PluginConstants.PUBLISH_PROPERTY_NAME);
if(rulesPath == null)
rulesPath = "rules";
if(sourcePath == null)
sourcePath = "src";
if(publishPath == null)
publishPath = "publish";
rulesPath = project.getFolder(rulesPath).getLocation().toOSString();
sourcePath = project.getFolder(sourcePath).getLocation().toOSString();
IFolder publishFolder = project.getFolder(publishPath);
publishPath = publishFolder.getLocation().toOSString();
DirectoryWalker walker = new DirectoryWalker(messenger,rulesPath,build);
walker.walk(sourcePath,publishPath);
return publishFolder;
}
private void saveDirtyEditors()
{
Display display = Display.getCurrent();
if(display == null)
display = Display.getDefault();
display.syncExec(new Runnable()
{
public void run()
{
IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
for(int i = 0;i < windows.length;i++)
{
IWorkbenchPage[] pages = windows[i].getPages();
for(int j = 0;j < pages.length;j++)
pages[j].saveAllEditors(false);
}
}
});
}
}

AUTO_BUILDINCREMENTAL_BUILD 映射到普通 XM 构建操作(它使用 DirectoryWalker 自己的增量构建器)以及将 FULL_BUILD 映射到 XM 完整的构建好象都是合乎逻辑的。

构建过程

构建器通过继承的 getProject() 方法检索要构建的项目,在试图构建之前首先进行测试,以确保该项目是可访问的。

接下来,构建器验证作为参数接收的 IProgressMonitor 接口。如果监视器丢失,那么它就创建一个 NullProgressMonitor 对象作为替代。 NullProgressMonitor 没有用户界面,它忽略进度报告。它很方便,因为它省去了在发出进度报告之前测试监视器是否为空这一工作。

构建器然后试图保存在 IDE 中打开的各种编辑器。这一代码借鉴原始的 XM 插件。我承认,我还没有确定是将它留在其中还是将它去掉。您可能会争辩说,用户不需要这种手把手的指导。但我发现在原始插件中这一功能很方便,因此我决定目前还是留着它。(您可以在 论坛中讲述 您的经验。)

下一步是初始化各种 Messenger 对象。 Messenger 是一个接口,XM 用它来向用户报告进度和错误。请参阅 MessengerMonitorMessengerProxy这两节以获取有关这一主题的更多详细信息。

最后, build() 调用 runXM() 方法,这相当于从命令行调用 XM。该方法返回一个 IResource 实例,后者指向已发布新站点的目录。 build() 方法然后通过刷新目录来终止。该方法还捕获异常,并在标准 ErrorDialog 中显示这些异常。

XM 调用

实际的 XM 调用发生在 runXM() 方法中。该方法十分类似于批处理接口中的 main() 。它从项目属性检索各种参数(源代码、规则和发布目录)。这些参数由项目向导初始化,专栏文章“Creating the project”对此进行了讨论(请参阅 参考资料)。该方法然后创建并允许释放一个 DirectoryWalker (原始的批处理)的实例,它负责重新构建网站。

MessengerMonitor

XM 的原始设计要求将用户接口与处理分隔开来。在编写插件时,这一点被证明是十分宝贵的。分隔是通过 Messenger 接口实现的, Messenger 接口定义了四个方法,XM 调用这些方法来向用户报告进度:

  • progress()
  • info()
  • warning()
  • error()

分隔是十分有效的。在“Integrating XM and Eclipse”(请参阅 参考资料)中,我编写了一个 Messenger 版本,它在工作空间上的一个视图中显示消息。

由于平台向构建器传递了一个 IProgressMonitor 实例,因此向构建器转发进度信息看起来是合乎逻辑的。 清单 2显示了 MessengerMonitor ,它是一个恰好执行这一任务的 Messenger 实现。

清单 2. MessengerMonitor.java
package org.ananas.xm.eclipse;
import java.io.File;
import org.ananas.xm.*;
import org.eclipse.core.runtime.IProgressMonitor;
public class MessengerMonitor
implements Messenger
{
private IProgressMonitor monitor;
public MessengerMonitor(IProgressMonitor monitor,String taskName)
{
this.monitor = monitor;
monitor.beginTask(taskName,IProgressMonitor.UNKNOWN);
}
public void done()
{
monitor.done();
}
public boolean progress(File sourceFile,File resultFile)
throws XMException
{
monitor.worked(IProgressMonitor.UNKNOWN);
return !monitor.isCanceled();
}
public void error(XMException e)
throws XMException
{
}
public void fatal(XMException e)
throws XMException
{
}
public void warning(XMException e)
throws XMException
{
}
public void info(String msg)
throws XMException
{
}
public void info(String pattern,Object[] arguments)
throws XMException
{
}
}

该方法还进行测试以了解用户是否取消了构建。如果 progress() 返回 false,则 DirectoryWalker 会停止。

MessengerProxy

显然, MessengerMonitorprogress() 方法中只做一些较敏感的事情。如果 XM 报告了错误,那该怎么办呢? IProgressMonitor 不提供错误消息。一种选择是打开错误框。但错误框有一个问题:当您关闭(dismiss)它们时,您将再也不能看到那些消息。在调试样式表的时候,在屏幕上保留错误列表将会更方便。因此,理想的解决方案是打开 XM 控制台,然后将错误消息路由到该控制台。

清单 3MessengerProxy 的摘录。这个类为将消息路由到不同 Messenger 实例提供了一种方便的解决方案。它实现了 Messenger 接口,并且将收到的任何消息转发给所有注册过的 Messenger — 有点象多点广播 AWT 事件。

清单 3. MessengerProxy.java 摘录
public synchronized void error(XMException e)
throws XMException
{
Iterator iterator = messengers.iterator();
XMException caught = null;
while(iterator.hasNext())
try
{
((Messenger)iterator.next()).error(e);
}
catch(XMException x)
{
caught = x;
}
if(caught != null)
throw new XMException(caught);
}

构建器使用代理将消息从 XM 发送到进度监视器和控制台。这一解决方案特别灵活,而且使得将来添加更多 Messenger (如更新状态栏的 Messenger )也很容易。

刷新目录

如同您看到的那样,平台时刻监控着项目,以确定是否需要一个重新构建,以及更新用户界面(如 Eclipse 导航程序)。当插件创建文件时,它应该使用 IResource 及其子代。这一点在前一篇专栏文章所介绍的向导中作了说明。当向导创建项目文件和目录时,它是通过 IFileIFolder 这样做的。

只要应用程序是特别为 Eclipse 设计的,这种方法就起作用。但利用诸如 XSLT 处理器之类的大多数 Java 语言库的话,这种方法的作用就不是很好。这些库通常使用 java.io 而不是使用 Eclipse 中与 java.io 对应的部分。解决方案就是让 Eclipse 从本地文件系统读取资源。顺便说一下,如果项目调用外部工具(比如本机应用程序),那么您将使用相同的技术。

XMRunner

如果您从开始就一直在学习 Eclipse 系列文章,那么您可能记得 XMRunner 类。它管理上下文(用鼠标右键单击)菜单中的 Run XM项,并且它还是 Eclipse 的原始插件。

为了利用增量构建器,已经对 XMRunner 进行了全部重写。维护两个调用 XM 的不同类好象有些浪费资源。恰恰相反,让一个类调用另一个类更有意义。

XMRunner 仍然继承 ActionDelegate 接口(以便在用户选择菜单项时得到通知)。对 run() 进行了重写,以便只请求完全构建项目。这是通过 IProject 接口上的 build() 方法实现的。

请注意 validateProjectNature() 方法。该方法确保项目是用 XM 性质注册的,以便启用特定于 XM 的菜单。为了确定 project 是否有正确的性质, validateProjectNature() 调用了 project 对象上的 hasNature() 方法。如果不具有 XM 性质,那么该方法获取一个带有当前全部项目性质的数组,然后将它们复制到一个新数组(这个新数组比原数组多一项),接下来将 XM 性质添加到新数组,然后向项目注册这个更大的数组。保留现有的项目性质是很重要的,因为项目可能是(例如)PHP 代码(一种流行的网站语言)和 XM 代码的组合。因此,它将同时具备 PHP 和 XM 性质。

诚然,如果项目没有正确的性质,那么目前就不可能调用 XMRunner (因为 Run XM菜单项是基于项目性质注册的),因此这是一个尚有争议的问题。但我期待添加一个选项来将任何项目都转换成 XM 项目,这将更容易地创建同时使用 XM 和(例如)PHP 的混合项目。

DirectoryWalker 的将来

虽然我决定不依赖 Eclipse 自己的资源管理来跟踪对项目的更改,但我却对它进行了一些研究。在这一节中,我将讨论我的发现。而且,如同我前面所解释的那样,我打算修改 DirectoryWalker 以便于更好地利用 Eclipse。

新的 DirectoryWalker

准备新的 DirectoryWalker 的第一步是分析需求。根据我自己 XM 的经验和我同事的经验,下面是可能会添加到 DirectoryWalker 新版本中的一些新功能:

  • 改进的内容生成支持:以我的经验,XM 最有用的功能之一在于它能够读取目录,然后用目录中的文件生成 XML 文档。我曾使用该功能来生成下载页面、目录(当文档跨多个不同文件时)、统计信息及更多其它内容。但是,在处理这些文件时,XM 并不十分有效。它总是重新生成它们,对于大型站点,这可能会降低处理的速度。
  • 改进的链接管理:链接管理工作得很好,但它却采用 XSLT 1.0。XSLT 2.0 将增加把一个 XML 文档拆分成几个文档的能力。大多数 XSLT 处理器已经有了模拟这一能力的扩展。在过去设计的 XM 链接管理中并未提供这一功能。
  • 支持 XInclude 或另一种将一个文档合并到另一个文档的机制: 此外,XM 理论上应该管理两个文档之间的关系,以便如果两者中有一个发生了变化就发布组合的文档。
  • 能够忽略隐藏文件
  • 能够在文档的样式表被修改后发布文档
  • 能够与外部管理管理器(如 Eclipse)连接。

您的列表上有什么呢?请花点时间在 论坛上与别人分享您的想法。

目前, DirectoryWalker 搜索目录,并在找到文件时处理这些文件。为了更好地管理文件之间的关系,我需要更改 DirectoryWalker ,让它运行两遍 — 第一遍收集有关所有文件(及其关系)的信息,第二遍处理文档并应用样式表。

增量(Delta)管理

为了更好地理解 Eclipse 如何跟踪项目的更改,我编写了 IncrementalBuilderDemo 。这个类继承了 IncrementalProjectBuilder ,因此您可以将其注册为构建器。在从 Eclipse 平台接收到资源时, IncrementalProjectBuilder 并不编译项目,而是报告对资源的更改。

和以前一样,报告开始调用 build() 。如果请求的是增量构建,控制就流向 incrementalBuild() ;否则就向控制台打印一条消息。

incrementalBuild() 检索项目的状态,并将更改打印到控制台。平台通过 IResourceDelta 接口报告这些更改。构建器然后通过调用 getDelta() 方法来检索 IResourceDelta 的实例。

令人奇怪的是, getDelta() 方法把 IProject 接口作为参数。除了当前正在构建的项目之外,构建器还可以请求其它项目的信息,这一点对于子项目尤为方便。如同我们在 XMBuilder一节所讨论的那样,构建器应该通过从 build() 方法返回对那些项目感兴趣的内容来注册它们。

IResourceDelta 接口实现了访问者模式(visitor pattern)。访问者模式是一种处理任意数据结构的简单技术。这里,数据结构是 IResourceDelta

在访问者模式中,调用方法(这里是 builder)实现了访问者接口(要实现的特定接口是 IResourceDeltaVisitor )。该数据结构接受 visitor 对象,并对数据结构中的每一项调用其 visit() 方法。在这种特定情形下, IResourceDelta 对每个文件更改调用 visit()

清单 4IncrementalBuilderDemo 。访问者是在一个内部类(称为 Visitor 比较好)中实现的。相关代码在 visit() 方法中。它把 IResourceDelta 作为参数。通过这个类,您可以访问有关更改的信息,例如,更改是新建文件、删除还是文件更改。演示只是将数据打印到控制台。

清单 4. IncrementalBuilderDemo.java
package org.ananas.xm.eclipse;
import java.io.*;
import java.util.Map;
import org.ananas.xm.*;
import org.eclipse.ui.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.resources.*;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.widgets.Display;
public class IncrementalBuilderDemo
extends IncrementalProjectBuilder
{
private IWorkbench workbench;
private class Visitor
implements IResourceDeltaVisitor
{
Messenger messenger;
public Visitor(Messenger messenger)
{
this.messenger = messenger;
}
public boolean visit(IResourceDelta delta)
throws CoreException
{
try
{
Object[] path = new Object[]
{
delta.getResource().getProjectRelativePath().toOSString(),
};
switch(delta.getKind())
{
case IResourceDelta.ADDED:
messenger.info("Resource {0} added.",path);
break;
case IResourceDelta.REMOVED:
messenger.info("Resource {0} removed.",path);
break;
case IResourceDelta.CHANGED:
messenger.info("Resource {0} changed.",path);
break;
default:
messenger.fatal(new XMException("Unknown delta type"));
}
return true;
}
catch(XMException x)
{
throw new CoreException(PluginTools.makeStatus(x));
}
}
}
public IncrementalBuilderDemo()
{
workbench = PlatformUI.getWorkbench();
}
protected IProject[] build(int kind,Map args,IProgressMonitor _monitor)
{
try
{
IProject project = getProject();
if(project != null && project.isAccessible())
{
if(_monitor == null)
_monitor = new NullProgressMonitor();
MessengerProxy proxy = new MessengerProxy();
MessengerView console = MessengerView.showConsole(workbench);
if(console != null)
{
console.clean();
proxy.addMessenger(console);
}
MessengerMonitor monitor =
new MessengerMonitor(_monitor,"Publish XML documents");
proxy.addMessenger(monitor);
switch(kind)
{
case INCREMENTAL_BUILD:
proxy.info("INCREMENTAL_BUILD requested");
incrementalBuild(proxy);
break;
case AUTO_BUILD:
proxy.info("AUTO_BUILD requested");
incrementalBuild(proxy);
break;
case FULL_BUILD:
proxy.info("FULL_BUILD requested");
break;
default:
proxy.fatal(new XMException("unknown build kind requested"));
}
monitor.done();
}
}
catch(Exception x)
{
ErrorDialog.openError(workbench.getActiveWorkbenchWindow().getShell(),
Resources.getString("eclipse.dialogtitle"),
Resources.getString("eclipse.builderror"),
PluginTools.makeStatus(x));
}
return new IProject[0];
}
private void incrementalBuild(Messenger messenger)
throws CoreException, IOException, XMException
{
IResourceDelta delta = getDelta(getProject());
if(delta != null)
delta.accept(new Visitor(messenger));
else
messenger.info("Delta is empty (null)");
}
}

测试 IncrementalBuilderDemo 很容易 — 只需更新 plugin.xml ,以便于将构建器的类设置为 IncrementalBuilderDemo ,如 清单 5中所示。运行 Eclipse 并尝试修改 XM 项目中的资源。XM 控制台应该会报告您所做的更改。

清单 5. plugin.xml 摘录
<extension id="xmbuilder"
name="XM Builder"
point="org.eclipse.core.resources.builders">
<builder>
<run class="org.ananas.xm.eclipse.IncrementalBuilderDemo"/>
</builder>
</extension>

对 Eclipse 的评判

在前四篇专栏文章中,我介绍了大量关于如何编写 Eclipse 插件的知识。如果必须总结一下我在编写插件方面的经验的话,我会说:Eclipse 是一个非常有趣的平台 — 它提供了精心设计的 API — 但仍然缺乏文档。

虽然十分严重,相对较新项目仍将存在 Eclipse 文档不足这一问题。但是,我希望本系列文章会有助于解决这一问题。


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
  • 参与有关本文的 论坛。(您还可以单击文章顶部或底部的 讨论来访问论坛。)
  • 了解更多关于 Eclipse的知识,这是一项旨在开发 IDE 框架的开放源码成果。它由 IBM 发起。
  • 请阅读“ Getting started with the Eclipse Platform”,这篇文章向您演示了如何安装和使用 Eclipse 插件( developerWorks,2002 年 11 月)。
  • 查阅“ 使用 Eclipse 为 XM 构建用户界面”这篇文章,它讨论了 Eclipse 插件的体系结构( developerWorks,2002 年 10 月)。
  • 要获得“Working XML”专栏文章的完整清单,请访问 汇总页面
  • 请阅读 Uche Ogbuji 的“ EXSLT 实例”,作者在文中向您传授了 EXSLT 的基础知识,EXSLT 是对 XSLT 的很好补充。XM 用户应当会对 EXSLT 非常感兴趣( developerWorks,2003 年 2 月)。
  • 看一看 Eclipse 插件是如何致力于构建大量的插件的。
  • 请研究 Improve XSLT plug-in,它是另外一种将 XSLT 与 Eclipse 结合在一起的插件。
  • 还请查阅 Stylo Plume,它是另一项试图将 XSLT 与 Eclipse 合并的工作。
  • developerWorks XML 专区上找到更多 XML 参考资料。
  • 了解如何才能成为一名 IBM 认证的 XML 及相关技术开发人员

评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=21307
ArticleTitle=使用 XML: 用 Eclipse 和 XM 构建项目
publish-date=03012003