blob: 1cbb2ca66433c26d878d1c777354ed5c62745ff4 [file] [log] [blame]
package org.eclipse.ui.internal;
/************************************************************************
Copyright (c) 2000, 2003 IBM Corporation and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Common Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/cpl-v10.html
Contributors:
IBM - Initial implementation
************************************************************************/
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPart;
/**
* This manager is used to populate a popup menu manager with actions
* for a given type.
*/
public class ObjectActionContributorManager extends ObjectContributorManager {
private static ObjectActionContributorManager sharedInstance;
/**
* PopupMenuManager constructor.
*/
public ObjectActionContributorManager() {
loadContributors();
}
/**
* Returns the class search order starting with <code>extensibleClass</code>.
* The search order is defined in this class' comment.
*/
private List computeCombinedOrder(Class inputClass) {
List result = new ArrayList(4);
Class clazz = inputClass;
while (clazz != null) {
// add the class
result.add(clazz);
// add all the interfaces it implements
Class[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
result.add(interfaces[i]);
}
// get the superclass
clazz = clazz.getSuperclass();
}
return result;
}
/**
* Contributes submenus and/or actions applicable to the selection in the
* provided viewer into the provided popup menu.
*/
public boolean contributeObjectActions(IWorkbenchPart part, IMenuManager popupMenu, ISelectionProvider selProv) {
// Get a selection.
ISelection selection = selProv.getSelection();
if (selection == null)
return false;
// Convert the selection into an element vector.
// According to the dictionary, a selection is "one that
// is selected", or "a collection of selected things".
// In reflection of this, we deal with one or a collection.
List elements = null;
if (selection instanceof IStructuredSelection) {
elements = ((IStructuredSelection) selection).toList();
} else {
elements = new ArrayList(1);
elements.add(selection);
}
// Calculate the common class and interfaces.
List commonClasses = getCommonClasses(elements);
if (commonClasses == null || commonClasses.isEmpty())
return false;
// Get the resource class. It will be null if any of the
// elements are resources themselves or do not adapt to
// IResource.
Class resourceClass = getCommonResourceClass(elements);
// Get the contributors.
// If there is a resource class add it in
List contributors = null;
if (resourceClass == null) {
if (commonClasses.size() == 1) {
contributors = getContributors((Class)commonClasses.get(0));
} else {
contributors = new ArrayList();
for (int i = 0; i < commonClasses.size(); i++) {
List results = getContributors((Class)commonClasses.get(i));
if (results != null)
contributors.addAll(results);
}
}
} else {
contributors = getContributors((Class)commonClasses.get(0), resourceClass);
for (int i = 1; i < commonClasses.size(); i++) {
List results = getContributors((Class)commonClasses.get(i));
if (results != null)
contributors.addAll(results);
}
}
if (contributors == null || contributors.isEmpty())
return false;
// Do the contributions. Add menus first, then actions
boolean actualContributions = false;
for (int i = 0; i < contributors.size(); i++) {
IObjectActionContributor contributor = (IObjectActionContributor) contributors.get(i);
if (!isApplicableTo(elements, contributor))
continue;
if (contributor.contributeObjectMenus(popupMenu, selProv))
actualContributions = true;
}
for (int i = 0; i < contributors.size(); i++) {
IObjectActionContributor contributor = (IObjectActionContributor) contributors.get(i);
if (!isApplicableTo(elements, contributor))
continue;
if (contributor.contributeObjectActions(part, popupMenu, selProv))
actualContributions = true;
}
return actualContributions;
}
/**
* Returns the common denominator class for
* two input classes.
*/
private Class getCommonClass(Class class1, Class class2) {
List list1 = computeCombinedOrder(class1);
List list2 = computeCombinedOrder(class2);
for (int i = 0; i < list1.size(); i++) {
for (int j = 0; j < list2.size(); j++) {
Class candidate1 = (Class) list1.get(i);
Class candidate2 = (Class) list2.get(j);
if (candidate1.equals(candidate2))
return candidate1;
}
}
// no common class
return null;
}
/**
* Returns the common denominator class for the given
* collection of objects.
*/
private Class getCommonClass(List objects) {
if (objects == null || objects.size() == 0)
return null;
Class commonClass = objects.get(0).getClass();
// try easy
if (objects.size() == 1)
return commonClass;
// try harder
for (int i = 1; i < objects.size(); i++) {
Object object = objects.get(i);
Class newClass = object.getClass();
// try the short cut
if (newClass.equals(commonClass))
continue;
// compute common class
commonClass = getCommonClass(commonClass, newClass);
// give up
if (commonClass == null)
return null;
}
return commonClass;
}
/**
* Returns the common denominator class and interfaces for the given
* collection of objects.
*/
private List getCommonClasses(List objects) {
if (objects == null || objects.size() == 0)
return null;
// Quickly handle the easy case...
if (objects.size() == 1) {
List results = new ArrayList(1);
results.add(objects.get(0).getClass());
return results;
}
// Compute all the super classes for the first element
// and then all of the interfaces for the first element
// and it's super classes.
List classes = computeClassOrder(objects.get(0).getClass());
List interfaces = computeInterfaceOrder(classes);
boolean classesEmpty = classes.isEmpty();
boolean interfacesEmpty = interfaces.isEmpty();
for (int i = 1; i < objects.size(); i++) {
// Compute all the super classes for the current element
List results = computeClassOrder(objects.get(i).getClass());
if (!classesEmpty) {
classesEmpty = true;
if (results.isEmpty()) {
// When no super classes, then it is obvious there
// are no common super classes with the first element
// so clear its list.
classes.clear();
} else {
// Remove any super classes of the first element that
// are not in the current element's super classes list.
for (int j = 0; j < classes.size(); j++) {
if (classes.get(j) != null) {
classesEmpty = false;
if (!results.contains(classes.get(j))) {
classes.set(j, null);
}
}
}
}
}
if (!interfacesEmpty) {
// Compute all the interfaces for the current element
// and all of its super classes.
results = computeInterfaceOrder(results);
interfacesEmpty = true;
if (results.isEmpty()) {
// When no interfaces, the it is obvious there are
// no common interfaces between this current element
// and the first element, so clear its list.
interfaces.clear();
} else {
// Remove any interfaces of the first element that
// are not in the current element's interfaces list.
for (int j = 0; j < interfaces.size(); j++) {
if (interfaces.get(j) != null) {
interfacesEmpty = false;
if (!results.contains(interfaces.get(j))) {
interfaces.set(j, null);
}
}
}
}
}
if (interfacesEmpty && classesEmpty) {
// As soon as we detect nothing in common, just exit.
return null;
}
}
ArrayList results = new ArrayList(4);
ArrayList superClasses = new ArrayList(4);
if (!classesEmpty) {
for (int j = 0; j < classes.size(); j++) {
if (classes.get(j) != null) {
superClasses.add(classes.get(j));
}
}
// Just keep the first super class
if (!superClasses.isEmpty()) {
results.add(superClasses.get(0));
}
}
if (!interfacesEmpty) {
// Do no include the interfaces belonging to the common
// super classes as these will be calculated again later
// in addContributors method.
List dropInterfaces = null;
if (!superClasses.isEmpty()) {
dropInterfaces = computeInterfaceOrder(superClasses);
}
for (int j = 0; j < interfaces.size(); j++) {
if (interfaces.get(j) != null) {
if (dropInterfaces != null && !dropInterfaces.contains(interfaces.get(j))) {
results.add(interfaces.get(j));
}
}
}
}
return results;
}
/**
* Returns the shared instance of this manager.
*/
public static ObjectActionContributorManager getManager() {
if (sharedInstance == null) {
sharedInstance = new ObjectActionContributorManager();
}
return sharedInstance;
}
/**
* Loads the contributors from the workbench's registry.
*/
private void loadContributors() {
ObjectActionContributorReader reader = new ObjectActionContributorReader();
reader.readPopupContributors(this);
}
/**
* Returns the common denominator resource class for the given
* collection of objects.
* Do not return a resource class if the objects are resources
* themselves so as to prevent double registration of actions.
*/
private Class getCommonResourceClass(List objects) {
if (objects == null || objects.size() == 0)
return null;
List testList = new ArrayList();
for (int i = 0; i < objects.size(); i++) {
Object object = objects.get(i);
if (object instanceof IAdaptable) {
if (object instanceof IResource)
continue;
IResource resource = getAdaptedResource((IAdaptable) object);
if (resource == null)
//Not a resource and does not adapt. No common resource class
return null;
testList.add(resource);
} else
return null;
}
return getCommonClass(testList);
}
}