Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 157089
Collapse All | Expand All

(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/DefaultTypeProvider.java (-40 / +21 lines)
Lines 11-16 Link Here
11
 **********************************************************************/
11
 **********************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
13
14
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashMap;
15
import java.util.Map;
16
import java.util.Map;
16
17
Lines 18-26 Link Here
18
import org.eclipse.core.resources.IResourceChangeEvent;
19
import org.eclipse.core.resources.IResourceChangeEvent;
19
import org.eclipse.core.resources.IResourceChangeListener;
20
import org.eclipse.core.resources.IResourceChangeListener;
20
import org.eclipse.core.resources.IResourceDelta;
21
import org.eclipse.core.resources.IResourceDelta;
21
import org.eclipse.core.resources.ResourcesPlugin;
22
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
22
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
23
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
24
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
23
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
25
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
24
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
26
import org.eclipse.hyades.test.ui.navigator.ITypeProviderContext;
25
import org.eclipse.hyades.test.ui.navigator.ITypeProviderContext;
Lines 43-49 Link Here
43
    private IFileProxyManager fileProxyManager;
42
    private IFileProxyManager fileProxyManager;
44
43
45
    public DefaultTypeProvider() {
44
    public DefaultTypeProvider() {
46
        cache = new HashMap(); 
45
        cache = Collections.synchronizedMap(new HashMap()); 
47
        //- register this class as a resource change listener to the proxy node cache
46
        //- register this class as a resource change listener to the proxy node cache
48
        FileProxyNodeCache.getInstance().addResourceListener(this);
47
        FileProxyNodeCache.getInstance().addResourceListener(this);
49
    }
48
    }
Lines 70-122 Link Here
70
	}
69
	}
71
   
70
   
72
    public void resourceChanged(IResourceChangeEvent event) {
71
    public void resourceChanged(IResourceChangeEvent event) {
73
        Object lowestChanged = null;
74
        IResourceDelta resDelta = event.getDelta();
72
        IResourceDelta resDelta = event.getDelta();
75
        //- get all projects affected by the current change
73
        //- get all projects affected by the current change
76
        IResourceDelta[] affectedProjects = resDelta.getAffectedChildren();
74
        IResourceDelta[] affectedProjects = resDelta.getAffectedChildren();
77
        boolean oneProjectAlreadyChanged = false;
78
        for (int i = 0; i < affectedProjects.length; i++) {
75
        for (int i = 0; i < affectedProjects.length; i++) {
76
        	Object lowestChanged = null;
79
            IResourceDelta delta = affectedProjects[i];
77
            IResourceDelta delta = affectedProjects[i];
80
            IProject project = (IProject) delta.getResource();
78
            IProject project = (IProject) delta.getResource();
81
            DefaultTypeProviderProxyNode proxy = (DefaultTypeProviderProxyNode) cache.get(project);
79
            DefaultTypeProviderProxyNode proxy = (DefaultTypeProviderProxyNode) cache.get(project);
82
            // proxy may be null if we receive a resource change event before get() is invoked:
80
            // proxy may be null if we receive a resource change event before get() is invoked:
83
            // in this case, there are no proxies to update.
81
            // in this case, there are no proxies to update.
84
            if (proxy == null) return;
82
            if (delta.getKind() == IResourceDelta.REMOVED) {
85
            boolean wasEmpty = proxy.getChildren().length == 0;
83
            	cache.remove(project);
86
            IProxyNode lowestTP = proxy.resourceChanged(delta);
84
            } else if (delta.getKind() == IResourceDelta.CHANGED) {
87
            if (proxy.getChildren().length == 0 && !wasEmpty) {
85
            	if (proxy == null) {
88
                //- after update the type provider does no longer contain children, we need to remove it as well.
86
            		//lowestChanged = project;
89
                cache.remove(project);
87
            	} else {
90
                lowestChanged = project;
88
	            	int previousChildrenCount = proxy.getChildren().length;
91
                lowestTP = null;
89
		            lowestChanged = proxy.resourceChanged(delta);
92
            } else if (proxy.getChildren().length != 0 && wasEmpty) {
90
		            if (lowestChanged == proxy) {
93
                //- after update the type provider is no longer empty, we need to show its content.
91
						int newChildrenCount = proxy.getChildren().length;
94
                lowestChanged = project;
92
						if ((previousChildrenCount == 0 || newChildrenCount == 0) && previousChildrenCount != newChildrenCount) {
95
                lowestTP = null;
93
							lowestChanged = project;
94
						}
95
					}
96
            	}
97
	            if(lowestChanged != null) {
98
	            	refresher.nodeChanged(lowestChanged);
99
	            }
96
            }
100
            }
97
            if (lowestTP != null) {
98
                //- a change is found is it the first one or not ?
99
                if (lowestChanged == null) {
100
                    //- that is the first one, save it
101
                    lowestChanged = lowestTP;
102
                } else {
103
                    //- not the first one, so lowest is the parent -> current project
104
                    if (!oneProjectAlreadyChanged) {
105
                        lowestChanged = project;
106
                        oneProjectAlreadyChanged = true;
107
                    } else {
108
                        //- this is the second time that a project is changed, lowest is now the root workspace
109
                        lowestChanged = ResourcesPlugin.getWorkspace().getRoot();
110
                    }
111
                }
112
            }
113
            if(delta.getKind() == IResourceDelta.REMOVED) {
114
                cache.remove(project);
115
            }
116
        }
117
        //- refresh only the lowest node
118
        if(lowestChanged != null) {
119
            refresher.nodeChanged(lowestChanged);
120
        }
101
        }
121
    }
102
    }
122
    
103
    
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/TestAssetGroupProxyManager.java (-152 / +76 lines)
Lines 11-22 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
13
14
import java.util.Collection;
14
import java.util.HashMap;
15
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Iterator;
16
import java.util.LinkedList;
17
import java.util.List;
18
import java.util.Map;
17
import java.util.Map;
19
import java.util.Set;
20
18
21
import org.eclipse.core.resources.IProject;
19
import org.eclipse.core.resources.IProject;
22
import org.eclipse.core.resources.IResourceChangeEvent;
20
import org.eclipse.core.resources.IResourceChangeEvent;
Lines 26-33 Link Here
26
import org.eclipse.core.runtime.IExtensionPoint;
24
import org.eclipse.core.runtime.IExtensionPoint;
27
import org.eclipse.core.runtime.Platform;
25
import org.eclipse.core.runtime.Platform;
28
import org.eclipse.hyades.test.ui.UiPlugin;
26
import org.eclipse.hyades.test.ui.UiPlugin;
27
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.SynchronizedAccess;
29
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
28
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
30
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
29
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
30
import org.eclipse.hyades.test.ui.navigator.ITypeProviderContext;
31
31
32
/** This class manage the extensions of org.eclipse.hyades.test.ui.testNavigatorTestAssetGroupProxy
32
/** This class manage the extensions of org.eclipse.hyades.test.ui.testNavigatorTestAssetGroupProxy
33
 *  This class manage all test asset groups created using a cache.
33
 *  This class manage all test asset groups created using a cache.
Lines 36-42 Link Here
36
 */
36
 */
37
public class TestAssetGroupProxyManager implements IResourceChangeListener {
37
public class TestAssetGroupProxyManager implements IResourceChangeListener {
38
	
38
	
39
    static class TestAssetInfo {
39
    static class TestAssetInfo extends SynchronizedAccess {
40
        private String name;
40
        private String name;
41
        private boolean isFlat;
41
        private boolean isFlat;
42
        private String imageKey;
42
        private String imageKey;
Lines 67-88 Link Here
67
        }
67
        }
68
    }
68
    }
69
    
69
    
70
	private Map extensions;
71
    /**
70
    /**
72
     * project -> List(ITestAssetGroupProxyNode)
71
     * string -> TestAssetInfo
73
     */
72
     */
74
    private Map cache;
73
	private Map extensions;
75
    /**
74
    /**
76
     * project -> List(ITestAssetGroupProxyNode)
75
     * project -> List(ITestAssetGroupProxyNode)
77
     */
76
     */
78
    private Map removedElements;
77
    private Map cache;
79
    private IProxyNodeListener refresher;
78
    private IProxyNodeListener refresher;
80
	
79
	
81
	public TestAssetGroupProxyManager() {
80
	public TestAssetGroupProxyManager(ITypeProviderContext context) {
82
		extensions = new HashMap();
81
		extensions = new HashMap();
83
        cache = new HashMap();
82
        cache = new HashMap();
84
        removedElements = new HashMap();
83
        refresher = context.getProxyNodeListener();
85
        refresher = new TestNavigatorRefresher();
86
		IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint(UiPlugin.getID() + ".testNavigatorTestAssetGroupProxy"); //$NON-NLS-1$
84
		IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint(UiPlugin.getID() + ".testNavigatorTestAssetGroupProxy"); //$NON-NLS-1$
87
		if (extPoint != null) {
85
		if (extPoint != null) {
88
			IConfigurationElement[] members = extPoint.getConfigurationElements();
86
			IConfigurationElement[] members = extPoint.getConfigurationElements();
Lines 113-169 Link Here
113
			UiPlugin.logInfo("extension: "+extension+" already registered"); //$NON-NLS-1$ //$NON-NLS-2$			
111
			UiPlugin.logInfo("extension: "+extension+" already registered"); //$NON-NLS-1$ //$NON-NLS-2$			
114
		}
112
		}
115
	}
113
	}
116
114
	
117
    /** Returns all test asset groups for a given project
115
	public Collection getExtensions() {
118
     * 
116
		return extensions.keySet();
119
     * @param project the project in which the test asset groups are required
117
	}
120
     * @return a list of ITestAssetGroupProxyNode.
121
     */
122
    public List getTestAssetGroups(IProject project) {
123
        if(project != null) {
124
            List groups = new LinkedList();
125
            if(cache.containsKey(project)) {
126
                return (List)cache.get(project);
127
            } else {
128
                //- not yet built
129
                Set keys = extensions.keySet();
130
                for (Iterator it = keys.iterator(); it.hasNext();) {
131
                    ITestAssetGroupProxyNode proxy = getTestAssetGroup(project,(String) it.next());
132
                    if(proxy != null) {
133
                        groups.add(proxy);
134
                    }
135
                }
136
            }
137
            return groups;
138
        } else {
139
            //- return empty list
140
            return new LinkedList();
141
        }
142
    }
143
    
118
    
144
    private ITestAssetGroupProxyNode getTestAssetGroup(IProject project, String extension) {
119
    public ITestAssetGroupProxyNode getTestAssetGroup(IProject project, String extension) {
145
        if(extensions.containsKey(extension)) {
120
    	ITestAssetGroupProxyNode group = cacheGet(project, extension);
146
            //- this extension has a test asset group registered to display those files
121
    	if (group == null) {
147
            TestAssetInfo info = (TestAssetInfo)extensions.get(extension);
122
	        if(extensions.containsKey(extension)) {
148
            //- build the proxy node with the right parameters
123
	        	//- this extension has a test asset group registered to display those files
149
            ITestAssetGroupProxyNode proxy = new TestAssetGroupProxyNode(project, extension, info.getName(), info.getImageKey(), info.isFlat, project);
124
	            TestAssetInfo info = (TestAssetInfo)extensions.get(extension);
150
            if(proxy.getChildren().length > 0) {
125
	            //- build the proxy node with the right parameters
151
                cachePut(project, proxy);
126
	            try {
152
                return proxy;
127
	            	if (info.acquireLock(project)) {
153
            }
128
	            		// the group may be have been computed in another thread while we
154
        }
129
	            		// were acquiring the lock.
130
	            		group = cacheGet(project, extension);
131
	            		if (group == null) {
132
				            group = new TestAssetGroupProxyNode(project, extension, info.getName(), info.getImageKey(), info.isFlat, project);
133
				            cachePut(project, extension, group);
134
	            		}
135
	            	}
136
	            } finally {
137
	            	info.releaseLock(project);
138
	            }
139
	        }
140
    	}
141
    	
142
    	if(group != null && group.getChildren().length > 0) {
143
    		return group;
144
    	}
155
        return null;
145
        return null;
156
    }
146
    }
157
147
158
    private void cachePut(IProject project, ITestAssetGroupProxyNode testAsset) {
148
    private void cachePut(IProject project, String extension, ITestAssetGroupProxyNode testAsset) {
159
        List list;
149
    	Map map = (Map) cache.get(project);
160
        if (cache.containsKey(project)) {
150
    	if (map == null) {
161
            list = (List) cache.get(project);
151
    		map = new HashMap();
162
        } else {
152
    		cache.put(project, map);
163
            list = new LinkedList();
153
    	}
164
            cache.put(project, list);
154
    	map.put(extension, testAsset);
165
        }
155
    }
166
        list.add(testAsset);
156
    
157
    private ITestAssetGroupProxyNode cacheGet(IProject project, String extension) {
158
    	Map map = (Map) cache.get(project);
159
    	if (map != null) {
160
    		ITestAssetGroupProxyNode group = (ITestAssetGroupProxyNode) map.get(extension);
161
    		if (group != null) return group;
162
    	}
163
    	return null;
167
    }
164
    }
