How You‘ve Changed!

来源:百度文库 编辑:神马文学网 时间:2024/05/02 01:59:41
Copyright © 2002, 2004 International Business Machines, Inc.  Eclipse Corner Article

How You‘ve Changed!
Responding to resource changes in the Eclipse workspace
Summary
Many tools and user interface elements are interested in processing resourcechanges as they happen. For example, the task list wants to update new or changedmarkers, the navigator wants to reflect added and deleted resources, and theJava compiler wants to recompile modified Java files. Such notifications arepotentially costly to compute, manage and broadcast. The Eclipse Platform resourcemodel includes a series of mechanisms for efficiently notifying clients of resourcechanges. This article outlines these facilities and gives some examples of theiruse.
By John Arthorne, OTI
August 23, 2002
Updated November 23, 2004 for Eclipse 3.0
IResourceChangeListener interface, and are registered using the methodaddResourceChangeListener onIWorkspace. It is also important to remove your resource change listener when it is no longerneeded, using IWorkspace.removeResourceChangeListener.
During a resource change notification, the workspace is locked to prevent further modificationwhile the notifications are happening. This is necessary to ensure that all listeners are notifiedof all workspace changes. Otherwise, a change made by one listener would have to be broadcastto all other listeners, easily creating the possibility of an infinite loop. There is a special exceptionto this rule for the PRE_BUILD and POST_BUILDevent types that will be discussed later on.
Before we get into the details, let‘s start with a simple examplethat shows how to add and remove a resource change listener:
IWorkspace workspace = ResourcesPlugin.getWorkspace();IResourceChangeListener listener = new IResourceChangeListener() {public void resourceChanged(IResourceChangeEvent event) {System.out.println("Something changed!");}};workspace.addResourceChangeListener(listener);//... some time later one ...workspace.removeResourceChangeListener(listener);
IWorkspaceRunnable, and passing it toIWorkspace.run(IWorkspaceRunnable). Wrapping high-level operations insidean IWorkspaceRunnable can lead to a substantial performance improvement,because it ensures that only one resource change broadcast occurs, instead of potentially thousands.
Below is an example of an operation that is nested using theIWorkspaceRunnable mechanism. In this case, a single resource change eventwill be broadcast, indicating that one project and ten files have been created. To keep it simple,progress monitoring and exception handling have been omitted from this example.
IWorkspace workspace = ResourcesPlugin.getWorkspace();final IProject project = workspace.getRoot().getProject("My Project");IWorkspaceRunnable operation = new IWorkspaceRunnable() {public void run(IProgressMonitor monitor) throws CoreException {int fileCount = 10;project.create(null);project.open(null);for (int i = 0; i < fileCount; i++) {IFile file = project.getFile("File" + i);file.create(null, IResource.NONE, null);}}};workspace.run(operation, null);
Since Eclipse 3.0, it is no longer guaranteed that anIWorkspaceRunnable will prevent notifications for the entire durationof an operation. The workspace can now decide to perform notifications during an operationto ensure UI responsiveness. This is particularly important when several workspacemodifying operations are running simultaneously. The use of IWorkspaceRunnable is still stronglyencouraged, functioning as a strong hint to the workspace that a set of changesis occurring that can be batched. Also in Eclipse 3.0, a background equivant toIWorkspaceRunnable was introduced.WorkspaceJob will batch a set of workspace changes that occurinside a Job running in the background. Read theConcurrency infrastructure documentation for more details on jobs.
A very powerful feature of the resource change infrastructure is that listeners will evenbe notified of changes that occur outside the workspace API. If some external editoror tool makes changes to resources in the workspace directly from the filesystem, resourcechange listeners will still receive the same notification describing exactly what changed andhow they changed. The drawback is that since most operating systems don‘t have sucha resource change mechanism of their own, the eclipse workspace may not"discover" the change until later on. Specifically, the workspace will notsend the notification until someone performs a IResource.refreshLocaloperation on a resource subtree that has changed in the filesystem. After therefreshLocal operation, the workspace will send resource change notificationto all listeners, describing everything that has changed since the last local refresh.
The contents of a resource change event
Know how to listen, and you will profit
even from those who talk badly.
– Plutarch
Now that we know how to add listeners and when to expect them to be called,let‘s take a closer look at what these change events look like. The object passedto a resource change listener is an instance ofIResourceChangeEvent. The most important bits of information in the eventare the event type, and the resource delta. The event type is simply an integer thatdescribes what kind of event occurred. Listeners are typically mainly interested in thePOST_CHANGE event type, and that is the one we will focus on here.The resource delta is actually the root of a tree of IResourceDelta objects.The tree of deltas is structured much like the tree of IResource objects thatmakes up the workspace, so that each delta object corresponds to exactly one resource.The top-most delta object, provided by the event object, corresponds tothe IWorkspaceRoot resource obtained by IWorkspace.getRoot.The resource delta hierarchy will include deltas for all affected resources that existed prior to theresource changing operation, and all affected resources that existed after the operation.Think of it as the union of the workspace contents before and after a particular operation,with all unchanged sub-trees pruned out. Each delta object provides the following information:
The resource it corresponds to.
The kind of modification (added, removed, or changed).
The precise nature of the change (the change flags).
A summary of what markers changed on the resource.
Deltas for any added, removed, or changed children.
In the case where a resource has moved, the delta for the destination also supplies the path itmoved from, and the delta for the source supplies the path it moved to. This allows listeners toaccurately track moved resources.
To give an example of the structure of a resource delta, assume we begin with a workspacewith the following contents:

