blob: d2d77786d35a2fa3cc00578c91f61068c40f3fb4 [file] [log] [blame]
/*******************************************************************************
* 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
* 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
* 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 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
*
* @see IAdaptable
* @see IAdapterManager
* @since 3.8
*/
public class Adapters {
/**
* If it is possible to adapt the given object to the given type, this
* returns the adapter. Performs the following checks:
*
* <ol>
* <li>Returns <code>sourceObject</code> if it is an instance of the
* adapter type.</li>
* <li>If sourceObject implements IAdaptable, it is queried for adapters.</li>
* <li>Finally, the adapter manager is consulted for adapters</li>
* </ol>
*
* Otherwise returns null.
*
* @param <T> class type to adapt to
* @param sourceObject
* object to adapt, can be null
* @param adapter
* type to adapt to
* @param allowActivation
* if true, plug-ins may be activated if necessary to provide the requested adapter.
* if false, the method will return null if an adapter cannot be provided from activated plug-ins.
* @return a representation of sourceObject that is assignable to the
* adapter type, or null if no such representation exists
*/
@SuppressWarnings("unchecked")
public static <T> T adapt(Object sourceObject, Class<T> adapter, boolean allowActivation) {
if (sourceObject == null) {
return null;
}
if (adapter.isInstance(sourceObject)) {
return (T) sourceObject;
}
if (sourceObject instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) sourceObject;
Object result = adaptable.getAdapter(adapter);
if (result != null) {
// Sanity-check
if (!adapter.isInstance(result)) {
throw new AssertionFailedException(adaptable.getClass().getName() + ".getAdapter(" + adapter.getName() + ".class) returned " //$NON-NLS-1$//$NON-NLS-2$
+ result.getClass().getName() + " that is not an instance the requested type"); //$NON-NLS-1$
}
return (T) result;
}
}
// If the source object is a platform object then it's already tried calling AdapterManager.getAdapter,
// so there's no need to try it again.
if ((sourceObject instanceof PlatformObject) && !allowActivation) {
return null;
}
String adapterId = adapter.getName();
Object result = queryAdapterManager(sourceObject, adapterId, allowActivation);
if (result != null) {
// Sanity-check
if (!adapter.isInstance(result)) {
throw new AssertionFailedException("An adapter factory for " //$NON-NLS-1$
+ sourceObject.getClass().getName() + " returned " + result.getClass().getName() //$NON-NLS-1$
+ " that is not an instance of " + adapter.getName()); //$NON-NLS-1$
}
return (T) result;
}
return null;
}
/**
* If it is possible to adapt the given object to the given type, this
* returns the adapter.
* <p>
* Convenience method for calling <code>adapt(Object, Class, true)</code>.
* <p>
* See {@link #adapt(Object, Class, boolean)}.
*
* @param <T> class type to adapt to
* @param sourceObject
* object to adapt, can be null
* @param adapter
* type to adapt to
* @return a representation of sourceObject that is assignable to the
* adapter type, or null if no such representation exists
*/
public static <T> T adapt(Object sourceObject, Class<T> adapter) {
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) {
result = AdapterManager.getDefault().loadAdapter(sourceObject, adapterId);
} else {
result = AdapterManager.getDefault().getAdapter(sourceObject, adapterId);
}
return result;
}
}