From 2737784763f9041067e9d07bb935c1c9c3952d11 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 3 Apr 2014 11:00:33 -0400 Subject: contextual: Add an extension SPI. --- .../sangria/contextual/ContextSensitiveBinder.java | 144 ++++++++++++++++----- 1 file changed, 112 insertions(+), 32 deletions(-) (limited to 'sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java') diff --git a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java index 7f86973..f398f71 100644 --- a/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java +++ b/sangria-contextual/src/main/java/com/tavianator/sangria/contextual/ContextSensitiveBinder.java @@ -18,24 +18,27 @@ package com.tavianator.sangria.contextual; import java.lang.annotation.Annotation; -import javax.annotation.Nullable; +import java.util.*; import javax.inject.Inject; import com.google.common.base.Objects; import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Binding; +import com.google.inject.ConfigurationException; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.TypeLiteral; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.matcher.Matcher; +import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.Dependency; import com.google.inject.spi.DependencyAndSource; import com.google.inject.spi.InjectionPoint; +import com.google.inject.spi.ProviderInstanceBinding; +import com.google.inject.spi.ProviderWithExtensionVisitor; import com.google.inject.spi.ProvisionListener; -import com.google.inject.util.Providers; import com.tavianator.sangria.core.DelayedError; @@ -137,7 +140,7 @@ public class ContextSensitiveBinder { public void toContextSensitiveProvider(Key> key) { error.cancel(); - binder.bind(bindingKey).toProvider(new ProviderAdapter<>(key)); + binder.bind(bindingKey).toProvider(new ProviderKeyAdapter<>(key)); binder.bindListener(new BindingMatcher(bindingKey), new Trigger(bindingKey)); } @@ -145,7 +148,7 @@ public class ContextSensitiveBinder { public void toContextSensitiveProvider(ContextSensitiveProvider provider) { error.cancel(); - binder.bind(bindingKey).toProvider(new ProviderAdapter<>(provider)); + binder.bind(bindingKey).toProvider(new ProviderInstanceAdapter<>(provider)); binder.bindListener(new BindingMatcher(bindingKey), new Trigger(bindingKey)); // Match the behaviour of LinkedBindingBuilder#toProvider(Provider) binder.requestInjection(provider); @@ -155,66 +158,143 @@ public class ContextSensitiveBinder { /** * Adapter from {@link ContextSensitiveProvider} to {@link Provider}. */ - private static class ProviderAdapter implements Provider { + private static abstract class ProviderAdapter implements ProviderWithExtensionVisitor { private static final ThreadLocal CURRENT_CONTEXT = new ThreadLocal<>(); - private final Object equalityKey; - private final @Nullable Key> providerKey; - private Provider> provider; + static void pushContext(InjectionPoint ip) { + CURRENT_CONTEXT.set(ip); + } - ProviderAdapter(Key> providerKey) { - this.equalityKey = providerKey; - this.providerKey = providerKey; + static void popContext() { + CURRENT_CONTEXT.remove(); } - ProviderAdapter(ContextSensitiveProvider provider) { - this.equalityKey = provider; - this.providerKey = null; - this.provider = Providers.of(provider); + @Override + public T get() { + InjectionPoint ip = CURRENT_CONTEXT.get(); + if (ip != null) { + return delegate().getInContext(ip); + } else { + return delegate().getInUnknownContext(); + } + } + + abstract ContextSensitiveProvider delegate(); + + // Have to implement equals()/hashCode() to support binding de-duplication + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract int hashCode(); + } + + private static class ProviderKeyAdapter extends ProviderAdapter implements ContextSensitiveProviderKeyBinding { + private final Key> providerKey; + private Provider> provider; + + ProviderKeyAdapter(Key> providerKey) { + this.providerKey = providerKey; } @Inject void inject(Injector injector) { - if (provider == null) { - provider = injector.getProvider(providerKey); + provider = injector.getProvider(providerKey); + } + + @Override + ContextSensitiveProvider delegate() { + return provider.get(); + } + + @Override + public V acceptExtensionVisitor(BindingTargetVisitor visitor, ProviderInstanceBinding binding) { + if (visitor instanceof ContextSensitiveBindingVisitor) { + return ((ContextSensitiveBindingVisitor)visitor).visit(this); + } else { + return visitor.visit(binding); } } - static void pushContext(InjectionPoint ip) { - CURRENT_CONTEXT.set(ip); + @Override + public Key> getContextSensitiveProviderKey() { + return providerKey; } - static void popContext() { - CURRENT_CONTEXT.remove(); + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof ProviderKeyAdapter)) { + return false; + } + + ProviderKeyAdapter other = (ProviderKeyAdapter)obj; + return providerKey.equals(other.providerKey); } @Override - public T get() { - ContextSensitiveProvider delegate = provider.get(); - InjectionPoint ip = CURRENT_CONTEXT.get(); - if (ip != null) { - return delegate.getInContext(ip); + public int hashCode() { + return Objects.hashCode(providerKey); + } + } + + private static class ProviderInstanceAdapter extends ProviderAdapter implements ContextSensitiveProviderInstanceBinding { + private final ContextSensitiveProvider instance; + private Set injectionPoints; + + ProviderInstanceAdapter(ContextSensitiveProvider instance) { + this.instance = instance; + + Set injectionPoints; + try { + injectionPoints = InjectionPoint.forInstanceMethodsAndFields(instance.getClass()); + } catch (ConfigurationException e) { + // We can ignore the error, the earlier requestInjection(instance) call will have reported it + injectionPoints = e.getPartialValue(); + } + this.injectionPoints = injectionPoints; + } + + @Override + ContextSensitiveProvider delegate() { + return instance; + } + + @Override + public V acceptExtensionVisitor(BindingTargetVisitor visitor, ProviderInstanceBinding binding) { + if (visitor instanceof ContextSensitiveBindingVisitor) { + return ((ContextSensitiveBindingVisitor)visitor).visit(this); } else { - return delegate.getInUnknownContext(); + return visitor.visit(binding); } } - // Have to implement equals()/hashCode() here to support binding de-duplication + @Override + public ContextSensitiveProvider getContextSensitiveProviderInstance() { + return instance; + } + + @Override + public Set getInjectionPoints() { + return injectionPoints; + } + @Override public boolean equals(Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ProviderAdapter)) { + } else if (!(obj instanceof ProviderInstanceAdapter)) { return false; } - ProviderAdapter other = (ProviderAdapter)obj; - return equalityKey.equals(other.equalityKey); + ProviderInstanceAdapter other = (ProviderInstanceAdapter)obj; + return instance.equals(other.instance); } @Override public int hashCode() { - return Objects.hashCode(equalityKey); + return Objects.hashCode(instance); } } -- cgit v1.2.3