All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.equinox.common.src.org.eclipse.core.internal.runtime.AdapterManager Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * 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.core.internal.runtime;

import java.util.*;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.core.runtime.IAdapterManager;

/**
 * This class is the standard implementation of IAdapterManager. It provides
 * fast lookup of property values with the following semantics:
 * 
    *
  • At most one factory will be invoked per property lookup *
  • If multiple installed factories provide the same adapter, only the first found in * the search order will be invoked. *
  • The search order from a class with the definition
    * class X extends Y implements A, B
    is as follows: *
  • the target's class: X *
  • X's superclasses in order to Object *
  • a breadth-first traversal of the target class's interfaces in the order returned by * getInterfaces (in the example, A and its superinterfaces then B and its * superinterfaces) *
* * @see IAdapterFactory * @see IAdapterManager */ public final class AdapterManager implements IAdapterManager { /** * Cache of adapters for a given adaptable class. Maps String -> Map * (adaptable class name -> (adapter class name -> factory instance)) */ protected HashMap adapterLookup; /** * Cache of classes for a given type name. Avoids too many loadClass calls. * (factory -> (type name -> clazz)) */ protected HashMap classLookup; /** * Cache of class lookup order. This avoids having to compute often, and * provides clients with quick lookup for instanceOf checks based on type name. */ protected HashMap classSearchOrderLookup; /** * Map of factories, keyed by String, fully qualified class name of * the adaptable class that the factory provides adapters for. Value is a List * of IAdapterFactory. */ protected final HashMap factories; private ArrayList lazyFactoryProviders; private static final AdapterManager singleton = new AdapterManager(); public static AdapterManager getDefault() { return singleton; } /** * Private constructor to block instance creation. */ private AdapterManager() { factories = new HashMap(5); lazyFactoryProviders = new ArrayList(1); adapterLookup = null; } /** * Given a type name, add all of the factories that respond to those types into * the given table. Each entry will be keyed by the adapter class name (supplied in * IAdapterFactory.getAdapterList). */ private void addFactoriesFor(String typeName, Map table) { List factoryList = (List) getFactories().get(typeName); if (factoryList == null) return; for (int i = 0, imax = factoryList.size(); i < imax; i++) { IAdapterFactory factory = (IAdapterFactory) factoryList.get(i); if (factory instanceof IAdapterFactoryExt) { String[] adapters = ((IAdapterFactoryExt) factory).getAdapterNames(); for (int j = 0; j < adapters.length; j++) { if (table.get(adapters[j]) == null) table.put(adapters[j], factory); } } else { Class[] adapters = factory.getAdapterList(); for (int j = 0; j < adapters.length; j++) { String adapterName = adapters[j].getName(); if (table.get(adapterName) == null) table.put(adapterName, factory); } } } } private void cacheClassLookup(IAdapterFactory factory, Class clazz) { //cache reference to lookup to protect against concurrent flush HashMap lookup = classLookup; if (lookup == null) classLookup = lookup = new HashMap(4); HashMap classes = (HashMap) lookup.get(factory); if (classes == null) { classes = new HashMap(4); lookup.put(factory, classes); } classes.put(clazz.getName(), clazz); } private Class cachedClassForName(IAdapterFactory factory, String typeName) { Class clazz = null; //cache reference to lookup to protect against concurrent flush HashMap lookup = classLookup; if (lookup != null) { HashMap classes = (HashMap) lookup.get(factory); if (classes != null) { clazz = (Class) classes.get(typeName); } } return clazz; } /** * Returns the class with the given fully qualified name, or null * if that class does not exist or belongs to a plug-in that has not * yet been loaded. */ private Class classForName(IAdapterFactory factory, String typeName) { Class clazz = cachedClassForName(factory, typeName); if (clazz == null) { try { if (factory instanceof IAdapterFactoryExt) factory = ((IAdapterFactoryExt) factory).loadFactory(false); if (factory != null) { clazz = factory.getClass().getClassLoader().loadClass(typeName); cacheClassLookup(factory, clazz); } } catch (ClassNotFoundException e) { // class not yet loaded } } return clazz; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdapterManager#getAdapterTypes(java.lang.Class) */ public String[] computeAdapterTypes(Class adaptable) { Set types = getFactories(adaptable).keySet(); return (String[]) types.toArray(new String[types.size()]); } /** * Computes the adapters that the provided class can adapt to, along * with the factory object that can perform that transformation. Returns * a table of adapter class name to factory object. * @param adaptable */ private Map getFactories(Class adaptable) { //cache reference to lookup to protect against concurrent flush HashMap lookup = adapterLookup; if (lookup == null) adapterLookup = lookup = new HashMap(30); Map table = (Map) lookup.get(adaptable.getName()); if (table == null) { // calculate adapters for the class table = new HashMap(4); Class[] classes = computeClassOrder(adaptable); for (int i = 0; i < classes.length; i++) addFactoriesFor(classes[i].getName(), table); // cache the table lookup.put(adaptable.getName(), table); } return table; } public Class[] computeClassOrder(Class adaptable) { List classes = null; //cache reference to lookup to protect against concurrent flush HashMap lookup = classSearchOrderLookup; if (lookup != null) classes = (List) lookup.get(adaptable); // compute class order only if it hasn't been cached before if (classes == null) { classes = new ArrayList(); computeClassOrder(adaptable, classes); if (lookup == null) classSearchOrderLookup = lookup = new HashMap(); lookup.put(adaptable, classes); } return (Class[]) classes.toArray(new Class[classes.size()]); } /** * Builds and returns a table of adapters for the given adaptable type. * The table is keyed by adapter class name. The * value is the sole factory that defines that adapter. Note that * if multiple adapters technically define the same property, only the * first found in the search order is considered. * * Note that it is important to maintain a consistent class and interface * lookup order. See the class comment for more details. */ private void computeClassOrder(Class adaptable, Collection classes) { Class clazz = adaptable; Set seen = new HashSet(4); while (clazz != null) { classes.add(clazz); computeInterfaceOrder(clazz.getInterfaces(), classes, seen); clazz = clazz.getSuperclass(); } } private void computeInterfaceOrder(Class[] interfaces, Collection classes, Set seen) { List newInterfaces = new ArrayList(interfaces.length); for (int i = 0; i < interfaces.length; i++) { Class interfac = interfaces[i]; if (seen.add(interfac)) { //note we cannot recurse here without changing the resulting interface order classes.add(interfac); newInterfaces.add(interfac); } } for (Iterator it = newInterfaces.iterator(); it.hasNext();) computeInterfaceOrder(((Class) it.next()).getInterfaces(), classes, seen); } /** * Flushes the cache of adapter search paths. This is generally required whenever an * adapter is added or removed. *

* It is likely easier to just toss the whole cache rather than trying to be smart * and remove only those entries affected. *

*/ public synchronized void flushLookup() { adapterLookup = null; classLookup = null; classSearchOrderLookup = null; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class) */ public Object getAdapter(Object adaptable, Class adapterType) { IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterType.getName()); Object result = null; if (factory != null) result = factory.getAdapter(adaptable, adapterType); if (result == null && adapterType.isInstance(adaptable)) return adaptable; return result; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class) */ public Object getAdapter(Object adaptable, String adapterType) { return getAdapter(adaptable, adapterType, false); } /** * Returns an adapter of the given type for the provided adapter. * @param adaptable the object to adapt * @param adapterType the type to adapt the object to * @param force true if the plug-in providing the * factory should be activated if necessary. false * if no plugin activations are desired. */ private Object getAdapter(Object adaptable, String adapterType, boolean force) { IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterType); if (force && factory instanceof IAdapterFactoryExt) factory = ((IAdapterFactoryExt) factory).loadFactory(true); Object result = null; if (factory != null) { Class clazz = classForName(factory, adapterType); if (clazz != null) result = factory.getAdapter(adaptable, clazz); } if (result == null && adaptable.getClass().getName().equals(adapterType)) return adaptable; return result; } public boolean hasAdapter(Object adaptable, String adapterTypeName) { return getFactories(adaptable.getClass()).get(adapterTypeName) != null; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdapterManager#loadAdapter(java.lang.Object, java.lang.String) */ public Object loadAdapter(Object adaptable, String adapterTypeName) { return getAdapter(adaptable, adapterTypeName, true); } /* * @see IAdapterManager#registerAdapters */ public synchronized void registerAdapters(IAdapterFactory factory, Class adaptable) { registerFactory(factory, adaptable.getName()); flushLookup(); } /* * @see IAdapterManager#registerAdapters */ public void registerFactory(IAdapterFactory factory, String adaptableType) { List list = (List) factories.get(adaptableType); if (list == null) { list = new ArrayList(5); factories.put(adaptableType, list); } list.add(factory); } /* * @see IAdapterManager#unregisterAdapters */ public synchronized void unregisterAdapters(IAdapterFactory factory) { for (Iterator it = factories.values().iterator(); it.hasNext();) ((List) it.next()).remove(factory); flushLookup(); } /* * @see IAdapterManager#unregisterAdapters */ public synchronized void unregisterAdapters(IAdapterFactory factory, Class adaptable) { List factoryList = (List) factories.get(adaptable.getName()); if (factoryList == null) return; factoryList.remove(factory); flushLookup(); } /* * Shuts down the adapter manager by removing all factories * and removing the registry change listener. Should only be * invoked during platform shutdown. */ public synchronized void unregisterAllAdapters() { factories.clear(); flushLookup(); } public void registerLazyFactoryProvider(IAdapterManagerProvider factoryProvider) { synchronized (lazyFactoryProviders) { lazyFactoryProviders.add(factoryProvider); } } public boolean unregisterLazyFactoryProvider(IAdapterManagerProvider factoryProvider) { synchronized (lazyFactoryProviders) { return lazyFactoryProviders.remove(factoryProvider); } } public HashMap getFactories() { // avoid the synchronize if we don't have to call it if (lazyFactoryProviders.size() == 0) return factories; synchronized (lazyFactoryProviders) { while (lazyFactoryProviders.size() > 0) { IAdapterManagerProvider provider = (IAdapterManagerProvider) lazyFactoryProviders.remove(0); if (provider.addFactories(this)) flushLookup(); } } return factories; } }