168
165
169
    /* (non-Javadoc)
166
    /* (non-Javadoc)
Lines 178-288 Link Here
178
            IResourceDelta delta = affectedProjects[i];
175
            IResourceDelta delta = affectedProjects[i];
179
            IProject project = (IProject)delta.getResource();
176
            IProject project = (IProject)delta.getResource();
180
            //- given a project we need to update all test asset group proxy nodes created in this project
177
            //- given a project we need to update all test asset group proxy nodes created in this project
181
            List groups = (List)cache.get(project);
178
            Map groups = (Map)cache.get(project);
182
            if(groups != null) {
179
            if(groups != null) {
183
                for (Iterator it = groups.iterator(); it.hasNext();) {
180
                for (Iterator it = extensions.keySet().iterator(); it.hasNext();) {
184
                    TestAssetGroupProxyNode proxy = (TestAssetGroupProxyNode) it.next();
181
                	String ext = (String)it.next();
185
                    IProxyNode lowestProxy = proxy.resourceChanged(delta);
182
                    TestAssetGroupProxyNode proxy = (TestAssetGroupProxyNode) groups.get(ext);
186
                    if(proxy.getChildren().length == 0) {
183
                    if (proxy != null) {
187
                        //- this means that the group no longer contains children
184
	                    IProxyNode lowestProxy = proxy.resourceChanged(delta);
188
                        prepareCacheRemove(project, proxy);
185
	                    if(proxy.getChildren().length == 0) {
189
                        lowestChanged = project;
186
	                        //- this means that the group no longer contains children
190
                        lowestProxy = null;
187
	                        lowestChanged = project;
191
                    }
188
	                        lowestProxy = null;
192
                    if(lowestProxy != null) {
189
	                    }
193
                        //- there was a change in this test asset group proxy node tree
190
	                    if(lowestProxy != null) {
194
                        if(lowestChanged == null) {
191
	                        //- there was a change in this test asset group proxy node tree
195
                            //- this was the first change
192
	                        if(lowestChanged == null) {
196
                            lowestChanged = lowestProxy;
193
	                            //- this was the first change
197
                        } else {
194
	                            lowestChanged = lowestProxy;
198
                            //- the second one ... we need to refresh the owner of those nodes (the project)
195
	                        } else {
199
                            lowestChanged = project;
196
	                            //- the second one ... we need to refresh the owner of those nodes (the project)
200
                        }
197
	                            lowestChanged = project;
198
	                        }
199
	                    }
201
                    }
200
                    }
202
                }
201
                }
203
            }
202
            }
204
            //- we need to handle test asset creation in empty group
205
            boolean newTAG = getAddedTestAssetGroups(project, delta);
206
            if(!(lowestChanged instanceof IProject) && newTAG) {
207
                lowestChanged = project;
208
            }
209
        }
203
        }
210
        //- after the for loop, we can update the cache for removes
211
        completeCacheRemoves();
212
        //- refresh only the lowest node
204
        //- refresh only the lowest node
213
        if(lowestChanged != null) {
205
        if(lowestChanged != null) {
214
            refresher.nodeChanged(lowestChanged);
206
            refresher.nodeChanged(lowestChanged);
215
        }
207
        }
216
    }
208
    }
217
209
218
    /** Return whether the given project has changed by addition of new test asset group found in the resource delta.
219
     * @param project the project affected by the resource change
220
     * @param delta the resource change delta used to know what the change is.
221
     * @return <code>true</code> if the project has at least one new test asset group proxy node and <code>false</code> otherwise.
222
     */
223
    private boolean getAddedTestAssetGroups(IProject project, IResourceDelta delta) {
224
        List groups = (List)cache.get(project);
225
        List notExistingExt = new LinkedList();
226
        //- initializing with all extensions
227
        for (Iterator it = extensions.keySet().iterator(); it.hasNext();) {
228
            notExistingExt.add(it.next());
229
        }
230
        //- remove from all those that already been handled by a test asset group proxy node
231
        if(groups != null) {
232
            for (Iterator it = groups.iterator(); it.hasNext();) {
233
                TestAssetGroupProxyNode tagpn = (TestAssetGroupProxyNode) it.next();
234
                notExistingExt.remove(tagpn.getExtension());
235
            }
236
        }
237
        boolean projectChanged = false;
238
        //- then try to create a test asset group from this list of not yet handled extension 
239
        for (Iterator it = notExistingExt.iterator(); it.hasNext();) {
240
            String ext = (String) it.next();
241
            ITestAssetGroupProxyNode proxy = getTestAssetGroup(project, ext);
242
            //- if the proxy has been created, that means that the project need to be refreshed
243
            if(proxy != null) {
244
                projectChanged  = true;
245
            }
246
        }
247
        return projectChanged;
248
    }
249
250
    private void prepareCacheRemove(IProject project, TestAssetGroupProxyNode proxy) {
251
        List proxies;
252
        if(removedElements.containsKey(project)) {
253
            //- a test asset group of this project has already been added to removals
254
            proxies = (List)removedElements.get(project);
255
        } else {
256
            proxies = new LinkedList();
257
            removedElements.put(project, proxies);
258
        }
259
        proxies.add(proxy);
260
    }
261
262
    private void completeCacheRemoves() {
263
        Set removed = removedElements.entrySet();
264
        for (Iterator it = removed.iterator(); it.hasNext();) {
265
            Map.Entry element = (Map.Entry) it.next();
266
            IProject project = (IProject)element.getKey();
267
            //- remove saved test asset groups of this project
268
            removeCacheElements(project, (List)element.getValue());
269
        }
270
        //- clear removal cache.
271
        removedElements.clear();
272
    }
273
274
    /**
275
     * Removes all element given by the list argument from the project cached elements
276
     * @param project the project from which the elements are removed
277
     * @param list elments to remove. (those elements should be ITestAssetGroupProxyNode)
278
     */
279
    private void removeCacheElements(IProject project, List list) {
280
        List groups = (List)cache.get(project);
281
        for(Iterator it = list.iterator(); it.hasNext(); ) {
282
            groups.remove(it.next());
283
        }
284
    }
285
    
286
    public void dispose() {
210
    public void dispose() {
287
        FileProxyNodeCache.getInstance().removeResourceListener(this);
211
        FileProxyNodeCache.getInstance().removeResourceListener(this);
288
    }
212
    }
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/FileProxyNodeCache.java (-15 / +71 lines)
Lines 12-17 Link Here
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.HashSet;
Lines 20-25 Link Here
20
import java.util.Set;
21
import java.util.Set;
21
22
22
import org.eclipse.core.resources.IFile;
23
import org.eclipse.core.resources.IFile;
24
import org.eclipse.core.resources.IProject;
23
import org.eclipse.core.resources.IResource;
25
import org.eclipse.core.resources.IResource;
24
import org.eclipse.core.resources.IResourceChangeEvent;
26
import org.eclipse.core.resources.IResourceChangeEvent;
25
import org.eclipse.core.resources.IResourceChangeListener;
27
import org.eclipse.core.resources.IResourceChangeListener;
Lines 30-35 Link Here
30
import org.eclipse.hyades.test.ui.UiPlugin;
32
import org.eclipse.hyades.test.ui.UiPlugin;
31
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
33
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
32
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigatorMessages;
34
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigatorMessages;
35
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.SynchronizedAccess;
33
import org.eclipse.hyades.test.ui.navigator.FileProxyNode;
36
import org.eclipse.hyades.test.ui.navigator.FileProxyNode;
34
import org.eclipse.hyades.test.ui.navigator.IFileProxyFactory;
37
import org.eclipse.hyades.test.ui.navigator.IFileProxyFactory;
35
import org.eclipse.hyades.test.ui.navigator.IPersistableProxyNode;
38
import org.eclipse.hyades.test.ui.navigator.IPersistableProxyNode;
Lines 45-51 Link Here
45
 * @author jgout
48
 * @author jgout
46
 * @since 4.2
49
 * @since 4.2
47
 */
50
 */
