级别: 初级 Benoit Marchal (bmarchal@pineapplesoft.com), 顾问, Pineapplesoft
2002 年 11 月 01 日 作家和专栏写作者 Benoît Marchal 对 Eclipse 和插件 API 学习得越多,就越喜欢他所了解的内容。在本专栏文章中,Benoît 将其正在进行的项目继续放在集成 XM(基于 XML 和 XSLT 的简单内容管理和发布解决方案)和 Eclipse(一个开放源码项目,用来定义针对 Java 开发人员的下一代集成开发环境)之上。当 XM 从 IDE 启动时,他的努力得到了回报。有个意外的收获,Benoît 发现了工具箱中早已隐藏的基本 XML 编辑器!
Eclipse 是出色的集成开发环境(IDE),因为它促进了可扩展的体系结构。Eclipse 认识到现代开发需要许多技能。它不再满足于仅仅使用 Java
或HTML
或C++。现代的 IDE 需要支持所有这些语言以及其它一些语言。
为满足这一需要,Eclipse 使用插件。插件扩展了 IDE,以支持新的语言、编译器以及其它开发工具。在 Eclipse 中没有缺省语言。Eclipse 通过插件支持每种语言 - 包括 Java 语言。因此,倘若您编写适当的插件,那么 Eclipse 所能支持的语言数目是没有限制的。正如我通过此项目发现的,编写 Eclipse 插件并不是那么困难。要从 Eclipse 启动 XM,我只需编写两个类。对于开发人员,这是个特棒的消息。根据我的经验,无论供应商在工具箱中包括了什么 - 不管它是自产的实用程序、第三方工具或是软件管理 - 我始终需要其它工具。
任何 IDE 都可以启动外部工具,但是这往往意味着我必须在 IDE 和外部工具之间来回切换。我确信使用 Eclipse 可以将工具集成到 IDE 中并且有条理地组织每样东西。要学习如何编写这样的插件,请继续阅读。
向 Eclipse 开发团队脱帽致敬 - 我开始非常喜欢你们的工作了。
XM 背景知识
距离我上次在本专栏讨论 XM 已有很长时间了。从我所收到的邮件来看,一些读者对它究竟是什么产生了误解。XM 是用于发布使用 XML 和 XSLT 的静态网站的低成本解决方案。它和 WebSphere Portal 或 Cocoon 并非竞争关系。我发起 XM 是因为我发现用于 XML 发布的简单解决方案很缺乏,对此深感不便。
大多数发布项目最后都发展到同一个终点:它们需要应用程序服务器,但并不是每个项目一开始就有数量巨大的文档。XM 旨在在早期阶段帮助您。例如,撰写这篇专栏文章时,我刚访问一个 XM 新用户后回来。这个组织大约有 400 个极少更新的简单 XML 文档。它们的样式表长度少于 100 行。那么,仅仅为了发布这 400 个文档而获取并安装成熟的应用程序服务器有意义吗?我认为没什么意义,而他们也持相同观点。目前,每个晚上批处理运行 XM 更有意义。显然,当他们更熟悉 XML 时,他们会想要功能更强大的解决方案,但是现在 XM 满足了他们的需求,它也满足了许多小到中型网站的需求。
为 XM 编写插件
编写 Eclipse 插件时,第一步是决定扩展哪个元素。插件可以扩展导航器或概述文件、添加菜单项、替换编辑器或绘制新的控制台和状态窗口。
如图 1 所示,我决定从将新的
Run XM项添加到导航器的弹出菜单开始。我还添加了新的
XM Console窗口来显示状态消息。通过这两个添加,有可能不用离开 Eclipse 窗口就可使用 XM。
图 1. 从 Eclipse 启动 XM
这儿有个难题是确定何时显示
Run XM 菜单。在 XM 开发的较早时期,我选择了消除配置文件。遗憾的是,没剩下什么可以在上面单击鼠标右键了。因此,第一步是定义
.xmp (XM 项目)文件。配置插件以使
Run XM 和
.xmp 文件相关联。
简而言之,
.xmp 文件是带有以下四个特性的 Java 特性文件,这四个特性对应于命令行参数(请参阅
清单 1,以获取示例):
-
source :指向源目录(缺省值:
src )
-
publish :指向发布或目标目录(缺省值:
publish )
-
rules :指向样式表目录(缺省值:
rules )
-
build :为
true 或
false 值 -
true 强制执行重新构建,其中,XM 处理每个文件(缺省值:
false )
清单 1. 样本 .xmp 文件
# this publishes the ananas.org site
source=src
publish=ananas-html
rules=style-sheet
build=false
|
XM 控制台
XM 控制台是在
MessengerView 中实现的。它是一个
部件(part)- 工作台中“窗口”的 Eclipse 术语。因为
上个月的专栏文章包括了用作部件的插件,因此代码对您来说应该很熟悉了,所以我将只是重新生成摘录(请查阅
参考资料以下载所有该代码)。如果您没有阅读上一篇专栏文章(该文件简介了 Eclipse 插件体系结构),那么我建议您现在就回头阅读它。
本文和上个月的专栏文章最显著的差异是使用 SWT 表代替了标签。该表有两列,其中第一列包含了消息状态(出错、警告等),第二列包含了消息本身。对于一列消息来说,表很方便,因为它允许用户用滚动栏上下翻页。
在 SWT 中,表是
org.eclipse.swt.widgets.Table 的实例。使用
TableColumn 类定义每个列的宽度及其标题,如清单 2 所示。
清单 2. 创建 SWT 表
table = new Table(parent,SWT.SINGLE);
TableColumn column = new TableColumn(
table
,SWT.NONE);
column.setText("Status");
column.setWidth(50);
column = new TableColumn(
table
,SWT.NONE);
column.setText("Message");
column.setWidth(500);
table.setHeaderVisible(true);
|
请注意,这里没有
addColumn() 方法。相反,您把表实例传入
TableColumn 构造器中。
TableItem 代表了表行。要对它进行初始化,将字符串数组(每列一个字符串)传递给它的
setText() 方法,如清单 3 所示。
清单 3. 添加新行
TableItem item = new TableItem(table,SWT.NONE);
String[] text = new String[2];
text[0] = type;
text[1] = t.getLocalizedMessage();
item.setText(text);
|
MessengerView 还实现了
Messenger 接口。老读者会记得
Messenger 是 XM 用来打印消息的接口。
MessengerView 只是将消息重定向到窗口。
运行 XM 菜单
弹出菜单在
XMRunner 类中实现。当用户选择菜单时,Eclipse 使用
IObjectActionDelegate 接口通知插件。
XMRunner 可在清单 4 中获得。
清单 4. 应答用户点击
package org.ananas.xm.eclipse.runner;
import java.io.*;
import java.util.*;
import org.ananas.xm.*;
import org.eclipse.ui.*;
import org.eclipse.ui.actions.*;
import org.eclipse.jface.action.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.core.resources.*;
public class XMRunner
extends ActionDelegate
implements IObjectActionDelegate
{
public static final String PLUGIN_ID =
"org.ananas.xm.eclipse.runner";
public static final String CONSOLE_ID =
"org.ananas.xm.eclipse.runner.view.Console";
private IFile selectedFile;
private IWorkbenchPart part;
public void run(IAction action)
{
try
{
if(part != null)
{
saveDirtyEditors();
MessengerView messenger = showConsole();
if(messenger != null)
{
messenger.clean();
runXM(messenger);
}
else
throw new NullPointerException("Failed.");
}
}
catch(Exception x)
{
ErrorDialog.openError(part.getSite().getShell(),
"XM","Exception.",makeStatus(x));
}
}
public void selectionChanged(IAction action,
ISelection selection)
{
selectedFile = null;
if(selection instanceof IStructuredSelection)
{
IStructuredSelection structuredSelection =
(IStructuredSelection)selection;
if(structuredSelection.size() == 1)
{
Object selectedResource =
structuredSelection.getFirstElement();
if(selectedResource instanceof IFile)
selectedFile = (IFile)selectedResource;
}
}
}
public void setActivePart(IAction action,
IWorkbenchPart targetPart)
{
part = targetPart;
}
private MessengerView showConsole()
throws PartInitException
{
IWorkbenchPage page =
part.getSite().getWorkbenchWindow().getActivePage();
MessengerView messenger = null;
if(page != null)
messenger = (MessengerView)page.showView(CONSOLE_ID);
return messenger;
}
private void runXM(Messenger messenger)
throws CoreException, IOException, XMException
{
InputStream is = selectedFile.getContents();
Properties properties = new Properties();
properties.load(is);
String rulesPath =
properties.getProperty("rules","rules"),
sourcePath =
properties.getProperty("source","src"),
publishPath =
properties.getProperty("publish","publish"),
buildString =
properties.getProperty("build","false");
boolean build =
Boolean.valueOf(buildString).booleanValue();
IResource parent = selectedFile.getParent();
if(parent != null)
{
IPath parentPath = parent.getLocation();
rulesPath =
parentPath.append(rulesPath).toOSString();
sourcePath =
parentPath.append(sourcePath).toOSString();
publishPath =
parentPath.append(publishPath).toOSString();
}
DirectoryWalker walker =
new DirectoryWalker(messenger,rulesPath,build);
walker.walk(sourcePath,publishPath);
}
private IStatus makeStatus(Exception x)
{
Throwable t = MessengerView.popThrowables(x);
if(t instanceof CoreException)
return ((CoreException)t).getStatus();
else
return new Status(IStatus.ERROR,
PLUGIN_ID,
IStatus.ERROR,
x.getMessage(),
t);
}
private void saveDirtyEditors()
{
IWorkbenchWindow window =
part.getSite().getWorkbenchWindow();
IWorkbenchWindow[] windows =
window.getWorkbench().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);
}
}
}
|
您应该把这个类当作
事件侦听器。Eclipse 工作台使用它将用户的选择转发给插件。该类从
ActionDelegate 继承 - ActionDelegate 为
IObjectActionDelegate 的大部分提供了缺省实现。当用户选择导航器中的新文件时,Eclipse 工作台调用
selectionChanged() 。当用户选择菜单时,它调用
run() 方法,了解这一点非常重要。
在
run() 中,插件保存编辑器的内容(如果用户正在编辑文件,就保存它),将 XM 控制台调到前台并启动 XM。
runXM() 和 XM 本身的
main() 方法之间的主要区别在于,
main() 从命令行获得其参数,而
runXM() 从特性文件读取这些参数。
额外时间
这样就完成了本月专栏文章要介绍的集成工作。但是请稍候,这里还有一个工具!我发现 Eclipse 提供了基本 XML 编辑器。您只须编译它。现在您编辑 XML 文档时语法会突出显现,并且可以通过 XM 发布它们。
XML 编辑器
Eclipse 的项目向导可以生成基本 XML 编辑器。过程如下:
- 从
File菜单,选择
New然后选择
Project。
- 在项目向导中,选择
Plug-in Development和
Plug-in Project。如果您没看到
Plug-in Development选项,请从 Eclipse 网站下载 Plug-in SDK。
- 单击
Next。
- 给您的项目取个名字,比如
org.ananas.eclipse.xml.editor ,然后单击
Next。
- 单击
Next以接受下一屏幕中的缺省值 -
Plug-in Project Structure。
- 确保选择了
Create a plug-in project using a code generation wizard,并指向
Plug-in with an editor(请参阅
图 2)。该向导自动生成带有语法突出显示的基本 XML 编辑器。
- 单击
Next。
- 单击
Finish接受下一屏幕中的选项 -
Plug-in Content。Eclipse 创建新项目并编写 XML 编辑器。
- 从
Project菜单,选择
Rebuild All来构建项目。
- 用该类文件生成名为
editor.jar 的 JAR 压缩文档。要从 Eclipse 创建 JAR,使用
File菜单中的
Export选项。这很重要 - 如果不生成 JAR 文件,Eclipse 就不会装入您的插件。
- 退出 Eclipse,在
workspace 目录下寻找新项目。
- 将项目目录从
workspace 复制到
plug-in 目录,然后重新启动 Eclipse。
当您双击 XML 文件时,XML 编辑器自动启动。要与更多文件相关联(比如
.xsl ),请选择
Window > Preferences > Workbench,然后选择
File Associations。
图 2. 编译隐藏的 XML 编辑器
XM 更新
当我使用 XM 时,我修正了几个错误并添加一个选项以更改文件扩展名。缺省情况下,XM 把
.html 扩展名给 HTML 文件。XML 文件得到
.xml 扩展名。有些用户可能需要将缺省值更改成
.htm 、
.shtml 、
.rss 、
.wml 或其它扩展名。如果您需要这项功能,请复制清单 5 的代码。请注意新的
rules:extension 属性。
清单 5. 更改文件扩展名
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rules="http://ananas.org/2001/XM/XSLT/Rules">
<xsl:output method="html"
rules:extension="htm"
/>
<!-- style sheet comes here -->
</xsl:stylesheet>
|
这一特性和 XM 链接管理完全集成在了一起,因此您的所有链接都保持有效。
工作将继续进行
只用了两个类就从 Eclipse 启动了 XM,这是对插件功能的证明。在我的下一篇专栏文章中,我打算编写向导插件来初始化新的 XM 项目。同时,您可以下载并测试 XM 运行器插件。您将看到通过 Eclipsee 编辑和发布网站是件很愉快的事。然而,要提醒的是:目前插件只可用于 JDK 1.4。如果需要在 JDK 1.3.x 中运行 Eclipse,您必须安装 Xalan 并相应地调整插件。
参考资料
关于作者
对本文的评价
|