Now, say we perform a workspace operation that does all of the following changes:
Delete "Folder1", which causes deletion of "File1" as well.
Modify the contents of "File2"
Create a folder called "NewFolder" inside "Project1"
Create a file called "NewFile" inside "NewFolder"
This operation will result in a resource delta tree with the following structure:

In this diagram, the symbol next to each resource delta represents the kind ofmodification, + for addition, - for removal, * for change. Note that all resources withaffected children are marked as changed, and that unaffected resources (Project2),are not included in the tree.
It is worth giving a bit more detail about what the delta change flags (IResourceDelta.getFlags()), are all about. More than one flagmay be applicable for a given resource, in which case the flag values are maskedtogether to form a single flag integer. The following table summarizes the differentflags and what they signify:
Constant (on IResourceDelta) Applicable resources What it means
CONTENT IFile, IFolder The filesystem modification timestamp has changed since the last notification. IResource.touch() will also trigger a content change notification, even though the content may not have changed in the file system.
ENCODING IFile, IFolder, IProject The character encoding for a file, or for the files inside a container, have changed. For listeners that care about the character content of the file, as opposed to the raw bytes, this should typically be treated the same as a content change.
MOVED_FROM IFile, IFolder, IProject The resource was moved from another location. You can find out the path it came from by calling IResourceDelta.getMovedFromPath.
MOVED_TO IFile, IFolder, IProject The resource was moved to another location. The location it was moved to is indicated by IResourceDelta.getMovedToPath.
OPEN IProject The project has either been opened or closed. If the project is now open, then it was previously closed, and vice-versa.
TYPE IFile, IFolder The resource has changed type. If the resource was previously a file then it is now a folder, and vice-versa.
MARKERS All The resource‘s markers have changed. Markers are annotations to resources such as breakpoints, bookmarks, to-do items, etc. The method IResourceDelta.getMarkerDeltas() is used to find out exactly which markers have changed.
REPLACED IFile, IFolder, IProject The resource has been replaced by a different resource at the same location (i.e., the resource has been deleted and then re-added).
DESCRIPTION IProject The project description has changed.
SYNC All The resource‘s synchronization information has changed. Sync info is used to determine if a resource is in sync with some remote server, and is not typically of interest to local tools. See the API interface ISynchronizer for more details.
IMarker objects). These listeners can useIResourceChangeEvent.findMarkerDeltas to quickly collect all changed markersof a given type.
There is also a visitor mechanism (IResourceDelta.accept(IResourceDeltaVisitor))for easily processing all changed resources in a given sub-tree. However, visitors should onlybe used where appropriate. Using a visitor to process two or three resources doesn‘tmake sense, as the overhead of visiting the entire delta tree is incurred for no good reason.Since it‘s so easy to write a visitor, there is a tendency for programmers to use them tooliberally, even in cases where they only want to process a well-defined subset of a tree.The return value from the visitor‘s visit method is used to indicate if that resource‘s children should betraversed. This can be used to short-circuit the traversal to avoid visiting sub-trees that you knowyou are not interested in.
Thread safety. There are some multi-threading issues to keep in mind whenwriting listeners. First, you have no control over what thread your listener will run in.Workspace operations can occur in any thread, and resource change listeners willrun in whatever thread that triggered the operation. So, if some of your update codemust be run in a particular thread, you‘ll have to make sure your code getsposted to that thread. The most common example of this is UI updates. With theStandard Widget Toolkit (SWT), the UI toolkit that is included with Eclipse, there isonly a single UI thread per display. If your resource changelistener needs to update the UI, you will need to use the methods syncExecor asyncExec in class org.eclipse.swt.widgets.Display topost the update code to the UI thread.
If any of your update code runs asynchronously (i.e., you used asyncExec or somesimilar mechanism to post your code to another thread), there is another consideration tokeep in mind. The resource delta objects supplied to your listener are designed to "expire"when the resourceChanged method returns. So, if you pass referencesto IResourceDelta objects to another thread, they may cause failures ifthey are accessed after the listener method has returned back in the other thread. The reasonfor this resource delta "expiry date", is to ensure that listeners don‘t hold onto resource deltareferences indefinitely. These delta structures are potentially quite large, and if a listenerholds onto them, it essentially causes a memory leak because these structures can no longerbe garbage collected.
ISaveParticipant interface, and are installed using IWorkspace.addSaveParticipant.The main purpose of save participants is to allow plug-ins to save their importantmodel state at the same time that the workspace saves its state. This ensuresthat the persisted workspace state stays synchronized with any domain modelstate that relies on it. Once a save participant is registered with the workspace,subsequent calls to addSaveParticipant will return anISavedState object. By passing a resource change listener toISavedState.processResourceChangeEvents, participants are giventhe opportunity to process the changes that have occurred since the last saveoccurred. This fills in the "blind spot" between workspace startup and activationof the plug-in that the listener belongs to. To find out about other facilitiesprovided by the save participant mechanism, read the API Javadoc forISaveParticipant,ISavedState, andISaveContext.
builder article, it is usefulto know about the major differences between resource change listeners and builders:
Builders are always allowed to modify the workspace.
Builders have support for progress monitoring, cancellation, and error reporting.
Listeners operate on the entire workspace; builders are installed on a per-project basis.
Builders are run in a concrete order that can be specified individually on each project. Builders can enforce dependency relationships with other builders (my builder must run after the "Foo" builder). Projects are also built in a specified order. With resource change listeners, there is no way to ensure or even discover the order in which listeners receive the change events.
There are different build policies: incremental build, full build, and auto-build.
Builder lifecycle and persistence is managed by the platform. Once a builder is installed on a project, it will remain on that project across sessions.
Builder configurations can easily be shared with other users, since the information is contained in the shareable project description file (called ".project").
These differences aside, the principle behind project builders and resourcechange listeners is the same. Builders are provided with a similar IResourceDeltahierarchy that describes what has changed since the last time that builder wascalled. This gives builders a chance to incrementally update the resources theyoperate on in response to changes made by others. For more details on builders,see the eclipse.org articleProjectNatures and Builders.