48
public class FileProxyNodeCache {
51
public class FileProxyNodeCache extends SynchronizedAccess {
49
52
50
    /**
53
    /**
51
     * Internal class associated to files that do not have a proxy. It is used as a marker
54
     * Internal class associated to files that do not have a proxy. It is used as a marker
Lines 112-117 Link Here
112
    private IFileProxyPersister defaultPersister = new FileProxyMarkerPersister();
115
    private IFileProxyPersister defaultPersister = new FileProxyMarkerPersister();
113
    
116
    
114
    /**
117
    /**
118
     * The set of locked files. A file is locked when a proxy is being computed for this
119
     * file. Only one thread is allowed to access a file at one time.
120
     */
121
    private Collection locks = Collections.synchronizedList(new ArrayList(TestNavigator.BACKGROUND_JOB_POOL_SIZE + 1));
122
    
123
    /**
115
     * Singleton instance.
124
     * Singleton instance.
116
     */
125
     */
117
    private static FileProxyNodeCache instance;
126
    private static FileProxyNodeCache instance;
Lines 196-209 Link Here
196
    	IProxyNode proxy = (IProxyNode) proxies.get(file);
205
    	IProxyNode proxy = (IProxyNode) proxies.get(file);
197
        if (proxy == null && file.exists()) {
206
        if (proxy == null && file.exists()) {
198
	    	try {
207
	    	try {
199
	            //- not yet in the cache, try to load it.
208
	    		if (acquireLock(file)) {
200
	            proxy = loadProxy(file);
209
		    		// Test again if file is in the map. It may have been added while we were
201
	            if(proxy == null) {
210
		    		// acquiring the lock.
202
	                //- load failed, create the proxy using a proxy factory.
211
		    		proxy = (IProxyNode) proxies.get(file);
203
	                proxy = createProxy(file);
212
		    		if (proxy == null) {
204
	            }
213
			            //- not yet in the cache, try to load it.
214
			            proxy = loadProxy(file);
215
			            if(proxy == null) {
216
			                //- load failed, create the proxy using a proxy factory.
217
			                proxy = internalCreateProxy(file);
218
			            }
219
		    		}
220
	    		}
205
	    	} catch (Throwable t) {
221
	    	} catch (Throwable t) {
206
	    		proxy = createErrorProxy(file, t);
222
	    		proxy = createErrorProxy(file, t);
223
	    	} finally {
224
	    		releaseLock(file);
207
	    	}
225
	    	}
208
        }
226
        }
209
        if (proxy == nullProxy) return null;
227
        if (proxy == nullProxy) return null;
Lines 245-250 Link Here
245
     * @return the new proxy node ot <code>null</code> if there is no factory that is able to create proxies from this kind of file. 
263
     * @return the new proxy node ot <code>null</code> if there is no factory that is able to create proxies from this kind of file. 
246
     */
264
     */
247
    public IProxyNode createProxy(IFile file) {
265
    public IProxyNode createProxy(IFile file) {
266
    	try {
267
    		if (acquireLock(file)) {
268
	    		proxies.remove(file);
269
	    		return internalCreateProxy(file);
270
    		} else {
271
    			return null;
272
    		}
273
    	} finally {
274
    		releaseLock(file);
275
    	}
276
    }
277
    
278
    private IProxyNode internalCreateProxy(IFile file) {
248
        IProxyNode proxy = null;
279
        IProxyNode proxy = null;
249
        //- get all factories (FileProxyFactory) for this file element
280
        //- get all factories (FileProxyFactory) for this file element
250
        ArrayList factories = TestNavigator.getFileFactoryManager().getFactories(file.getFileExtension());
281
        ArrayList factories = TestNavigator.getFileFactoryManager().getFactories(file.getFileExtension());
Lines 292-297 Link Here
292
		resourceListeners.remove(listener);
323
		resourceListeners.remove(listener);
293
    }
324
    }
294
    
325
    
326
    /**
327
     * Invoked when a project is closed. This method cleans up the map and removes any
328
     * cached files that belong the closed project.
329
     * @param project
330
     */
331
    protected void projectClosed(IProject project) {
332
    	for (Iterator it = proxies.keySet().iterator(); it.hasNext();) {
333
			IFile file = (IFile) it.next();
334
			if (project.equals(file.getProject())) {
335
				it.remove();
336
			}
337
		}
338
    }
339
    
295
    private class ResourceChangeListener implements IResourceChangeListener, IResourceDeltaVisitor {
340
    private class ResourceChangeListener implements IResourceChangeListener, IResourceDeltaVisitor {
296
341
297
    	public ResourceChangeListener() {
342
    	public ResourceChangeListener() {
Lines 306-320 Link Here
306
		    IResourceDelta delta = event.getDelta();
351
		    IResourceDelta delta = event.getDelta();
307
            if(delta == null) return;
352
            if(delta == null) return;
308
            //- first delegates to other listeners registered in the cache.
353
            //- first delegates to other listeners registered in the cache.
354
            IResourceChangeListener[] copy;
309
            synchronized(resourceListeners) {
355
            synchronized(resourceListeners) {
310
	            for (Iterator it = resourceListeners.iterator(); it.hasNext();) {
356
            	copy = (IResourceChangeListener[]) resourceListeners.toArray(new IResourceChangeListener[resourceListeners.size()]);
311
	                IResourceChangeListener listener = (IResourceChangeListener) it.next();
357
            }
312
	                try {
358
            for (int i = 0; i < copy.length; i++) {
313
	                    listener.resourceChanged(event);
359
                try {
314
	                } catch (Throwable e) {
360
                    copy[i].resourceChanged(event);
315
	                    UiPlugin.logError(e);
361
                } catch (Throwable e) {
316
	                }                
362
                    UiPlugin.logError(e);
317
	            }
363
                }                
318
            }
364
            }
319
            //- then cache can handle proxies removal properly.
365
            //- then cache can handle proxies removal properly.
320
			try {
366
			try {
Lines 328-333 Link Here
328
374
329
		public boolean visit(IResourceDelta delta) throws CoreException {
375
		public boolean visit(IResourceDelta delta) throws CoreException {
330
			if (delta.getKind() == IResourceDelta.CHANGED) {
376
			if (delta.getKind() == IResourceDelta.CHANGED) {
377
				if (delta.getResource().getType() == IResource.PROJECT) {
378
					if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
379
						IProject project = (IProject)delta.getResource();
380
						if (!project.isOpen()) {
381
							// Project has been closed.
382
							projectClosed(project);
383
						}
384
					}
385
				}
331
				return delta.getResource().getType() != IResource.FILE;
386
				return delta.getResource().getType() != IResource.FILE;
332
			}
387
			}
333
			if (delta.getKind() == IResourceDelta.REMOVED) {
388
			if (delta.getKind() == IResourceDelta.REMOVED) {
Lines 341-344 Link Here
341
			return false;
396
			return false;
342
		}
397
		}
343
    }
398
    }
399
    
344
}
400
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/TypeProviderManager.java (-9 / +22 lines)
Lines 11-16 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
13
14
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Iterator;
16
import java.util.Map;
17
import java.util.Map;
Lines 22-27 Link Here
22
import org.eclipse.core.runtime.Platform;
23
import org.eclipse.core.runtime.Platform;
23
import org.eclipse.hyades.test.ui.UiPlugin;
24
import org.eclipse.hyades.test.ui.UiPlugin;
24
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
25
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
26
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.ISynchronizedAccess;
27
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.JobSynchronizedAccess;
25
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
28
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
26
import org.eclipse.hyades.test.ui.navigator.ITypeProviderContext;
29
import org.eclipse.hyades.test.ui.navigator.ITypeProviderContext;
27
import org.eclipse.hyades.test.ui.navigator.ITypeProviderProxyNode;
30
import org.eclipse.hyades.test.ui.navigator.ITypeProviderProxyNode;
Lines 37-43 Link Here
37
 */
40
 */
38
public class TypeProviderManager {
41
public class TypeProviderManager {
39
42
40
    static interface ITypeProviderInfo {
43
    static interface ITypeProviderInfo extends ISynchronizedAccess {
41
44
42
        /**
45
        /**
43
         * @return Returns the imageKey.
46
         * @return Returns the imageKey.
Lines 56-62 Link Here
56
59
57
    }
60
    }
58
    
61
    
59
    class ExtensionTypeProviderInfo implements ITypeProviderInfo {
62
    class ExtensionTypeProviderInfo extends JobSynchronizedAccess implements ITypeProviderInfo {
60
        private String name;
63
        private String name;
61
        private String imageKey;
64
        private String imageKey;
62
        /**
65
        /**
Lines 103-109 Link Here
103
        }
106
        }
104
    }
107
    }
105
    
108
    
106
    class DefaultTypeProviderInfo implements ITypeProviderInfo {
109
    class DefaultTypeProviderInfo extends JobSynchronizedAccess implements ITypeProviderInfo {
107
        private DefaultTypeProvider defaultTypeProvider;
110
        private DefaultTypeProvider defaultTypeProvider;
108
        private String name;
111
        private String name;
109
        
112
        
Lines 137-143 Link Here
137
	private ITypeProviderContext context;
140
	private ITypeProviderContext context;
138
141
139
	public TypeProviderManager(ITypeProviderContext context) {
142
	public TypeProviderManager(ITypeProviderContext context) {
140
        providers = new HashMap();
143
        providers = Collections.synchronizedMap(new HashMap());
141
        this.context = context;
144
        this.context = context;
142
        IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint(UiPlugin.getID() + ".testNavigatorTypeProvider"); //$NON-NLS-1$
145
        IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint(UiPlugin.getID() + ".testNavigatorTypeProvider"); //$NON-NLS-1$
143
        if (extPoint != null) {
146
        if (extPoint != null) {
Lines 178-191 Link Here
178
	 * @return a type provider proxy node. 
181
	 * @return a type provider proxy node. 
179
	 */
182
	 */
180
	public ITypeProviderProxyNode getTypeProviderProxyNode(IProject project, String type) {
183
	public ITypeProviderProxyNode getTypeProviderProxyNode(IProject project, String type) {
181
    	if(!providers.containsKey(type)) {
184
		synchronized(providers) {
182
    		//- since no provider registered for the given type, add the default one for the given type in the map
185
	    	if(!providers.containsKey(type)) {
183
            addProvider(type, new DefaultTypeProviderInfo(type));
186
	    		//- since no provider registered for the given type, add the default one for the given type in the map
184
    	}
187
	            addProvider(type, new DefaultTypeProviderInfo(type));
188
	    	}
189
		}
185
        //- By construction, the internal map of providers has a provider for the given type.
190
        //- By construction, the internal map of providers has a provider for the given type.
186
    	ITypeProviderInfo info = (ITypeProviderInfo)providers.get(type);
191
    	ITypeProviderInfo info = (ITypeProviderInfo)providers.get(type);
187
    	//- deleguate to client
192
    	//- deleguate to client
188
    	return info.getProvider().get(project, type);
193
    	ITypeProvider provider = info.getProvider();
194
    	try {
195
    		if (info.acquireLock(project)) {
196
    			return provider.get(project, type);
197
    		}
198
    		return null;
199
    	} finally {
200
    		info.releaseLock(project);
201
    	}
189
	}
202
	}
190
203
191
    /** Returns the name registered in the extension for the given type of element.
204
    /** Returns the name registered in the extension for the given type of element.
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/FileProxyManager.java (-2 / +3 lines)
Lines 11-16 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
13
14
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Iterator;
16
import java.util.Map;
17
import java.util.Map;
Lines 43-50 Link Here
43
    private FileProxyNodeCache fileProxyNodeCache;
44
    private FileProxyNodeCache fileProxyNodeCache;
44
    
45
    
45
	public FileProxyManager() {
46
	public FileProxyManager() {
46
        parents = new HashMap();
47
        parents = Collections.synchronizedMap(new HashMap());
47
        parentsToCache = new HashMap();
48
        parentsToCache = Collections.synchronizedMap(new HashMap());
48
        fileProxyNodeCache = FileProxyNodeCache.getInstance();
49
        fileProxyNodeCache = FileProxyNodeCache.getInstance();
49
	}
50
	}
50
		
51
		
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/TestNavigatorRefresher.java (-31 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: TestNavigatorRefresher.java,v 1.2 2005/04/26 15:55:53 jcanches Exp $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator.proxy;
13
14
import java.util.Iterator;
15
16
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
17
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
18
19
/**
20
 * @author jgout
21
 *
22
 */
23
public class TestNavigatorRefresher implements IProxyNodeListener {
24
25
    public void nodeChanged(Object node) {
26
        for(Iterator it = TestNavigator.getAllInstancesIterator(); it.hasNext();) {
27
            ((TestNavigator)it.next()).nodeChanged(node);
28
        }
29
    }
30
31
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/TestNavigatorProvider.java (+57 lines)
Lines 15-30 Link Here
15
15
16
import org.eclipse.core.resources.IProject;
16
import org.eclipse.core.resources.IProject;
17
import org.eclipse.core.resources.IWorkspaceRoot;
17
import org.eclipse.core.resources.IWorkspaceRoot;
18
import org.eclipse.core.runtime.jobs.Job;
18
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
19
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
19
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
20
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
21
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
20
import org.eclipse.jface.viewers.ITreeContentProvider;
22
import org.eclipse.jface.viewers.ITreeContentProvider;
21
import org.eclipse.jface.viewers.Viewer;
23
import org.eclipse.jface.viewers.Viewer;
22
24
23
/**
25
/**
24
 * @author jgout
26
 * @author jgout
27
 * @author jcanches
25
 */
28
 */
26
public abstract class TestNavigatorProvider extends TestNavigatorLabelProvider implements ITreeContentProvider {
29
public abstract class TestNavigatorProvider extends TestNavigatorLabelProvider implements ITreeContentProvider {
27
30
31
	public static final int CONTEXT_LOADING = 0;
32
	public static final int CONTEXT_INTERACTIVE = 1;
33
	
34
	protected TestNavigator testNavigator;
35
	private int context;
36
37
	public TestNavigatorProvider(TestNavigator testNavigator) {
38
		this.testNavigator = testNavigator;
39
		this.context = CONTEXT_LOADING;
40
	}
41
42
	protected abstract IProxyNodeListener getProxyNodeListener();
43
	
28
	public Object getParent(Object element) {
44
	public Object getParent(Object element) {
29
		if (element instanceof IProxyNode) {
45
		if (element instanceof IProxyNode) {
30
            Object parent = ((IProxyNode) element).getParent();
46
            Object parent = ((IProxyNode) element).getParent();
Lines 71-74 Link Here
71
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
87
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
72
	}
88
	}
73
89
90
	/**
91
	 * Sets the context in which the provider is used. There are two available values:
92
	 * INTERACTIVE and LOADING. The context has an effect on two parameters:
93
	 * 1) responsiveness: time that getChildren() is allowed to spent before returning
94
	 * control. High responsiveness means that the provider will return incomplete nodes,
95
	 * compute the remaining in a background task, and update the navigator when computation
96
	 * is complete. Low responsiveness means that the UI will be blocked for a longer period of
97
	 * time but the returned node are more likely to be complete.
98
	 * 2) computation job priority: the priority allocated to the computation job, if the provider
99
	 * does not manage to compute nodes in the allocated time (responsiveness parameter). This does
100
	 * not affect the thread priority, but helps eclipse decide which job should be launched first.
101
	 * In LOADING mode, responsiveness is high (100ms) and priority is low (NORMAL).
102
	 * In INTERACTIVE mode, responsiveness is lower (800ms) and priority is high (INTERACTIVE).
103
	 */
104
	public void setContext(int context) {
105
		this.context = context;
106
	}
107
	
108
	protected int getContext() {
109
		return context;
110
	}
111
	
112
	protected int getResponseTime() {
113
		switch (context) {
114
		case CONTEXT_INTERACTIVE:
115
			return 800;
116
		case CONTEXT_LOADING:
117
			return 100;
118
		}
119
		return 100;
120
	}
121
	
122
	protected int getJobPriority() {
123
		switch(context) {
124
		case CONTEXT_INTERACTIVE:
125
			return Job.INTERACTIVE;
126
		case CONTEXT_LOADING:
127
			return Job.LONG;
128
		}
129
		return Job.LONG;
130
	}
74
}
131
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/messages.properties (+6 lines)
Lines 15-20 Link Here
15
15
16
TEST_NAVIGATOR_UNNAMED_ELEMENT=unnamed
16
TEST_NAVIGATOR_UNNAMED_ELEMENT=unnamed
17
PROXY_LOADING_ERROR=loading error
17
PROXY_LOADING_ERROR=loading error
18
NODE_PENDING=Pending...
18
OpenWithActionGroup_ActionLabel=Open Wit&h
19
OpenWithActionGroup_ActionLabel=Open Wit&h
19
FileFolderInContainerPaste_NameTwoArg=Copy ({0}) of {1}
20
FileFolderInContainerPaste_NameTwoArg=Copy ({0}) of {1}
20
FileFolderInContainerPaste_NameOneArg=Copy of {0}
21
FileFolderInContainerPaste_NameOneArg=Copy of {0}
Lines 24-26 Link Here
24
FileFolderInContainerPaste_MessageLabel=Enter a new name for ''{0}''
25
FileFolderInContainerPaste_MessageLabel=Enter a new name for ''{0}''
25
TestNavigatorActionGroup_ToggleAction_Text=Link with Editor
26
TestNavigatorActionGroup_ToggleAction_Text=Link with Editor
26
RenameAction_ActionName=Rena&me
27
RenameAction_ActionName=Rena&me
28
PM_INDEXING_TYPE=Indexing type {0}
29
PM_INDEXING_ASSET_GROUP=Indexing .{0} assets
30
PM_EXPLORING_PROJECT=Exploring project {0}
31
PM_EXPLORING_FOLDER=Exploring {0}
32
PM_INDEXING_FILE=Indexing {0}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/ResourceTestNavigatorProvider.java (-10 / +117 lines)
Lines 11-27 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator;
12
package org.eclipse.hyades.test.ui.internal.navigator;
13
13
14
import java.util.ArrayList;
15
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.Iterator;
14
import java.util.List;
18
import java.util.List;
19
import java.util.Map;
15
20
16
import org.eclipse.core.resources.IContainer;
21
import org.eclipse.core.resources.IContainer;
17
import org.eclipse.core.resources.IFile;
22
import org.eclipse.core.resources.IFile;
18
import org.eclipse.core.resources.IProject;
23
import org.eclipse.core.resources.IProject;
19
import org.eclipse.core.resources.IResource;
24
import org.eclipse.core.resources.IResource;
20
import org.eclipse.core.runtime.CoreException;
25
import org.eclipse.core.runtime.CoreException;
21
import org.eclipse.emf.common.util.UniqueEList;
22
import org.eclipse.hyades.test.ui.UiPlugin;
26
import org.eclipse.hyades.test.ui.UiPlugin;
23
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
27
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
28
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.FileProxiesRequest;
29
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.IProxiesRequestListener;
30
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.ProxiesRequest;
24
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
31
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
32
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
33
import org.eclipse.hyades.ui.util.IDisposable;
25
34
26
/** This the resource oriented provider for the test navigator view.
35
/** This the resource oriented provider for the test navigator view.
27
 * @author jgout
36
 * @author jgout
Lines 39-44 Link Here
39
        return fileProxyManager;
48
        return fileProxyManager;
40
    }
49
    }
41
50
51
    public ResourceTestNavigatorProvider(TestNavigator testNavigator) {
52
    	super(testNavigator);
53
    }
54
    
55
    private FileProxyRequests requests = new FileProxyRequests();
56
    
42
	public Object getParent(Object element) {
57
	public Object getParent(Object element) {
43
		if (element instanceof IResource) {
58
		if (element instanceof IResource) {
44
			return ((IResource) element).getParent();
59
			return ((IResource) element).getParent();
Lines 101-107 Link Here
101
	 * @return an array of Object which cn be empty but not null
116
	 * @return an array of Object which cn be empty but not null
102
	 */
117
	 */
103
	private Object[] getContainerChildren(Object parentElement) {
118
	private Object[] getContainerChildren(Object parentElement) {
104
		List children = new UniqueEList();
105
		IResource[] resources = null;
119
		IResource[] resources = null;
106
		IContainer container = ((IContainer) parentElement);
120
		IContainer container = ((IContainer) parentElement);
107
        if (container.isAccessible()) {
121
        if (container.isAccessible()) {
Lines 111-133 Link Here
111
                UiPlugin.logError(e);
125
                UiPlugin.logError(e);
112
                return new Object[0];
126
                return new Object[0];
113
            }
127
            }
128
            List children = new ArrayList(resources.length);
129
            FileProxiesRequest request = requests.getRequest(container, resources);
114
            for (int i = 0, maxi = resources.length; i < maxi; i++) {
130
            for (int i = 0, maxi = resources.length; i < maxi; i++) {
115
                if (!TestNavigator.getFiltersManager().filter(resources[i])) {
131
                if (!TestNavigator.getFiltersManager().filter(resources[i])) {
116
                    if (resources[i].getType() != IResource.FILE) {
132
                	if (resources[i].getType() != IResource.FILE) { 
117
                        //- folders are displayed as they are
133
                		children.add(resources[i]);
118
                        children.add(resources[i]);
134
                	} else {
119
                    } else {
120
                        //- convert the file in something else (proxy)
135
                        //- convert the file in something else (proxy)
121
                        IProxyNode node = fileProxyManager.getProxy((IFile) resources[i], resources[i].getParent());
136
                    	IFile file = (IFile)resources[i];
137
                        IProxyNode node = request.getProxy(file);
122
                        if (!TestNavigator.getFiltersManager().filter(node)) {
138
                        if (!TestNavigator.getFiltersManager().filter(node)) {
123
                            children.add(node);
139
                            children.add(node);
124
                            fileProxyManager.cacheProxy((IFile) resources[i], node);
140
                            fileProxyManager.cacheProxy(file, node);
125
                        }
141
                        }
126
                    }
142
                    }
127
                }
143
                }
128
            }
144
            }
129
        }		
145
            return children.toArray();
130
        return children.toArray();
146
        }
147
        return new Object[0];
148
	}
149
150
    public IProxyNodeListener getProxyNodeListener() {
151
    	return requests;
152
    }
153
154
	private class FileProxyRequests implements IProxiesRequestListener, IDisposable, IProxyNodeListener {
155
		
156
	    private Map containerToRequestMap = Collections.synchronizedMap(new HashMap());
157
	    
158
	    public FileProxiesRequest getRequest(IContainer container, IResource[] members) {
159
	    	FileProxiesRequest request = (FileProxiesRequest)containerToRequestMap.get(container);
160
	    	//if (request != null && !request.isPending()) containerToRequestMap.remove(container);
161
	    	if (request == null) {
162
	    		request = newRequest(container, members);
163
	    	}
164
	    	return request;
165
	    }
166
	    
167
	    public FileProxiesRequest newRequest(IContainer container, IResource[] members) {
168
	    	ArrayList filesToRequest = new ArrayList(members.length);
169
            for (int i = 0, maxi = members.length; i < maxi; i++) {
170
            	if (members[i].getType() == IResource.FILE) {
171
            		if (!TestNavigator.getFiltersManager().filter(members[i])) {
172
                    	filesToRequest.add(members[i]);
173
                    }
174
                }
175
            }
176
            if (filesToRequest.isEmpty()) return null;
177
            FileProxiesRequest req = new FileProxiesRequest(filesToRequest, fileProxyManager, container, testNavigator);
178
            req.setPriority(getJobPriority());
179
            testNavigator.getJobPool().scheduleJob(req);
180
            if (!req.wait(getResponseTime(), this, 2000)) {
181
            	containerToRequestMap.put(container, req);
182
            }
183
            return req;
184
	    }
185
	    
186
		public void proxyComputed(final ProxiesRequest request, Object key) {
187
		}
188
	
189
		public void computationCompleted(ProxiesRequest request) {
190
			IContainer parent = ((FileProxiesRequest)request).getParent();
191
			if (containerToRequestMap.containsKey(parent)) {
192
				// A last refresh is necessary to remove the request from the map.
193
				testNavigator.refresh(parent);
194
			}
195
		}
196
197
		public void dispose() {
198
			// Cancel all running requests. As the atomic action of FileProxiesRequest is
199
			// the computation of a single file proxy, all requests should honor the cancel
200
			// request quite quickly. However we don't wait them to complete (i.e. they may
201
			// be still running while this instance has been disposed).
202
			for (Iterator it = containerToRequestMap.values().iterator(); it.hasNext();) {
203
				FileProxiesRequest request = (FileProxiesRequest) it.next();
204
				request.dispose();
205
				it.remove();
206
			}
207
		}
208
209
		public void proxiesComputed(ProxiesRequest request) {
210
			testNavigator.refresh(((FileProxiesRequest)request).getParent());
211
		}
212
213
		public void nodeChanged(Object node) {
214
			if (node instanceof IContainer) {
215
				FileProxiesRequest req = (FileProxiesRequest) containerToRequestMap.remove(node);
216
				if (req != null) {
217
					// Then we need to cancel the current request. A new one will be spawned
218
					// when getChildren() is invoked on this project.
219
					if (req.cancel(1000)) {
220
						containerToRequestMap.remove(node);
221
						testNavigator.refresh(node);
222
					}
223
				}
224
			}
225
		}
226
		
227
		public void computationCancelled(ProxiesRequest request) {
228
			IContainer parent = ((FileProxiesRequest)request).getParent();
229
			containerToRequestMap.remove(parent);
230
			testNavigator.refresh(parent);
231
		}
232
    	
233
	}
234
	
235
	public void dispose() {
236
		requests.dispose();
237
		super.dispose();
131
	}
238
	}
132
239
133
}
240
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/TestResourceChangeUpdater.java (-9 / +9 lines)
Lines 15-20 Link Here
15
import java.util.Iterator;
15
import java.util.Iterator;
16
import java.util.List;
16
import java.util.List;
17
17
18
import org.eclipse.core.resources.IContainer;
18
import org.eclipse.core.resources.IFile;
19
import org.eclipse.core.resources.IFile;
19
import org.eclipse.core.resources.IResource;
20
import org.eclipse.core.resources.IResource;
20
import org.eclipse.emf.common.util.UniqueEList;
21
import org.eclipse.emf.common.util.UniqueEList;
Lines 23-28 Link Here
23
import org.eclipse.hyades.models.common.common.CMNNamedElement;
24
import org.eclipse.hyades.models.common.common.CMNNamedElement;
24
import org.eclipse.hyades.test.core.util.EMFUtil;
25
import org.eclipse.hyades.test.core.util.EMFUtil;
25
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
26
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
27
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyNodeCache;
26
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
28
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
27
import org.eclipse.hyades.ui.internal.provider.IResourceChangeUpdater;
29
import org.eclipse.hyades.ui.internal.provider.IResourceChangeUpdater;
28
import org.eclipse.jface.viewers.IStructuredSelection;
30
import org.eclipse.jface.viewers.IStructuredSelection;
Lines 167-180 Link Here
167
						addedObjects.add(affectedResources[i]);
169
						addedObjects.add(affectedResources[i]);
168
					}
170
					}
169
				} else {
171
				} else {
170
					//- ask to the file proxy manager the proxy corresponding to this file 
172
					// Don't bother computing the proxy here, since this may be long.
171
					IProxyNode convertedObject = TestNavigator.getFileProxyManager().getProxy((IFile)affectedResources[i], parent);
173
					// Refresh the container so its children are re-computed and the added
172
					if(convertedObject != null) {
174
					// file gets discovered (the discovery will happen in a background job,
173
						//- file has been converted into another object, insert ojbect instead of the file
175
					// which is much better).
174
						addedObjects.add(convertedObject);
176
					IContainer container = affectedResources[i].getParent();
175
                        //- since we succeed to create a proxy from that file, we need to cache it.
177
					changedObjects.add(container);
176
                        TestNavigator.getFileProxyManager().cacheProxy((IFile)affectedResources[i], convertedObject);
177
					}
178
				}
178
				}
179
			}
179
			}
180
		} else {
180
		} else {
Lines 205-211 Link Here
205
					//- in case of file removed, we need to find the right corresponding element in the test navigator
205
					//- in case of file removed, we need to find the right corresponding element in the test navigator
206
					//- try to retrieve a proxy from this file
206
					//- try to retrieve a proxy from this file
207
					//- the object to remove from the navigator is the proxy
207
					//- the object to remove from the navigator is the proxy
208
					IProxyNode proxy = TestNavigator.getFileProxyManager().getProxy((IFile)affectedResources[i], null);
208
					IProxyNode proxy = FileProxyNodeCache.getInstance().getCachedProxy((IFile)affectedResources[i]);
209
					if(proxy != null) {
209
					if(proxy != null) {
210
						removedObjects.add(proxy);
210
						removedObjects.add(proxy);
211
					}
211
					}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/TestNavigator.java (-38 / +81 lines)
Lines 23-28 Link Here
23
import org.eclipse.core.resources.ResourcesPlugin;
23
import org.eclipse.core.resources.ResourcesPlugin;
24
import org.eclipse.core.runtime.IAdaptable;
24
import org.eclipse.core.runtime.IAdaptable;
25
import org.eclipse.core.runtime.Path;
25
import org.eclipse.core.runtime.Path;
26
import org.eclipse.core.runtime.Platform;
26
import org.eclipse.emf.common.util.URI;
27
import org.eclipse.emf.common.util.URI;
27
import org.eclipse.emf.ecore.EObject;
28
import org.eclipse.emf.ecore.EObject;
28
import org.eclipse.emf.ecore.resource.Resource;
29
import org.eclipse.emf.ecore.resource.Resource;
Lines 40-48 Link Here
40
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
41
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
41
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyNodeCache;
42
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyNodeCache;
42
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TestAssetGroupProxyManager;
43
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TestAssetGroupProxyManager;
43
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TestNavigatorRefresher;
44
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TypeProviderManager;
44
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TypeProviderManager;
45
import org.eclipse.hyades.test.ui.internal.util.ContextIds;
45
import org.eclipse.hyades.test.ui.internal.util.ContextIds;
46
import org.eclipse.hyades.test.ui.internal.util.FixedSizeJobPool;
46
import org.eclipse.hyades.test.ui.navigator.CMNNamedElementProxyNode;
47
import org.eclipse.hyades.test.ui.navigator.CMNNamedElementProxyNode;
47
import org.eclipse.hyades.test.ui.navigator.EObjectProxyNode;
48
import org.eclipse.hyades.test.ui.navigator.EObjectProxyNode;
48
import org.eclipse.hyades.test.ui.navigator.FileProxyNode;
49
import org.eclipse.hyades.test.ui.navigator.FileProxyNode;
Lines 110-115 Link Here
110
111
111
    public static final int VIEW_ID_LOGICAL = 1;
112
    public static final int VIEW_ID_LOGICAL = 1;
112
113
114
    /**
115
     * The size of the background job pool. This is the maximal number of background
116
     * jobs that can run at the same time.
117
     */
118
    public static final int BACKGROUND_JOB_POOL_SIZE = 3;
119
   
113
    private int currentViewIndex;
120
    private int currentViewIndex;
114
121
115
    /**
122
    /**
Lines 149-156 Link Here
149
    /** actions to switch between both logical and resource views. */
156
    /** actions to switch between both logical and resource views. */
150
    private ToggleViewAction[] viewActions;
157
    private ToggleViewAction[] viewActions;
151
158
152
    /** Label and content provider for both logical and resource views. */
159
    /** Current content and label provider. */
153
    private TestNavigatorProvider[] providers;
160
    private TestNavigatorProvider currentProvider;
154
161
155
    // - constants are used in the IDialogSettings
162
    // - constants are used in the IDialogSettings
156
    protected final static String SET_SHOW_FOLDERS = "SHOW_FOLDERS"; //$NON-NLS-1$
163
    protected final static String SET_SHOW_FOLDERS = "SHOW_FOLDERS"; //$NON-NLS-1$
Lines 164-170 Link Here
164
    protected final static int CP_FOLDER = 0;
171
    protected final static int CP_FOLDER = 0;
165
172
166
    protected final static int CP_LOGICAL = 1;
173
    protected final static int CP_LOGICAL = 1;
167
174
    
168
    private List rootLogicalFolders;
175
    private List rootLogicalFolders;
169
176
170
    private boolean showingFolders = true;
177
    private boolean showingFolders = true;
Lines 172-178 Link Here
172
    private boolean showingEObjectChildren = true;
179
    private boolean showingEObjectChildren = true;
173
180
174
    private ResourceChangeUpdaterProvider resourceChangeUpdaterProvider;
181
    private ResourceChangeUpdaterProvider resourceChangeUpdaterProvider;
175
182
    
183
    private FixedSizeJobPool jobPool;
184
    
176
    /**
185
    /**
177
     * Constructor for TestNavigator
186
     * Constructor for TestNavigator
178
     */
187
     */
Lines 180-186 Link Here
180
        super(ITestNavigator.ID);
189
        super(ITestNavigator.ID);
181
190
182
        viewActions = new ToggleViewAction[] { new ToggleViewAction(this, VIEW_ID_RESOURCE), new ToggleViewAction(this, VIEW_ID_LOGICAL) };
191
        viewActions = new ToggleViewAction[] { new ToggleViewAction(this, VIEW_ID_RESOURCE), new ToggleViewAction(this, VIEW_ID_LOGICAL) };
183
        providers = new TestNavigatorProvider[] { new ResourceTestNavigatorProvider(), new LogicalTestNavigatorProvider() };
184
    }
192
    }
185
193
186
    /**
194
    /**
Lines 263-275 Link Here
263
    /**
271
    /**
264
     * @return Returns the typeProviderProxyManager.
272
     * @return Returns the typeProviderProxyManager.
265
     */
273
     */
266
    public static TypeProviderManager getTypeProviderManager() {
274
    public synchronized static TypeProviderManager getTypeProviderManager() {
267
        if (typeProviderManager == null) {
275
        if (typeProviderManager == null) {
268
            typeProviderManager = new TypeProviderManager(getTypeProviderContext());
276
            typeProviderManager = new TypeProviderManager(getTypeProviderContext());
269
        }
277
        }
270
        return typeProviderManager;
278
        return typeProviderManager;
271
    }
279
    }
272
    
280
    
281
    private static class AllInstancesRefresher implements IProxyNodeListener {
282
        public void nodeChanged(Object node) {
283
            for(Iterator it = TestNavigator.getAllInstancesIterator(); it.hasNext();) {
284
                ((TestNavigator)it.next()).nodeChanged(node);
285
            }
286
        }
287
    }
288
    
273
    /**
289
    /**
274
     * This class is responsible to give the correct context to the used ITypeProvider (DefaultTypeProvider).
290
     * This class is responsible to give the correct context to the used ITypeProvider (DefaultTypeProvider).
275
     * It stores the file proxy manager of the logical view to be used by the ITypeProvider,
291
     * It stores the file proxy manager of the logical view to be used by the ITypeProvider,
Lines 283-289 Link Here
283
        }
299
        }
284
		public IProxyNodeListener getProxyNodeListener() {
300
		public IProxyNodeListener getProxyNodeListener() {
285
			if (refresher == null) {
301
			if (refresher == null) {
286
				refresher = new TestNavigatorRefresher();
302
				refresher = new AllInstancesRefresher();
287
			}
303
			}
288
			return refresher;
304
			return refresher;
289
		}
305
		}
Lines 299-307 Link Here
299
    	return typeProviderContext;
315
    	return typeProviderContext;
300
    }
316
    }
301
317
302
    public static TestAssetGroupProxyManager getTestAssetGroupProxyManager() {
318
    public synchronized static TestAssetGroupProxyManager getTestAssetGroupProxyManager() {
303
        if (testAssetGroupProxyManager == null) {
319
        if (testAssetGroupProxyManager == null) {
304
            testAssetGroupProxyManager = new TestAssetGroupProxyManager();
320
            testAssetGroupProxyManager = new TestAssetGroupProxyManager(getTypeProviderContext());
305
        }
321
        }
306
        return testAssetGroupProxyManager;
322
        return testAssetGroupProxyManager;
307
    }
323
    }
Lines 310-315 Link Here
310
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
326
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
311
     */
327
     */
312
    public void dispose() {
328
    public void dispose() {
329
    	if (jobPool != null) {
330
    		// Disposing the job pool cancels all pending jobs that have net been yet
331
    		// scheduled.
332
    		jobPool.dispose();
333
    	}
334
    	// Cancel all "TestNavigatorJob"s, i.e. jobs that belongs the "family" represented by
335
    	// this instance. This cancels all jobs that have already been scheduled.
336
    	Platform.getJobManager().cancel(this);
337
    	
313
        if (rootLogicalFolders != null)
338
        if (rootLogicalFolders != null)
314
            rootLogicalFolders.clear();
339
            rootLogicalFolders.clear();
315
        removeTestNavigator(this);
340
        removeTestNavigator(this);
Lines 319-331 Link Here
319
        resourceChangeUpdaterProvider.dispose();
344
        resourceChangeUpdaterProvider.dispose();
320
        resourceChangeUpdaterProvider = null;
345
        resourceChangeUpdaterProvider = null;
321
346
322
        for (int i = 0; i < providers.length; i++) {
323
            if(providers[i] != null){
324
                providers[i].dispose();
325
                providers[i] = null;
326
            }
327
        }
328
        
329
        if(testAssetGroupProxyManager != null){
347
        if(testAssetGroupProxyManager != null){
330
            testAssetGroupProxyManager.dispose();
348
            testAssetGroupProxyManager.dispose();
331
            testAssetGroupProxyManager=null;
349
            testAssetGroupProxyManager=null;
Lines 362-368 Link Here
362
        addTestNavigator(this);
380
        addTestNavigator(this);
363
    }
381
    }
364
382
365
    /**
383
    private void adjustCurrentProviderContext(boolean isLoading) {
384
		if (currentProvider != null) {
385
			if (isLoading) {
386
				currentProvider.setContext(TestNavigatorProvider.CONTEXT_LOADING);
387
			} else {
388
				currentProvider.setContext(TestNavigatorProvider.CONTEXT_INTERACTIVE);
389
			}
390
		}
391
	}
392
393
	/**
366
     * Returns the file associated to a given object that is located in this
394
     * Returns the file associated to a given object that is located in this
367
     * navigator.
395
     * navigator.
368
     * 
396
     * 
Lines 496-502 Link Here
496
        };
524
        };
497
        treeViewer.addFilter(viewerFilter);
525
        treeViewer.addFilter(viewerFilter);
498
526
499
        resourceChangeUpdaterProvider = new ResourceChangeUpdaterProvider.UIUpdaterProvider();
527
        resourceChangeUpdaterProvider = new ResourceChangeUpdaterProvider.UIUpdaterProvider(false);
500
        FileProxyNodeCache.getInstance().addResourceListener(resourceChangeUpdaterProvider);
528
        FileProxyNodeCache.getInstance().addResourceListener(resourceChangeUpdaterProvider);
501
        IResourceChangeUpdater resourceChangeUpdater = new TestResourceChangeUpdater(this);
529
        IResourceChangeUpdater resourceChangeUpdater = new TestResourceChangeUpdater(this);
502
        resourceChangeUpdaterProvider.setResourceChangeUpdater(resourceChangeUpdater);
530
        resourceChangeUpdaterProvider.setResourceChangeUpdater(resourceChangeUpdater);
Lines 508-516 Link Here
508
     * @param treeViewer
536
     * @param treeViewer
509
     */
537
     */
510
    private void updateTestNavigatorProvider(TreeViewer treeViewer) {
538
    private void updateTestNavigatorProvider(TreeViewer treeViewer) {
511
        treeViewer.setContentProvider(providers[currentViewIndex]);
539
    	if (currentViewIndex == VIEW_ID_RESOURCE) {
540
    		currentProvider = new ResourceTestNavigatorProvider(this);
541
    	} else {
542
    		currentProvider = new LogicalTestNavigatorProvider(this);
543
    	}
544
    	adjustCurrentProviderContext(true);
545
        treeViewer.setContentProvider(currentProvider);
512
        ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator();
546
        ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator();
513
        treeViewer.setLabelProvider(new TestDecoratingLabelProvider(providers[currentViewIndex], decorator, this));
547
        ILabelProvider labelProvider = new TestDecoratingLabelProvider(currentProvider, decorator, this); 
548
        treeViewer.setLabelProvider(labelProvider);
549
        adjustCurrentProviderContext(false);
514
    }
550
    }
515
551
516
    /**
552
    /**
Lines 524-544 Link Here
524
        if (input instanceof IFileEditorInput) {
560
        if (input instanceof IFileEditorInput) {
525
            IFileEditorInput fileInput = (IFileEditorInput) input;
561
            IFileEditorInput fileInput = (IFileEditorInput) input;
526
            IFile file = fileInput.getFile();
562
            IFile file = fileInput.getFile();
527
563
            selectReveal(new StructuredSelection(file));
528
            Resource resource = EMFUtil.getResource(null, file);
564
            return true;
529
            if ((resource != null) && (!resource.getContents().isEmpty())) {
530
                IStructuredSelection structuredSelection = getStructuredSelection();
531
                if (structuredSelection.size() == 1) {
532
                    Object currentSelection = structuredSelection.getFirstElement();
533
                    if (currentSelection instanceof EObject) {
534
                        if (((EObject) currentSelection).eResource() == resource)
535
                            return true;
536
                    }
537
                }
538
539
                selectReveal(new StructuredSelection(resource.getContents().get(0)));
540
                return true;
541
            }
542
        }
565
        }
543
566
544
        return false;
567
        return false;
Lines 962-971 Link Here
962
        }
985
        }
963
    }
986
    }
964
987
988
    /**
989
     * Invoked by test providers to notify that a node and its children should be refreshed.
990
     */
965
    public void nodeChanged(Object node) {
991
    public void nodeChanged(Object node) {
966
        Display.getDefault().asyncExec(new Refresher(node));
992
    	currentProvider.getProxyNodeListener().nodeChanged(node);
993
        refresh(node);
967
    }
994
    }
968
995
    
996
    /**
997
     * Internal method used by the provider to refresh a node and its sub-nodes. The difference
998
     * with nodeChanged(Object) is that this method does not notify the provider with the change.
999
     * @param node
1000
     */
1001
    /*package*/void refresh(Object node) {
1002
    	Display.getDefault().asyncExec(new Refresher(node));
1003
    }
1004
    
969
    /**
1005
    /**
970
     * @return
1006
     * @return
971
     */
1007
     */
Lines 1008-1011 Link Here
1008
            return "[Invalid Descriptor]"; //$NON-NLS-1$ 
1044
            return "[Invalid Descriptor]"; //$NON-NLS-1$ 
1009
        }
1045
        }
1010
    }
1046
    }
1047
1048
	public FixedSizeJobPool getJobPool() {
1049
		if (jobPool == null) {
1050
			 jobPool = new FixedSizeJobPool(BACKGROUND_JOB_POOL_SIZE);
1051
		}
1052
		return jobPool;
1053
	}
1011
}
1054
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/LogicalTestNavigatorProvider.java (-16 / +172 lines)
Lines 11-30 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.hyades.test.ui.internal.navigator;
12
package org.eclipse.hyades.test.ui.internal.navigator;
13
13
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
14
import java.util.Iterator;
17
import java.util.Iterator;
15
import java.util.LinkedList;
16
import java.util.List;
18
import java.util.List;
19
import java.util.Map;
17
20
18
import org.eclipse.core.resources.IProject;
21
import org.eclipse.core.resources.IProject;
22
import org.eclipse.core.resources.IResource;
19
import org.eclipse.hyades.test.ui.TestUIExtension;
23
import org.eclipse.hyades.test.ui.TestUIExtension;
20
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
24
import org.eclipse.hyades.test.ui.internal.navigator.proxy.FileProxyManager;
25
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.IProxiesRequestListener;
26
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.LogicalProjectProxiesRequest;
27
import org.eclipse.hyades.test.ui.internal.navigator.proxy.async.ProxiesRequest;
21
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
28
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
22
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
29
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
23
import org.eclipse.hyades.test.ui.navigator.ITypeProviderProxyNode;
30
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
24
import org.eclipse.hyades.ui.extension.IAssociationConstants;
31
import org.eclipse.hyades.ui.extension.IAssociationConstants;
25
import org.eclipse.hyades.ui.extension.IAssociationDescriptor;
32
import org.eclipse.hyades.ui.extension.IAssociationDescriptor;
26
import org.eclipse.hyades.ui.extension.IAssociationMapping;
33
import org.eclipse.hyades.ui.extension.IAssociationMapping;
27
import org.eclipse.hyades.ui.internal.extension.AssociationMappingRegistry;
34
import org.eclipse.hyades.ui.internal.extension.AssociationMappingRegistry;
35
import org.eclipse.hyades.ui.util.IDisposable;
36
import org.eclipse.swt.graphics.Image;
28
37
29
/** This is the provider of the logical test navigator.
38
/** This is the provider of the logical test navigator.
30
 * @author jgout
39
 * @author jgout
Lines 35-40 Link Here
35
    /** The file proxy manager to acces to proxies from files */
