跟踪资源更改

我们刚刚了解过如何在可运行程序中对资源更改进行批处理(对资源更改进行批处理)。我们再来看看另一方面。如果想要跟踪插件运行时发生的所有工作空间更改又该怎么办?可向工作空间注册 IResourceChangeListener。将通过 IResourceChangeEvent 对象(用来描述更改)来向侦听器通知更改。

注册侦听器

首先必须向工作空间注册资源更改侦听器。

      IResourceChangeListener listener = new MyResourceChangeReporter();
   ResourcesPlugin.getWorkspace().addResourceChangeListener(
      listener, IResourceChangeEvent.POST_CHANGE);

在执行了对工作空间资源的修改之后,将通知侦听器。修改资源的资源 API 方法将触发这些事件,作为描述行为的一部分。资源 API 方法的方法注释显式声明它是否触发了资源更改事件。例如,以下是 IFile.setContents() 注释中所包含的内容:

      This method changes resources; these changes will be reported in a subsequent
   resource change event, including an indication that this file's content have
   been changed.

通常,创建、删除或更改资源的方法将触发这些事件。读取但不写入资源的方法通常不会触发这些事件。

资源更改事件

资源更改事件描述在工作空间中已经发生的更改(或更改集合)的细节。事件中包含资源变化,它描述更改的效应。例如,如果您在一次批处理中添加了资源,稍后又删除了该资源,则该资源将不会出现在变化中。

资源变化构造为起源于工作空间根目录的树。资源变化树描述下列类型的更改:

要遍历资源变化树,可以实现 IResourceDeltaVisitor 接口,或者使用 IResource.getAffectedChildren 来显式遍历树。资源变化访问器实现这样一种 visit 方法,当资源变化枚举树中的每个更改时,就要调用此方法。

注意:资源变化中不会标识对资源会话属性或者资源持久属性所作的更改。

每当对工作空间进行更改(或一批更改)时,就会发送资源更改事件。另外,对于某些特定工作空间操作也会发送资源更改事件。下表总结了资源更改事件的类型以及是何时报告它们的。

事件类型

描述

PRE_CLOSE

通知侦听器项目即将关闭。此事件可以用来在关闭项目之前从该项目的内存表示法(例如,会话属性)中抽取和保存所需的信息。(关闭项目时,将除去其内存表示法)。在处理此事件期间,会锁定工作空间(不能更新任何资源)。事件中包含正关闭的项目。

PRE_DELETE

通知侦听器即将删除项目。此事件可以用来执行清理操作,例如,从插件的目录中除去与项目相关的任何保存状态。在进行此事件期间,会锁定工作空间(不更新任何资源)。事件中包含正删除的项目。

PRE_AUTOBUILD

在发生任何自动构建之前通知侦听器。当平台检测到需要进行自动构建时,就会广播此事件,而不管实际上是否启用了自动构建。在进行此事件期间,工作空间锁定(可以更新资源)。事件包含用来描述自上次报告 POST_CHANGE 事件后发生的更改的资源变化。

POST_AUTOBUILD

在发生任何自动构建后通知侦听器。在平台执行自动构建后广播此事件,而不管实际上是否启用了自动构建。在进行此事件期间,工作空间锁定(可以更新资源)。事件包含用来描述自上次报告 POST_CHANGE 事件后发生的更改的资源变化。

POST_CHANGE

描述自上次报告 POST_CHANGE 事件后对工作空间进行的一组更改。在单独使用资源更改 API 之后或者在一批工作空间更改中触发。还会在任何 PRE_AUTOBUILDPOST_AUTOBUILD 通知完成后触发。事件包含用来描述自上一个 POST_CHANGE 事件后的净更改的资源变化。在进行此事件期间,会锁定工作空间(不能更新任何资源)。

实现资源更改侦听器

以下示例实现基于控制台的资源更改侦听器。为特定类型的事件注册了资源更改侦听器,而将关于这些事件的信息打印至控制台:

      IResourceChangeListener listener = new MyResourceChangeReporter();
   ResourcesPlugin.getWorkspace().addResourceChangeListener(listener,
      IResourceChangeEvent.PRE_CLOSE
      | IResourceChangeEvent.PRE_DELETE
      | IResourceChangeEvent.PRE_AUTO_BUILD
      | IResourceChangeEvent.POST_AUTO_BUILD
      | IResourceChangeEvent.POST_CHANGE);

