Bug 576024 - Multiple adapters for a adaptableType/class tuple (try 2)

Change-Id: I8c185ac49f21f6d65ba3ad0b50c907ebbe1903b8
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.bundles/+/186292
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
Reviewed-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF
index e3a1a48..5ab8017 100644
--- a/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF
@@ -8,7 +8,7 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Bundle-ActivationPolicy: lazy
 Require-Bundle: org.junit,
- org.eclipse.equinox.common;bundle-version="3.13.0",
+ org.eclipse.equinox.common;bundle-version="3.15.100",
  org.eclipse.core.tests.harness;bundle-version="3.11.400",
  org.eclipse.equinox.registry;bundle-version="3.8.200"
 Import-Package: org.eclipse.osgi.service.localization,
diff --git a/bundles/org.eclipse.equinox.common.tests/plugin.xml b/bundles/org.eclipse.equinox.common.tests/plugin.xml
index 3a998bd..18bf6e8 100644
--- a/bundles/org.eclipse.equinox.common.tests/plugin.xml
+++ b/bundles/org.eclipse.equinox.common.tests/plugin.xml
@@ -11,6 +11,20 @@
                type="org.eclipse.equinox.common.tests.adaptable.TestAdapter">
          </adapter>
       </factory>
+      <factory
+            adaptableType="org.eclipse.equinox.common.tests.adaptable.TestAdaptable"
+            class="org.eclipse.equinox.common.tests.adaptable.NullAdapter2Factory">
+         <adapter
+               type="org.eclipse.equinox.common.tests.adaptable.TestAdapter2">
+         </adapter>
+      </factory>
+      <factory
+            adaptableType="org.eclipse.equinox.common.tests.adaptable.TestAdaptable"
+            class="org.eclipse.equinox.common.tests.adaptable.TestAdapter2Factory">
+         <adapter
+               type="org.eclipse.equinox.common.tests.adaptable.TestAdapter2">
+         </adapter>
+      </factory>
    </extension> 
    <extension-point id="factoryLoaderTest" name="factoryLoaderTest" schema="schema/factoryLoaderTest.exsd"/>
    <extension point="org.eclipse.core.runtime.adapters">
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/IAdapterManagerTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/IAdapterManagerTest.java
index 191ce0d..a424a15 100644
--- a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/IAdapterManagerTest.java
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/IAdapterManagerTest.java
@@ -287,4 +287,75 @@
 			manager.unregisterAdapters(fac, Private.class);
 		}
 	}
