0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.Collection;
0015: import java.util.Collections;
0016: import java.util.HashMap;
0017: import java.util.HashSet;
0018: import java.util.Hashtable;
0019: import java.util.Iterator;
0020: import java.util.List;
0021: import java.util.Map;
0022: import java.util.Set;
0023:
0024: import org.eclipse.core.runtime.IAdaptable;
0025: import org.eclipse.core.runtime.IAdapterManager;
0026: import org.eclipse.core.runtime.IConfigurationElement;
0027: import org.eclipse.core.runtime.IExtension;
0028: import org.eclipse.core.runtime.Platform;
0029: import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
0030: import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
0031: import org.eclipse.jface.viewers.IStructuredSelection;
0032: import org.eclipse.ui.PlatformUI;
0033: import org.eclipse.ui.internal.util.Util;
0034:
0035: /**
0036: * This class is a default implementation of <code>IObjectContributorManager</code>.
0037: * It provides fast merging of contributions with the following semantics:
0038: * <ul>
0039: * <li> All of the matching contributors will be invoked per property lookup
0040: * <li> The search order from a class with the definition<br>
0041: * <code>class X extends Y implements A, B</code><br>
0042: * is as follows:
0043: * <il>
0044: * <li>the target's class: X
0045: * <li>X's superclasses in order to <code>Object</code>
0046: * <li>a depth-first traversal of the target class's interaces in the order
0047: * returned by <code>getInterfaces()</code> (in the example, A and
0048: * its superinterfaces then B and its superinterfaces)
0049: * </il>
0050: * </ul>
0051: *
0052: * @see IObjectContributor
0053: */
0054: public abstract class ObjectContributorManager implements
0055: IExtensionChangeHandler {
0056:
0057: /**
0058: * @since 3.1
0059: */
0060: private class ContributorRecord {
0061: /**
0062: * @param contributor
0063: * @param targetType
0064: */
0065: public ContributorRecord(IObjectContributor contributor,
0066: String targetType) {
0067: this .contributor = contributor;
0068: this .objectClassName = targetType;
0069: }
0070:
0071: String objectClassName;
0072: IObjectContributor contributor;
0073: }
0074:
0075: /** Table of contributors. */
0076: protected Map contributors;
0077:
0078: /** Cache of object class contributor search paths; <code>null</code> if none. */
0079: protected Map objectLookup;
0080:
0081: /** Cache of resource adapter class contributor search paths; <code>null</code> if none. */
0082: protected Map resourceAdapterLookup;
0083:
0084: /** Cache of adaptable class contributor search paths; <code>null</code> if none. */
0085: protected Map adaptableLookup;
0086:
0087: protected Set contributorRecordSet;
0088:
0089: /**
0090: * Constructs a new contributor manager.
0091: */
0092: public ObjectContributorManager() {
0093: contributors = new Hashtable(5);
0094: contributorRecordSet = new HashSet(5);
0095: objectLookup = null;
0096: resourceAdapterLookup = null;
0097: adaptableLookup = null;
0098: if (canHandleExtensionTracking()) {
0099: IExtensionTracker tracker = PlatformUI.getWorkbench()
0100: .getExtensionTracker();
0101: tracker.registerHandler(this , null);
0102: }
0103: }
0104:
0105: /**
0106: * Return whether or not the receiver handles extension
0107: * tracking. Default is <code>true</code>. Subclasses may override.
0108: * @return boolean <code>true</code> if it should be registered
0109: * as an extension handler.
0110: */
0111: protected boolean canHandleExtensionTracking() {
0112: return true;
0113: }
0114:
0115: /**
0116: * Adds contributors for the given types to the result list.
0117: */
0118: private void addContributorsFor(List types, List result) {
0119: for (Iterator classes = types.iterator(); classes.hasNext();) {
0120: Class clazz = (Class) classes.next();
0121: List contributorList = (List) contributors.get(clazz
0122: .getName());
0123: if (contributorList != null) {
0124: result.addAll(contributorList);
0125: }
0126: }
0127: }
0128:
0129: /**
0130: * Returns the class search order starting with <code>extensibleClass</code>.
0131: * The search order is defined in this class' comment.
0132: */
0133: protected final List computeClassOrder(Class extensibleClass) {
0134: ArrayList result = new ArrayList(4);
0135: Class clazz = extensibleClass;
0136: while (clazz != null) {
0137: result.add(clazz);
0138: clazz = clazz.getSuperclass();
0139: }
0140: return result;
0141: }
0142:
0143: /**
0144: * Returns the interface search order for the class hierarchy described
0145: * by <code>classList</code>.
0146: * The search order is defined in this class' comment.
0147: */
0148: protected final List computeInterfaceOrder(List classList) {
0149: ArrayList result = new ArrayList(4);
0150: Map seen = new HashMap(4);
0151: for (Iterator list = classList.iterator(); list.hasNext();) {
0152: Class[] interfaces = ((Class) list.next()).getInterfaces();
0153: internalComputeInterfaceOrder(interfaces, result, seen);
0154: }
0155: return result;
0156: }
0157:
0158: /**
0159: * Flushes the cache of contributor search paths. This is generally required
0160: * whenever a contributor is added or removed.
0161: * <p>
0162: * It is likely easier to just toss the whole cache rather than trying to be
0163: * smart and remove only those entries affected.
0164: */
0165: public void flushLookup() {
0166: objectLookup = null;
0167: resourceAdapterLookup = null;
0168: adaptableLookup = null;
0169: }
0170:
0171: /**
0172: * Cache the real adapter class contributor search path.
0173: */
0174: private void cacheResourceAdapterLookup(Class adapterClass,
0175: List results) {
0176: if (resourceAdapterLookup == null) {
0177: resourceAdapterLookup = new HashMap();
0178: }
0179: resourceAdapterLookup.put(adapterClass, results);
0180: }
0181:
0182: /**
0183: * Cache the real adapter class contributor search path.
0184: */
0185: private void cacheAdaptableLookup(String adapterClass, List results) {
0186: if (adaptableLookup == null) {
0187: adaptableLookup = new HashMap();
0188: }
0189: adaptableLookup.put(adapterClass, results);
0190: }
0191:
0192: /**
0193: * Cache the object class contributor search path.
0194: */
0195: private void cacheObjectLookup(Class objectClass, List results) {
0196: if (objectLookup == null) {
0197: objectLookup = new HashMap();
0198: }
0199: objectLookup.put(objectClass, results);
0200: }
0201:
0202: /**
0203: * Get the contributions registered to this manager.
0204: *
0205: * @return an unmodifiable <code>Collection</code> containing all registered
0206: * contributions. The objects in this <code>Collection</code> will be
0207: * <code>List</code>s containing the actual contributions.
0208: * @since 3.0
0209: */
0210: public Collection getContributors() {
0211: return Collections
0212: .unmodifiableCollection(contributors.values());
0213: }
0214:
0215: /**
0216: * Return the list of contributors for the supplied class.
0217: */
0218: protected List addContributorsFor(Class objectClass) {
0219:
0220: List classList = computeClassOrder(objectClass);
0221: List result = new ArrayList();
0222: addContributorsFor(classList, result);
0223: classList = computeInterfaceOrder(classList); // interfaces
0224: addContributorsFor(classList, result);
0225: return result;
0226: }
0227:
0228: /**
0229: * Returns true if contributors exist in the manager for
0230: * this object and any of it's super classes, interfaces, or
0231: * adapters.
0232: *
0233: * @param object the object to test
0234: * @return whether the object has contributors
0235: */
0236: public boolean hasContributorsFor(Object object) {
0237:
0238: List contributors = getContributors(object);
0239: return contributors.size() > 0;
0240: }
0241:
0242: /**
0243: * Add interface Class objects to the result list based
0244: * on the class hierarchy. Interfaces will be searched
0245: * based on their position in the result list.
0246: */
0247: private void internalComputeInterfaceOrder(Class[] interfaces,
0248: List result, Map seen) {
0249: List newInterfaces = new ArrayList(seen.size());
0250: for (int i = 0; i < interfaces.length; i++) {
0251: Class interfac = interfaces[i];
0252: if (seen.get(interfac) == null) {
0253: result.add(interfac);
0254: seen.put(interfac, interfac);
0255: newInterfaces.add(interfac);
0256: }
0257: }
0258: for (Iterator newList = newInterfaces.iterator(); newList
0259: .hasNext();) {
0260: internalComputeInterfaceOrder(((Class) newList.next())
0261: .getInterfaces(), result, seen);
0262: }
0263: }
0264:
0265: /**
0266: * Return whether the given contributor is applicable to all elements in the
0267: * selection.
0268: *
0269: * @param selection
0270: * the selection
0271: * @param contributor
0272: * the contributor
0273: * @return whether it is applicable
0274: */
0275: public boolean isApplicableTo(IStructuredSelection selection,
0276: IObjectContributor contributor) {
0277: Iterator elements = selection.iterator();
0278: while (elements.hasNext()) {
0279: if (contributor.isApplicableTo(elements.next()) == false) {
0280: return false;
0281: }
0282: }
0283: return true;
0284: }
0285:
0286: /**
0287: * Return whether the given contributor is applicable to all elements in the
0288: * list.
0289: *
0290: * @param list
0291: * the selection
0292: * @param contributor
0293: * the contributor
0294: * @return whether it is applicable
0295: */
0296:
0297: public boolean isApplicableTo(List list,
0298: IObjectContributor contributor) {
0299: Iterator elements = list.iterator();
0300: while (elements.hasNext()) {
0301: if (contributor.isApplicableTo(elements.next()) == false) {
0302: return false;
0303: }
0304: }
0305: return true;
0306: }
0307:
0308: /**
0309: * Register a contributor.
0310: *
0311: * @param contributor the contributor
0312: * @param targetType the target type
0313: */
0314: public void registerContributor(IObjectContributor contributor,
0315: String targetType) {
0316: List contributorList = (List) contributors.get(targetType);
0317: if (contributorList == null) {
0318: contributorList = new ArrayList(5);
0319: contributors.put(targetType, contributorList);
0320: }
0321: contributorList.add(contributor);
0322: flushLookup();
0323:
0324: IConfigurationElement element = (IConfigurationElement) Util
0325: .getAdapter(contributor, IConfigurationElement.class);
0326:
0327: //hook the object listener
0328: if (element != null) {
0329: ContributorRecord contributorRecord = new ContributorRecord(
0330: contributor, targetType);
0331: contributorRecordSet.add(contributorRecord);
0332: PlatformUI.getWorkbench().getExtensionTracker()
0333: .registerObject(element.getDeclaringExtension(),
0334: contributorRecord,
0335: IExtensionTracker.REF_WEAK);
0336: }
0337: }
0338:
0339: /**
0340: * Unregister all contributors.
0341: */
0342: public void unregisterAllContributors() {
0343: contributors = new Hashtable(5);
0344: flushLookup();
0345: }
0346:
0347: /**
0348: * Unregister a contributor from the target type.
0349: *
0350: * @param contributor the contributor
0351: * @param targetType the target type
0352: */
0353: public void unregisterContributor(IObjectContributor contributor,
0354: String targetType) {
0355: List contributorList = (List) contributors.get(targetType);
0356: if (contributorList == null) {
0357: return;
0358: }
0359: contributorList.remove(contributor);
0360: if (contributorList.isEmpty()) {
0361: contributors.remove(targetType);
0362: }
0363: flushLookup();
0364: }
0365:
0366: /**
0367: * Unregister all contributors for the target type.
0368: *
0369: * @param targetType the target type
0370: */
0371: public void unregisterContributors(String targetType) {
0372: contributors.remove(targetType);
0373: flushLookup();
0374: }
0375:
0376: protected List getContributors(Object object) {
0377: // Determine is the object is a resource
0378: Object resource = LegacyResourceSupport
0379: .getAdaptedContributorResource(object);
0380:
0381: // Fetch the unique adapters
0382: List adapters = new ArrayList(Arrays.asList(Platform
0383: .getAdapterManager().computeAdapterTypes(
0384: object.getClass())));
0385: removeCommonAdapters(adapters, Arrays
0386: .asList(new Class[] { object.getClass() }));
0387:
0388: List contributors = new ArrayList();
0389:
0390: // Calculate the contributors for this object class
0391: addAll(contributors, getObjectContributors(object.getClass()));
0392: // Calculate the contributors for resource classes
0393: if (resource != null) {
0394: addAll(contributors, getResourceContributors(resource
0395: .getClass()));
0396: }
0397: // Calculate the contributors for each adapter type
0398: if (adapters != null) {
0399: for (Iterator it = adapters.iterator(); it.hasNext();) {
0400: String adapter = (String) it.next();
0401: addAll(contributors, getAdaptableContributors(adapter));
0402: }
0403: }
0404:
0405: // Remove duplicates. Note: this -must- maintain the element order to preserve menu order.
0406: contributors = removeDups(contributors);
0407:
0408: return contributors.isEmpty() ? Collections.EMPTY_LIST
0409: : new ArrayList(contributors);
0410: }
0411:
0412: /**
0413: * Returns the contributions for the given class. This considers
0414: * contributors on any super classes and interfaces.
0415: *
0416: * @param objectClass the class to search for contributions.
0417: * @return the contributions for the given class. This considers
0418: * contributors on any super classes and interfaces.
0419: *
0420: * @since 3.1
0421: */
0422: protected List getObjectContributors(Class objectClass) {
0423: List objectList = null;
0424: // Lookup the results in the cache first.
0425: if (objectLookup != null) {
0426: objectList = (List) objectLookup.get(objectClass);
0427: }
0428: if (objectList == null) {
0429: objectList = addContributorsFor(objectClass);
0430: if (objectList.size() == 0) {
0431: objectList = Collections.EMPTY_LIST;
0432: } else {
0433: objectList = Collections.unmodifiableList(objectList);
0434: }
0435: cacheObjectLookup(objectClass, objectList);
0436: }
0437: return objectList;
0438: }
0439:
0440: /**
0441: * Returns the contributions for the given <code>IResource</code>class.
0442: * This considers contributors on any super classes and interfaces. This
0443: * will only return contributions that are adaptable.
0444: *
0445: * @param resourceClass the class to search for contributions.
0446: * @return the contributions for the given class. This considers
0447: * adaptable contributors on any super classes and interfaces.
0448: *
0449: * @since 3.1
0450: */
0451: protected List getResourceContributors(Class resourceClass) {
0452: List resourceList = null;
0453: if (resourceAdapterLookup != null) {
0454: resourceList = (List) resourceAdapterLookup
0455: .get(resourceClass);
0456: }
0457: if (resourceList == null) {
0458: resourceList = addContributorsFor(resourceClass);
0459: if (resourceList.size() == 0) {
0460: resourceList = Collections.EMPTY_LIST;
0461: } else {
0462: resourceList = Collections
0463: .unmodifiableList(filterOnlyAdaptableContributors(resourceList));
0464: }
0465: cacheResourceAdapterLookup(resourceClass, resourceList);
0466: }
0467: return resourceList;
0468: }
0469:
0470: /**
0471: * Returns the contributions for the given type name.
0472: *
0473: * @param adapterType the class to search for contributions.
0474: * @return the contributions for the given class. This considers
0475: * contributors to this specific type.
0476: *
0477: * @since 3.1
0478: */
0479: protected List getAdaptableContributors(String adapterType) {
0480: List adaptableList = null;
0481: // Lookup the results in the cache first, there are two caches
0482: // one that stores non-adapter contributions and the other
0483: // contains adapter contributions.
0484: if (adaptableLookup != null) {
0485: adaptableList = (List) adaptableLookup.get(adapterType);
0486: }
0487: if (adaptableList == null) {
0488: // ignore resource adapters because these must be adapted via the
0489: // IContributorResourceAdapter.
0490: if (LegacyResourceSupport.isResourceType(adapterType)
0491: || LegacyResourceSupport
0492: .isResourceMappingType(adapterType)) {
0493: adaptableList = Collections.EMPTY_LIST;
0494: } else {
0495: adaptableList = (List) contributors.get(adapterType);
0496: if (adaptableList == null || adaptableList.size() == 0) {
0497: adaptableList = Collections.EMPTY_LIST;
0498: } else {
0499: adaptableList = Collections
0500: .unmodifiableList(filterOnlyAdaptableContributors(adaptableList));
0501: }
0502: }
0503: cacheAdaptableLookup(adapterType, adaptableList);
0504: }
0505: return adaptableList;
0506: }
0507:
0508: /**
0509: * Prunes from the list of adapters type names that are in the class
0510: * search order of every class in <code>results</code>.
0511: * @param adapters
0512: * @param results
0513: * @since 3.1
0514: */
0515: protected void removeCommonAdapters(List adapters, List results) {
0516: for (Iterator it = results.iterator(); it.hasNext();) {
0517: Class clazz = ((Class) it.next());
0518: List commonTypes = computeCombinedOrder(clazz);
0519: for (Iterator it2 = commonTypes.iterator(); it2.hasNext();) {
0520: Class type = (Class) it2.next();
0521: adapters.remove(type.getName());
0522: }
0523: }
0524: }
0525:
0526: /**
0527: * Returns the class search order starting with <code>extensibleClass</code>.
0528: * The search order is defined in this class' comment.
0529: */
0530: protected List computeCombinedOrder(Class inputClass) {
0531: List result = new ArrayList(4);
0532: Class clazz = inputClass;
0533: while (clazz != null) {
0534: // add the class
0535: result.add(clazz);
0536: // add all the interfaces it implements
0537: Class[] interfaces = clazz.getInterfaces();
0538: for (int i = 0; i < interfaces.length; i++) {
0539: result.add(interfaces[i]);
0540: }
0541: // get the superclass
0542: clazz = clazz.getSuperclass();
0543: }
0544: return result;
0545: }
0546:
0547: private List filterOnlyAdaptableContributors(List contributors) {
0548: List adaptableContributors = null;
0549: for (Iterator it = contributors.iterator(); it.hasNext();) {
0550: IObjectContributor c = (IObjectContributor) it.next();
0551: if (c.canAdapt()) {
0552: if (adaptableContributors == null) {
0553: adaptableContributors = new ArrayList();
0554: }
0555: adaptableContributors.add(c);
0556: }
0557: }
0558: return adaptableContributors == null ? Collections.EMPTY_LIST
0559: : adaptableContributors;
0560: }
0561:
0562: /* (non-Javadoc)
0563: * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension, java.lang.Object[])
0564: */
0565: public void removeExtension(IExtension source, Object[] objects) {
0566: for (int i = 0; i < objects.length; i++) {
0567: if (objects[i] instanceof ContributorRecord) {
0568: ContributorRecord contributorRecord = (ContributorRecord) objects[i];
0569: unregisterContributor((contributorRecord).contributor,
0570: (contributorRecord).objectClassName);
0571: contributorRecordSet.remove(contributorRecord);
0572: }
0573: }
0574: }
0575:
0576: /**
0577: * Remove listeners and dispose of this manager.
0578: *
0579: * @since 3.1
0580: */
0581: public void dispose() {
0582: if (canHandleExtensionTracking()) {
0583: PlatformUI.getWorkbench().getExtensionTracker()
0584: .unregisterHandler(this );
0585: }
0586: }
0587:
0588: /**
0589: * Returns the list of contributors that are interested in the
0590: * given list of model elements.
0591: * @param elements a list of model elements (<code>Object</code>)
0592: * @return the list of interested contributors (<code>IObjectContributor</code>)
0593: */
0594: protected List getContributors(List elements) {
0595: // Calculate the common class, interfaces, and adapters registered
0596: // via the IAdapterManager.
0597: List commonAdapters = new ArrayList();
0598: List commonClasses = getCommonClasses(elements, commonAdapters);
0599:
0600: // Get the resource class. It will be null if any of the
0601: // elements are resources themselves or do not adapt to
0602: // IResource.
0603: Class resourceClass = getCommonResourceClass(elements);
0604: Class resourceMappingClass = getResourceMappingClass(elements);
0605:
0606: // Get the contributors.
0607:
0608: List contributors = new ArrayList();
0609:
0610: // Add the resource contributions to avoid duplication
0611: if (resourceClass != null) {
0612: addAll(contributors, getResourceContributors(resourceClass));
0613: }
0614: if (commonClasses != null && !commonClasses.isEmpty()) {
0615: for (int i = 0; i < commonClasses.size(); i++) {
0616: List results = getObjectContributors((Class) commonClasses
0617: .get(i));
0618: addAll(contributors, results);
0619: }
0620: }
0621: // Add the resource mappings explicitly to avoid possible duplication
0622: if (resourceMappingClass == null) {
0623: // Still show the menus if the object is not adaptable but the adapter manager
0624: // has an entry for it
0625: resourceMappingClass = LegacyResourceSupport
0626: .getResourceMappingClass();
0627: if (resourceMappingClass != null
0628: && commonAdapters.contains(resourceMappingClass
0629: .getName())) {
0630: addAll(contributors,
0631: getResourceContributors(resourceMappingClass));
0632: }
0633: } else {
0634: contributors
0635: .addAll(getResourceContributors(resourceMappingClass));
0636: }
0637: if (!commonAdapters.isEmpty()) {
0638: for (Iterator it = commonAdapters.iterator(); it.hasNext();) {
0639: String adapter = (String) it.next();
0640: addAll(contributors, getAdaptableContributors(adapter));
0641: }
0642: }
0643:
0644: // Remove duplicates. Note: this -must- maintain the element order to preserve menu order.
0645: contributors = removeDups(contributors);
0646:
0647: return contributors.isEmpty() ? Collections.EMPTY_LIST
0648: : new ArrayList(contributors);
0649: }
0650:
0651: /**
0652: * Adds all items in toAdd to the given collection. Optimized to avoid creating an iterator.
0653: * This assumes that toAdd is efficient to index (i.e. it's an ArrayList or some other RandomAccessList),
0654: * which is the case for all uses in this class.
0655: */
0656: private static void addAll(Collection collection, List toAdd) {
0657: for (int i = 0, size = toAdd.size(); i < size; ++i) {
0658: collection.add(toAdd.get(i));
0659: }
0660: }
0661:
0662: /**
0663: * Removes duplicates from the given list, preserving order.
0664: */
0665: private static List removeDups(List list) {
0666: if (list.size() <= 1) {
0667: return list;
0668: }
0669: HashSet set = new HashSet(list);
0670: if (set.size() == list.size()) {
0671: return list;
0672: }
0673: ArrayList result = new ArrayList(set.size());
0674: for (Iterator i = list.iterator(); i.hasNext();) {
0675: Object o = i.next();
0676: if (set.remove(o)) {
0677: result.add(o);
0678: }
0679: }
0680: return result;
0681: }
0682:
0683: /**
0684: * Returns the common denominator class, interfaces, and adapters
0685: * for the given collection of objects.
0686: */
0687: private List getCommonClasses(List objects, List commonAdapters) {
0688: if (objects == null || objects.size() == 0) {
0689: return null;
0690: }
0691:
0692: // Optimization: if n==1 (or if all objects are of the same class), then the common class is the object's class,
0693: // and the common adapters are the adapters cached for that class in the adapter manager
0694: // See bug 177592 for more details.
0695: if (allSameClass(objects)) {
0696:
0697: Class clazz = objects.get(0).getClass();
0698: commonAdapters.addAll(Arrays.asList(Platform
0699: .getAdapterManager().computeAdapterTypes(clazz)));
0700: List result = new ArrayList(1);
0701: result.add(clazz);
0702: return result;
0703: }
0704:
0705: // Compute all the super classes, interfaces, and adapters
0706: // for the first element.
0707: List classes = computeClassOrder(objects.get(0).getClass());
0708: List adapters = computeAdapterOrder(classes);
0709: List interfaces = computeInterfaceOrder(classes);
0710:
0711: // Cache of all types found in the selection - this is needed
0712: // to compute common adapters.
0713: List lastCommonTypes = new ArrayList();
0714:
0715: boolean classesEmpty = classes.isEmpty();
0716: boolean interfacesEmpty = interfaces.isEmpty();
0717:
0718: // Traverse the selection if there is more than one element selected.
0719: for (int i = 1; i < objects.size(); i++) {
0720: // Compute all the super classes for the current element
0721: List otherClasses = computeClassOrder(objects.get(i)
0722: .getClass());
0723: if (!classesEmpty) {
0724: classesEmpty = extractCommonClasses(classes,
0725: otherClasses);
0726: }
0727:
0728: // Compute all the interfaces for the current element
0729: // and all of its super classes.
0730: List otherInterfaces = computeInterfaceOrder(otherClasses);
0731: if (!interfacesEmpty) {
0732: interfacesEmpty = extractCommonClasses(interfaces,
0733: otherInterfaces);
0734: }
0735:
0736: // Compute all the adapters provided for the calculated
0737: // classes and interfaces for this element.
0738: List classesAndInterfaces = new ArrayList(otherClasses);
0739: if (otherInterfaces != null) {
0740: classesAndInterfaces.addAll(otherInterfaces);
0741: }
0742: List otherAdapters = computeAdapterOrder(classesAndInterfaces);
0743:
0744: // Compute common adapters
0745: // Note here that an adapter can match a class or interface, that is
0746: // that an element in the selection may not adapt to a type but instead
0747: // be of that type.
0748: // If the selected classes doesn't have adapters, keep
0749: // adapters that match the given classes types (classes and interfaces).
0750: if (otherAdapters.isEmpty() && !adapters.isEmpty()) {
0751: removeNonCommonAdapters(adapters, classesAndInterfaces);
0752: } else {
0753: if (adapters.isEmpty()) {
0754: removeNonCommonAdapters(otherAdapters,
0755: lastCommonTypes);
0756: if (!otherAdapters.isEmpty()) {
0757: adapters.addAll(otherAdapters);
0758: }
0759: } else {
0760: // Remove any adapters of the first element that
0761: // are not in the current element's adapter list.
0762: for (Iterator it = adapters.iterator(); it
0763: .hasNext();) {
0764: String adapter = (String) it.next();
0765: if (!otherAdapters.contains(adapter)) {
0766: it.remove();
0767: }
0768: }
0769: }
0770: }
0771:
0772: // Remember the common search order up to now, this is
0773: // used to match adapters against common classes or interfaces.
0774: lastCommonTypes.clear();
0775: lastCommonTypes.addAll(classes);
0776: lastCommonTypes.addAll(interfaces);
0777:
0778: if (interfacesEmpty && classesEmpty && adapters.isEmpty()) {
0779: // As soon as we detect nothing in common, just exit.
0780: return null;
0781: }
0782: }
0783:
0784: // Once the common classes, interfaces, and adapters are
0785: // calculated, let's prune the lists to remove duplicates.
0786: ArrayList results = new ArrayList(4);
0787: ArrayList super Classes = new ArrayList(4);
0788: if (!classesEmpty) {
0789: for (int j = 0; j < classes.size(); j++) {
0790: if (classes.get(j) != null) {
0791: super Classes.add(classes.get(j));
0792: }
0793: }
0794: // Just keep the first super class
0795: if (!super Classes.isEmpty()) {
0796: results.add(super Classes.get(0));
0797: }
0798: }
0799:
0800: if (!interfacesEmpty) {
0801: removeCommonInterfaces(super Classes, interfaces, results);
0802: }
0803:
0804: // Remove adapters already included as common classes
0805: if (!adapters.isEmpty()) {
0806: removeCommonAdapters(adapters, results);
0807: commonAdapters.addAll(adapters);
0808: }
0809: return results;
0810: }
0811:
0812: /**
0813: * Returns <code>true</code> if all objects in the given list are of the same class,
0814: * <code>false</code> otherwise.
0815: */
0816: private boolean allSameClass(List objects) {
0817: int size = objects.size();
0818: if (size <= 1)
0819: return true;
0820: Class clazz = objects.get(0).getClass();
0821: for (int i = 1; i < size; ++i) {
0822: if (!objects.get(i).getClass().equals(clazz)) {
0823: return false;
0824: }
0825: }
0826: return true;
0827: }
0828:
0829: private boolean extractCommonClasses(List classes, List otherClasses) {
0830: boolean classesEmpty = true;
0831: if (otherClasses.isEmpty()) {
0832: // When no super classes, then it is obvious there
0833: // are no common super classes with the first element
0834: // so clear its list.
0835: classes.clear();
0836: } else {
0837: // Remove any super classes of the first element that
0838: // are not in the current element's super classes list.
0839: for (int j = 0; j < classes.size(); j++) {
0840: if (classes.get(j) != null) {
0841: classesEmpty = false; // TODO: should this only be set if item not nulled out?
0842: if (!otherClasses.contains(classes.get(j))) {
0843: classes.set(j, null);
0844: }
0845: }
0846: }
0847: }
0848: return classesEmpty;
0849: }
0850:
0851: private void removeNonCommonAdapters(List adapters, List classes) {
0852: for (int i = 0; i < classes.size(); i++) {
0853: Object o = classes.get(i);
0854: if (o != null) {
0855: Class clazz = (Class) o;
0856: String name = clazz.getName();
0857: if (adapters.contains(name)) {
0858: return;
0859: }
0860: }
0861: }
0862: adapters.clear();
0863: }
0864:
0865: private void removeCommonInterfaces(List super Classes, List types,
0866: List results) {
0867: List dropInterfaces = null;
0868: if (!super Classes.isEmpty()) {
0869: dropInterfaces = computeInterfaceOrder(super Classes);
0870: }
0871: for (int j = 0; j < types.size(); j++) {
0872: if (types.get(j) != null) {
0873: if (dropInterfaces != null
0874: && !dropInterfaces.contains(types.get(j))) {
0875: results.add(types.get(j));
0876: }
0877: }
0878: }
0879: }
0880:
0881: private List computeAdapterOrder(List classList) {
0882: Set result = new HashSet(4);
0883: IAdapterManager adapterMgr = Platform.getAdapterManager();
0884: for (Iterator list = classList.iterator(); list.hasNext();) {
0885: Class clazz = ((Class) list.next());
0886: String[] adapters = adapterMgr.computeAdapterTypes(clazz);
0887: for (int i = 0; i < adapters.length; i++) {
0888: String adapter = adapters[i];
0889: if (!result.contains(adapter)) {
0890: result.add(adapter);
0891: }
0892: }
0893: }
0894: return new ArrayList(result);
0895: }
0896:
0897: /**
0898: * Returns the common denominator resource class for the given
0899: * collection of objects.
0900: * Do not return a resource class if the objects are resources
0901: * themselves so as to prevent double registration of actions.
0902: */
0903: private Class getCommonResourceClass(List objects) {
0904: if (objects == null || objects.size() == 0) {
0905: return null;
0906: }
0907: Class resourceClass = LegacyResourceSupport.getResourceClass();
0908: if (resourceClass == null) {
0909: // resources plug-in not loaded - no resources. period.
0910: return null;
0911: }
0912:
0913: List testList = new ArrayList(objects.size());
0914:
0915: for (int i = 0; i < objects.size(); i++) {
0916: Object object = objects.get(i);
0917:
0918: if (object instanceof IAdaptable) {
0919: if (resourceClass.isInstance(object)) {
0920: continue;
0921: }
0922:
0923: Object resource = LegacyResourceSupport
0924: .getAdaptedContributorResource(object);
0925:
0926: if (resource == null) {
0927: //Not a resource and does not adapt. No common resource class
0928: return null;
0929: }
0930: testList.add(resource);
0931: } else {
0932: return null;
0933: }
0934: }
0935:
0936: return getCommonClass(testList);
0937: }
0938:
0939: /**
0940: * Return the ResourceMapping class if the elements all adapt to it.
0941: */
0942: private Class getResourceMappingClass(List objects) {
0943: if (objects == null || objects.size() == 0) {
0944: return null;
0945: }
0946: Class resourceMappingClass = LegacyResourceSupport
0947: .getResourceMappingClass();
0948: if (resourceMappingClass == null) {
0949: // resources plug-in not loaded - no resources. period.
0950: return null;
0951: }
0952:
0953: for (int i = 0; i < objects.size(); i++) {
0954: Object object = objects.get(i);
0955:
0956: if (object instanceof IAdaptable) {
0957: if (resourceMappingClass.isInstance(object)) {
0958: continue;
0959: }
0960:
0961: Object resourceMapping = LegacyResourceSupport
0962: .getAdaptedContributorResourceMapping(object);
0963:
0964: if (resourceMapping == null) {
0965: //Not a resource and does not adapt. No common resource class
0966: return null;
0967: }
0968: } else {
0969: return null;
0970: }
0971: }
0972: // If we get here then all objects adapt to ResourceMapping
0973: return resourceMappingClass;
0974: }
0975:
0976: /**
0977: * Returns the common denominator class for the given
0978: * collection of objects.
0979: */
0980: private Class getCommonClass(List objects) {
0981: if (objects == null || objects.size() == 0) {
0982: return null;
0983: }
0984: Class commonClass = objects.get(0).getClass();
0985: // try easy
0986: if (objects.size() == 1) {
0987: return commonClass;
0988: // try harder
0989: }
0990:
0991: for (int i = 1; i < objects.size(); i++) {
0992: Object object = objects.get(i);
0993: Class newClass = object.getClass();
0994: // try the short cut
0995: if (newClass.equals(commonClass)) {
0996: continue;
0997: }
0998: // compute common class
0999: commonClass = getCommonClass(commonClass, newClass);
1000: // give up
1001: if (commonClass == null) {
1002: return null;
1003: }
1004: }
1005: return commonClass;
1006: }
1007:
1008: /**
1009: * Returns the common denominator class for
1010: * two input classes.
1011: */
1012: private Class getCommonClass(Class class1, Class class2) {
1013: List list1 = computeCombinedOrder(class1);
1014: List list2 = computeCombinedOrder(class2);
1015: for (int i = 0; i < list1.size(); i++) {
1016: for (int j = 0; j < list2.size(); j++) {
1017: Class candidate1 = (Class) list1.get(i);
1018: Class candidate2 = (Class) list2.get(j);
1019: if (candidate1.equals(candidate2)) {
1020: return candidate1;
1021: }
1022: }
1023: }
1024: // no common class
1025: return null;
1026: }
1027: }
|