侦听器检查每种事件类型,并报告关于被更改的资源的信息,以及发生的更改的种类。尽管此示例用来显示处理所有类型的资源事件的通用侦听器,但是,典型的侦听器只为一种类型的事件注册。

POST_CHANGE 的实现使用另一个类,该类可以用来访问资源变化中的更改。

      import org.eclipse.resources.*;
   import org.eclipse.runtime.*;

   public class MyResourceChangeReporter implements IResourceChangeListener {
      public void resourceChanged(IResourceChangeEvent event) {
         IResource res = event.getResource();
         switch (event.getType()) {
            case IResourceChangeEvent.PRE_CLOSE:
               System.out.print("Project ");
               System.out.print(res.getFullPath());
               System.out.println(" is about to close.");
      break;
            case IResourceChangeEvent.PRE_DELETE:
               System.out.print("Project ");
               System.out.print(res.getFullPath());
               System.out.println(" is about to be deleted.");
      break;
            case IResourceChangeEvent.POST_CHANGE:
               System.out.println("Resources have changed.");
               event.getDelta().accept(new DeltaPrinter());
      break;
            case IResourceChangeEvent.PRE_AUTO_BUILD:
               System.out.println("Auto build about to run.");
               event.getDelta().accept(new DeltaPrinter());
      break;
            case IResourceChangeEvent.POST_AUTO_BUILD:
               System.out.println("Auto build complete.");
               event.getDelta().accept(new DeltaPrinter());
      break;
         }
      }
   }

DeltaPrinter 类实现 IResourceDeltaVisitor 接口,以查询资源变化。对资源变化中的每个资源更改都会调用 visit() 方法。访问者使用返回值来指示是否应访问子资源的变化。

      class DeltaPrinter implements IResourceDeltaVisitor {
      public boolean visit(IResourceDelta delta) {
         IResource res = delta.getResource();
         switch (delta.getKind()) {
            case IResourceDelta.ADDED:
      System.out.print("Resource ");
               System.out.print(res.getFullPath());
               System.out.println(" was added.");
      break;
            case IResourceDelta.REMOVED"
      System.out.print("Resource ");
               System.out.print(res.getFullPath());
               System.out.println(" was removed.");
      break;
   case IResourceDelta.CHANGED:
      System.out.print("Resource ");
               System.out.print(res.getFullPath());
      System.out.println(" has changed.");
      break;
         }
         return true; // visit the children
      }
   }

可以从提供的资源变化中获取进一步的信息。以下代码段显示可以如何实现 IResourceDelta.CHANGED 事例,以进一步描述资源更改。

   ...
   case IResourceDelta.CHANGED:
      System.out.print("Resource ");
      System.out.print(delta.getFullPath());
      System.out.println(" has changed.");
      int flags = delta.getFlags();
      if ((flags & IResourceDelta.CONTENT) != 0) {
            System.out.println("--> Content Change");
      }
      if ((flags & IResourceDelta.REPLACED) != 0) {
            System.out.println("--> Content Replaced");
      }
      if ((flags & IResourceDelta.MARKERS) != 0) {
            System.out.println("--> Marker Change");
            IMarkerDelta[] markers = delta.getMarkerDeltas();
            // if interested in markers, check these deltas
      }
      break;
   ...

有关资源变化、访问器和标记变化的完整描述,参阅 IResourceDeltaIResourceDeltaVisitorIMarkerDelta 的 API 规范。

注意:如果激活了插件,资源更改侦听器对于跟踪对资源的更改是很有用的。如果插件在它的启动代码中注册了资源更改侦听器,则可能在激活插件之前就已经触发了许多资源更改事件。对于插件接收到的第一个资源更改事件中所包含的资源变化,它将不包含自上次激活插件后所作的所有更改。如果需要跟踪在激活插件之间所作的更改,则应当使用为工作空间保存所提供的支持。在工作空间保存参与中对此作了描述。
注意:某些资源更改事件是在处理后台线程时触发的。资源更改侦听器应该是线程安全的。有关用户界面的线程安全的讨论,请参阅线程技术问题