package org.erlide.util.services;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.xtext.xbase.lib.Pair;
public class ServiceLocator {
private final static Map<Pair<Class<?>, Object>, Object> services = new ConcurrentHashMap<>();
private final static Map<Pair<Class<?>, Object>, Provider<?>> providers = new ConcurrentHashMap<>();
/**
* Acquire an implementation of a service, identified by id key. If one has
* not already been instantiated, instantiate the class defined by the
* Implementor annotation on the interface. If the implementor is an
* instance of Provider<T>, use it to instantiate the service.
*/
public static <T> T get(final Class<T> interfaceClass, final Object key) {
synchronized (interfaceClass) {
Object service = services
.get(new Pair<Class<?>, Object>(interfaceClass, key));
if (service == null) {
final Provider<?> provider = providers
.get(new Pair<Class<?>, Object>(interfaceClass, key));
if (provider == null) {
try {
final Class<?> implementingClass = interfaceClass
.getAnnotation(Implementor.class).value();
service = implementingClass.newInstance();
} catch (final Exception e) {
throw new RuntimeException(e);
}
} else {
service = provider.get();
}
services.put(new Pair<Class<?>, Object>(interfaceClass, key), service);
}
return interfaceClass.cast(service);
}
}
@SuppressWarnings("unchecked")
public static <T> Provider<T> getProvider(final Class<T> interfaceClass,
final Object key) {
synchronized (interfaceClass) {
final Provider<?> provider = providers
.get(new Pair<Class<?>, Object>(interfaceClass, key));
return (Provider<T>) provider;
}
}
/**
* Acquire an implementation of a service. If one has not already been
* instantiated, instantiate the class defined by the Implementor annotation
* on the interface. If the implementor is an instance of Provider<T>, use
* it to instantiate the service.
*/
public static <T> T get(final Class<T> interfaceClass) {
return get(interfaceClass, null);
}
/**
* Set an alternate service implementation. Typically only called in unit
* tests.
*/
public static <T, TI extends T> void set(final Class<T> interfaceClass,
final Object key, final TI implementor) {
synchronized (interfaceClass) {
services.put(new Pair<Class<?>, Object>(interfaceClass, key), implementor);
}
}
public static <T> void setProvider(final Class<T> interfaceClass, final Object key,
final Provider<T> provider) {
synchronized (interfaceClass) {
providers.put(new Pair<Class<?>, Object>(interfaceClass, key), provider);
}
}
/**
* Set an alternate service implementation. Typically only called in unit
* tests.
*/
public static <T, TI extends T> void set(final Class<T> interfaceClass,
final TI implementor) {
set(interfaceClass, null, implementor);
}
public static <T> void setProvider(final Class<T> interfaceClass,
final Provider<T> provider) {
setProvider(interfaceClass, null, provider);
}
}