44
    /** The file proxy manager to acces to proxies from files */
36
    private static FileProxyManager fileProxyManager = new FileProxyManager();
45
    private static FileProxyManager fileProxyManager = new FileProxyManager();
37
    
46
    
47
    private ProxiesRequests requests = new ProxiesRequests();
48
    
38
    /**
49
    /**
39
     * @return Returns the fileProxyManager.
50
     * @return Returns the fileProxyManager.
40
     */
51
     */
Lines 42-47 Link Here
42
        return fileProxyManager;
53
        return fileProxyManager;
43
    }
54
    }
44
55
56
    public LogicalTestNavigatorProvider(TestNavigator testNavigator) {
57
    	super(testNavigator);
58
    }
59
    
45
	public Object getParent(Object element) {
60
	public Object getParent(Object element) {
46
        Object parent = super.getParent(element);
61
        Object parent = super.getParent(element);
47
        if (parent instanceof FileProxyManager.IUnboundedParent) {
62
        if (parent instanceof FileProxyManager.IUnboundedParent) {
Lines 63-96 Link Here
63
	}
78
	}
64
	
79
	
65
	public Object[] getChildren(Object parentElement) {
80
	public Object[] getChildren(Object parentElement) {
66
		if(parentElement instanceof IProject && ((IProject)parentElement).isOpen()) {
81
		if(parentElement instanceof IProject) {
67
			//- for the given project we need to build all the type provider proxy node trees.
82
			IProject project = (IProject)parentElement;
68
			 List providers = getTypeProviders((IProject)parentElement);
83
			if (project.isAccessible()) {
69
			//- append all test asset groups found in this project
84
				Collection types = getTypes();
70
			List groups = TestNavigator.getTestAssetGroupProxyManager().getTestAssetGroups((IProject)parentElement);
85
				Collection extensions = getExtensions();
71
			return mergeChildren(providers, groups);
86
				ArrayList ret = new ArrayList(types.size() + extensions.size());
87
				LogicalProjectProxiesRequest request = requests.getRequest(project, types, extensions);
88
				for (Iterator it = types.iterator(); it.hasNext();) {
89
					String type = (String) it.next();
90
					IProxyNode proxy = request.getProxy(LogicalProjectProxiesRequest.TYPE_PROVIDER_PREFIX + type);
91
					if (proxy != null) {
92
						ret.add(proxy);
93
					}
94
				}
95
				for (Iterator it = extensions.iterator(); it.hasNext();) {
96
					String ext = (String) it.next();
97
					IProxyNode proxy = request.getProxy(LogicalProjectProxiesRequest.TEST_ASSET_EXTENSION_PREFIX + ext);
98
					if (proxy != null) {
99
						ret.add(proxy);
100
					}
101
				}
102
				if (request.isPending()) {
103
					ret.add(0, new PendingProxy(project));
104
				}
105
				return ret.toArray();
106
			}
107
			return new Object[0];
72
		}
108
		}
73
		return super.getChildren(parentElement);
109
		return super.getChildren(parentElement);
74
	}
110
	}
75
111
76
	private List getTypeProviders(IProject project) {
112
	private Collection getTypes() {
77
        List typeProviders = new LinkedList();
78
        //- for all defined test suite types, call the type provider factory
79
        AssociationMappingRegistry registry = (AssociationMappingRegistry)TestUIExtension.getTestSuiteMappingRegistry();
113
        AssociationMappingRegistry registry = (AssociationMappingRegistry)TestUIExtension.getTestSuiteMappingRegistry();
80
        IAssociationMapping associationMapping = registry.getAssociationMapping(IAssociationConstants.EP_TYPE_DESCRIPTIONS);
114
        IAssociationMapping associationMapping = registry.getAssociationMapping(IAssociationConstants.EP_TYPE_DESCRIPTIONS);
81
        String[] types = associationMapping.getTypes();
115
        String[] types = associationMapping.getTypes();
116
        List typeProviders = new ArrayList(types.length);
82
        for (int i = 0; i < types.length; i++) {
117
        for (int i = 0; i < types.length; i++) {
83
        	IAssociationDescriptor descriptor = associationMapping.getDefaultAssociationDescriptor(types[i]);
118
        	IAssociationDescriptor descriptor = associationMapping.getDefaultAssociationDescriptor(types[i]);
84
        	if (descriptor != null) {
119
        	if (descriptor != null) {
85
	            ITypeProviderProxyNode proxy = TestNavigator.getTypeProviderManager().getTypeProviderProxyNode(project, types[i]);
120
                typeProviders.add(types[i]);
86
	            if(proxy != null) {
87
	                typeProviders.add(proxy);
88
	            }
89
        	}
121
        	}
90
        }
122
        }
91
        return typeProviders;
123
        return typeProviders;
92
    }
124
    }
93
125
	
126
	private Collection getExtensions() {
127
		return TestNavigator.getTestAssetGroupProxyManager().getExtensions();
128
	}
129
	
94
    private Object[] mergeChildren(List providers, List groups) {
130
    private Object[] mergeChildren(List providers, List groups) {
95
		Object[] children = new Object[providers.size()+groups.size()];
131
		Object[] children = new Object[providers.size()+groups.size()];
96
		int i = 0;
132
		int i = 0;
Lines 102-107 Link Here
102
		}
138
		}
103
		return children;
139
		return children;
104
	}
140
	}
