站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Eclipse 专题使用 XML: 使用 XML:集成 XM 和 Eclipse

使用 XML: 使用 XML:集成 XM 和 Eclipse

摘要: 作家和专栏写作者 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 满足了他们的需求,它也满足了许多小到中型网站的需求。


<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>

为 XM 编写插件

编写 Eclipse 插件时,第一步是决定扩展哪个元素。插件可以扩展导航器或概述文件、添加菜单项、替换编辑器或绘制新的控制台和状态窗口。

如图 1 所示,我决定从将新的 Run XM项添加到导航器的弹出菜单开始。我还添加了新的 XM Console窗口来显示状态消息。通过这两个添加,有可能不用离开 Eclipse 窗口就可使用 XM。


图 1. 从 Eclipse 启动 XM

这儿有个难题是确定何时显示 Run XM 菜单。在 XM 开发的较早时期,我选择了消除配置文件。遗憾的是,没剩下什么可以在上面单击鼠标右键了。因此,第一步是定义 .xmp (XM 项目)文件。配置插件以使 Run XM.xmp 文件相关联。

简而言之, .xmp 文件是带有以下四个特性的 Java 特性文件,这四个特性对应于命令行参数(请参阅 清单 1,以获取示例):


清单 1. 样本 .xmp 文件
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

# this publishes the ananas.org site
source=src
publish=ananas-html
rules=style-sheet
build=false
</td></tr></table>

XM 控制台

XM 控制台是在 MessengerView 中实现的。它是一个 部件(part)- 工作台中“窗口”的 Eclipse 术语。因为 上个月的专栏文章包括了用作部件的插件,因此代码对您来说应该很熟悉了,所以我将只是重新生成摘录(请查阅 参考资料以下载所有该代码)。如果您没有阅读上一篇专栏文章(该文件简介了 Eclipse 插件体系结构),那么我建议您现在就回头阅读它。

本文和上个月的专栏文章最显著的差异是使用 SWT 表代替了标签。该表有两列,其中第一列包含了消息状态(出错、警告等),第二列包含了消息本身。对于一列消息来说,表很方便,因为它允许用户用滚动栏上下翻页。

在 SWT 中,表是 org.eclipse.swt.widgets.Table 的实例。使用 TableColumn 类定义每个列的宽度及其标题,如清单 2 所示。


清单 2. 创建 SWT 表
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

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);
     
      
</td></tr></table>

请注意,这里没有 addColumn() 方法。相反,您把表实例传入 TableColumn 构造器中。 TableItem 代表了表行。要对它进行初始化,将字符串数组(每列一个字符串)传递给它的 setText() 方法,如清单 3 所示。


清单 3. 添加新行
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

TableItem item = new TableItem(table,SWT.NONE);
String[] text = new String[2];
text[0] = type;
text[1] = t.getLocalizedMessage();
item.setText(text);
</td></tr></table>

MessengerView 还实现了 Messenger 接口。老读者会记得 Messenger 是 XM 用来打印消息的接口。 MessengerView 只是将消息重定向到窗口。

运行 XM 菜单

弹出菜单在 XMRunner 类中实现。当用户选择菜单时,Eclipse 使用 IObjectActionDelegate 接口通知插件。 XMRunner 可在清单 4 中获得。


清单 4. 应答用户点击
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

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);
      }
   }
}

     
      
</td></tr></table>

您应该把这个类当作 事件侦听器。Eclipse 工作台使用它将用户的选择转发给插件。该类从 ActionDelegate 继承 - ActionDelegate 为 IObjectActionDelegate 的大部分提供了缺省实现。当用户选择导航器中的新文件时,Eclipse 工作台调用 selectionChanged() 。当用户选择菜单时,它调用 run() 方法,了解这一点非常重要。

run() 中,插件保存编辑器的内容(如果用户正在编辑文件,就保存它),将 XM 控制台调到前台并启动 XM。 runXM() 和 XM 本身的 main() 方法之间的主要区别在于, main() 从命令行获得其参数,而 runXM() 从特性文件读取这些参数。


<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>

额外时间

这样就完成了本月专栏文章要介绍的集成工作。但是请稍候,这里还有一个工具!我发现 Eclipse 提供了基本 XML 编辑器。您只须编译它。现在您编辑 XML 文档时语法会突出显现,并且可以通过 XM 发布它们。

XML 编辑器

Eclipse 的项目向导可以生成基本 XML 编辑器。过程如下:

  1. File菜单,选择 New然后选择 Project
  2. 在项目向导中,选择 Plug-in DevelopmentPlug-in Project。如果您没看到 Plug-in Development选项,请从 Eclipse 网站下载 Plug-in SDK。
  3. 单击 Next
  4. 给您的项目取个名字,比如 org.ananas.eclipse.xml.editor ,然后单击 Next
  5. 单击 Next以接受下一屏幕中的缺省值 - Plug-in Project Structure
  6. 确保选择了 Create a plug-in project using a code generation wizard,并指向 Plug-in with an editor(请参阅 图 2)。该向导自动生成带有语法突出显示的基本 XML 编辑器。
  7. 单击 Next
  8. 单击 Finish接受下一屏幕中的选项 - Plug-in Content。Eclipse 创建新项目并编写 XML 编辑器。
  9. Project菜单,选择 Rebuild All来构建项目。
  10. 用该类文件生成名为 editor.jar 的 JAR 压缩文档。要从 Eclipse 创建 JAR,使用 File菜单中的 Export选项。这很重要 - 如果不生成 JAR 文件,Eclipse 就不会装入您的插件。
  11. 退出 Eclipse,在 workspace 目录下寻找新项目。
  12. 将项目目录从 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. 更改文件扩展名
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

<?xml version="1.0"?>


rules:extension="htm"
       
        />


     
      
</td></tr></table>

这一特性和 XM 链接管理完全集成在了一起,因此您的所有链接都保持有效。


<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>

工作将继续进行

只用了两个类就从 Eclipse 启动了 XM,这是对插件功能的证明。在我的下一篇专栏文章中,我打算编写向导插件来初始化新的 XM 项目。同时,您可以下载并测试 XM 运行器插件。您将看到通过 Eclipsee 编辑和发布网站是件很愉快的事。然而,要提醒的是:目前插件只可用于 JDK 1.4。如果需要在 JDK 1.3.x 中运行 Eclipse,您必须安装 Xalan 并相应地调整插件。


<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>


<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>

关于作者<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td></td><td></td><td width="100%">

Beno?t Marchal 是住在比利时那慕尔的顾问和作家。他刚出版了 XML by Example 第二版,该版包括了不断发展的 XML 标准的最新特性(包括最终的 XML Schema 建议书和 XSL 的最新进展)。在 marchal.com上可获得更多的详细信息。可以通过 bmarchal@pineapplesoft.com与 Beno?t 联系。

</td></tr></table>

<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>


↑返回目录
前一篇: 使用 XML: 创建项目
后一篇: 利用视图链接使 Eclipse 应用程序更丰富