Tracking resource changes

We've just seen how to batch resource changes in a runnable (Batching resource changes). Let's look at the other side of the coin. What if you want to keep track of all of the changes to the workspace that happen while your plug-in is running? You can register an IResourceChangeListener with the workspace. Your listener will be notified of the changes via an IResourceChangeEvent object, which describes the changes.

Registering a listener

First, you must register a resource change listener with the workspace.

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

Your listener will be notified after modifications to the workspace resources have been made. Resource API methods that modify resources trigger these events as part of their documented behavior. The method comment for a resource API method explicitly states whether or not it triggers a resource change event. For example, the following is included in the IFile.setContents() comment:

   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.

Methods that create, delete, or change a resource typically trigger these events. Methods that read, but do not write, resources typically do not trigger these events.

Resource change events

The resource change event describes the specifics of the change (or set of changes) that have occurred in the workspace. The event contains a resource delta that describes the net effect of the changes. For example, if you add a resource and later delete it during one batch of changes, the resource will not appear in the delta.

The resource delta is structured as a tree rooted at the workspace root. The resource delta tree describes these types of changes:

To traverse a resource delta tree, you may implement the IResourceDeltaVisitor interface or traverse the tree explicitly using IResource.getAffectedChildren. Resource delta visitors implement a visit method that is called by the resource delta as it enumerates each change in the tree.

Note:  Changes made to resource session properties or resource persistent properties are not identified in the resource delta.

Resource change events are sent whenever a change (or batched set of changes) is made to the workspace. In addition, resource change events are sent for certain specific workspace operations. The table below summarizes the types of resource change events and when they are reported.

Event type

Description

PRE_CLOSE

Notifies listeners that a project is about to be closed. This event can be used to extract and save necessary information from the in-memory representation (e.g., session properties) of a project before it is closed. (When a project is closed, the in-memory representation is disposed). The workspace is locked (no resources can be updated) during this event. The event contains the project that is being closed.

PRE_DELETE

Notifies listeners that a project is about to deleted. This event can be used to perform clean-up operations, such as removing any saved state that is related to the project from your plug-in's directory.  The workspace is locked (no resources can be updated) during this event. The event contains the project that is being deleted.

PRE_BUILD

Notifies listeners before any building occurs.  This event is broadcast when an explicit build is requested, or when the platform detects a build needs to occur, regardless of whether auto-building is actually enabled. The workspace is not locked during this event (resources can be updated). The event contains a resource delta describing the changes that have occurred since the end of the last POST_BUILD event.

POST_BUILD

Notifies listeners after any building occurs.  This event is broadcast after an explicit build is requested, or when the platform detects a build needs to occur, regardless of whether auto-building is actually enabled. The workspace is not locked during this event (resources can be updated). The event contains a resource delta describing the changes that have occurred since the end of the last POST_BUILD event.

POST_CHANGE

Describes a set of changes that have occurred to the workspace since the last POST_CHANGE event was reported. Triggered after a resource change API is used individually or in a batched set of workspace changes. Also triggered before any PRE_BUILD and after any POST_BUILD notification is complete. The event contains a resource delta describing the net changes since the last POST_CHANGE event.  The workspace is locked (no resources can be updated) during this event.

Implementing a resource change listener

The following example implements a console-based resource change listener. A resource change listener is registered for specific types of events and information about these events is printed to the console:

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

The listener checks for each event type and reports information about the resource that was changed and the kinds of changes that occurred.  Although this example is designed to show a general listener that handles all the types of resource events, a typical listener would register for just one type of event.

The implementation for POST_CHANGE uses another class that can be used to visit the changes in the resource delta.

   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_BUILD:
               System.out.println("Build about to run.");
               event.getDelta().accept(new DeltaPrinter());
               break;
            case IResourceChangeEvent.POST_BUILD:
               System.out.println("Build complete.");
               event.getDelta().accept(new DeltaPrinter());
               break;
         }
      }
   }

The DeltaPrinter class implements the IResourceDeltaVisitor interface to interrogate the resource delta. The visit() method is called for each resource change in the resource delta. The visitor uses a return value to indicate whether deltas for child resources should be visited.

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

Further information can be obtained from the supplied resource delta. The following snippet shows how the IResourceDelta.CHANGED case could be implemented to further describe the resource changes.

   ...
   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;
   ...

For a complete description of resource deltas, visitors, and marker deltas, consult the API specification for IResourceDelta, IResourceDeltaVisitor, and IMarkerDelta.

Note:  Resource change listeners are useful for tracking changes that occur to resources while your plug-in is activated. If your plug-in registers a resource change listener during its startup code, it's possible that many resource change events have been triggered before the activation of your plug-in. The resource delta contained in the first resource change event received by your plug-in will not contain all of the changes made since your plug-in was last activated. If you need to track changes made between activations of your plug-in, you should use the support provided for workspace saving. This is described in Workspace save participation.
Note:  Some resource change events are triggered during processing that occurs in a background thread. Resource change listeners should be thread-safe. See Threading issues for a discussion about thread safety with the UI.