141
    
142
    public IProxyNodeListener getProxyNodeListener() {
143
    	return requests;
144
    }
145
146
	public void dispose() {
147
		requests.dispose();
148
		super.dispose();
149
	}
150
151
    class ProxiesRequests implements IProxiesRequestListener, IProxyNodeListener, IDisposable {
152
    	
153
    	private Map projectToRequestMap = new HashMap();
154
    	
155
    	public LogicalProjectProxiesRequest getRequest(IProject project, Collection types, Collection extensions) {
156
    		LogicalProjectProxiesRequest req = (LogicalProjectProxiesRequest)projectToRequestMap.get(project);
157
    		if (req == null) {
158
    			ArrayList requests = new ArrayList(types.size() + extensions.size());
159
    			for(Iterator it = types.iterator(); it.hasNext(); ) {
160
    				requests.add(LogicalProjectProxiesRequest.TYPE_PROVIDER_PREFIX + (String)it.next());
161
    			}
162
    			for(Iterator it = extensions.iterator(); it.hasNext(); ) {
163
    				requests.add(LogicalProjectProxiesRequest.TEST_ASSET_EXTENSION_PREFIX + (String)it.next());
164
    			}
165
    			req = new LogicalProjectProxiesRequest(requests, TestNavigator.getTypeProviderManager(), TestNavigator.getTestAssetGroupProxyManager(), project, testNavigator);
166
    			req.setPriority(getJobPriority());
167
    			testNavigator.getJobPool().scheduleJob(req);
168
                if (!req.wait(getResponseTime(), this, 2000)) {
169
                	projectToRequestMap.put(project, req);
170
                }
171
    		}
172
    		return req;
173
    	}
174
    	
175
		public void computationCompleted(ProxiesRequest request) {
176
			IProject project = ((LogicalProjectProxiesRequest)request).getProject();
177
			if (projectToRequestMap.containsKey(project)) {
178
				// A last refresh is necessary to remove the "Pending..." node.
179
				testNavigator.refresh(project);
180
			}
181
		}
182
183
		public void proxiesComputed(ProxiesRequest request) {
184
			IProject project = ((LogicalProjectProxiesRequest)request).getProject();
185
			testNavigator.refresh(project);
186
		}
105
187
188
		public void proxyComputed(ProxiesRequest request, Object key) {
189
		}
190
191
		public void nodeChanged(Object node) {
192
			if (node instanceof IProject) {
193
				LogicalProjectProxiesRequest req = (LogicalProjectProxiesRequest) projectToRequestMap.get(node);
194
				if (req != null) {
195
					// Then we need to cancel the current request. A new one will be spawned
196
					// when getChildren() is invoked on this project.
197
					if (req.cancel(1000)) {
198
						projectToRequestMap.remove(node);
199
						testNavigator.refresh(node);
200
					}
201
				}
202
			}
203
		}
204
205
		public void computationCancelled(ProxiesRequest request) {
206
			IProject project = ((LogicalProjectProxiesRequest)request).getProject();
207
			projectToRequestMap.remove(project);
208
			testNavigator.refresh(project);
209
		}
210
    	
211
		public void dispose() {
212
			// Cancel all running requests. Note that a LogicalProjectProxiesRequest may take
213
			// considerable time before it can honor the cancel request. So in general the
214
			// request may be still running for a long time after this instance has been
215
			// disposed.
216
			// If the user switches from the logical view to the resource view, and then back
217
			// to the logical view, this provider is disposed, and then another instance gets
218
			// created. As a result, two requests for the same project may be running: the old
219
			// one, which has been cancelled but has not stopped yet, and the new one. This
220
			// will look as if two jobs are doing the same thing at the same time. However what
221
			// really happens is that the new job is blocked on a wait until the old request 
222
			// completes, and then the new job automatically leverages the computation performed
223
			// by the old one, so there is no CPU waste. The only drawback is that the new job
224
			// occupies a slot in the Test navigator job pool.
225
			for (Iterator it = projectToRequestMap.values().iterator(); it.hasNext();) {
226
				ProxiesRequest request = (ProxiesRequest) it.next();
227
				request.dispose();
228
				it.remove();
229
			}
230
		}
231
232
    }