+
+	@Test
+	public void testContinueAfterNullAdapterFactory() {
+		class PrivateAdapter {
+		}
+		class PrivateAdaptable {
+		}
+		IAdapterFactory nullAdapterFactory = new IAdapterFactory() {
+			@Override
+			public Class<?>[] getAdapterList() {
+				return new Class<?>[] { PrivateAdapter.class };
+			}
+
+			@Override
+			public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+				return null;
+			}
+		};
+		IAdapterFactory adapterFactory = new IAdapterFactory() {
+			@Override
+			public Class<?>[] getAdapterList() {
+				return new Class<?>[] { PrivateAdapter.class };
+			}
+
+			@Override
+			public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+				return (T) new PrivateAdapter();
+			}
+		};
+		try {
+			manager.registerAdapters(nullAdapterFactory, PrivateAdaptable.class);
+			manager.registerAdapters(adapterFactory, PrivateAdaptable.class);
+			assertNotNull(manager.getAdapter(new PrivateAdaptable(), PrivateAdapter.class));
+		} catch (Exception ex) {
+			manager.unregisterAdapters(nullAdapterFactory);
+			manager.unregisterAdapters(adapterFactory);
+		}
+	}
+
+	@Test
+	public void testMultipleAdapterFactoriesFromExtensionPoint() {
+		assertNotNull(manager.getAdapter(new TestAdaptable(), TestAdapter2.class));
+	}
+
+	@Test
+	public void testNoAdapterForType() {
+		manager.queryAdapter(new Object() {
+		}, String.class.getName());
+		// verifies it doesn't fail with NPE or other exception
+	}
+
+	@Test
+	public void testGetAdapterForSpecializedNamedSubtype() {
+		IAdapterFactory factory = new IAdapterFactory() {
+			@Override
+			public Class<?>[] getAdapterList() {
+				return new Class<?>[] { C.class };
+			}
+
+			@Override
+			public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+				return (T) new Y();
+			}
+		};
+		manager.registerAdapters(factory, Object.class);
+		try {
+			assertNotNull(manager.getAdapter(new Object(), C.class.getName()));
+		} finally {
+			manager.unregisterAdapters(factory);
+		}
+	}
 }
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/NullAdapter2Factory.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/NullAdapter2Factory.java
new file mode 100644
index 0000000..671db07
--- /dev/null
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/NullAdapter2Factory.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ *  Copyright (c) 2021 Red Hat, Inc. 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
+ *******************************************************************************/
+package org.eclipse.equinox.common.tests.adaptable;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+
+public class NullAdapter2Factory implements IAdapterFactory {
+
+	@Override
+	public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+		return null;
+	}
+
+	@Override
+	public Class<?>[] getAdapterList() {
+		return new Class<?>[] { TestAdaptable.class };
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/TestAdapter2Factory.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/TestAdapter2Factory.java
new file mode 100644
index 0000000..9b11ec4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/TestAdapter2Factory.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ *  Copyright (c) 2021 Red Hat, Inc. 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
+ *******************************************************************************/
+package org.eclipse.equinox.common.tests.adaptable;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+
+public class TestAdapter2Factory implements IAdapterFactory {
+
+	@Override
+	public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+		return (T) new TestAdapter2();
+	}
+
+	@Override
+	public Class<?>[] getAdapterList() {
+		return new Class<?>[] { TestAdapter2.class };
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/AdapterManager.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/AdapterManager.java
index f03be7e..9c4c206 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/AdapterManager.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/AdapterManager.java
@@ -17,23 +17,27 @@
 package org.eclipse.core.internal.runtime;
 
 import java.util.*;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import org.eclipse.core.runtime.*;
 
 /**
  * This class is the standard implementation of <code>IAdapterManager</code>. It provides
  * fast lookup of property values with the following semantics:
  * <ul>
- * <li>At most one factory will be invoked per property lookup
- * <li>If multiple installed factories provide the same adapter, only the first found in
- * the search order will be invoked.
+ * <li>If multiple installed factories provide the same adapter, iterate until one of the factories
+ * return a non-<code>null</code> value. Remaining factories won't be invoked.</li>
  * <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>
+ * <code>class X extends Y implements A, B</code><br> is as follows:
+ * <ul>
  * <li>the target's class: X
  * <li>X's superclasses in order to <code>Object</code>
  * <li>a breadth-first traversal of each class's interfaces in the
  * order returned by <code>getInterfaces</code> (in the example, X's 
  * superinterfaces then Y's superinterfaces) </li>
  * </ul>
+ * </ul>
  * 
  * @see IAdapterFactory
  * @see IAdapterManager
@@ -46,7 +50,7 @@
 	 * map wrapper class.  The inner map is not synchronized, but it is immutable
 	 * so synchronization is not necessary.
 	 */
-	private Map<String, Map<String, IAdapterFactory>> adapterLookup;
+	private Map<String, Map<String, List<IAdapterFactory>>> adapterLookup;
 
 	/**
 	 * Cache of classes for a given type name. Avoids too many loadClass calls.
@@ -76,12 +80,32 @@
 	 * the adaptable class that the factory provides adapters for. Value is a <code>List</code>
 	 * of <code>IAdapterFactory</code>.
 	 */
-	private final HashMap<String, List<IAdapterFactory>> factories;
+	private final Map<String, List<IAdapterFactory>> factories;
 
 	private final ArrayList<IAdapterManagerProvider> lazyFactoryProviders;
 
 	private static final AdapterManager singleton = new AdapterManager();
 
+	private static final Comparator<? super IAdapterFactory> ACTIVE_FIRST = new Comparator<IAdapterFactory>() {
+
+		@Override
+		public int compare(IAdapterFactory o1, IAdapterFactory o2) {
+			boolean factory1Loaded = isFactoryLoaded(o1);
+			boolean factory2Loaded = isFactoryLoaded(o2);
+			if (factory1Loaded == factory2Loaded) {
+				return 0;
+			}
+			if (factory1Loaded && !factory2Loaded) {
+				return -1;
+			}
+			if (!factory1Loaded && factory2Loaded) {
+				return +1;
+			}
+			return 0;
+		}
+
+	};
+
 	public static AdapterManager getDefault() {
 		return singleton;
 	}
@@ -94,29 +118,29 @@
 		lazyFactoryProviders = new ArrayList<>(1);
 	}
 
+	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 typeName, Map<String, IAdapterFactory> table) {
-		List<IAdapterFactory> factoryList = getFactories().get(typeName);
+	private void addFactoriesFor(String adaptableTypeName, Map<String, List<IAdapterFactory>> table) {
+		List<IAdapterFactory> 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) {
-					if (table.get(adapter) == null) {
-						table.put(adapter, factory);
-					}
+					table.computeIfAbsent(adapter, any -> new ArrayList<>(1)).add(factory);
 				}
 			} else {
 				Class<?>[] adapters = factory.getAdapterList();
 				for (Class<?> adapter : adapters) {
-					String adapterName = adapter.getName();
-					if (table.get(adapterName) == null)
-						table.put(adapterName, factory);
+					table.computeIfAbsent(adapter.getName(), any -> new ArrayList<>(1)).add(factory);
 				}
 			}
 		}
