Source code: org/eclipse/ui/internal/ObjectContributorManager.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.ui.internal;
12
13 import java.lang.reflect.Method;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Hashtable;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Vector;
24
25 import org.eclipse.core.runtime.IAdaptable;
26
27 import org.eclipse.jface.viewers.IStructuredSelection;
28
29 /**
30 * This class is a default implementation of <code>IObjectContributorManager</code>.
31 * It provides fast merging of contributions with the following semantics:
32 * <ul>
33 * <li> All of the matching contributors will be invoked per property lookup
34 * <li> The search order from a class with the definition<br>
35 * <code>class X extends Y implements A, B</code><br>
36 * is as follows:
37 * <il>
38 * <li>the target's class: X
39 * <li>X's superclasses in order to <code>Object</code>
40 * <li>a depth-first traversal of the target class's interaces in the order
41 * returned by <code>getInterfaces()</code> (in the example, A and
42 * its superinterfaces then B and its superinterfaces)
43 * </il>
44 * </ul>
45 *
46 * @see IObjectContributor
47 * @see IObjectContributorManager
48 */
49 public abstract class ObjectContributorManager {
50 // Empty list that is immutable
51 private static final List EMPTY_LIST = Arrays.asList(new Object[0]);
52
53 /** Table of contributors. */
54 protected Map contributors;
55
56 /** Cache of object class contributor search paths; <code>null</code> if none. */
57 protected Map objectLookup;
58
59 /** Cache of resource adapter class contributor search paths; <code>null</code> if none. */
60 protected Map adapterLookup;
61
62 /**
63 * Constructs a new contributor manager.
64 */
65 public ObjectContributorManager() {
66 contributors = new Hashtable(5);
67 objectLookup = null;
68 adapterLookup = null;
69 }
70 /**
71 * Adds contributors for the given types to the result list.
72 */
73 private void addContributorsFor(List types, List result) {
74 for (Iterator classes = types.iterator(); classes.hasNext();) {
75 Class clazz = (Class) classes.next();
76 List contributorList = (List) contributors.get(clazz.getName());
77 if (contributorList != null)
78 result.addAll(contributorList);
79 }
80 }
81 /**
82 * Returns the class search order starting with <code>extensibleClass</code>.
83 * The search order is defined in this class' comment.
84 */
85 protected final List computeClassOrder(Class extensibleClass) {
86 ArrayList result = new ArrayList(4);
87 Class clazz = extensibleClass;
88 while (clazz != null) {
89 result.add(clazz);
90 clazz = clazz.getSuperclass();
91 }
92 return result;
93 }
94 /**
95 * Returns the interface search order for the class hierarchy described
96 * by <code>classList</code>.
97 * The search order is defined in this class' comment.
98 */
99 protected final List computeInterfaceOrder(List classList) {
100 ArrayList result = new ArrayList(4);
101 Map seen = new HashMap(4);
102 for (Iterator list = classList.iterator(); list.hasNext();) {
103 Class[] interfaces = ((Class) list.next()).getInterfaces();
104 internalComputeInterfaceOrder(interfaces, result, seen);
105 }
106 return result;
107 }
108
109 /**
110 * Flushes the cache of contributor search paths. This is generally required
111 * whenever a contributor is added or removed.
112 * <p>
113 * It is likely easier to just toss the whole cache rather than trying to be
114 * smart and remove only those entries affected.
115 */
116 public void flushLookup() {
117 objectLookup = null;
118 adapterLookup = null;
119 }
120 /**
121 * Cache the resource adapter class contributor search path.
122 */
123 private void cacheAdapterLookup(Class adapterClass, List results) {
124 if (adapterLookup == null)
125 adapterLookup = new HashMap();
126 adapterLookup.put(adapterClass, results);
127 }
128 /**
129 * Cache the object class contributor search path.
130 */
131 private void cacheObjectLookup(Class objectClass, List results) {
132 if (objectLookup == null)
133 objectLookup = new HashMap();
134 objectLookup.put(objectClass, results);
135 }
136
137 /**
138 * Get the contributions registered to this manager.
139 *
140 * @return an unmodifiable <code>Collection</code> containing all registered
141 * contributions. The objects in this <code>Collection</code> will be
142 * <code>List</code>s containing the actual contributions.
143 * @since 3.0
144 */
145 public Collection getContributors() {
146 return Collections.unmodifiableCollection(contributors.values());
147 }
148
149 /**
150 * Returns all the contributors registered against
151 * the given object class.
152 */
153 protected List getContributors(Class objectClass) {
154
155 List objectList = null;
156
157 // Lookup the results in the cache first
158 if (objectLookup != null) {
159 objectList = (List) objectLookup.get(objectClass);
160 }
161
162 // If not in cache, build it
163 if (objectList == null) {
164 objectList = addContributorsFor(objectClass);
165 if (objectList.size() == 0)
166 objectList = EMPTY_LIST;
167
168 // Store the contribution list into the cache.
169 cacheObjectLookup(objectClass, objectList);
170 }
171
172 return objectList;
173 }
174
175 /**
176 * Return the list of contributors for the supplied class.
177 */
178 protected List addContributorsFor(Class objectClass) {
179
180 List classList = computeClassOrder(objectClass);
181 List result = new ArrayList();
182 addContributorsFor(classList, result);
183 classList = computeInterfaceOrder(classList); // interfaces
184 addContributorsFor(classList, result);
185 return result;
186 }
187
188 /**
189 * Get the contributors for object including those it adapts
190 * to.
191 *
192 * @return The list of contributors, empty if none.
193 */
194 protected List getContributors(Object object) {
195
196 Class objectClass = object.getClass();
197 Object adapted = getAdaptedResource(object);
198
199 if (adapted == null)
200 return getContributors(objectClass);
201 else
202 return getContributors(objectClass, adapted.getClass());
203 }
204
205 /**
206 * Returns true if contributors exist in the manager for
207 * this object.
208 */
209 public boolean hasContributorsFor(Object object) {
210
211 List contributors = getContributors(object);
212 return contributors.size() > 0;
213 }
214 /**
215 * Add interface Class objects to the result list based
216 * on the class hierarchy. Interfaces will be searched
217 * based on their position in the result list.
218 */
219 private void internalComputeInterfaceOrder(Class[] interfaces, List result, Map seen) {
220 List newInterfaces = new ArrayList(seen.size());
221 for (int i = 0; i < interfaces.length; i++) {
222 Class interfac = interfaces[i];
223 if (seen.get(interfac) == null) {
224 result.add(interfac);
225 seen.put(interfac, interfac);
226 newInterfaces.add(interfac);
227 }
228 }
229 for (Iterator newList = newInterfaces.iterator(); newList.hasNext();)
230 internalComputeInterfaceOrder(((Class) newList.next()).getInterfaces(), result, seen);
231 }
232 /**
233 *
234 */
235 public boolean isApplicableTo(IStructuredSelection selection, IObjectContributor contributor) {
236 Iterator elements = selection.iterator();
237 while (elements.hasNext()) {
238 if (contributor.isApplicableTo(elements.next()) == false)
239 return false;
240 }
241 return true;
242 }
243 /**
244 *
245 */
246 public boolean isApplicableTo(List list, IObjectContributor contributor) {
247 Iterator elements = list.iterator();
248 while (elements.hasNext()) {
249 if (contributor.isApplicableTo(elements.next()) == false)
250 return false;
251 }
252 return true;
253 }
254 /**
255 * @see IContributorManager#registerContributor
256 */
257 public void registerContributor(IObjectContributor contributor, String targetType) {
258 Vector contributorList = (Vector) contributors.get(targetType);
259 if (contributorList == null) {
260 contributorList = new Vector(5);
261 contributors.put(targetType, contributorList);
262 }
263 contributorList.addElement(contributor);
264 flushLookup();
265 }
266 /**
267 * @see IContributorManager#unregisterAllContributors
268 */
269 public void unregisterAllContributors() {
270 contributors = new Hashtable(5);
271 flushLookup();
272 }
273 /**
274 * @see IContributorManager#unregisterContributor
275 */
276 public void unregisterContributor(IObjectContributor contributor, String targetType) {
277 Vector contributorList = (Vector) contributors.get(targetType);
278 if (contributorList == null)
279 return;
280 contributorList.removeElement(contributor);
281 flushLookup();
282 }
283 /**
284 * @see IContributorManager#unregisterContributors
285 */
286 public void unregisterContributors(String targetType) {
287 contributors.remove(targetType);
288 flushLookup();
289 }
290
291 /**
292 * Returns all the contributors registered against
293 * the given object class and the resource class that
294 * it has an Adaptable for.
295 */
296 protected List getContributors(Class objectClass, Class resourceClass) {
297
298 List objectList = null;
299 List resourceList = null;
300
301 // Lookup the results in the cache first
302 if (objectLookup != null) {
303 objectList = (List) objectLookup.get(objectClass);
304 }
305 if (adapterLookup != null) {
306 resourceList = (List) adapterLookup.get(resourceClass);
307 }
308
309 if (objectList == null) {
310 objectList = addContributorsFor(objectClass);
311 if (objectList.size() == 0)
312 objectList = EMPTY_LIST;
313 cacheObjectLookup(objectClass, objectList);
314 }
315 if (resourceList == null) {
316 List contributors = addContributorsFor(resourceClass);
317 resourceList = new ArrayList(contributors.size());
318 Iterator enum = contributors.iterator();
319 while (enum.hasNext()) {
320 IObjectContributor contributor = (IObjectContributor) enum.next();
321 if (contributor.canAdapt())
322 resourceList.add(contributor);
323 }
324 if (resourceList.size() == 0)
325 resourceList = EMPTY_LIST;
326 cacheAdapterLookup(resourceClass, resourceList);
327 }
328
329 // Collect the contribution lists into one result
330 ArrayList results = new ArrayList(objectList.size() + resourceList.size());
331 results.addAll(objectList);
332 results.addAll(resourceList);
333 return results;
334 }
335
336 /**
337 * Get the adapted resource for the supplied object. If the
338 * object is an instance of IResource or is not an instance
339 * of IAdaptable return null. Otherwise see if it adapts
340 * to IResource via IContributorResourceAdapter.
341 *
342 * @param object Object
343 * @return an <code>IResource</code> or null
344 */
345 protected Object getAdaptedResource(Object object) {
346 Class resourceClass = LegacyResourceSupport.getResourceClass();
347 if (resourceClass == null) {
348 return null;
349 }
350 if (resourceClass.isInstance(object)) {
351 return null;
352 }
353
354 if (object instanceof IAdaptable) {
355 IAdaptable adaptable = (IAdaptable) object;
356
357 Class contributorResourceAdapterClass = LegacyResourceSupport.getIContributorResourceAdapterClass();
358 if (contributorResourceAdapterClass == null) {
359 return null;
360 }
361 Object resourceAdapter = adaptable.getAdapter(contributorResourceAdapterClass);
362 if (resourceAdapter == null) {
363 // reflective equivalent of
364 // resourceAdapter = DefaultContributorResourceAdapter.getDefault();
365 try {
366 Class c = LegacyResourceSupport.getDefaultContributorResourceAdapterClass();
367 Method m = c.getDeclaredMethod("getDefault", new Class[0]); //$NON-NLS-1$
368 resourceAdapter = m.invoke(null, new Object[0]);
369 } catch (Exception e) {
370 // shouldn't happen - but play it safe
371 return null;
372 }
373 }
374
375 Object result;
376 // reflective equivalent of
377 // result = ((IContributorResourceAdapter) resourceAdapter).getAdaptedResource(adaptable);
378 try {
379 Method m = contributorResourceAdapterClass.getDeclaredMethod("getAdaptedResource", new Class[] {IAdaptable.class}); //$NON-NLS-1$
380 result = m.invoke(resourceAdapter, new Object[] {adaptable});
381 } catch (Exception e) {
382 // shouldn't happen - but play it safe
383 return null;
384 }
385 return result;
386 }
387
388 return null;
389 }
390 }