233
    
234
    private static class PendingProxy implements IProxyNode {
235
    	public PendingProxy(Object parent) {
236
    		this.parent = parent;
237
    	}
238
    	private Object parent;
239
    	private static IProxyNode[] NO_CHILDREN = new IProxyNode[0];
240
		public IProxyNode[] getChildren() {
241
			return NO_CHILDREN;
242
		}
243
		public Image getImage() {
244
			return null;
245
		}
246
		public Object getParent() {
247
			return parent;
248
		}
249
		public String getText() {
250
			return TestNavigatorMessages.NODE_PENDING;
251
		}
252
		public Object getAdapter(Class adapter) {
253
			return null;
254
		}
255
		public String getIdentifier() {
256
			return "~"; //$NON-NLS-1$
257
		}
258
		public IResource getUnderlyingResource() {
259
			return null;
260
		}
261
    }
106
262
107
}
263
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/TestNavigatorMessages.java (+6 lines)
Lines 26-31 Link Here
26
26
27
    public static String TEST_NAVIGATOR_UNNAMED_ELEMENT;
27
    public static String TEST_NAVIGATOR_UNNAMED_ELEMENT;
28
    public static String PROXY_LOADING_ERROR;
28
    public static String PROXY_LOADING_ERROR;
29
    public static String NODE_PENDING;
29
    public static String OpenWithActionGroup_ActionLabel;
30
    public static String OpenWithActionGroup_ActionLabel;
30
    public static String FileFolderInContainerPaste_NameTwoArg;
31
    public static String FileFolderInContainerPaste_NameTwoArg;
31
    public static String FileFolderInContainerPaste_NameOneArg;
32
    public static String FileFolderInContainerPaste_NameOneArg;
Lines 35-38 Link Here
35
    public static String FileFolderInContainerPaste_MessageLabel;
36
    public static String FileFolderInContainerPaste_MessageLabel;
36
    public static String TestNavigatorActionGroup_ToggleAction_Text;
37
    public static String TestNavigatorActionGroup_ToggleAction_Text;
37
    public static String RenameAction_ActionName;
38
    public static String RenameAction_ActionName;
39
    public static String PM_INDEXING_TYPE;
40
    public static String PM_INDEXING_ASSET_GROUP;
41
    public static String PM_EXPLORING_PROJECT;
42
    public static String PM_EXPLORING_FOLDER;
43
    public static String PM_INDEXING_FILE;
