| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.eclipse.core.runtime.Adapters; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IAdapterManager; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker; |
| import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler; |
| import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * This class is a default implementation of <code>IObjectContributorManager</code>. |
| * It provides fast merging of contributions with the following semantics: |
| * <ul> |
| * <li> All of the matching contributors will be invoked per property lookup |
| * <li> The search order from a class with the definition<br> |
| * <code>class X extends Y implements A, B</code><br> |
| * is as follows: |
| * <il> |
| * <li>the target's class: X |
| * <li>X's superclasses in order to <code>Object</code> |
| * <li>a depth-first traversal of the target class's interaces in the order |
| * returned by <code>getInterfaces()</code> (in the example, A and |
| * its superinterfaces then B and its superinterfaces) |
| * </il> |
| * </ul> |
| * |
| * @see IObjectContributor |
| */ |
| public abstract class ObjectContributorManager implements IExtensionChangeHandler { |
| |
| /** |
| * @since 3.1 |
| */ |
| private class ContributorRecord { |
| /** |
| * @param contributor |
| * @param targetType |
| */ |
| public ContributorRecord(IObjectContributor contributor, String targetType) { |
| this.contributor = contributor; |
| this.objectClassName = targetType; |
| } |
| |
| String objectClassName; |
| IObjectContributor contributor; |
| } |
| |
| /** Table of contributors. */ |
| protected Map contributors; |
| |
| /** Cache of object class contributor search paths; <code>null</code> if none. */ |
| protected Map objectLookup; |
| |
| /** Cache of resource adapter class contributor search paths; <code>null</code> if none. */ |
| protected Map resourceAdapterLookup; |
| |
| /** Cache of adaptable class contributor search paths; <code>null</code> if none. */ |
| protected Map adaptableLookup; |
| |
| protected Set contributorRecordSet; |
| |
| /** |
| * Constructs a new contributor manager. |
| */ |
| public ObjectContributorManager() { |
| contributors = new Hashtable(5); |
| contributorRecordSet = new HashSet(5); |
| objectLookup = null; |
| resourceAdapterLookup = null; |
| adaptableLookup = null; |
| String extensionPointId = getExtensionPointFilter(); |
| if (extensionPointId != null) { |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( |
| PlatformUI.PLUGIN_ID, extensionPointId); |
| IExtensionTracker tracker = PlatformUI.getWorkbench() |
| .getExtensionTracker(); |
| tracker.registerHandler(this, ExtensionTracker |
| .createExtensionPointFilter(extensionPoint)); |
| } |
| } |
| |
| /** |
| * Return the extension point id (local to org.eclipse.ui) that this manager |
| * is associated with. Default implementation returns null, which implies no |
| * relationship with a particular extension. |
| * |
| * @return the extension point id |
| * @since 3.4 |
| */ |
| protected String getExtensionPointFilter() { |
| return null; |
| } |
| |
| /** |
| * Adds contributors for the given types to the result list. |
| */ |
| private void addContributorsFor(List types, List result) { |
| for (Iterator classes = types.iterator(); classes.hasNext();) { |
| Class clazz = (Class) classes.next(); |
| List contributorList = (List) contributors.get(clazz.getName()); |
| if (contributorList != null) { |
| result.addAll(contributorList); |
| } |
| } |
| } |
| |
| /** |
| * Returns the class search order starting with <code>extensibleClass</code>. |
| * The search order is defined in this class' comment. |
| */ |
| protected final List computeClassOrder(Class extensibleClass) { |
| ArrayList result = new ArrayList(4); |
| Class clazz = extensibleClass; |
| while (clazz != null) { |
| result.add(clazz); |
| clazz = clazz.getSuperclass(); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the interface search order for the class hierarchy described |
| * by <code>classList</code>. |
| * The search order is defined in this class' comment. |
| */ |
| protected final List computeInterfaceOrder(List classList) { |
| ArrayList result = new ArrayList(4); |
| Map seen = new HashMap(4); |
| for (Iterator list = classList.iterator(); list.hasNext();) { |
| Class[] interfaces = ((Class) list.next()).getInterfaces(); |
| internalComputeInterfaceOrder(interfaces, result, seen); |
| } |
| return result; |
| } |
| |
| /** |
| * Flushes the cache of contributor search paths. This is generally required |
| * whenever a contributor is added or removed. |
| * <p> |
| * It is likely easier to just toss the whole cache rather than trying to be |
| * smart and remove only those entries affected. |
| */ |
| public void flushLookup() { |
| objectLookup = null; |
| resourceAdapterLookup = null; |
| adaptableLookup = null; |
| } |
| |
| /** |
| * Cache the real adapter class contributor search path. |
| */ |
| private void cacheResourceAdapterLookup(Class adapterClass, List results) { |
| if (resourceAdapterLookup == null) { |
| resourceAdapterLookup = new HashMap(); |
| } |
| resourceAdapterLookup.put(adapterClass, results); |
| } |
| |
| /** |
| * Cache the real adapter class contributor search path. |
| */ |
| private void cacheAdaptableLookup(String adapterClass, List results) { |
| if (adaptableLookup == null) { |
| adaptableLookup = new HashMap(); |
| } |
| adaptableLookup.put(adapterClass, results); |
| } |
| |
| /** |
| * Cache the object class contributor search path. |
| */ |
| private void cacheObjectLookup(Class objectClass, List results) { |
| if (objectLookup == null) { |
| objectLookup = new HashMap(); |
| } |
| objectLookup.put(objectClass, results); |
| } |
| |
| /** |
| * Get the contributions registered to this manager. |
| * |
| * @return an unmodifiable <code>Collection</code> containing all registered |
| * contributions. The objects in this <code>Collection</code> will be |
| * <code>List</code>s containing the actual contributions. |
| * @since 3.0 |
| */ |
| public Collection getContributors() { |
| return Collections.unmodifiableCollection(contributors.values()); |
| } |
| |
| /** |
| * Return the list of contributors for the supplied class. |
| */ |
| protected List addContributorsFor(Class objectClass) { |
| |
| List classList = computeClassOrder(objectClass); |
| List result = new ArrayList(); |
| addContributorsFor(classList, result); |
| classList = computeInterfaceOrder(classList); // interfaces |
| addContributorsFor(classList, result); |
| return result; |
| } |
| |
| /** |
| * Returns true if contributors exist in the manager for |
| * this object and any of it's super classes, interfaces, or |
| * adapters. |
| * |
| * @param object the object to test |
| * @return whether the object has contributors |
| */ |
| public boolean hasContributorsFor(Object object) { |
| |
| List contributors = getContributors(object); |
| return contributors.size() > 0; |
| } |
| |
| /** |
| * Add interface Class objects to the result list based |
| * on the class hierarchy. Interfaces will be searched |
| * based on their position in the result list. |
| */ |
| private void internalComputeInterfaceOrder(Class[] interfaces, List result, |
| Map seen) { |
| List newInterfaces = new ArrayList(seen.size()); |
| for (Class currentInterface : interfaces) { |
| if (seen.get(currentInterface) == null) { |
| result.add(currentInterface); |
| seen.put(currentInterface, currentInterface); |
| newInterfaces.add(currentInterface); |
| } |
| } |
| for (Iterator newList = newInterfaces.iterator(); newList.hasNext();) { |
| internalComputeInterfaceOrder(((Class) newList.next()) |
| .getInterfaces(), result, seen); |
| } |
| } |
| |
| /** |
| * Return whether the given contributor is applicable to all elements in the |
| * selection. |
| * |
| * @param selection |
| * the selection |
| * @param contributor |
| * the contributor |
| * @return whether it is applicable |
| */ |
| public boolean isApplicableTo(IStructuredSelection selection, |
| IObjectContributor contributor) { |
| Iterator elements = selection.iterator(); |
| while (elements.hasNext()) { |
| if (contributor.isApplicableTo(elements.next()) == false) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Return whether the given contributor is applicable to all elements in the |
| * list. |
| * |
| * @param list |
| * the selection |
| * @param contributor |
| * the contributor |
| * @return whether it is applicable |
| */ |
| |
| public boolean isApplicableTo(List list, IObjectContributor contributor) { |
| Iterator elements = list.iterator(); |
| while (elements.hasNext()) { |
| if (contributor.isApplicableTo(elements.next()) == false) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Register a contributor. |
| * |
| * @param contributor the contributor |
| * @param targetType the target type |
| */ |
| public void registerContributor(IObjectContributor contributor, |
| String targetType) { |
| List contributorList = (List) contributors.get(targetType); |
| if (contributorList == null) { |
| contributorList = new ArrayList(5); |
| contributors.put(targetType, contributorList); |
| } |
| contributorList.add(contributor); |
| flushLookup(); |
| |
| IConfigurationElement element = Adapters.adapt(contributor, IConfigurationElement.class); |
| |
| //hook the object listener |
| if (element != null) { |
| ContributorRecord contributorRecord = new ContributorRecord( |
| contributor, targetType); |
| contributorRecordSet.add(contributorRecord); |
| PlatformUI.getWorkbench().getExtensionTracker().registerObject( |
| element.getDeclaringExtension(), contributorRecord, |
| IExtensionTracker.REF_WEAK); |
| } |
| } |
| |
| /** |
| * Unregister all contributors. |
| */ |
| public void unregisterAllContributors() { |
| contributors = new Hashtable(5); |
| flushLookup(); |
| } |
| |
| /** |
| * Unregister a contributor from the target type. |
| * |
| * @param contributor the contributor |
| * @param targetType the target type |
| */ |
| public void unregisterContributor(IObjectContributor contributor, |
| String targetType) { |
| List contributorList = (List) contributors.get(targetType); |
| if (contributorList == null) { |
| return; |
| } |
| contributorList.remove(contributor); |
| if (contributorList.isEmpty()) { |
| contributors.remove(targetType); |
| } |
| flushLookup(); |
| } |
| |
| |
| /** |
| * Unregister all contributors for the target type. |
| * |
| * @param targetType the target type |
| */ |
| public void unregisterContributors(String targetType) { |
| contributors.remove(targetType); |
| flushLookup(); |
| } |
| |
| protected List getContributors(Object object) { |
| // Determine is the object is a resource |
| Object resource = LegacyResourceSupport.getAdaptedContributorResource(object); |
| |
| // Fetch the unique adapters |
| List adapters = new ArrayList(Arrays.asList(Platform.getAdapterManager().computeAdapterTypes(object.getClass()))); |
| removeCommonAdapters(adapters, Arrays.asList(new Class[] {object.getClass()})); |
| |
| List contributors = new ArrayList(); |
| |
| // Calculate the contributors for this object class |
| addAll(contributors, getObjectContributors(object.getClass())); |
| // Calculate the contributors for resource classes |
| if(resource != null) { |
| addAll(contributors, getResourceContributors(resource.getClass())); |
| } |
| // Calculate the contributors for each adapter type |
| if(adapters != null) { |
| for (Iterator it = adapters.iterator(); it.hasNext();) { |
| String adapter = (String) it.next(); |
| addAll(contributors, getAdaptableContributors(adapter)); |
| } |
| } |
| |
| // Remove duplicates. Note: this -must- maintain the element order to preserve menu order. |
| contributors = removeDups(contributors); |
| |
| return contributors.isEmpty() ? Collections.EMPTY_LIST : new ArrayList(contributors); |
| } |
| |
| /** |
| * Returns the contributions for the given class. This considers |
| * contributors on any super classes and interfaces. |
| * |
| * @param objectClass the class to search for contributions. |
| * @return the contributions for the given class. This considers |
| * contributors on any super classes and interfaces. |
| * |
| * @since 3.1 |
| */ |
| protected List getObjectContributors(Class objectClass) { |
| List objectList = null; |
| // Lookup the results in the cache first. |
| if (objectLookup != null) { |
| objectList = (List) objectLookup.get(objectClass); |
| } |
| if (objectList == null) { |
| objectList = addContributorsFor(objectClass); |
| if (objectList.size() == 0) { |
| objectList = Collections.EMPTY_LIST; |
| } |
| else { |
| objectList = Collections.unmodifiableList(objectList); |
| } |
| cacheObjectLookup(objectClass, objectList); |
| } |
| return objectList; |
| } |
| |
| /** |
| * Returns the contributions for the given <code>IResource</code>class. |
| * This considers contributors on any super classes and interfaces. This |
| * will only return contributions that are adaptable. |
| * |
| * @param resourceClass the class to search for contributions. |
| * @return the contributions for the given class. This considers |
| * adaptable contributors on any super classes and interfaces. |
| * |
| * @since 3.1 |
| */ |
| protected List getResourceContributors(Class resourceClass) { |
| List resourceList = null; |
| if (resourceAdapterLookup != null) { |
| resourceList = (List) resourceAdapterLookup.get(resourceClass); |
| } |
| if (resourceList == null) { |
| resourceList = addContributorsFor(resourceClass); |
| if (resourceList.size() == 0) { |
| resourceList = Collections.EMPTY_LIST; |
| } else { |
| resourceList = Collections.unmodifiableList(filterOnlyAdaptableContributors(resourceList)); |
| } |
| cacheResourceAdapterLookup(resourceClass, resourceList); |
| } |
| return resourceList; |
| } |
| |
| /** |
| * Returns the contributions for the given type name. |
| * |
| * @param adapterType the class to search for contributions. |
| * @return the contributions for the given class. This considers |
| * contributors to this specific type. |
| * |
| * @since 3.1 |
| */ |
| protected List getAdaptableContributors(String adapterType) { |
| List adaptableList = null; |
| // Lookup the results in the cache first, there are two caches |
| // one that stores non-adapter contributions and the other |
| // contains adapter contributions. |
| if (adaptableLookup != null) { |
| adaptableList = (List) adaptableLookup.get(adapterType); |
| } |
| if (adaptableList == null) { |
| // ignore resource adapters because these must be adapted via the |
| // IContributorResourceAdapter. |
| if (LegacyResourceSupport.isResourceType(adapterType) || LegacyResourceSupport.isResourceMappingType(adapterType)) { |
| adaptableList = Collections.EMPTY_LIST; |
| } |
| else { |
| adaptableList = (List) contributors.get(adapterType); |
| if (adaptableList == null || adaptableList.size() == 0) { |
| adaptableList = Collections.EMPTY_LIST; |
| } else { |
| adaptableList = Collections.unmodifiableList(filterOnlyAdaptableContributors(adaptableList)); |
| } |
| } |
| cacheAdaptableLookup(adapterType, adaptableList); |
| } |
| return adaptableList; |
| } |
| |
| /** |
| * Prunes from the list of adapters type names that are in the class |
| * search order of every class in <code>results</code>. |
| * @param adapters |
| * @param results |
| * @since 3.1 |
| */ |
| protected void removeCommonAdapters(List adapters, List results) { |
| for (Iterator it = results.iterator(); it.hasNext();) { |
| Class clazz = ((Class) it.next()); |
| List commonTypes = computeCombinedOrder(clazz); |
| for (Iterator it2 = commonTypes.iterator(); it2.hasNext();) { |
| Class type = (Class) it2.next(); |
| adapters.remove(type.getName()); |
| } |
| } |
| } |
| |
| /** |
| * Returns the class search order starting with <code>extensibleClass</code>. |
| * The search order is defined in this class' comment. |
| */ |
| protected 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 (Class currentInterface : interfaces) { |
| result.add(currentInterface); |
| } |
| // get the superclass |
| clazz = clazz.getSuperclass(); |
| } |
| return result; |
| } |
| |
| private List filterOnlyAdaptableContributors(List contributors) { |
| List adaptableContributors = null; |
| for (Iterator it = contributors.iterator(); it.hasNext();) { |
| IObjectContributor c = (IObjectContributor) it.next(); |
| if(c.canAdapt()) { |
| if(adaptableContributors == null) { |
| adaptableContributors = new ArrayList(); |
| } |
| adaptableContributors.add(c); |
| } |
| } |
| return adaptableContributors == null ? Collections.EMPTY_LIST : adaptableContributors; |
| } |
| |
| @Override |
| public void removeExtension(IExtension source, Object[] objects) { |
| for (Object object : objects) { |
| if (object instanceof ContributorRecord) { |
| ContributorRecord contributorRecord = (ContributorRecord) object; |
| unregisterContributor((contributorRecord).contributor, (contributorRecord).objectClassName); |
| contributorRecordSet.remove(contributorRecord); |
| } |
| } |
| } |
| |
| /** |
| * Remove listeners and dispose of this manager. |
| * |
| * @since 3.1 |
| */ |
| public void dispose() { |
| if(getExtensionPointFilter() != null) { |
| PlatformUI.getWorkbench().getExtensionTracker().unregisterHandler(this); |
| } |
| } |
| |
| /** |
| * Returns the list of contributors that are interested in the |
| * given list of model elements. |
| * @param elements a list of model elements (<code>Object</code>) |
| * @return the list of interested contributors (<code>IObjectContributor</code>) |
| */ |
| protected List getContributors(List elements) { |
| // Calculate the common class, interfaces, and adapters registered |
| // via the IAdapterManager. |
| List commonAdapters = new ArrayList(); |
| List commonClasses = getCommonClasses(elements, commonAdapters); |
| |
| // 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); |
| Class resourceMappingClass = getResourceMappingClass(elements); |
| |
| // Get the contributors. |
| |
| List contributors = new ArrayList(); |
| |
| // Add the resource contributions to avoid duplication |
| if (resourceClass != null) { |
| addAll(contributors, getResourceContributors(resourceClass)); |
| } |
| if (commonClasses != null && !commonClasses.isEmpty()) { |
| for (int i = 0; i < commonClasses.size(); i++) { |
| List results = getObjectContributors((Class) commonClasses |
| .get(i)); |
| addAll(contributors, results); |
| } |
| } |
| // Add the resource mappings explicitly to avoid possible duplication |
| if (resourceMappingClass == null) { |
| // Still show the menus if the object is not adaptable but the adapter manager |
| // has an entry for it |
| resourceMappingClass = LegacyResourceSupport |
| .getResourceMappingClass(); |
| if (resourceMappingClass != null |
| && commonAdapters.contains(resourceMappingClass.getName())) { |
| addAll(contributors, getResourceContributors(resourceMappingClass)); |
| } |
| } else { |
| contributors.addAll(getResourceContributors(resourceMappingClass)); |
| } |
| if (!commonAdapters.isEmpty()) { |
| for (Iterator it = commonAdapters.iterator(); it.hasNext();) { |
| String adapter = (String) it.next(); |
| addAll(contributors, getAdaptableContributors(adapter)); |
| } |
| } |
| |
| // Remove duplicates. Note: this -must- maintain the element order to preserve menu order. |
| contributors = removeDups(contributors); |
| |
| return contributors.isEmpty() ? Collections.EMPTY_LIST : new ArrayList(contributors); |
| } |
| |
| /** |
| * Adds all items in toAdd to the given collection. Optimized to avoid creating an iterator. |
| * This assumes that toAdd is efficient to index (i.e. it's an ArrayList or some other RandomAccessList), |
| * which is the case for all uses in this class. |
| */ |
| private static void addAll(Collection collection, List toAdd) { |
| for (int i = 0, size = toAdd.size(); i < size; ++i) { |
| collection.add(toAdd.get(i)); |
| } |
| } |
| |
| /** |
| * Removes duplicates from the given list, preserving order. |
| */ |
| private static List removeDups(List list) { |
| if (list.size() <= 1) { |
| return list; |
| } |
| HashSet set = new HashSet(list); |
| if (set.size() == list.size()) { |
| return list; |
| } |
| ArrayList result = new ArrayList(set.size()); |
| for (Iterator i = list.iterator(); i.hasNext();) { |
| Object o = i.next(); |
| if (set.remove(o)) { |
| result.add(o); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the common denominator class, interfaces, and adapters |
| * for the given collection of objects. |
| */ |
| private List getCommonClasses(List objects, List commonAdapters) { |
| if (objects == null || objects.size() == 0) { |
| return null; |
| } |
| |
| // Optimization: if n==1 (or if all objects are of the same class), then the common class is the object's class, |
| // and the common adapters are the adapters cached for that class in the adapter manager |
| // See bug 177592 for more details. |
| if (allSameClass(objects)) { |
| |
| Class clazz = objects.get(0).getClass(); |
| commonAdapters.addAll(Arrays.asList(Platform.getAdapterManager().computeAdapterTypes(clazz))); |
| List result = new ArrayList(1); |
| result.add(clazz); |
| return result; |
| } |
| |
| // Compute all the super classes, interfaces, and adapters |
| // for the first element. |
| List classes = computeClassOrder(objects.get(0).getClass()); |
| List adapters = computeAdapterOrder(classes); |
| List interfaces = computeInterfaceOrder(classes); |
| |
| // Cache of all types found in the selection - this is needed |
| // to compute common adapters. |
| List lastCommonTypes = new ArrayList(); |
| |
| boolean classesEmpty = classes.isEmpty(); |
| boolean interfacesEmpty = interfaces.isEmpty(); |
| |
| // Traverse the selection if there is more than one element selected. |
| for (int i = 1; i < objects.size(); i++) { |
| // Compute all the super classes for the current element |
| List otherClasses = computeClassOrder(objects.get(i).getClass()); |
| if (!classesEmpty) { |
| classesEmpty = extractCommonClasses(classes, otherClasses); |
| } |
| |
| // Compute all the interfaces for the current element |
| // and all of its super classes. |
| List otherInterfaces = computeInterfaceOrder(otherClasses); |
| if (!interfacesEmpty) { |
| interfacesEmpty = extractCommonClasses(interfaces, |
| otherInterfaces); |
| } |
| |
| // Compute all the adapters provided for the calculated |
| // classes and interfaces for this element. |
| List classesAndInterfaces = new ArrayList(otherClasses); |
| if (otherInterfaces != null) { |
| classesAndInterfaces.addAll(otherInterfaces); |
| } |
| List otherAdapters = computeAdapterOrder(classesAndInterfaces); |
| |
| // Compute common adapters |
| // Note here that an adapter can match a class or interface, that is |
| // that an element in the selection may not adapt to a type but instead |
| // be of that type. |
| // If the selected classes doesn't have adapters, keep |
| // adapters that match the given classes types (classes and interfaces). |
| if (otherAdapters.isEmpty() && !adapters.isEmpty()) { |
| removeNonCommonAdapters(adapters, classesAndInterfaces); |
| } else { |
| if (adapters.isEmpty()) { |
| removeNonCommonAdapters(otherAdapters, lastCommonTypes); |
| if (!otherAdapters.isEmpty()) { |
| adapters.addAll(otherAdapters); |
| } |
| } else { |
| // Remove any adapters of the first element that |
| // are not in the current element's adapter list. |
| for (Iterator it = adapters.iterator(); it.hasNext();) { |
| String adapter = (String) it.next(); |
| if (!otherAdapters.contains(adapter)) { |
| it.remove(); |
| } |
| } |
| } |
| } |
| |
| // Remember the common search order up to now, this is |
| // used to match adapters against common classes or interfaces. |
| lastCommonTypes.clear(); |
| lastCommonTypes.addAll(classes); |
| lastCommonTypes.addAll(interfaces); |
| |
| if (interfacesEmpty && classesEmpty && adapters.isEmpty()) { |
| // As soon as we detect nothing in common, just exit. |
| return null; |
| } |
| } |
| |
| // Once the common classes, interfaces, and adapters are |
| // calculated, let's prune the lists to remove duplicates. |
| 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) { |
| removeCommonInterfaces(superClasses, interfaces, results); |
| } |
| |
| // Remove adapters already included as common classes |
| if (!adapters.isEmpty()) { |
| removeCommonAdapters(adapters, results); |
| commonAdapters.addAll(adapters); |
| } |
| return results; |
| } |
| |
| /** |
| * Returns <code>true</code> if all objects in the given list are of the same class, |
| * <code>false</code> otherwise. |
| */ |
| private boolean allSameClass(List objects) { |
| int size = objects.size(); |
| if (size <= 1) return true; |
| Class clazz = objects.get(0).getClass(); |
| for (int i = 1; i < size; ++i) { |
| if (!objects.get(i).getClass().equals(clazz)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean extractCommonClasses(List classes, List otherClasses) { |
| boolean classesEmpty = true; |
| if (otherClasses.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; // TODO: should this only be set if item not nulled out? |
| if (!otherClasses.contains(classes.get(j))) { |
| classes.set(j, null); |
| } |
| } |
| } |
| } |
| return classesEmpty; |
| } |
| |
| private void removeNonCommonAdapters(List adapters, List classes) { |
| for (int i = 0; i < classes.size(); i++) { |
| Object o = classes.get(i); |
| if (o != null) { |
| Class clazz = (Class) o; |
| String name = clazz.getName(); |
| if (adapters.contains(name)) { |
| return; |
| } |
| } |
| } |
| adapters.clear(); |
| } |
| |
| private void removeCommonInterfaces(List superClasses, List types, |
| List results) { |
| List dropInterfaces = null; |
| if (!superClasses.isEmpty()) { |
| dropInterfaces = computeInterfaceOrder(superClasses); |
| } |
| for (int j = 0; j < types.size(); j++) { |
| if (types.get(j) != null) { |
| if (dropInterfaces != null |
| && !dropInterfaces.contains(types.get(j))) { |
| results.add(types.get(j)); |
| } |
| } |
| } |
| } |
| |
| private List computeAdapterOrder(List classList) { |
| Set result = new HashSet(4); |
| IAdapterManager adapterMgr = Platform.getAdapterManager(); |
| for (Iterator list = classList.iterator(); list.hasNext();) { |
| Class clazz = ((Class) list.next()); |
| for (String adapter : adapterMgr.computeAdapterTypes(clazz)) { |
| if (!result.contains(adapter)) { |
| result.add(adapter); |
| } |
| } |
| } |
| return new ArrayList(result); |
| } |
| |
| /** |
| * 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; |
| } |
| Class resourceClass = LegacyResourceSupport.getResourceClass(); |
| if (resourceClass == null) { |
| // resources plug-in not loaded - no resources. period. |
| return null; |
| } |
| |
| List testList = new ArrayList(objects.size()); |
| |
| for (int i = 0; i < objects.size(); i++) { |
| Object object = objects.get(i); |
| |
| if (object instanceof IAdaptable) { |
| if (resourceClass.isInstance(object)) { |
| continue; |
| } |
| |
| Object resource = LegacyResourceSupport |
| .getAdaptedContributorResource(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); |
| } |
| |
| /** |
| * Return the ResourceMapping class if the elements all adapt to it. |
| */ |
| private Class getResourceMappingClass(List objects) { |
| if (objects == null || objects.size() == 0) { |
| return null; |
| } |
| Class resourceMappingClass = LegacyResourceSupport |
| .getResourceMappingClass(); |
| if (resourceMappingClass == null) { |
| // resources plug-in not loaded - no resources. period. |
| return null; |
| } |
| |
| for (int i = 0; i < objects.size(); i++) { |
| Object object = objects.get(i); |
| |
| if (object instanceof IAdaptable) { |
| if (resourceMappingClass.isInstance(object)) { |
| continue; |
| } |
| |
| Object resourceMapping = LegacyResourceSupport |
| .getAdaptedContributorResourceMapping(object); |
| |
| if (resourceMapping == null) { |
| //Not a resource and does not adapt. No common resource class |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } |
| // If we get here then all objects adapt to ResourceMapping |
| return resourceMappingClass; |
| } |
| |
| /** |
| * 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 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; |
| } |
| } |