Source code: org/eclipse/ui/internal/ObjectActionContributorManager.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.util.ArrayList;
14 import java.util.List;
15
16 import org.eclipse.core.runtime.IAdaptable;
17 import org.eclipse.jface.action.IMenuManager;
18 import org.eclipse.jface.viewers.ISelection;
19 import org.eclipse.jface.viewers.ISelectionProvider;
20 import org.eclipse.jface.viewers.IStructuredSelection;
21 import org.eclipse.ui.IWorkbenchPart;
22
23 /**
24 * This manager is used to populate a popup menu manager with actions
25 * for a given type.
26 */
27 public class ObjectActionContributorManager extends ObjectContributorManager {
28 private static ObjectActionContributorManager sharedInstance;
29
30 /**
31 * PopupMenuManager constructor.
32 */
33 public ObjectActionContributorManager() {
34 loadContributors();
35 }
36
37 /**
38 * Returns the class search order starting with <code>extensibleClass</code>.
39 * The search order is defined in this class' comment.
40 */
41 private List computeCombinedOrder(Class inputClass) {
42 List result = new ArrayList(4);
43 Class clazz = inputClass;
44 while (clazz != null) {
45 // add the class
46 result.add(clazz);
47 // add all the interfaces it implements
48 Class[] interfaces = clazz.getInterfaces();
49 for (int i = 0; i < interfaces.length; i++) {
50 result.add(interfaces[i]);
51 }
52 // get the superclass
53 clazz = clazz.getSuperclass();
54 }
55 return result;
56 }
57
58 /**
59 * Contributes submenus and/or actions applicable to the selection in the
60 * provided viewer into the provided popup menu.
61 */
62 public boolean contributeObjectActions(IWorkbenchPart part, IMenuManager popupMenu, ISelectionProvider selProv) {
63 // Get a selection.
64 ISelection selection = selProv.getSelection();
65 if (selection == null)
66 return false;
67
68 // Convert the selection into an element vector.
69 // According to the dictionary, a selection is "one that
70 // is selected", or "a collection of selected things".
71 // In reflection of this, we deal with one or a collection.
72 List elements = null;
73 if (selection instanceof IStructuredSelection) {
74 elements = ((IStructuredSelection) selection).toList();
75 } else {
76 elements = new ArrayList(1);
77 elements.add(selection);
78 }
79
80 // Calculate the common class and interfaces.
81 List commonClasses = getCommonClasses(elements);
82 if (commonClasses == null || commonClasses.isEmpty())
83 return false;
84
85 // Get the resource class. It will be null if any of the
86 // elements are resources themselves or do not adapt to
87 // IResource.
88 Class resourceClass = getCommonResourceClass(elements);
89
90 // Get the contributors.
91 // If there is a resource class add it in
92 List contributors = null;
93 if (resourceClass == null) {
94 if (commonClasses.size() == 1) {
95 contributors = getContributors((Class)commonClasses.get(0));
96 } else {
97 contributors = new ArrayList();
98 for (int i = 0; i < commonClasses.size(); i++) {
99 List results = getContributors((Class)commonClasses.get(i));
100 if (results != null)
101 contributors.addAll(results);
102 }
103 }
104 } else {
105 contributors = getContributors((Class)commonClasses.get(0), resourceClass);
106 for (int i = 1; i < commonClasses.size(); i++) {
107 List results = getContributors((Class)commonClasses.get(i));
108 if (results != null)
109 contributors.addAll(results);
110 }
111 }
112
113 if (contributors == null || contributors.isEmpty())
114 return false;
115
116 // Do the contributions. Add menus first, then actions
117 boolean actualContributions = false;
118 ArrayList overrides = new ArrayList(4);
119 for (int i = 0; i < contributors.size(); i++) {
120 IObjectActionContributor contributor = (IObjectActionContributor) contributors.get(i);
121 if (!isApplicableTo(elements, contributor))
122 continue;
123 if (contributor.contributeObjectMenus(popupMenu, selProv))
124 actualContributions = true;
125 contributor.contributeObjectActionIdOverrides(overrides);
126 }
127 for (int i = 0; i < contributors.size(); i++) {
128 IObjectActionContributor contributor = (IObjectActionContributor) contributors.get(i);
129 if (!isApplicableTo(elements, contributor))
130 continue;
131 if (contributor.contributeObjectActions(part, popupMenu, selProv, overrides))
132 actualContributions = true;
133 }
134 return actualContributions;
135 }
136
137 /**
138 * Returns the common denominator class for
139 * two input classes.
140 */
141 private Class getCommonClass(Class class1, Class class2) {
142 List list1 = computeCombinedOrder(class1);
143 List list2 = computeCombinedOrder(class2);
144 for (int i = 0; i < list1.size(); i++) {
145 for (int j = 0; j < list2.size(); j++) {
146 Class candidate1 = (Class) list1.get(i);
147 Class candidate2 = (Class) list2.get(j);
148 if (candidate1.equals(candidate2))
149 return candidate1;
150 }
151 }
152 // no common class
153 return null;
154 }
155
156 /**
157 * Returns the common denominator class for the given
158 * collection of objects.
159 */
160 private Class getCommonClass(List objects) {
161 if (objects == null || objects.size() == 0)
162 return null;
163 Class commonClass = objects.get(0).getClass();
164 // try easy
165 if (objects.size() == 1)
166 return commonClass;
167 // try harder
168
169 for (int i = 1; i < objects.size(); i++) {
170 Object object = objects.get(i);
171 Class newClass = object.getClass();
172 // try the short cut
173 if (newClass.equals(commonClass))
174 continue;
175 // compute common class
176 commonClass = getCommonClass(commonClass, newClass);
177 // give up
178 if (commonClass == null)
179 return null;
180 }
181 return commonClass;
182 }
183
184 /**
185 * Returns the common denominator class and interfaces for the given
186 * collection of objects.
187 */
188 private List getCommonClasses(List objects) {
189 if (objects == null || objects.size() == 0)
190 return null;
191
192 // Quickly handle the easy case...
193 if (objects.size() == 1) {
194 List results = new ArrayList(1);
195 results.add(objects.get(0).getClass());
196 return results;
197 }
198
199 // Compute all the super classes for the first element
200 // and then all of the interfaces for the first element
201 // and it's super classes.
202 List classes = computeClassOrder(objects.get(0).getClass());
203 List interfaces = computeInterfaceOrder(classes);
204 boolean classesEmpty = classes.isEmpty();
205 boolean interfacesEmpty = interfaces.isEmpty();
206
207 for (int i = 1; i < objects.size(); i++) {
208 // Compute all the super classes for the current element
209 List results = computeClassOrder(objects.get(i).getClass());
210 if (!classesEmpty) {
211 classesEmpty = true;
212 if (results.isEmpty()) {
213 // When no super classes, then it is obvious there
214 // are no common super classes with the first element
215 // so clear its list.
216 classes.clear();
217 } else {
218 // Remove any super classes of the first element that
219 // are not in the current element's super classes list.
220 for (int j = 0; j < classes.size(); j++) {
221 if (classes.get(j) != null) {
222 classesEmpty = false;
223 if (!results.contains(classes.get(j))) {
224 classes.set(j, null);
225 }
226 }
227 }
228 }
229 }
230
231 if (!interfacesEmpty) {
232 // Compute all the interfaces for the current element
233 // and all of its super classes.
234 results = computeInterfaceOrder(results);
235 interfacesEmpty = true;
236 if (results.isEmpty()) {
237 // When no interfaces, the it is obvious there are
238 // no common interfaces between this current element
239 // and the first element, so clear its list.
240 interfaces.clear();
241 } else {
242 // Remove any interfaces of the first element that
243 // are not in the current element's interfaces list.
244 for (int j = 0; j < interfaces.size(); j++) {
245 if (interfaces.get(j) != null) {
246 interfacesEmpty = false;
247 if (!results.contains(interfaces.get(j))) {
248 interfaces.set(j, null);
249 }
250 }
251 }
252 }
253 }
254
255 if (interfacesEmpty && classesEmpty) {
256 // As soon as we detect nothing in common, just exit.
257 return null;
258 }
259 }
260
261 ArrayList results = new ArrayList(4);
262 ArrayList superClasses = new ArrayList(4);
263 if (!classesEmpty) {
264 for (int j = 0; j < classes.size(); j++) {
265 if (classes.get(j) != null) {
266 superClasses.add(classes.get(j));
267 }
268 }
269 // Just keep the first super class
270 if (!superClasses.isEmpty()) {
271 results.add(superClasses.get(0));
272 }
273 }
274
275 if (!interfacesEmpty) {
276 // Do no include the interfaces belonging to the common
277 // super classes as these will be calculated again later
278 // in addContributors method.
279 List dropInterfaces = null;
280 if (!superClasses.isEmpty()) {
281 dropInterfaces = computeInterfaceOrder(superClasses);
282 }
283
284 for (int j = 0; j < interfaces.size(); j++) {
285 if (interfaces.get(j) != null) {
286 if (dropInterfaces != null && !dropInterfaces.contains(interfaces.get(j))) {
287 results.add(interfaces.get(j));
288 }
289 }
290 }
291 }
292
293 return results;
294 }
295
296 /**
297 * Returns the shared instance of this manager.
298 */
299 public static ObjectActionContributorManager getManager() {
300 if (sharedInstance == null) {
301 sharedInstance = new ObjectActionContributorManager();
302 }
303 return sharedInstance;
304 }
305
306 /**
307 * Loads the contributors from the workbench's registry.
308 */
309 private void loadContributors() {
310 ObjectActionContributorReader reader = new ObjectActionContributorReader();
311 reader.readPopupContributors(this);
312 }
313
314 /**
315 * Returns the common denominator resource class for the given
316 * collection of objects.
317 * Do not return a resource class if the objects are resources
318 * themselves so as to prevent double registration of actions.
319 */
320 private Class getCommonResourceClass(List objects) {
321 if (objects == null || objects.size() == 0) {
322 return null;
323 }
324 Class resourceClass = LegacyResourceSupport.getResourceClass();
325 if (resourceClass == null) {
326 // resources plug-in not loaded - no resources. period.
327 return null;
328 }
329
330 List testList = new ArrayList();
331
332 for (int i = 0; i < objects.size(); i++) {
333 Object object = objects.get(i);
334
335 if (object instanceof IAdaptable) {
336 if (resourceClass.isInstance(object)) {
337 continue;
338 }
339
340 Object resource = getAdaptedResource((IAdaptable) object);
341
342 if (resource == null) {
343 //Not a resource and does not adapt. No common resource class
344 return null;
345 }
346 testList.add(resource);
347 } else {
348 return null;
349 }
350 }
351
352 return getCommonClass(testList);
353 }
354 }