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:
-
Resources that have been created, deleted, or changed. If you have deleted (or added) a folder,
the resource delta will include the folder and all files contained in the folder.
-
Resources that have been moved or renamed using the IResource.move() API.
-
Markers that have been added, removed, or changed. Marker modification is considered to be a
workspace modification operation.
-
Files that have been modified. Changed files are identified in the resource delta, but you do not
have access to the previous content of the file in the resource delta.
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.
|
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.