New API: toConstructor(Constructor, TypeLiteral).
The pair {Constructor, TypeLiteral} is necessary to specify a parameterized constructor reference (like "new HashMap<Integer, String>()")
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1025 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/binder/LinkedBindingBuilder.java b/src/com/google/inject/binder/LinkedBindingBuilder.java
index 782831e..9650d15 100644
--- a/src/com/google/inject/binder/LinkedBindingBuilder.java
+++ b/src/com/google/inject/binder/LinkedBindingBuilder.java
@@ -78,5 +78,11 @@
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*/
- ScopedBindingBuilder toConstructor(Constructor<? extends T> constructor);
+ <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor);
+
+ /**
+ * See the EDSL examples at {@link com.google.inject.Binder}.
+ */
+ <S extends T> ScopedBindingBuilder toConstructor(
+ Constructor<S> constructor, TypeLiteral<? extends S> type);
}
diff --git a/src/com/google/inject/internal/BindingBuilder.java b/src/com/google/inject/internal/BindingBuilder.java
index f7cc76b..1ce28e6 100644
--- a/src/com/google/inject/internal/BindingBuilder.java
+++ b/src/com/google/inject/internal/BindingBuilder.java
@@ -130,19 +130,21 @@
return this;
}
- public ScopedBindingBuilder toConstructor(Constructor<? extends T> constructor) {
+ public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
+ return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
+ }
+
+ public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
+ TypeLiteral<? extends S> type) {
checkNotNull(constructor, "constructor");
+ checkNotNull(type, "type");
checkNotTargetted();
BindingImpl<T> base = getBinding();
- TypeLiteral<T> keyType = base.getKey().getTypeLiteral();
- TypeLiteral<? extends T> toConstruct = (constructor.getDeclaringClass() == keyType.getRawType())
- ? keyType
- : TypeLiteral.get(constructor.getDeclaringClass());
Set<InjectionPoint> injectionPoints;
try {
- injectionPoints = InjectionPoint.forInstanceMethodsAndFields(toConstruct);
+ injectionPoints = InjectionPoint.forInstanceMethodsAndFields(type);
} catch (ConfigurationException e) {
copyErrorsToBinder(e);
injectionPoints = e.getPartialValue();
@@ -150,8 +152,7 @@
try {
@SuppressWarnings("unchecked") // safe; constructor is a subtype of toConstruct
- InjectionPoint constructorPoint = InjectionPoint.forConstructor((Constructor) constructor,
- toConstruct);
+ InjectionPoint constructorPoint = InjectionPoint.forConstructor(constructor, type);
setBinding(new ConstructorBindingImpl<T>(base.getKey(), base.getSource(), base.getScoping(),
constructorPoint, injectionPoints));
} catch (ConfigurationException e) {
diff --git a/src/com/google/inject/internal/ConstructorBindingImpl.java b/src/com/google/inject/internal/ConstructorBindingImpl.java
index 5e07306..755fc18 100644
--- a/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -19,16 +19,17 @@
import com.google.inject.Binder;
import com.google.inject.ConfigurationException;
import com.google.inject.Key;
-import static com.google.inject.internal.Preconditions.checkState;
+import com.google.inject.TypeLiteral;
import static com.google.inject.internal.Annotations.findScopeAnnotation;
+import static com.google.inject.internal.Preconditions.checkState;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -153,8 +154,9 @@
}
public void applyTo(Binder binder) {
- getScoping().applyTo(binder.withSource(getSource())
- .bind(getKey()).toConstructor((Constructor) getConstructor().getMember()));
+ InjectionPoint constructor = getConstructor();
+ getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).toConstructor(
+ (Constructor) getConstructor().getMember(), (TypeLiteral) constructor.getDeclaringType()));
}
@Override public String toString() {
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index 6e44fbe..259b6bc 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -88,6 +88,8 @@
Key<?> key = null;
try {
key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
+ } catch (ConfigurationException e) {
+ errors.merge(e.getErrorMessages());
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@@ -111,6 +113,8 @@
Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
index++;
+ } catch (ConfigurationException e) {
+ errors.merge(e.getErrorMessages());
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
diff --git a/test/com/google/inject/BindingTest.java b/test/com/google/inject/BindingTest.java
index e5b7bf3..fe5a40a 100644
--- a/test/com/google/inject/BindingTest.java
+++ b/test/com/google/inject/BindingTest.java
@@ -216,27 +216,6 @@
@Inject TooManyConstructors() {}
}
- public void testToConstructorBindings() throws NoSuchMethodException {
- final Constructor constructor = C.class.getConstructor(Stage.class, Object.class);
-
- Injector injector = Guice.createInjector(new AbstractModule() {
- protected void configure() {
- bind(new TypeLiteral<C<Stage>>() {}).toConstructor(constructor);
- bind(new TypeLiteral<C<Injector>>() {}).toConstructor(constructor);
- }
- });
-
- C<Stage> one = injector.getInstance(new Key<C<Stage>>() {});
- assertEquals(Stage.DEVELOPMENT, one.stage);
- assertEquals(Stage.DEVELOPMENT, one.t);
- assertEquals(Stage.DEVELOPMENT, one.anotherT);
-
- C<Injector> two = injector.getInstance(new Key<C<Injector>>() {});
- assertEquals(Stage.DEVELOPMENT, two.stage);
- assertEquals(injector, two.t);
- assertEquals(injector, two.anotherT);
- }
-
public void testToConstructorBinding() throws NoSuchMethodException {
final Constructor<D> constructor = D.class.getConstructor(Stage.class);
@@ -250,6 +229,48 @@
assertEquals(Stage.DEVELOPMENT, d.stage);
}
+ public void testToConstructorBindingsOnParameterizedTypes() throws NoSuchMethodException {
+ final Constructor<C> constructor = C.class.getConstructor(Stage.class, Object.class);
+ final Key<Object> s = new Key<Object>(named("s")) {};
+ final Key<Object> i = new Key<Object>(named("i")) {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(s).toConstructor(constructor, new TypeLiteral<C<Stage>>() {});
+ bind(i).toConstructor(constructor, new TypeLiteral<C<Injector>>() {});
+ }
+ });
+
+ C<Stage> one = (C<Stage>) injector.getInstance(s);
+ assertEquals(Stage.DEVELOPMENT, one.stage);
+ assertEquals(Stage.DEVELOPMENT, one.t);
+ assertEquals(Stage.DEVELOPMENT, one.anotherT);
+
+ C<Injector> two = (C<Injector>) injector.getInstance(i);
+ assertEquals(Stage.DEVELOPMENT, two.stage);
+ assertEquals(injector, two.t);
+ assertEquals(injector, two.anotherT);
+ }
+
+ public void testToConstructorBindingsFailsOnRawTypes() throws NoSuchMethodException {
+ final Constructor constructor = C.class.getConstructor(Stage.class, Object.class);
+
+ try {
+ Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(Object.class).toConstructor(constructor);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertContains(expected.getMessage(),
+ "1) T cannot be used as a key; It is not fully specified.",
+ "at " + C.class.getName() + ".<init>(BindingTest.java:",
+ "2) T cannot be used as a key; It is not fully specified.",
+ "at " + C.class.getName() + ".anotherT(BindingTest.java:");
+ }
+ }
+
public void testToConstructorAndMethodInterceptors() throws NoSuchMethodException {
final Constructor<D> constructor = D.class.getConstructor(Stage.class);
final AtomicInteger count = new AtomicInteger();
diff --git a/test/com/google/inject/spi/ElementsTest.java b/test/com/google/inject/spi/ElementsTest.java
index 24b7e60..56a401b 100644
--- a/test/com/google/inject/spi/ElementsTest.java
+++ b/test/com/google/inject/spi/ElementsTest.java
@@ -1080,20 +1080,37 @@
}
public void testBindToConstructor() throws NoSuchMethodException, NoSuchFieldException {
- final Constructor<B> constructor = B.class.getDeclaredConstructor(Object.class);
+ final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
+ final Constructor<B> bConstructor = B.class.getDeclaredConstructor(Object.class);
final Field field = B.class.getDeclaredField("stage");
checkModule(
new AbstractModule() {
protected void configure() {
- bind(new TypeLiteral<B<Integer>>() {}).toConstructor((Constructor) constructor)
+ bind(A.class).toConstructor(aConstructor);
+ bind(B.class).toConstructor(bConstructor, new TypeLiteral<B<Integer>>() {})
.in(Singleton.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
- assertEquals(new Key<B<Integer>>() {}, binding.getKey());
+ assertEquals(new Key<A>() {}, binding.getKey());
+
+ return binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
+ @Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
+ InjectionPoint injectionPoint = constructorBinding.getConstructor();
+ assertEquals(aConstructor, injectionPoint.getMember());
+ assertEquals(new TypeLiteral<A>() {}, injectionPoint.getDeclaringType());
+ return null;
+ }
+ });
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public <T> Void visit(Binding<T> binding) {
+ assertEquals(new Key<B>() {}, binding.getKey());
binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
assertEquals(Singleton.class, annotation);
@@ -1103,7 +1120,7 @@
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
- assertEquals(constructor, constructorBinding.getConstructor().getMember());
+ assertEquals(bConstructor, constructorBinding.getConstructor().getMember());
assertEquals(Key.get(Integer.class),
getOnlyElement(constructorBinding.getConstructor().getDependencies()).getKey());
assertEquals(field,