Add an SPI for @Provides methods (using the extensions SPI) so that users can
do more analysis (with the enclosing instance, method, etc..).
Notably, this can let users write analysis that looks for @Provides methods
declared with @Nullable, and compare against injection points w/o @Nullable,
failing if so.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=62834918
diff --git a/core/src/com/google/inject/internal/ProviderMethod.java b/core/src/com/google/inject/internal/ProviderMethod.java
index 6221499..d8bc18b 100644
--- a/core/src/com/google/inject/internal/ProviderMethod.java
+++ b/core/src/com/google/inject/internal/ProviderMethod.java
@@ -24,8 +24,13 @@
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
-import com.google.inject.spi.ProviderWithDependencies;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderWithExtensionVisitor;
+import com.google.inject.spi.ProvidesMethodBinding;
+import com.google.inject.spi.ProvidesMethodTargetVisitor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
@@ -38,7 +43,8 @@
*
* @author jessewilson@google.com (Jesse Wilson)
*/
-public class ProviderMethod<T> implements ProviderWithDependencies<T> {
+public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
+ ProvidesMethodBinding<T> {
private final Key<T> key;
private final Class<? extends Annotation> scopeAnnotation;
private final Object instance;
@@ -76,6 +82,10 @@
public Object getInstance() {
return instance;
}
+
+ public Object getEnclosingInstance() {
+ return instance;
+ }
public void configure(Binder binder) {
binder = binder.withSource(method);
@@ -114,6 +124,15 @@
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
+
+ @SuppressWarnings("unchecked")
+ public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
+ ProviderInstanceBinding<? extends B> binding) {
+ if (visitor instanceof ProvidesMethodTargetVisitor) {
+ return ((ProvidesMethodTargetVisitor<T, V>)visitor).visit(this);
+ }
+ return visitor.visit(binding);
+ }
@Override public String toString() {
return "@Provides " + StackTraceElements.forMember(method).toString();
diff --git a/core/src/com/google/inject/spi/ProvidesMethodBinding.java b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
new file mode 100644
index 0000000..f35d9a4
--- /dev/null
+++ b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
@@ -0,0 +1,24 @@
+package com.google.inject.spi;
+
+import com.google.inject.Key;
+import com.google.inject.Provides;
+
+import java.lang.reflect.Method;
+
+/**
+ * An {@literal @}{@link Provides} binding.
+ *
+ * @since 4.0
+ * @author sameb@google.com (Sam Berlin)
+ */
+public interface ProvidesMethodBinding<T> extends HasDependencies {
+
+ /** Returns the method this binding uses. */
+ Method getMethod();
+
+ /** Returns the instance of the object the method is defined in. */
+ Object getEnclosingInstance();
+
+ /** Returns the key of the binding. */
+ Key<T> getKey();
+}
diff --git a/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java b/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java
new file mode 100644
index 0000000..d6c4973
--- /dev/null
+++ b/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java
@@ -0,0 +1,21 @@
+package com.google.inject.spi;
+
+import com.google.inject.Provides;
+import com.google.inject.spi.BindingTargetVisitor;
+
+/**
+ * A visitor for the {@literal @}{@link Provides} bindings.
+ * <p>
+ * If your {@link BindingTargetVisitor} implements this interface, bindings created by using
+ * {@code @Provides} will be visited through this interface.
+ *
+ * @since 4.0
+ * @author sameb@google.com (Sam Berlin)
+ */
+public interface ProvidesMethodTargetVisitor<T, V> extends BindingTargetVisitor<T, V> {
+
+ /**
+ * Visits an {@link ProvidesMethodBinding} created with an {@literal @}{@link Provides} method.
+ */
+ V visit(ProvidesMethodBinding<? extends T> providesMethodBinding);
+}
diff --git a/core/test/com/google/inject/spi/ProviderMethodsTest.java b/core/test/com/google/inject/spi/ProviderMethodsTest.java
index 433500e..466e835 100644
--- a/core/test/com/google/inject/spi/ProviderMethodsTest.java
+++ b/core/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
+import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.CreationException;
import com.google.inject.Guice;
@@ -403,4 +404,52 @@
return 42;
}
}
+
+ public void testSpi() throws Exception {
+ Module m1 = new AbstractModule() {
+ @Override protected void configure() {}
+ @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; }
+ };
+ Module m2 = new AbstractModule() {
+ @Override protected void configure() {}
+ @Provides Integer provideInt(@Named("foo") String dep) { return 42; }
+ };
+ Injector injector = Guice.createInjector(m1, m2);
+
+ Binding<String> stringBinding =
+ injector.getBinding(Key.get(String.class, Names.named("foo")));
+ ProvidesMethodBinding<String> stringMethod =
+ stringBinding.acceptTargetVisitor(new BindingCapturer<String>());
+ assertEquals(m1, stringMethod.getEnclosingInstance());
+ assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class),
+ stringMethod.getMethod());
+ assertEquals(((HasDependencies) stringBinding).getDependencies(),
+ stringMethod.getDependencies());
+ assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey());
+
+ Binding<Integer> intBinding = injector.getBinding(Integer.class);
+ ProvidesMethodBinding<Integer> intMethod =
+ intBinding.acceptTargetVisitor(new BindingCapturer<Integer>());
+ assertEquals(m2, intMethod.getEnclosingInstance());
+ assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class),
+ intMethod.getMethod());
+ assertEquals(((HasDependencies) intBinding).getDependencies(),
+ intMethod.getDependencies());
+ assertEquals(Key.get(Integer.class), intMethod.getKey());
+
+ }
+
+ private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
+ implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
+
+ @SuppressWarnings("unchecked")
+ public ProvidesMethodBinding<T> visit(
+ ProvidesMethodBinding<? extends T> providesMethodBinding) {
+ return (ProvidesMethodBinding<T>)providesMethodBinding;
+ }
+
+ @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
+ throw new IllegalStateException("unexpected visit of: " + binding);
+ }
+ }
}