@@ -198,23 +222,19 @@
 	 * with the factory object that can perform that transformation. Returns 
 	 * a table of adapter class name to factory object.
 	 */
-	private Map<String, IAdapterFactory> getFactories(Class<? extends Object> adaptable) {
+	private Map<String, List<IAdapterFactory>> getFactories(Class<? extends Object> adaptable) {
 		//cache reference to lookup to protect against concurrent flush
-		Map<String, Map<String, IAdapterFactory>> lookup = adapterLookup;
+		Map<String, Map<String, List<IAdapterFactory>>> lookup = adapterLookup;
 		if (lookup == null)
-			adapterLookup = lookup = Collections.synchronizedMap(new HashMap<String, Map<String, IAdapterFactory>>(30));
-		Map<String, IAdapterFactory> table = lookup.get(adaptable.getName());
-		if (table == null) {
+			adapterLookup = lookup = Collections.synchronizedMap(new HashMap<String, Map<String, List<IAdapterFactory>>>(30));
+		return lookup.computeIfAbsent(adaptable.getName(), adaptableType -> {
 			// calculate adapters for the class
-			table = new HashMap<>(4);
-			Class<?>[] classes = computeClassOrder(adaptable);
-			for (Class<?> cl : classes) {
+			Map<String, List<IAdapterFactory>> table = new HashMap<>(4);
+			for (Class<?> cl : computeClassOrder(adaptable)) {
 				addFactoriesFor(cl.getName(), table);
 			}
-			// cache the table
-			lookup.put(adaptable.getName(), table);
-		}
-		return table;
+			return table;
+		});
 	}
 
 	/**
@@ -224,19 +244,12 @@
 	@Override
 	@SuppressWarnings("unchecked")
 	public <T> Class<? super T>[] computeClassOrder(Class<T> adaptable) {
-		Class<?>[] classes = null;
 		//cache reference to lookup to protect against concurrent flush
 		Map<Class<?>, Class<?>[]> lookup = classSearchOrderLookup;
-		if (lookup == null)
+		if (lookup == null) {
 			classSearchOrderLookup = lookup = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>[]>());
-		else
-			classes = lookup.get(adaptable);
-		// compute class order only if it hasn't been cached before
-		if (classes == null) {
-			classes = doComputeClassOrder(adaptable);
-			lookup.put(adaptable, classes);
 		}
-		return (Class<? super T>[]) classes;
+		return (Class<? super T>[]) lookup.computeIfAbsent(adaptable, this::doComputeClassOrder);
 	}
 
 	/**
@@ -292,25 +305,35 @@
 	public <T> T getAdapter(Object adaptable, Class<T> adapterType) {
 		Assert.isNotNull(adaptable);
 		Assert.isNotNull(adapterType);
-		String adapterTypeName = adapterType.getName();
-		IAdapterFactory factory = getFactories(adaptable.getClass()).get(adapterTypeName);
-		T result = null;
-		if (factory != null) {
-			result = factory.getAdapter(adaptable, adapterType);
+		List<Entry<IAdapterFactory, Class<?>>> 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;
+			}
 		}
-		if (result == null && adapterType.isInstance(adaptable)) {
-			return (T) adaptable;
-		}
-		if (result == null || adapterType.isInstance(result)) {
-			return result;
-		}
-		// Here we have a result which does not match the expected type.
-		// Throwing an exception here allows us to identify the violating factory.
-		// If we don't do this, clients will just see CCE without any idea who
-		// supplied the wrong adapter
-		throw new AssertionFailedException("Adapter factory " //$NON-NLS-1$
-				+ factory + " returned " + result.getClass().getName() //$NON-NLS-1$
-				+ " that is not an instance of " + adapterTypeName); //$NON-NLS-1$
+		return adapterObject;
 	}
 
 	@Override
@@ -329,18 +352,22 @@
 	 * if no plugin activations are desired.
 	 */
 	private Object getAdapter(Object adaptable, String adapterType, boolean force) {
-		IAdapterFactory factory = 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;
+		Assert.isNotNull(adaptable);
+		Assert.isNotNull(adapterType);
+		return getFactories(adaptable.getClass()).getOrDefault(adapterType, Collections.emptyList()) //
+				.stream() //
+				.sorted(ACTIVE_FIRST) // prefer factories from already active bundles to minimize activation and return earlier when possible
+				.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
@@ -350,15 +377,14 @@
 
 	@Override
 	public int queryAdapter(Object adaptable, String adapterTypeName) {
-		IAdapterFactory factory = getFactories(adaptable.getClass()).get(adapterTypeName);
-		if (factory == null)
+		List<IAdapterFactory> eligibleFactories = getFactories(adaptable.getClass()).get(adapterTypeName);
+		if (eligibleFactories == null || eligibleFactories.isEmpty()) {
 			return NONE;
-		if (factory instanceof IAdapterFactoryExt) {
-			factory = ((IAdapterFactoryExt) factory).loadFactory(false); // don't force loading
-			if (factory == null)
-				return NOT_LOADED;
 		}
-		return LOADED;
+		if (eligibleFactories.stream().anyMatch(AdapterManager::isFactoryLoaded)) {
+			return LOADED;
+		}
+		return NOT_LOADED;
 	}
 
 	@Override
@@ -379,12 +405,7 @@
 	 * @see IAdapterManager#registerAdapters
 	 */
 	public void registerFactory(IAdapterFactory factory, String adaptableType) {
-		List<IAdapterFactory> list = factories.get(adaptableType);
-		if (list == null) {
-			list = new ArrayList<>(5);
-			factories.put(adaptableType, list);
-		}
-		list.add(factory);
+		factories.computeIfAbsent(adaptableType, any -> new ArrayList<>(5)).add(factory);
 	}
 
 	/*
@@ -431,9 +452,9 @@
 		}
 	}
 
-	public HashMap<String, List<IAdapterFactory>> getFactories() {
+	public Map<String, List<IAdapterFactory>> getFactories() {
 		synchronized (lazyFactoryProviders) {
-			while (lazyFactoryProviders.size() > 0) {
+			while (!lazyFactoryProviders.isEmpty()) {
 				IAdapterManagerProvider provider = lazyFactoryProviders.remove(0);
 				if (provider.addFactories(this))
 					flushLookup();
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IAdapterFactoryExt.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IAdapterFactoryExt.java
index bfd0c7a..4002228 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IAdapterFactoryExt.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IAdapterFactoryExt.java
@@ -27,6 +27,7 @@
 	 * @param force if <code>true</code> the plugin providing the 
 	 * factory will be loaded if necessary, otherwise no plugin activations
 	 * will occur.
+	 * @return the adapter factory, or <code>null</code>
 	 */
 	public IAdapterFactory loadFactory(boolean force);
 
diff --git a/bundles/org.eclipse.equinox.registry/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.registry/META-INF/MANIFEST.MF
index b9497a1..5ada6e8 100644
--- a/bundles/org.eclipse.equinox.registry/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.registry/META-INF/MANIFEST.MF
@@ -11,7 +11,7 @@
  org.eclipse.core.runtime;registry=split;version="3.7.0";mandatory:=registry,
  org.eclipse.core.runtime.dynamichelpers;version="3.4.0",
  org.eclipse.core.runtime.spi;version="3.4.0"
-Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.14.0,4.0.0)"
+Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.15.100,4.0.0)"
 Bundle-Vendor: %providerName
 Bundle-Activator: org.eclipse.core.internal.registry.osgi.Activator
 Import-Package: javax.xml.parsers,
diff --git a/bundles/org.eclipse.equinox.registry/src/org/eclipse/core/internal/adapter/AdapterManagerListener.java b/bundles/org.eclipse.equinox.registry/src/org/eclipse/core/internal/adapter/AdapterManagerListener.java
index a540f78..d57cbe7 100644
--- a/bundles/org.eclipse.equinox.registry/src/org/eclipse/core/internal/adapter/AdapterManagerListener.java
+++ b/bundles/org.eclipse.equinox.registry/src/org/eclipse/core/internal/adapter/AdapterManagerListener.java
@@ -13,7 +13,6 @@
  *******************************************************************************/
 package org.eclipse.core.internal.adapter;
 
-import java.util.Iterator;
 import java.util.List;
 import org.eclipse.core.internal.runtime.AdapterManager;
 import org.eclipse.core.internal.runtime.IAdapterManagerProvider;
@@ -85,14 +84,7 @@
 		theAdapterManager.flushLookup();
 		for (IExtension extension : extensions) {
 			for (List<IAdapterFactory> adapterFactories : theAdapterManager.getFactories().values()) {
-				for (Iterator<IAdapterFactory> it2 = (adapterFactories).iterator(); it2.hasNext();) {
-					IAdapterFactory factory = it2.next();
-					if (!(factory instanceof AdapterFactoryProxy))
-						continue;
-					if (((AdapterFactoryProxy) factory).originatesFrom(extension)) {
-						it2.remove();
-					}
-				}
+				adapterFactories.removeIf(factory -> factory instanceof AdapterFactoryProxy && ((AdapterFactoryProxy) factory).originatesFrom(extension));
 			}
 		}
 	}