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,