Bug 15479 - [resources] API: new API for registering IResourceChangeListeners
Summary: [resources] API: new API for registering IResourceChangeListeners
Status: RESOLVED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 2.0   Edit
Hardware: PC Windows 2000
: P4 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Platform-Resources-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: api
Depends on:
Blocks:
 
Reported: 2002-05-07 17:19 EDT by Judah Diament CLA
Modified: 2004-08-26 12:09 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Judah Diament CLA 2002-05-07 17:19:11 EDT
I have a class called ProjectNavigatorContentProvider which extends 
org.eclipse.ui.model.WorkbenchContentProvider. In my constructor I register for 
POST_AUTO_BUILD using 
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, 
IResourceChangeEvent.POST_AUTO_BUILD);   However, when I step into 
org.eclipse.core.internal.events.NotificationManager.broadcastChanges(ElementTre
e, int, booleab, boolean), listeners (the ResourceChangeListenerList) only had 
my object registered for mask = 1, i.e. for POST_CHANGED events. After 
putting a breakpoint in 
org.eclipse.core.internal.resources.Workspace.addResourceChangeListener(IResourc
eChangeListener, int) and watching what was happening at start-up, I realised 
that some time after my ProjectNavigatorContentProvider constructor was 
registering for POST_AUTO_BUILD, org.eclipse.ui.model.WorkbenchContentProvider, 
my super-class, was registering for POST_CHANGED, and that somehow knocked out 
my registration for POST_AUTO_BUILD. My work around was to register for 
POST_AUTO_BUILD again in my Object[] getElements(Object arg0), which is called 
some time after org.eclipse.ui.model.WorkbenchContentProvider, did its 
registration but before the user sees my navigator. I have yet to determine if 
my registration has in turn knocked out the registration of my super class, and 
if that has any negative affects. It doesn't seem to so far.
Comment 1 DJ Houghton CLA 2002-05-07 18:34:43 EDT
Listeners are added/changed/removed from the list based on identity. Are you 
sure that your listener is being removed/replaced? A good place to step into 
in debug mode would be ResourceChangeListenerList.add().

Here is a little test that I wrote which work ok. Is there something obvious 
which I am overlooking?

public void testMulti() {

	class Listener1 implements IResourceChangeListener {
		public boolean done = false;
		public void resourceChanged(IResourceChangeEvent event) {
			assertEquals("1.0", IResourceChangeEvent.POST_CHANGE, 
event.getType());
			done = true;
		}
	}

	class Listener2 extends Listener1 implements IResourceChangeListener {
		public void resourceChanged(IResourceChangeEvent event) {
			assertEquals("2.0", 
IResourceChangeEvent.POST_AUTO_BUILD, event.getType());
			done = true;
		}
	}

	Listener1 listener1 = new Listener1();
	Listener2 listener2 = new Listener2();

	getWorkspace().addResourceChangeListener(listener1, 
IResourceChangeEvent.POST_CHANGE);
	getWorkspace().addResourceChangeListener(listener2, 
IResourceChangeEvent.POST_AUTO_BUILD);

	try {
		project1.touch(getMonitor());
	} catch (CoreException e) {
		handleCoreException(e);
	}

	assertTrue("3.0", listener1.done);
	assertTrue("3.1", listener2.done);
	getWorkspace().removeResourceChangeListener(listener1);
	getWorkspace().removeResourceChangeListener(listener2);
}
Comment 2 Judah Diament CLA 2002-05-08 09:20:50 EDT
your re-creation is a off on key points, but rather than address your example, 
let me point you to the exact place in the Eclipse code that is causing the 
problem.

I just ran through one execution of my code in the debugger, and here is exactly 
what is happening: my class, an instace of ProjectNavigatorContentProvider whose 
ID = 87 on this run of the code, added itself with eventMask = 16 (i.e. 
POST_AUTO_BUILD). My class sub-classses 
org.eclipse.ui.model.WorkbenchContentProvider. Now, here is a snippet of code 
that appears in WorkbenchContentProvider (I cut out most of the method - just 
showing the relevant part):

public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		if (newWorkspace != null) {
			newWorkspace.addResourceChangeListener(this, 
IResourceChangeEvent.POST_CHANGE);
		}
	}
}
So, inside its inputChanged method, WorkbenchContentProvider registers itself 
with eventmask = 1. Since my sub-class, ProjectNavigatorContentProvider, doesn't 
have its own inputChanged method, the method in the super class is executed, and 
the debugger shows my instace, with ID = 87, registering itself with eventMask = 
1 (i.e. POST_CHANGE), and I am no longer registered for POST_AUTO_BUILD 
(eventMask = 16).

In short, the behavior of the code inside 
org.eclipse.core.internal.events.ResourceChangeListenerList.add(IResourceChangeL
istener, int) is to replace the old mask of the given listener with the new one, 
so despite the fact that in my constructor I am registering with eventMask = 16, 
every time inputChanged is called, 1 *replaces* 16 instead of 1 being *added to* 
16. 

Is that clearer?
Comment 3 John Arthorne CLA 2002-05-08 10:51:41 EDT
Look at the javadoc for IWorkspace.addResourceChangeListener, here is a snippet 
from the first paragraph:

 * After completion of this method, the given listener will be 
 * registered for exactly the specified events.  If they were 
 * previously registered for other events, they will be deregistered.  

So we're behaving as spec'ed.  I suggest you override the inputChanged method 
from the superclass so that it doesn't override your previous listener 
registration.
Comment 4 Judah Diament CLA 2002-05-08 10:56:37 EDT
John - I alrady made the change - my method is quite simple:
/**
 * @see IContentProvider#inputChanged(Viewer, Object, Object)
*/ 
public void inputChanged(Viewer arg0, Object arg1, Object arg2)
{
   super.inputChanged(arg0, arg1, arg2);
   ResourcesPlugin.getWorkspace().addResourceChangeListener(this, 
      IResourceChangeEvent.POST_AUTO_BUILD);
}

the real queston is - is that spec the most logical choice, or would adding the 
new eventMask to the old one be better? I agree that the knee-jerk reaction is 
that the spec is good, but when you consider the super/sub class example, one 
wonders if it would make more sense to add the new to the old instead of 
deleting the old....
Comment 5 DJ Houghton CLA 2002-05-08 11:15:05 EDT
The problems in changing things are:
- when you want to change the events that you are listening for you would have 
to de-register your listener and then re-register
- changing the API is difficult at this point in time because there are a lot 
of people out there depending on the behaviour as documented
Comment 6 Judah Diament CLA 2002-05-08 11:56:54 EDT
OK - just a thought for consideration in version 3, I guess. Minimally, you 
could add a new call - some like "changeListener" instead of "addListener" - 
which would allow people to update the eventMask instead of blowing it away.
Comment 7 DJ Houghton CLA 2002-05-08 13:50:34 EDT
Closing for now. 
Will consider adding new API for merging resource change listener events post 
2.0.
Comment 8 DJ Houghton CLA 2002-09-10 12:27:31 EDT
Renamed from:
super-class IResourceChangeListener registration kills sub-class registration

Reopening for consideration.
Comment 9 John Arthorne CLA 2004-08-26 12:09:45 EDT
Closing bugs we don't plan to fix.