Bug 577645 - [Adapters] provide a method that returns an Optional for an
adapted type

Change-Id: I50df41567f6f6627a0614ff4553bf034de49626c
Signed-off-by: Christoph Laeubrich <laeubi@laeubi-soft.de>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.bundles/+/188576
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
Reviewed-by: Thomas Watson <tjwatson@us.ibm.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 5ab8017..483f108 100644
--- a/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.common.tests/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: Common Eclipse Runtime Tests
 Bundle-Vendor: Eclipse.org - Equinox
 Bundle-SymbolicName: org.eclipse.equinox.common.tests;singleton:=true
-Bundle-Version: 3.15.100.qualifier
+Bundle-Version: 3.15.200.qualifier
 Automatic-Module-Name: org.eclipse.equinox.common.tests
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.equinox.common.tests/pom.xml b/bundles/org.eclipse.equinox.common.tests/pom.xml
index 3a01136..dcbd39e 100644
--- a/bundles/org.eclipse.equinox.common.tests/pom.xml
+++ b/bundles/org.eclipse.equinox.common.tests/pom.xml
@@ -19,7 +19,7 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.common.tests</artifactId>
-  <version>3.15.100-SNAPSHOT</version>
+  <version>3.15.200-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
   <properties>
     <testClass>org.eclipse.equinox.common.tests.AllTests</testClass>
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptableTests.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptableTests.java
index 92d59d9..28dc096 100644
--- a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptableTests.java
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptableTests.java
@@ -21,7 +21,8 @@
 @SuiteClasses({
 	AdapterManagerDynamicTest.class,
 	IAdapterManagerServiceTest.class,
-	IAdapterManagerTest.class
+	IAdapterManagerTest.class,
+	AdaptersTest.class
 })
 public class AdaptableTests {
 	// intentionally left blank
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptersTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptersTest.java
new file mode 100644
index 0000000..fd30dca
--- /dev/null
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/adaptable/AdaptersTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Christoph Läubrich 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:
+ *     Christoph Läubrich - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.common.tests.adaptable;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+
+import org.eclipse.core.runtime.Adapters;
+import org.junit.Test;
+
+public class AdaptersTest {
+
+	@Test
+	public void testOptionalObjectIsNull() {
+		Optional<?> optional = Adapters.of(null, Object.class);
+		assertTrue(optional.isEmpty());
+	}
+
+	@Test(expected = NullPointerException.class)
+	public void testOptionalAdapterTypeIsNull() {
+		Adapters.of(new Object(), null);
+	}
+
+	@Test
+	public void testOptionalOfNotAdaptableIsEmpty() {
+		Optional<?> optional = Adapters.of(new ThisWillNotAdapt(), Runnable.class);
+		assertTrue(optional.isEmpty());
+	}
+
+	@Test
+	public void testOptionalOfAdaptable() {
+		Optional<?> optional = Adapters.of(new ThisWillAdaptToRunnable(), Runnable.class);
+		assertTrue(optional.isPresent());
+	}
+
+	private static final class ThisWillNotAdapt {
+
+	}
+
+	private static final class ThisWillAdaptToRunnable implements Runnable {
+
+		@Override
+		public void run() {
+
+		}
+
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.common/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.common/.settings/org.eclipse.jdt.ui.prefs
index 5d17c29..e1f7d14 100644
--- a/bundles/org.eclipse.equinox.common/.settings/org.eclipse.jdt.ui.prefs
+++ b/bundles/org.eclipse.equinox.common/.settings/org.eclipse.jdt.ui.prefs
@@ -3,6 +3,7 @@
 formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
 formatter_settings_version=21
 internal.default.compliance=user
+sp_cleanup.format_source_code_changes_only=true
 org.eclipse.jdt.ui.ignorelowercasenames=true
 org.eclipse.jdt.ui.importorder=;
 org.eclipse.jdt.ui.ondemandthreshold=3
diff --git a/bundles/org.eclipse.equinox.common/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.common/META-INF/MANIFEST.MF
index 9be3d5b..0f8c6f8 100644
--- a/bundles/org.eclipse.equinox.common/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.common/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.equinox.common; singleton:=true
-Bundle-Version: 3.15.100.qualifier
+Bundle-Version: 3.16.0.qualifier
 Bundle-Localization: plugin
 Export-Package: org.eclipse.core.internal.boot;x-friends:="org.eclipse.core.resources,org.eclipse.pde.build",
  org.eclipse.core.internal.runtime;common=split;mandatory:=common;
diff --git a/bundles/org.eclipse.equinox.common/pom.xml b/bundles/org.eclipse.equinox.common/pom.xml
index b7a3943..7965968 100644
--- a/bundles/org.eclipse.equinox.common/pom.xml
+++ b/bundles/org.eclipse.equinox.common/pom.xml
@@ -19,6 +19,6 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.common</artifactId>
-  <version>3.15.100-SNAPSHOT</version>
+  <version>3.16.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/CommonMessages.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/CommonMessages.java
index 7cc4adc..6bf9a0f 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/CommonMessages.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/CommonMessages.java
@@ -1,5 +1,5 @@
 /**********************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM - Initial API and implementation
+ *     Christoph Läubrich - Bug 577645 - [Adapters] provide a method that returns an Optional for an adapted type
  **********************************************************************/
 package org.eclipse.core.internal.runtime;
 
@@ -57,6 +58,8 @@
 	public static String activator_resourceBundleNotFound;
 	public static String activator_resourceBundleNotStarted;
 
+	public static String adapters_internal_error_of;
+
 	static {
 		// load message values from bundle file
 		reloadMessages();
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/commonMessages.properties b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/commonMessages.properties
index 5458297..75b9b37 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/commonMessages.properties
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/commonMessages.properties
@@ -49,3 +49,5 @@
 activator_not_available = The bundle activator for the org.eclipse.equinox.common bundle is not available.
 activator_resourceBundleNotFound=Resource bundle not found for locale: {0}
 activator_resourceBundleNotStarted=ResourceTranslator called before plugin is started
+
+adapters_internal_error_of=Internal error while adapting {0} -> {1}: {2}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Adapters.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Adapters.java
index 24637ce..d2d7778 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Adapters.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Adapters.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 IBM Corporation and others.
+ * Copyright (c) 2015, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -11,10 +11,14 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 478685, 478864, 479849
+ *     Christoph Läubrich - Bug 577645 - [Adapters] provide a method that returns an Optional for an adapted type
  *******************************************************************************/
 package org.eclipse.core.runtime;
 
-import org.eclipse.core.internal.runtime.AdapterManager;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.core.internal.runtime.*;
+import org.eclipse.osgi.util.NLS;
 
 /**
  * Provides a standard way to request adapters from adaptable objects
@@ -112,6 +116,35 @@
 		return adapt(sourceObject, adapter, true);
 	}
 
+	/**
+	 * If it is possible to adapt the given object to the given type, this returns
+	 * an optional holding the adapter, in all other cases it returns an empty
+	 * optional.
+	 * 
+	 * @param sourceObject object to adapt, if <code>null</code> then
+	 *                     {@link Optional#empty()} is returned
+	 * @param adapter      type to adapt to, must not be <code>null</code>
+	 * @param <T>          type to adapt to
+	 * @return an Optional representation of sourceObject that is assignable to the
+	 *         adapter type, or an empty Optional otherwise
+	 * @since 3.16
+	 */
+	public static <T> Optional<T> of(Object sourceObject, Class<T> adapter) {
+		if (sourceObject == null) {
+			return Optional.empty();
+		}
+		Objects.requireNonNull(adapter);
+		try {
+			return Optional.ofNullable(adapt(sourceObject, adapter));
+		} catch (AssertionFailedException e) {
+			RuntimeLog.log(Status.error(
+					NLS.bind(CommonMessages.adapters_internal_error_of, new Object[] {
+							sourceObject.getClass().getName(), adapter.getClass().getName(), e.getLocalizedMessage() }),
+					e));
+			return Optional.empty();
+		}
+	}
+
 	private static Object queryAdapterManager(Object sourceObject, String adapterId, boolean allowActivation) {
 		Object result;
 		if (allowActivation) {