package tc.oc.commons.core.plugin;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import tc.oc.commons.core.inject.BelongsTo;
public abstract class AbstractPluginResolver<Plugin> implements PluginResolver<Plugin> {
/**
* Classes excluded from implicit plugin detection (we should have a more general way to do this)
*/
private static final Set<Class<?>> IMPLICIT_BLACKLIST = ImmutableSet.of(
PluginFacetLoader.class
);
private final ThreadLocal<Plugin> override = new ThreadLocal<>();
private final ThreadLocal<Plugin> fallback = new ThreadLocal<>();
private final TypeToken<Plugin> pluginType = new TypeToken<Plugin>(getClass()){};
protected abstract @Nullable Plugin getImplicitProvidingPlugin(Class<?> cls);
private void withPlugin(ThreadLocal<Plugin> threadLocal, Plugin plugin, Runnable runnable) {
final Plugin old = threadLocal.get();
try {
threadLocal.set(plugin);
runnable.run();
} finally {
if(old != null) {
threadLocal.set(old);
} else {
threadLocal.remove();
}
}
}
@Override
public void withOverridePlugin(Plugin plugin, Runnable runnable) {
withPlugin(override, plugin, runnable);
}
@Override
public void withFallbackPlugin(Plugin plugin, Runnable runnable) {
withPlugin(fallback, plugin, runnable);
}
@Override
public final @Nullable Plugin getProvidingPlugin(Class<?> cls) {
Plugin plugin = override.get();
if(plugin != null) return plugin;
final Class<?> owner = BelongsTo.Impl.owner(cls);
if(owner != null && pluginType.isAssignableFrom(owner)) {
return getPlugin((Class<? extends Plugin>) owner);
}
if(!IMPLICIT_BLACKLIST.contains(cls)) {
plugin = getImplicitProvidingPlugin(cls);
if(plugin != null) return plugin;
}
plugin = fallback.get();
if(plugin != null) return plugin;
return null;
}
@Override
public @Nullable Plugin getCurrentPlugin() {
Plugin plugin = override.get();
if(plugin != null) return plugin;
plugin = fallback.get();
if(plugin != null) return plugin;
return null;
}
@Override
public <T extends Plugin> T needPlugin(Class<T> cls) {
final T plugin = getPlugin(cls);
if(plugin == null) {
throw new IllegalStateException("Failed to resolve plugin type " + cls);
}
return plugin;
}
@Override
public Plugin needProvidingPlugin(Class<?> cls) {
final Plugin plugin = getProvidingPlugin(cls);
if(plugin == null) {
throw new IllegalStateException("Failed to resolve an associated plugin for type " + cls);
}
return plugin;
}
@Override
public Plugin needCurrentPlugin() {
final Plugin plugin = getCurrentPlugin();
if(plugin == null) {
throw new IllegalStateException("Failed to resolve current plugin");
}
return plugin;
}
}