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

org.eclipse.core.internal.runtime.AdapterManager Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2021 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     David Green - fix factories with non-standard class loading (bug 200068) 
 *     Filip Hrbek - fix thread safety problem described in bug 305863
 *     Sergey Prigogin (Google) - use parameterized types (bug 442021)
 *     Christoph Läubrich - Bug 576660 - AdapterManager should use more modern concurrency primitives
 *******************************************************************************/
package org.eclipse.core.internal.runtime;

import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.*;

/**
 * This class is the standard implementation of IAdapterManager. It provides
 * fast lookup of property values with the following semantics:
 * 
    *
  • If multiple installed factories provide the same adapter, iterate until one of the factories * return a non-null value. Remaining factories won't 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 each class's interfaces in the * order returned by getInterfaces (in the example, X's * superinterfaces then Y's 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)) * Thread safety note: always use the compute methods to update the map * and make sure the values (inner map) are never modified but replaced if necessary. */ private final ConcurrentMap>> adapterLookup; /** * Cache of classes for a given type name. Avoids too many loadClass calls. * (factory -> (type name -> Class)). * Thread safety note: always use the compute methods to update the map * and make sure the values (inner map) are modified also this way. */ private final ConcurrentMap>> classLookup; /** * Cache of class lookup order (Class -> Class[]). This avoids having to compute often, and * provides clients with quick lookup for instanceOf checks based on type name. * Thread safety note: always use the compute methods to update the map * and make sure the values (Class array) are never modified but replaced if necessary. */ private final ConcurrentMap, Class[]> 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. */ private final Map> factories; private final Queue lazyFactoryProviders; private static final AdapterManager singleton = new AdapterManager(); public static AdapterManager getDefault() { return singleton; } /** * Private constructor to block instance creation. */ private AdapterManager() { classSearchOrderLookup = new ConcurrentHashMap<>(); adapterLookup = new ConcurrentHashMap<>(); lazyFactoryProviders = new ConcurrentLinkedQueue<>(); factories = new ConcurrentHashMap<>(); classLookup = new ConcurrentHashMap<>(); } private static boolean isFactoryLoaded(IAdapterFactory adapterFactory) { return (!(adapterFactory instanceof IAdapterFactoryExt)) || ((IAdapterFactoryExt) adapterFactory).loadFactory(false) != 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 adaptableTypeName, Map> table) { List factoryList = getFactories().get(adaptableTypeName); if (factoryList == null) return; for (IAdapterFactory factory : factoryList) { if (factory instanceof IAdapterFactoryExt) { String[] adapters = ((IAdapterFactoryExt) factory).getAdapterNames(); for (String adapter : adapters) { table.computeIfAbsent(adapter, any -> new ArrayList<>(1)).add(factory); } } else { Class[] adapters = factory.getAdapterList(); for (Class adapter : adapters) { table.computeIfAbsent(adapter.getName(), any -> new ArrayList<>(1)).add(factory); } } } } /** * Queries an {@link IAdapterFactory} for a given type name to return a compatible class object * @param adapterFactory the {@link IAdapterFactory} to query for the given classname, must not be null * @param typeName the name of the desired class, must not be null * @return 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 adapterFactory, String typeName) { return classLookup.computeIfAbsent(adapterFactory, factory -> new ConcurrentHashMap<>()).computeIfAbsent(typeName, type -> { return loadFactory(adapterFactory, false).map(factory -> { try { return factory.getClass().getClassLoader().loadClass(typeName); } catch (ClassNotFoundException e) { // it is possible that the default bundle classloader is unaware of this class // but the adaptor factory can load it in some other way. See bug 200068. Class[] adapterList = factory.getAdapterList(); for (Class adapter : adapterList) { if (typeName.equals(adapter.getName())) { return adapter; } } } return null; // class not yet loaded }).orElse(null); // factory not loaded yet }); } @Override public String[] computeAdapterTypes(Class adaptable) { Set types = getFactories(adaptable).keySet(); return 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. */ private Map> getFactories(Class adaptable) { //cache reference to lookup to protect against concurrent flush return adapterLookup.computeIfAbsent(adaptable.getName(), adaptableType -> { // calculate adapters for the class Map> table = new HashMap<>(4); for (Class cl : computeClassOrder(adaptable)) { addFactoriesFor(cl.getName(), table); } return Collections.unmodifiableMap(table); }); } /** * Returns the super-type search order starting with adaptable. * The search order is defined in this class' comment. */ @Override public Class[] computeClassOrder(Class adaptable) { Class[] classOrder = getClassOrder(adaptable); return Arrays.copyOf(classOrder, classOrder.length); } @SuppressWarnings("unchecked") private Class[] getClassOrder(Class adaptable) { return (Class[]) classSearchOrderLookup.computeIfAbsent(adaptable, AdapterManager::doComputeClassOrder); } /** * Computes the super-type search order starting with adaptable. * The search order is defined in this class' comment. */ private static Class[] doComputeClassOrder(Class adaptable) { List> classes = new ArrayList<>(); Class clazz = adaptable; Set> seen = new HashSet<>(4); //first traverse class hierarchy while (clazz != null) { classes.add(clazz); clazz = clazz.getSuperclass(); } //now traverse interface hierarchy for each class Class[] classHierarchy = classes.toArray(new Class[classes.size()]); for (Class cl : classHierarchy) { computeInterfaceOrder(cl.getInterfaces(), classes, seen); } return classes.toArray(new Class[classes.size()]); } private static void computeInterfaceOrder(Class[] interfaces, Collection> classes, Set> seen) { List> newInterfaces = new ArrayList<>(interfaces.length); for (Class interfac : interfaces) { if (seen.add(interfac)) { //note we cannot recurse here without changing the resulting interface order classes.add(interfac); newInterfaces.add(interfac); } } for (Class clazz : newInterfaces) computeInterfaceOrder(clazz.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.clear(); classLookup.clear(); classSearchOrderLookup.clear(); } @Override @SuppressWarnings("unchecked") public T getAdapter(Object adaptable, Class adapterType) { Assert.isNotNull(adaptable); Assert.isNotNull(adapterType); List>> incorrectAdapters = new ArrayList<>(); T adapterObject = getFactories(adaptable.getClass()).getOrDefault(adapterType.getName(), Collections.emptyList()) // .stream() // .map(factory -> new SimpleEntry<>(factory, factory.getAdapter(adaptable, adapterType))) // .filter(entry -> { Object adapter = entry.getValue(); if (adapter == null) { return false; } boolean res = adapterType.isInstance(adapter); if (!res) { IAdapterFactory factory = entry.getKey(); incorrectAdapters.add(new SimpleEntry<>(factory, adapter.getClass())); } return res; }).map(Entry::getValue) // .findFirst() // .orElse(null); if (adapterObject == null) { if (!incorrectAdapters.isEmpty()) { throw new AssertionFailedException(incorrectAdapters.stream().map(entry -> "Adapter factory " //$NON-NLS-1$ + entry.getKey() + " returned " + entry.getValue().getName() //$NON-NLS-1$ + " that is not an instance of " + adapterType.getName()).collect(Collectors.joining("\n"))); //$NON-NLS-1$ //$NON-NLS-2$ } if (adapterType.isInstance(adaptable)) { return (T) adaptable; } } return adapterObject; } @Override public Object getAdapter(Object adaptable, String adapterType) { Assert.isNotNull(adaptable); Assert.isNotNull(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) { Assert.isNotNull(adaptable); Assert.isNotNull(adapterType); return getFactories(adaptable.getClass()).getOrDefault(adapterType, Collections.emptyList()) // .stream() // .map(factory -> force && factory instanceof IAdapterFactoryExt ? ((IAdapterFactoryExt) factory).loadFactory(true) : factory) // .filter(Objects::nonNull).map(factory -> { Class adapterClass = classForName(factory, adapterType); if (adapterClass == null) { return null; } return factory.getAdapter(adaptable, adapterClass); // }).filter(Objects::nonNull) // .findFirst() // .map(Object.class::cast) // casting to object seems necessary here; compiler issue? .orElseGet(() -> adapterType.equals(adaptable.getClass().getName()) ? adaptable : null); } @Override public boolean hasAdapter(Object adaptable, String adapterTypeName) { return getFactories(adaptable.getClass()).get(adapterTypeName) != null; } @Override public int queryAdapter(Object adaptable, String adapterTypeName) { List eligibleFactories = getFactories(adaptable.getClass()).get(adapterTypeName); if (eligibleFactories == null || eligibleFactories.isEmpty()) { return NONE; } if (eligibleFactories.stream().anyMatch(AdapterManager::isFactoryLoaded)) { return LOADED; } return NOT_LOADED; } @Override public Object loadAdapter(Object adaptable, String adapterTypeName) { return getAdapter(adaptable, adapterTypeName, true); } /* * @see IAdapterManager#registerAdapters */ @Override public synchronized void registerAdapters(IAdapterFactory factory, Class adaptable) { registerFactory(factory, adaptable.getName()); flushLookup(); } /* * @see IAdapterManager#registerAdapters */ public void registerFactory(IAdapterFactory factory, String adaptableType) { factories.computeIfAbsent(adaptableType, any -> new CopyOnWriteArrayList<>()).add(factory); } /* * @see IAdapterManager#unregisterAdapters */ @Override public synchronized void unregisterAdapters(IAdapterFactory factory) { for (List list : factories.values()) list.remove(factory); flushLookup(); } /* * @see IAdapterManager#unregisterAdapters */ @Override public synchronized void unregisterAdapters(IAdapterFactory factory, Class adaptable) { List factoryList = 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() { lazyFactoryProviders.clear(); factories.clear(); flushLookup(); } public void registerLazyFactoryProvider(IAdapterManagerProvider factoryProvider) { lazyFactoryProviders.add(factoryProvider); } public boolean unregisterLazyFactoryProvider(IAdapterManagerProvider factoryProvider) { return lazyFactoryProviders.remove(factoryProvider); } public Map> getFactories() { IAdapterManagerProvider provider; while ((provider = lazyFactoryProviders.poll()) != null) { if (provider.addFactories(this)) { flushLookup(); } } return factories; } /** * Try to laod the given factory according to the force parameter * @param factory the factory to load * @param force if loading should be forced * @return an {@link Optional} describing the loaded factory */ private static Optional loadFactory(IAdapterFactory factory, boolean force) { if (factory instanceof IAdapterFactoryExt) { return Optional.ofNullable(((IAdapterFactoryExt) factory).loadFactory(force)); } return Optional.ofNullable(factory); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy