blob: 77d8309a99bd8b38ecbaa125d72971ff10126a6e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.IAdaptable;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.Platform;
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;
import org.eclipse.ui.internal.util.Util;
/**
* 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;
if(canHandleExtensionTracking()){
IExtensionTracker tracker = PlatformUI.getWorkbench().getExtensionTracker();
tracker.registerHandler(this, null);
}
}
/**
* Return whether or not the receiver handles extension
* tracking. Default is <code>true</code>. Subclasses may override.
* @return boolean <code>true</code> if it should be registered
* as an extension handler.
*/
protected boolean canHandleExtensionTracking() {
return true;
}
/**
* 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 (int i = 0; i < interfaces.length; i++) {
Class interfac = interfaces[i];
if (seen.get(interfac) == null) {
result.add(interfac);
seen.put(interfac, interfac);
newInterfaces.add(interfac);
}
}
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 = (IConfigurationElement) Util.getAdapter(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()}));
// Calculate the contributors for this object class
List contributors = getObjectContributors(object.getClass());
// Calculate the contributors for resource classes
if(resource != null) {
contributors.addAll(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();
contributors.addAll(getAdaptableContributors(adapter));
}
}
return 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;
}
cacheObjectLookup(objectClass, objectList);
}
// return a shallow copy of the contributors, ensure that the caller
// cannot modify the cache directly.
return new ArrayList(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 = filterOnlyAdaptableContributors(resourceList);
}
cacheResourceAdapterLookup(resourceClass, resourceList);
}
// return a shallow copy of the contributors, ensure that the caller
// cannot modify the cache directly.
return new ArrayList(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) {
adaptableList = new ArrayList(contributors.size());
// ignore resource adapters because these must be adapted via the
// IContributorResourceAdapter.
if (LegacyResourceSupport.isResourceType(adapterType) || LegacyResourceSupport.isResourceMappingType(adapterType)) {
return Collections.EMPTY_LIST;
}
adaptableList = (List) contributors.get(adapterType);
if (adaptableList == null || adaptableList.size() == 0) {
adaptableList = Collections.EMPTY_LIST;
} else {
adaptableList = filterOnlyAdaptableContributors(adaptableList);
}
cacheAdaptableLookup(adapterType, adaptableList);
}
// return a shallow copy of the contributors, ensure that the caller
// cannot modify the cache directly.
return new ArrayList(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 (int i = 0; i < interfaces.length; i++) {
result.add(interfaces[i]);
}
// 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;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension, java.lang.Object[])
*/
public void removeExtension(IExtension source, Object[] objects) {
for (int i = 0; i < objects.length; i++) {
if (objects[i] instanceof ContributorRecord) {
ContributorRecord contributorRecord = (ContributorRecord) objects[i];
unregisterContributor((contributorRecord).contributor, (contributorRecord).objectClassName);
contributorRecordSet.remove(contributorRecord);
}
}
}
/**
* Remove listeners and dispose of this manager.
*
* @since 3.1
*/
public void dispose() {
if(canHandleExtensionTracking()) {
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) {
contributors.addAll(getResourceContributors(resourceClass));
}
if (commonClasses != null && !commonClasses.isEmpty()) {
for (int i = 0; i < commonClasses.size(); i++) {
List results = getObjectContributors((Class) commonClasses
.get(i));
if (results != null) {
contributors.addAll(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())) {
contributors
.addAll(getResourceContributors(resourceMappingClass));
}
} else {
contributors.addAll(getResourceContributors(resourceMappingClass));
}
if (!commonAdapters.isEmpty()) {
for (Iterator it = commonAdapters.iterator(); it.hasNext();) {
String adapter = (String) it.next();
contributors.addAll(getAdaptableContributors(adapter));
}
}
// Final 'cull' to ensure no dups
List culledContributors = contributors;
Set uniqueContribs = new HashSet(contributors);
// Are there dups?
if (uniqueContribs.size() < contributors.size()) {
// NOTE: We have to filter out the extras but
// -must- maintain the element order to preserve menu order
culledContributors = new ArrayList(uniqueContribs.size());
for (Iterator iter = contributors.iterator(); iter.hasNext();) {
Object element = iter.next();
if (!culledContributors.contains(element))
culledContributors.add(element);
}
}
return culledContributors;
}
/**
* 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;
}
// 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;
}
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());
String[] adapters = adapterMgr.computeAdapterTypes(clazz);
for (int i = 0; i < adapters.length; i++) {
String adapter = adapters[i];
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();
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;
}
List testList = new ArrayList();
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;
}
testList.add(resourceMapping);
} 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;
}
}