38
}
44
}
(-)src/org/eclipse/hyades/test/ui/navigator/ITypeProvider.java (-9 / +19 lines)
Lines 12-23 Link Here
12
package org.eclipse.hyades.test.ui.navigator;
12
package org.eclipse.hyades.test.ui.navigator;
13
13
14
import org.eclipse.core.resources.IProject;
14
import org.eclipse.core.resources.IProject;
15
import org.eclipse.hyades.ui.util.IDisposable;
15
16
16
/** 
17
/** 
17
 *  Type providers are responsible to collect and organize all elements of a given type
18
 * Type providers are responsible to collect and organize all elements of a given type
18
 *  located in a project. The returned node is shown in the logical view of the test navigator. 
19
 * located in a project. The returned node is shown in the logical view of the test navigator. 
19
 *  Implementors are responsible to manage the proxy nodes produced as they want 
20
 * The returned proxy nodes lifecyle is under the type provider responsibility. If the type provider
20
 *  (default implementation uses a cache to increase performance).  
21
 * implements the {@link IDisposable} interface, the {@link IDisposable#dispose()} will
22
 * be invoked when the provider is no longer necessary.
23
 * From a performance perspective, it is better for a type provider implementation to cache
24
 * its returned values, unless their computing is trivial.
25
 * Type providers also have the responsibility of providing updates of their content by
26
 * using the provided context's {@link IProxyNodeListener}.
21
 *  
27
 *  
22
 * @author jgout
28
 * @author jgout
23
 * @since 4.0
29
 * @since 4.0
Lines 25-41 Link Here
25
public interface ITypeProvider /*extends IDisposable*/ {
31
public interface ITypeProvider /*extends IDisposable*/ {
26
    
32
    
27
    /**
33
    /**
28
     * Make some initialization for the type provider.
34
     * Initializes the type provider with a context.
29
     * @param context this parameter contains all inputs necessary to initialize the type provider.
35
     * @param context The context in which the type provider will be used. The context
30
     * For instance, a file proxy manager is set.
36
     * provides various tools and information for the provider to interact with its
37
     * context.
31
     */
38
     */
32
    public void init(ITypeProviderContext context);
39
    public void init(ITypeProviderContext context);
33
40
34
	/** Returns a proxy node hierarchy based of the given type from elements located in the given project.
41
	/**
42
	 * Returns a proxy node hierarchy based of the given type from elements located in the given project.
35
	 * The returned hierarchy will appear as a special type provider node directly under the project.
43
	 * The returned hierarchy will appear as a special type provider node directly under the project.
44
	 * The implementation should not assume anything about the thread it will run in. This method
45
	 * may be invoked from any thread, and concurrent calls to this method may happen. However
46
	 * there will never be two concurrent calls for the same project.
36
	 * @param project the owner of the returned hierarchy. 
47
	 * @param project the owner of the returned hierarchy. 
37
	 * @param type the type of elements that type provider is interested in.
48
	 * @param type the type of elements that type provider is interested in.
38
 	 * 
39
	 * @return a proxy node tree that represents all elements of the given type contained by the given project.
49
	 * @return a proxy node tree that represents all elements of the given type contained by the given project.
40
	 */
50
	 */
41
	public ITypeProviderProxyNode get(IProject project, String type);
51
	public ITypeProviderProxyNode get(IProject project, String type);
(-)src/org/eclipse/hyades/test/ui/internal/util/FixedSizeJobPool.java (+73 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.util;
14
15
import java.util.Collections;
16
import java.util.LinkedList;
17
import java.util.List;
18
19
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
20
import org.eclipse.core.runtime.jobs.IJobChangeListener;
21
import org.eclipse.core.runtime.jobs.Job;
22
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
23
import org.eclipse.hyades.ui.util.IDisposable;
24
25
public class FixedSizeJobPool implements IDisposable {
26
27
	private int maxJobs;
28
	private int runningJobs = 0;
29
	private List queue = Collections.synchronizedList(new LinkedList());
30
	private Object lock = new Object();
31
	private boolean disposed;
32
	
33
	public FixedSizeJobPool(int maxJobs) {
34
		this.maxJobs = maxJobs;
35
	}
36
	
37
	public void scheduleJob(Job job) {
38
		queue.add(job);
39
		synchronized(lock) {
40
			if (runningJobs < maxJobs) {
41
				doScheduleJob();
42
			}
43
		}
44
	}
45
	
46
	private void doScheduleJob() {
47
		if (disposed) return;
48
		if (!queue.isEmpty()) {
49
			Job job = (Job)queue.remove(0);
50
			job.addJobChangeListener(jobListener);
51
			runningJobs++;
52
			job.schedule();
53
		}
54
	}
55
	
56
	private IJobChangeListener jobListener = new JobChangeAdapter() {
57
58
		public void done(IJobChangeEvent event) {
59
			if (event.getResult() != null) {
60
				synchronized(lock) {
61
					runningJobs--;
62
					doScheduleJob();
63
				}
64
			}
65
		}
66
		
67
	};
68
	
69
	public void dispose() {
70
		disposed = true;
71
	}
72
	
73
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/SynchronizedAccess.java (+58 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.Collections;
18
19
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
20
21
/**
22
 * A general purpose synchronizer that allows only one instance to access a given resource.
23
 * This synchronizer is mainly useful in multi-threaded contexts where one thread needs
24
 * exclusive access to a resource.
25
 * @author jcanches
26
 * @since 4.3
27
 */
28
public class SynchronizedAccess implements ISynchronizedAccess {
29
30
	private Collection locks = Collections.synchronizedList(new ArrayList(TestNavigator.BACKGROUND_JOB_POOL_SIZE + 1));
31
	
32
	/* (non-Javadoc)
33
	 * @see org.eclipse.hyades.test.ui.internal.navigator.proxy.async.ISynchronizedAccess#acquireLock(java.lang.Object)
34
	 */
35
	public boolean acquireLock(Object resource) {
36
		synchronized(locks) {
37
    		while (locks.contains(resource)) {
38
    			try {
39
					locks.wait();
40
				} catch (InterruptedException e) {
41
					return false;
42
				}
43
    		}
44
    		locks.add(resource);
45
    		return true;
46
    	}
47
	}
48
	/* (non-Javadoc)
49
	 * @see org.eclipse.hyades.test.ui.internal.navigator.proxy.async.ISynchronizedAccess#releaseLock(java.lang.Object)
50
	 */
51
	public void releaseLock(Object resource) {
52
		synchronized(locks) {
53
    		locks.remove(resource);
54
    		locks.notify();
55
    	}
56
	}
57
58
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/IProxiesRequestListener.java (+52 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
/**
16
 * Listener of ProxiesRequest. This listener is notified of various events that occur in
17
 * the lifecycle of ProxiesRequest instances.
18
 * @author jcanches
19
 * @since 4.3
20
 */
21
public interface IProxiesRequestListener {
22
23
	/**
24
	 * Indicates that a proxy has been computed.
25
	 * @param request The request that was emitted and from which this event is sent.
26
	 * @param key The key whose proxy was computed.
27
	 */
28
	void proxyComputed(ProxiesRequest request, Object key);
29
	
30
	/**
31
	 * Indicates that at least one proxy has been computed since the last call to this method,
32
	 * or since {@link ProxiesRequest#wait(int, IProxyRequestListener, int)} has been called if
33
	 * this method has never been called before. This kind of notification is guaranted not to
34
	 * occur more often than the specified notification interval specified in the wait method,
35
	 * unless there are no more proxies to compute, in this case the notification occurs as soon
36
	 * as the last proxy is computed.
37
	 */
38
	void proxiesComputed(ProxiesRequest request);
39
	
40
	/**
41
	 * Indicates that the request has been completed.
42
	 * @param request The request that was emitted and which is now complete.
43
	 */
44
	void computationCompleted(ProxiesRequest request);
45
	
46
	/**
47
	 * Indicates that the request has been cancelled.
48
	 * @param request The request that was emitted and then cancelled.
49
	 */
50
	void computationCancelled(ProxiesRequest request);
51
	
52
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/TestNavigatorJob.java (+43 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import org.eclipse.core.runtime.jobs.Job;
16
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
17
18
/**
19
 * Abstract job that belongs to a {@link TestNavigator} instance family. When disposed,
20
 * a test navigator cancels all jobs that belongs to its family.
21
 * @author jcanches
22
 * @since 4.3
23
 */
24
public abstract class TestNavigatorJob extends Job {
25
26
	private TestNavigator testNavigator;
27
	
28
	/**
29
	 * Constructs a job that belongs to the specified testNavigator.
30
	 * @param name Name of the job.
31
	 * @param testNavigator Instance of the test navigator that this job is a member of.
32
	 */
33
	protected TestNavigatorJob(String name, TestNavigator testNavigator) {
34
		super(name);
35
		this.testNavigator = testNavigator;
36
	}
37
	
38
	public boolean belongsTo(Object family) {
39
		if (testNavigator.equals(family)) return true;
40
		return super.belongsTo(family);
41
	}
42
43
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/ISynchronizedAccess.java (+37 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
/**
16
 * A synchronized access allows only one instance to access a given resource.
17
 * The typical use is:
18
 * <pre>
19
 * try {
20
 *   if (access.acquireLock(resource)) {
21
 *     // Do something with the resource
22
 *   }
23
 * } finally {
24
 *   access.releaseLock(resource);
25
 * }
26
 * </pre>
27
 * Note that releaseLock must be called EVEN IF acquireLock failed.
28
 * @author jcanches
29
 * @since 4.3
30
 */
31
public interface ISynchronizedAccess {
32
33
	boolean acquireLock(Object resource);
34
35
	void releaseLock(Object resource);
36
37
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/JobSynchronizedAccess.java (+99 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.Collections;
18
import java.util.Iterator;
19
20
import org.eclipse.core.resources.ResourcesPlugin;
21
import org.eclipse.core.runtime.NullProgressMonitor;
22
import org.eclipse.core.runtime.Platform;
23
import org.eclipse.core.runtime.jobs.ISchedulingRule;
24
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
25
26
/**
27
 * An implementation of ISynchronizedAccess specially optimized for running in jobs.
28
 * The platform job manager is used to acquire and release locks, and a job waiting
29
 * for lock acquisition may let another non-blocked job meanwhile.
30
 * @author jcanches
31
 */
32
public class JobSynchronizedAccess implements ISynchronizedAccess {
33
34
	/**
35
	 * The list of rules that this instance currently owns. Must be protected against concurrent
36
	 * access because two jobs may acquire/release a lock for two different resources at the
37
	 * same time. Use an array list as small as the maximum number of background jobs the
38
	 * test navigator may launch, plus one (the UI thread), since there should be not more
39
	 * than this number of threads acquiring a lock at any time.
40
	 */
41
	private Collection rules = Collections.synchronizedList(new ArrayList(TestNavigator.BACKGROUND_JOB_POOL_SIZE + 1));
42
	
43
	/**
44
	 * A rule that ensures exclusive access to a given resource, when it has the same owner.
45
	 * In other words: a given owner can only have one job running within a ResourceRule
46
	 * block for a given resource.
47
	 * @author jcanches
48
	 */
49
	private static class ResourceRule implements ISchedulingRule {
50
51
		private JobSynchronizedAccess owner;
52
		private Object resource;
53
54
		public ResourceRule(JobSynchronizedAccess owner, Object resource) {
55
			this.owner = owner;
56
			this.resource = resource;
57
		}
58
		
59
		public boolean contains(ISchedulingRule rule) {
60
			if (rule == this) return true;
61
			// Any rule than can be contained in the workspace rule is also accepted
62
			return ResourcesPlugin.getWorkspace().getRoot().contains(rule);
63
		}
64
65
		public boolean isConflicting(ISchedulingRule rule) {
66
			if (rule instanceof ResourceRule) {
67
				ResourceRule oRule = (ResourceRule) rule;
68
				return this.owner == oRule.owner && this.resource.equals(oRule.resource);
69
			}
70
			return false;
71
		}
72
	}
73
	
74
	public boolean acquireLock(Object resource) {
75
		ResourceRule rule = new ResourceRule(this, resource);
76
		Platform.getJobManager().beginRule(rule, new NullProgressMonitor());
77
		rules.add(rule);
78
		return true;
79
	}
80
81
	private ResourceRule removeRule(Object resource) {
82
		for (Iterator it = rules.iterator(); it.hasNext();) {
83
			ResourceRule rule = (ResourceRule) it.next();
84
			if (rule.resource.equals(resource)) {
85
				it.remove();
86
				return rule;
87
			}
88
		}
89
		return null;
90
	}
91
	
92
	public void releaseLock(Object resource) {
93
		ResourceRule rule = removeRule(resource);
94
		if (rule != null) {
95
			Platform.getJobManager().endRule(rule);
96
		}
97
	}
98
99
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/FileProxiesRequest.java (+119 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import java.util.Collection;
16
17
import org.eclipse.core.resources.IContainer;
18
import org.eclipse.core.resources.IFile;
19
import org.eclipse.core.resources.IResource;
20
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
21
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigatorMessages;
22
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
23
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
24
import org.eclipse.osgi.util.NLS;
25
import org.eclipse.swt.graphics.Image;
26
27
/**
28
 * A ProxiesRequest that performs the long-running computation of proxy nodes for a
29
 * collection of files, using a FileProxyManager.
30
 * @author jcanches
31
 * @since 4.3
32
 */
33
public class FileProxiesRequest extends ProxiesRequest {
34
35
	private IFileProxyManager fileProxyManager;
36
	private IContainer parent;
37
	
38
	/**
39
	 * Constructs a new request.
40
	 * @param files The list of files for which proxy nodes must be computed.
41
	 * @param fileProxyManager The FileProxyManager used to compute the proxies.
42
	 * @param parent The common parent of all returned proxies.
43
	 */
44
	public FileProxiesRequest(Collection files, IFileProxyManager fileProxyManager, IContainer parent, TestNavigator testNavigator) {
45
		super(NLS.bind(TestNavigatorMessages.PM_EXPLORING_FOLDER, parent.getFullPath().toString()), files, testNavigator);
46
		this.fileProxyManager = fileProxyManager;
47
		this.parent = parent;
48
		//this.setRule(parent);
49
	}
50
	
51
	/**
52
	 * Returns the common parent of all computed proxies.
53
	 * @return the common parent of all computed proxies.
54
	 */
55
	public IContainer getParent() {
56
		return parent;
57
	}
58
	
59
	protected IProxyNode computeProxy(Object key) {
60
		return fileProxyManager.getProxy((IFile)key, parent);
61
	}
62
	
63
	protected IProxyNode getPlaceHolder(Object key) {
64
		return new TemporaryProxy((IFile)key, parent);
65
	}
66
	
67
	protected String getTaskName() {
68
		return NLS.bind(TestNavigatorMessages.PM_EXPLORING_FOLDER, parent.getFullPath().toString());
69
	}
70
	
71
	protected String getSubTaskName(Object key) {
72
		return NLS.bind(TestNavigatorMessages.PM_INDEXING_FILE, ((IFile)key).getName());
73
	}
74
75
	private static class TemporaryProxy implements IProxyNode {
76
77
		private IFile file;
78
		private Object parent;
79
80
		public TemporaryProxy(IFile file, Object parent) {
81
			this.file = file;
82
			this.parent = parent;
83
		}
84
85
		public IProxyNode[] getChildren() {
86
			return new IProxyNode[0];
87
		}
88
89
		public Image getImage() {
90
			// TODO Auto-generated method stub
91
			return null;
92
		}
93
94
		public Object getParent() {
95
			return parent;
96
		}
97
98
		public String getText() {
99
			return file.getName();
100
		}
101
102
		public Object getAdapter(Class adapter) {
103
			if (adapter.isAssignableFrom(IFile.class)) {
104
				return file;
105
			}
106
			return null;
107
		}
108
109
		public String getIdentifier() {
110
			return "#"; //$NON-NLS-1$
111
		}
112
113
		public IResource getUnderlyingResource() {
114
			return file;
115
		}
116
		
117
	}
118
	
119
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/ProxiesRequest.java (+237 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.Collections;
18
import java.util.HashMap;
19
import java.util.Iterator;
20
import java.util.List;
21
import java.util.Map;
22
23
import org.eclipse.core.runtime.IProgressMonitor;
24
import org.eclipse.core.runtime.IStatus;
25
import org.eclipse.core.runtime.OperationCanceledException;
26
import org.eclipse.core.runtime.Status;
27
import org.eclipse.hyades.test.ui.UiPlugin;
28
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
29
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
30
import org.eclipse.hyades.ui.util.IDisposable;
31
32
/**
33
 * A proxies request is a job that performs the computation, for a list
34
 * of keys, of a list of matching proxy nodes. Assuming that the proxies computation is
35
 * a long-running operation, the request can be queried for results at any time, even if
36
 * the result for the given key has not been computed yet. In this case, the request
37
 * returns a placeholder proxy.
38
 * The caller has the choice of waiting until the computation is completed, or not waiting 
39
 * and being notified when the computation progresses, or a mix of the two (i.e. wait for a
40
 * limited time, then switch to a notification method).
41
 * @author jcanches
42
 * @since 4.3
43
 */
44
public abstract class ProxiesRequest extends TestNavigatorJob implements IDisposable {
45
46
	private IProxiesRequestListener listener;
47
	private List requests;
48
	private Map results = Collections.synchronizedMap(new HashMap());
49
	private boolean cancelled;
50
	private long lastNotificationTime;
51
	private long notificationInterval;
52
	private boolean notificationNeeded;
53
	private State state = new State();
54
	private boolean issueCancelNotification = false;
55
56
	protected ProxiesRequest(String name, Collection keys, TestNavigator testNavigator) {
57
		super(name, testNavigator);
58
		this.requests = new ArrayList(keys);
59
		for (Iterator it = keys.iterator(); it.hasNext();) {
60
			Object key = it.next();
61
			IProxyNode proxy = getPlaceHolder(key);
62
			results.put(key, proxy);
63
		}
64
	}
65
66
	public IProxyNode getProxy(Object key) {
67
		return (IProxyNode)results.get(key);
68
	}
69
70
	/**
71
	 * Waits until this request is executed, or until the specified time-out is reached.
72
	 * @param timeOut A time, in milliseconds, that this method should not exceed.
73
	 * @param listener A listener that will be notified, if the request exceeds the time.
74
	 * @param notificationInterval The pace, in milliseconds, at which the method
75
	 * {@link IProxiesRequestListener#proxiesComputed(ProxiesRequest)} should be
76
	 * invoked. Only used if the request exceeds the time allocated.
77
	 * @return Whether the request could be completed in time or not. If not, the listener will
78
	 * be used to get asynchronous update of the state of the request.
79
	 * @throws OperationCanceledException If the method dispose() has already been called.
80
	 */
81
	public boolean wait(int timeOut, IProxiesRequestListener listener, int notificationInterval) throws OperationCanceledException {
82
		if (cancelled) throw new OperationCanceledException();
83
		synchronized(state) {
84
			if (state.getState() != State.COMPLETED) {
85
				try {
86
					state.wait(timeOut);
87
				} catch (InterruptedException e) {
88
					// OK
89
				}
90
			}
91
		}
92
		switch(state.getState()) {
93
		case State.RUNNING:
94
		case State.INITIALIZED: {
95
			this.notificationInterval = notificationInterval;
96
			this.listener = listener;
97
			this.lastNotificationTime = System.currentTimeMillis();
98
			return false;
99
		}
100
		case State.COMPLETED: {
101
			this.listener = listener;
102
			fireNotification(state);
103
			return true;
104
		}
105
		case State.CANCELLED: {
106
			throw new OperationCanceledException();
107
		}
108
		default: {
109
			throw new RuntimeException("Illegal internal state"); //$NON-NLS-1$
110
		}
111
		}
112
	}
113
	
114
	public boolean isPending() {
115
		int s = state.getState();
116
		return s == State.INITIALIZED || s == State.RUNNING;
117
	}
118
119
	/**
120
	 * Performs the computation. This method will trigger notification once a listener
121
	 * is registered. This method should be run in a separate thread.
122
	 */
123
	public IStatus run(IProgressMonitor monitor) {
124
		monitor.beginTask(getTaskName(), requests.size());
125
		synchronized(state) {
126
			state.set(State.RUNNING);
127
		}
128
		Thread.currentThread().setPriority(Thread.MIN_PRIORITY + 2);
129
		try {
130
			for (Iterator it = requests.iterator(); !cancelled && it.hasNext();) {
131
				Object key = it.next();
132
				monitor.subTask(getSubTaskName(key));
133
				_computeProxy(key);
134
				if (monitor.isCanceled()) {
135
					cancelled = true;
136
				} else {
137
					monitor.worked(1);
138
				}
139
			}
140
		} finally {
141
			synchronized(state) {
142
				state.set(cancelled ? State.CANCELLED : State.COMPLETED);
143
				fireNotification(state);
144
				state.notifyAll();
145
			}
146
			monitor.done();
147
		}
148
		return new Status(IStatus.OK, UiPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
149
	}
150
		
151
	private void _computeProxy(Object key) {
152
		IProxyNode proxy = computeProxy(key);
153
		results.put(key, proxy);
154
		fireNotification(key);
155
	}
156
	
157
	abstract protected IProxyNode getPlaceHolder(Object key);
158
	
159
	abstract protected IProxyNode computeProxy(Object key);
160
	
161
	abstract protected String getTaskName();
162
	
163
	abstract protected String getSubTaskName(Object key);
164
	
165
	private void fireNotification(Object key) {
166
		if (listener == null) return;
167
		try {
168
			if (key instanceof State) {
169
				if (state.getState() == State.CANCELLED) {
170
					if (issueCancelNotification) {
171
						listener.computationCancelled(this);
172
					}
173
				} else {
174
					if (notificationNeeded) {
175
						listener.proxiesComputed(this);
176
					}
177
					listener.computationCompleted(this);
178
				}
179
			} else if (!cancelled) {
180
				listener.proxyComputed(this, key);
181
				long now = System.currentTimeMillis();
182
				if (lastNotificationTime + notificationInterval < now) {
183
					listener.proxiesComputed(this);
184
					notificationNeeded = false;
185
					lastNotificationTime = now;
186
				} else {
187
					notificationNeeded = true;
188
				}
189
			}
190
		} catch (Throwable t) {
191
			UiPlugin.logError(t);
192
		}
193
	}
194
	
195
	public boolean cancel(int timeOut) {
196
		cancelled = true;
197
		// Wait a few seconds for the running thread to complete
198
		synchronized(state) {
199
			if (state.getState() == State.RUNNING) {
200
				try {
201
					state.wait(timeOut);
202
				} catch (InterruptedException e) {
203
					// OK
204
				}
205
				if (state.getState() == State.RUNNING) {
206
					issueCancelNotification = true;
207
					return false;
208
				}
209
			}
210
			return true;
211
		}
212
	}
213
	
214
	public void dispose() {
215
		cancel(1);
216
	}
217
	
218
	private static class State {
219
		
220
		public final static int INITIALIZED = 0;
221
		public final static int RUNNING = 1;
222
		public final static int CANCELLED = 2;
223
		public final static int COMPLETED = 3;
224
		
225
		private int state = INITIALIZED;
226
		
227
		public void set(int state) {
228
			this.state = state;
229
		}
230
		
231
		public int getState() {
232
			return state;
233
		}
234
		
235
	}
236
	
237
}
(-)src/org/eclipse/hyades/test/ui/internal/navigator/proxy/async/LogicalProjectProxiesRequest.java (+83 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * $Id: $
8
 * 
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
13
package org.eclipse.hyades.test.ui.internal.navigator.proxy.async;
14
15
import java.util.Collection;
16
17
import org.eclipse.core.resources.IProject;
18
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
19
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigatorMessages;
20
import org.eclipse.hyades.test.ui.internal.navigator.proxy.ITestAssetGroupProxyNode;
21
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TestAssetGroupProxyManager;
22
import org.eclipse.hyades.test.ui.internal.navigator.proxy.TypeProviderManager;
23
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
24
import org.eclipse.hyades.test.ui.navigator.ITypeProviderProxyNode;
25
import org.eclipse.osgi.util.NLS;
26
27
/**
28
 * Request that computes the {@link ITypeProviderProxyNode}s and the {@link ITestAssetGroupProxyNode}
29
 * of a project. A request is a job that can be run as a background job, and that provides
30
 * additional events to the requester.
31
 * @author jcanches
32
 * @since 4.3
33
 */
34
public class LogicalProjectProxiesRequest extends ProxiesRequest {
35
36
	public static final String TYPE_PROVIDER_PREFIX = "#"; //$NON-NLS-1$
37
	public static final String TEST_ASSET_EXTENSION_PREFIX = "~"; //$NON-NLS-1$
38
	
39
	private IProject project;
40
	private TypeProviderManager typeProviderManager;
41
	private TestAssetGroupProxyManager testAssetGroupProxyManager;
42
	
43
	public LogicalProjectProxiesRequest(Collection requests, TypeProviderManager typeProviderManager, TestAssetGroupProxyManager testAssetGroupProxyManager, IProject project, TestNavigator testNavigator) {
44
		super(NLS.bind(TestNavigatorMessages.PM_EXPLORING_PROJECT, project.getName()), requests, testNavigator);
45
		this.project = project;
46
		this.typeProviderManager = typeProviderManager;
47
		this.testAssetGroupProxyManager = testAssetGroupProxyManager;
48
		//this.setRule(project);
49
	}
50
	
51
	public IProject getProject() {
52
		return project;
53
	}
54
	
55
	protected IProxyNode computeProxy(Object key) {
56
		String req = (String)key;
57
		if (req.startsWith(TYPE_PROVIDER_PREFIX)) {
58
			return typeProviderManager.getTypeProviderProxyNode(project, req.substring(1));
59
		} else if (req.startsWith(TEST_ASSET_EXTENSION_PREFIX)) {
60
			return testAssetGroupProxyManager.getTestAssetGroup(project, req.substring(1));
61
		}
62
		return null;
63
	}
64
65
	protected IProxyNode getPlaceHolder(Object key) {
66
		return null;
67
	}
68
69
	protected String getTaskName() {
70
		return NLS.bind(TestNavigatorMessages.PM_EXPLORING_PROJECT, project.getName());
71
	}
72
	
73
	protected String getSubTaskName(Object key) {
74
		String req = (String)key;
75
		if (req.startsWith(TYPE_PROVIDER_PREFIX)) {
76
			return NLS.bind(TestNavigatorMessages.PM_INDEXING_TYPE, req.substring(1));
77
		} else if (req.startsWith(TEST_ASSET_EXTENSION_PREFIX)) {
78
			return NLS.bind(TestNavigatorMessages.PM_INDEXING_ASSET_GROUP, req.substring(1));
79
		}
80
		return ""; //$NON-NLS-1$
81
	}
82
83
}
(-)src/org/eclipse/hyades/test/tools/ui/java/internal/junit/navigator/RegularJUnitProviderProxyNode.java (-17 lines)
Lines 12-20 Link Here
12
12
13
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
13
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
14
14
15
import java.util.Collections;
16
import java.util.List;
17
18
import org.eclipse.hyades.test.ui.TestUIExtension;
15
import org.eclipse.hyades.test.ui.TestUIExtension;
19
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
16
import org.eclipse.hyades.test.ui.internal.navigator.TestNavigator;
20
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
17
import org.eclipse.hyades.test.ui.navigator.IProxyNode;
Lines 59-78 Link Here
59
        setChildren(computeChildren());
56
        setChildren(computeChildren());
60
	}
57
	}
61
	
58
	
62
	protected List computeChildren() {
63
		RegularJUnitProvider provider = getProvider();
64
		if (provider.refresher == null) {
65
			return super.computeChildren();
66
		} else {
67
			provider.scheduleComputeChildren(this);
68
			return Collections.EMPTY_LIST;
69
		}
70
	}
71
	
72
	protected void computeActualChildren() {
73
		setChildren(RegularJUnitProviderProxyNode.super.computeChildren());
74
	}
75
76
	protected JavaElementProxyNode createChildProxy(IJavaElement childElement, IJavaElementDelta delta) {
59
	protected JavaElementProxyNode createChildProxy(IJavaElement childElement, IJavaElementDelta delta) {
77
		if (childElement instanceof IPackageFragmentRoot) {
60
		if (childElement instanceof IPackageFragmentRoot) {
78
			return RegularJUnitSourceFolderProxyNode.create((IPackageFragmentRoot)childElement, this, delta);
61
			return RegularJUnitSourceFolderProxyNode.create((IPackageFragmentRoot)childElement, this, delta);
(-)src/org/eclipse/hyades/test/tools/ui/java/internal/junit/navigator/RegularJUnitProvider.java (-93 lines)
Lines 13-39 Link Here
13
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
13
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
14
14
15
import java.util.HashMap;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.Iterator;
16
import java.util.Iterator;
18
import java.util.Map;
17
import java.util.Map;
19
import java.util.Set;
20
18
21
import org.eclipse.core.resources.IProject;
19
import org.eclipse.core.resources.IProject;
22
import org.eclipse.core.resources.IResource;
23
import org.eclipse.core.resources.IResourceDelta;
20
import org.eclipse.core.resources.IResourceDelta;
24
import org.eclipse.core.runtime.Assert;
25
import org.eclipse.core.runtime.IProgressMonitor;
26
import org.eclipse.core.runtime.IStatus;
27
import org.eclipse.core.runtime.Status;
28
import org.eclipse.core.runtime.jobs.ISchedulingRule;
29
import org.eclipse.core.runtime.jobs.Job;
30
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.IJUnitTestSuiteFactoryEvent;
21
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.IJUnitTestSuiteFactoryEvent;
31
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.IJUnitTestSuiteFactoryListener;
22
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.IJUnitTestSuiteFactoryListener;
32
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteCreatedEvent;
23
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteCreatedEvent;
33
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteDetachedEvent;
24
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteDetachedEvent;
34
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteFactoryEventManager;
25
import org.eclipse.hyades.test.tools.core.internal.java.modelsync.event.JUnitTestSuiteFactoryEventManager;
35
import org.eclipse.hyades.test.tools.ui.ToolsUiPlugin;
36
import org.eclipse.hyades.test.tools.ui.java.internal.JavaMessages;
37
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
26
import org.eclipse.hyades.test.ui.navigator.IFileProxyManager;
38
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
27
import org.eclipse.hyades.test.ui.navigator.IProxyNodeListener;
39
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
28
import org.eclipse.hyades.test.ui.navigator.ITypeProvider;
Lines 103-109 Link Here
103
    	disposed = true;
92
    	disposed = true;
104
    	// disposed must be set to true before cancelling the current job, otherwise another
93
    	// disposed must be set to true before cancelling the current job, otherwise another
105
    	// job may be triggered while we cancel this one.
94
    	// job may be triggered while we cancel this one.
106
    	if (computeJob != null) computeJob.cancel();
107
    	if (refresher != null) {
95
    	if (refresher != null) {
108
	    	JUnitTestSuiteFactoryEventManager.getInstance().removeListener(this);
96
	    	JUnitTestSuiteFactoryEventManager.getInstance().removeListener(this);
109
	        JavaCore.removeElementChangedListener(this);
97
	        JavaCore.removeElementChangedListener(this);
Lines 143-229 Link Here
143
		}
131
		}
144
	}
132
	}
145
	
133
	
146
	protected void proxyComputed(RegularJUnitProviderProxyNode proxy) {
147
		if (proxy.getChildren().length != 0) {
148
			IJavaProject project = proxy.getJavaProject();
149
			refresher.nodeChanged(project.getProject());
150
		}
151
	}
152
	
153
	protected ISchedulingRule mutualExlusionRule = new ISchedulingRule() {
154
		public boolean contains(ISchedulingRule rule) {
155
			if (rule == this) return true;
156
			if (rule instanceof IResource) return true;
157
			return false;
158
		}
159
		public boolean isConflicting(ISchedulingRule rule) {
160
			return rule == this;
161
		}
162
	};
163
	
164
	protected class ComputeJob extends Job {
165
		private Set proxies = new HashSet();
166
		public ComputeJob() {
167
			super(JavaMessages.REGULAR_JUNIT_EXPLORING_TESTS);
168
			setRule(mutualExlusionRule);
169
		}
170
		public void addProxy(RegularJUnitProviderProxyNode proxy) {
171
			proxies.add(proxy);
172
		}
173
		protected IStatus run(IProgressMonitor monitor) {
174
			monitor.beginTask(JavaMessages.REGULAR_JUNIT_EXPLORING_PROJECT, proxies.size());
175
			try {
176
				Iterator it = proxies.iterator();
177
				while (it.hasNext()) {
178
					RegularJUnitProviderProxyNode proxy = (RegularJUnitProviderProxyNode) it.next();
179
					try {
180
						monitor.subTask(proxy.getJavaElement().getElementName());
181
						proxy.computeActualChildren();
182
						proxyComputed(proxy);
183
					} catch (Throwable t) {
184
						ToolsUiPlugin.logError(t);
185
					} finally {
186
						it.remove();
187
						monitor.done();
188
					}
189
				}
190
				return new Status(IStatus.OK, ToolsUiPlugin.getID(), 0, "", null); //$NON-NLS-1$
191
			} finally {
192
				monitor.done();
193
			}
194
		}
195
	}
196
	
197
	private ComputeJob computeJob = null;
198
	
199
    /**
200
     * Returns an ComputeJob in NONE state (ie. ready to be scheduled).
201
     * @return
202
     */
203
    private synchronized ComputeJob getComputeJob() {
204
        if (computeJob != null) {
205
            if (computeJob.getState() != Job.RUNNING && computeJob.cancel()) {
206
                // The job is back to NONE state now
207
                return computeJob;
208
            } else {
209
                // Too late, our job is already running. Forget this one.
210
            	computeJob = null;
211
            }
212
        }
213
        if (computeJob == null) {
214
        	computeJob = new ComputeJob();
215
        }
216
        return computeJob;
217
    }
218
219
	/*package*/ void scheduleComputeChildren(RegularJUnitProviderProxyNode proxy) {
220
		Assert.isLegal(refresher != null, "Cannot delay children computation in static view mode"); //$NON-NLS-1$
221
		if (disposed) return;
222
		ComputeJob job = getComputeJob();
223
		job.addProxy(proxy);
224
		job.schedule(500);
225
	}
226
227
	/**
134
	/**
228
	 * Convenience method to get the proxy node associated to an arbitrary java element.
135
	 * Convenience method to get the proxy node associated to an arbitrary java element.
229
	 * @param element An opened java element.
136
	 * @param element An opened java element.
(-)src/org/eclipse/hyades/test/tools/ui/java/internal/junit/navigator/JUnitTypeProvider.java (-2 / +3 lines)
Lines 11-16 Link Here
11
 **********************************************************************/
11
 **********************************************************************/
12
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
12
package org.eclipse.hyades.test.tools.ui.java.internal.junit.navigator;
13
13
14
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashMap;
15
import java.util.Map;
16
import java.util.Map;
16
17
Lines 39-45 Link Here
39
    protected IFileProxyManager fileProxyManager;
40
    protected IFileProxyManager fileProxyManager;
40
	protected IProxyNodeListener refresher;
41
	protected IProxyNodeListener refresher;
41
42
42
	private Map projectToProxyMap = new HashMap();
43
	private Map projectToProxyMap = Collections.synchronizedMap(new HashMap());
43
	
44
	
44
	public JUnitTypeProvider() {
45
	public JUnitTypeProvider() {
45
        FileProxyNodeCache.getInstance().addResourceListener(this);
46
        FileProxyNodeCache.getInstance().addResourceListener(this);
Lines 96-102 Link Here
96
				if (lowestChange != null) {
97
				if (lowestChange != null) {
97
					refresher.nodeChanged(lowestChange);
98
					refresher.nodeChanged(lowestChange);
98
				}
99
				}
99
			} else if (event.getType() == IResourceDelta.ADDED) {
100
			} else if (affectedChildren[i].getKind() == IResourceDelta.ADDED) {
100
                //- if there is something added to this project, refresh it this can lead to type provider appearance
101
                //- if there is something added to this project, refresh it this can lead to type provider appearance
101
                refresher.nodeChanged(project);
102
                refresher.nodeChanged(project);
102
            }
103
            }

Return to bug 157089