toConstructor() core implementation. This still requires test coverage, and maybe a review of the API.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1022 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/internal/BindingProcessor.java b/src/com/google/inject/internal/BindingProcessor.java
index acacc87..103fd8b 100644
--- a/src/com/google/inject/internal/BindingProcessor.java
+++ b/src/com/google/inject/internal/BindingProcessor.java
@@ -85,6 +85,18 @@
         ((BindingImpl<?>) command).getScoping(), injector, errors);
 
     command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
+      public Void visit(ConstructorBinding<? extends T> binding) {
+        try {
+          ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, 
+              binding.getConstructor(), source, scoping, errors);
+          scheduleInitialization(onInjector);
+          putBinding(onInjector);
+        } catch (ErrorsException e) {
+          errors.merge(e.getErrors());
+          putBinding(invalidBinding(injector, key, source));
+        }
+        return null;
+      }
 
       public Void visit(InstanceBinding<? extends T> binding) {
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
@@ -148,26 +160,15 @@
         }
 
         // This cast is safe after the preceeding check.
-        final BindingImpl<T> binding;
         try {
-          binding = injector.createUnitializedBinding(key, scoping, source, errors);
+          BindingImpl<T> binding = injector.createUnitializedBinding(key, scoping, source, errors);
+          scheduleInitialization(binding);
           putBinding(binding);
         } catch (ErrorsException e) {
           errors.merge(e.getErrors());
           putBinding(invalidBinding(injector, key, source));
-          return null;
         }
 
-        uninitializedBindings.add(new Runnable() {
-          public void run() {
-            try {
-              binding.getInjector().initializeBinding(binding, errors.withSource(source));
-            } catch (ErrorsException e) {
-              errors.merge(e.getErrors());
-            }
-          }
-        });
-
         return null;
       }
 
@@ -179,12 +180,20 @@
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visit(ConstructorBinding<? extends T> binding) {
+      public Void visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visit(ProviderBinding<? extends T> binding) {
-        throw new IllegalArgumentException("Cannot apply a non-module element");
+      private void scheduleInitialization(final BindingImpl<?> binding) {
+        uninitializedBindings.add(new Runnable() {
+          public void run() {
+            try {
+              binding.getInjector().initializeBinding(binding, errors.withSource(source));
+            } catch (ErrorsException e) {
+              errors.merge(e.getErrors());
+            }
+          }
+        });
       }
     });
 
diff --git a/src/com/google/inject/internal/ConstructionContext.java b/src/com/google/inject/internal/ConstructionContext.java
index b5fd538..829f102 100644
--- a/src/com/google/inject/internal/ConstructionContext.java
+++ b/src/com/google/inject/internal/ConstructionContext.java
@@ -73,8 +73,7 @@
       invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
     }
 
-    DelegatingInvocationHandler<T> invocationHandler
-        = new DelegatingInvocationHandler<T>();
+    DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
     invocationHandlers.add(invocationHandler);
 
     ClassLoader classLoader = BytecodeGen.getClassLoader(expectedType);
diff --git a/src/com/google/inject/internal/ConstructorBindingImpl.java b/src/com/google/inject/internal/ConstructorBindingImpl.java
index 91de6db..5154126 100644
--- a/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -18,13 +18,17 @@
 
 import com.google.inject.Binder;
 import com.google.inject.Key;
+import com.google.inject.ConfigurationException;
 import static com.google.inject.internal.Preconditions.checkState;
+import static com.google.inject.internal.Annotations.findScopeAnnotation;
 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.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;
@@ -32,34 +36,78 @@
 final class ConstructorBindingImpl<T> extends BindingImpl<T> implements ConstructorBinding<T> {
 
   private final Factory<T> factory;
+  private final InjectionPoint constructorInjectionPoint;
 
   private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
-      InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory) {
+      InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
+      InjectionPoint constructorInjectionPoint) {
     super(injector, key, source, scopedFactory, scoping);
     this.factory = factory;
+    this.constructorInjectionPoint = constructorInjectionPoint;
   }
 
   public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
-      InjectionPoint constructorInjector, Set<InjectionPoint> injectionPoints) {
+      InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
     super(source, key, scoping);
     this.factory = new Factory<T>();
     ConstructionProxy<T> constructionProxy
-        = new DefaultConstructionProxyFactory<T>(constructorInjector).create();
+        = new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
+    this.constructorInjectionPoint = constructorInjectionPoint;
     factory.constructorInjector = new ConstructorInjector<T>(
         injectionPoints, constructionProxy, null, null);
   }
 
-  static <T> ConstructorBindingImpl<T> create(
-      InjectorImpl injector, Key<T> key, Object source, Scoping scoping) {
+  /**
+   * @param constructorInjector the constructor to use, or {@code null} to use the default.
+   */
+  static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key, 
+      InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors)
+      throws ErrorsException {
+    int numErrors = errors.size();
+    Class<? super T> rawType = key.getTypeLiteral().getRawType();
+
+    // We can't inject abstract classes.
+    if (Modifier.isAbstract(rawType.getModifiers())) {
+      errors.missingImplementation(key);
+    }
+
+    // Error: Inner class.
+    if (Classes.isInnerClass(rawType)) {
+      errors.cannotInjectInnerClass(rawType);
+    }
+
+    // if no scope is specified, look for a scoping annotation on the concrete class
+    if (!scoping.isExplicitlyScoped()) {
+      Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
+      if (scopeAnnotation != null) {
+        scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
+            injector, errors.withSource(rawType));
+      }
+    }
+
+    errors.throwIfNewErrors(numErrors);
+
     Factory<T> factoryFactory = new Factory<T>();
     InternalFactory<? extends T> scopedFactory
         = Scoping.scope(key, injector, factoryFactory, scoping);
+
+    // Find a constructor annotated @Inject
+    if (constructorInjector == null) {
+      try {
+        constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
+      } catch (ConfigurationException e) {
+        throw errors.merge(e.getErrorMessages()).toException();
+      }
+    }
+
     return new ConstructorBindingImpl<T>(
-        injector, key, source, scopedFactory, scoping, factoryFactory);
+        injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
   }
 
+  @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
-    factory.constructorInjector = injector.constructors.get(getKey().getTypeLiteral(), errors);
+    factory.constructorInjector
+        = (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
@@ -93,12 +141,12 @@
 
   @Override protected BindingImpl<T> withScoping(Scoping scoping) {
     return new ConstructorBindingImpl<T>(
-        null, getKey(), getSource(), factory, scoping, factory);
+        null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint);
   }
 
   @Override protected BindingImpl<T> withKey(Key<T> key) {
     return new ConstructorBindingImpl<T>(
-        null, key, getSource(), factory, getScoping(), factory);
+        null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
   }
 
   public void applyTo(Binder binder) {
diff --git a/src/com/google/inject/internal/ConstructorInjectorStore.java b/src/com/google/inject/internal/ConstructorInjectorStore.java
index af0b07d..e1122a5 100644
--- a/src/com/google/inject/internal/ConstructorInjectorStore.java
+++ b/src/com/google/inject/internal/ConstructorInjectorStore.java
@@ -16,8 +16,6 @@
 
 package com.google.inject.internal;
 
-import com.google.inject.ConfigurationException;
-import com.google.inject.TypeLiteral;
 import static com.google.inject.internal.Iterables.concat;
 import com.google.inject.spi.InjectionPoint;
 
@@ -29,12 +27,12 @@
 final class ConstructorInjectorStore {
   private final InjectorImpl injector;
 
-  private final FailableCache<TypeLiteral<?>, ConstructorInjector<?>>  cache
-      = new FailableCache<TypeLiteral<?>, ConstructorInjector<?>> () {
+  private final FailableCache<InjectionPoint, ConstructorInjector<?>>  cache
+      = new FailableCache<InjectionPoint, ConstructorInjector<?>> () {
     @SuppressWarnings("unchecked")
-    protected ConstructorInjector<?> create(TypeLiteral<?> type, Errors errors)
+    protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
         throws ErrorsException {
-      return createConstructor(type, errors);
+      return createConstructor(constructorInjector, errors);
     }
   };
 
@@ -45,26 +43,21 @@
   /**
    * Returns a new complete constructor injector with injection listeners registered.
    */
-  @SuppressWarnings("unchecked") // the ConstructorInjector type always agrees with the passed type
-  public <T> ConstructorInjector<T> get(TypeLiteral<T> key, Errors errors) throws ErrorsException {
-    return (ConstructorInjector<T>) cache.get(key, errors);
+  public ConstructorInjector<?> get(InjectionPoint constructorInjector, Errors errors)
+      throws ErrorsException {
+    return cache.get(constructorInjector, errors);
   }
 
-  private <T> ConstructorInjector<T> createConstructor(TypeLiteral<T> type, Errors errors)
+  private <T> ConstructorInjector<T> createConstructor(InjectionPoint injectionPoint, Errors errors)
       throws ErrorsException {
     int numErrorsBefore = errors.size();
 
-    InjectionPoint injectionPoint;
-    try {
-      injectionPoint = InjectionPoint.forConstructorOf(type);
-    } catch (ConfigurationException e) {
-      errors.merge(e.getErrorMessages());
-      throw errors.toException();
-    }
-
     SingleParameterInjector<?>[] constructorParameterInjectors
         = injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
-    MembersInjectorImpl<T> membersInjector = injector.membersInjectorStore.get(type, errors);
+
+    @SuppressWarnings("unchecked") // the injector type agrees with the injection point type
+    MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
+        .get(injectionPoint.getDeclaringType(), errors);
 
     /*if[AOP]*/
     ImmutableList<MethodAspect> injectorAspects = injector.state.getMethodAspects();
diff --git a/src/com/google/inject/internal/InjectorImpl.java b/src/com/google/inject/internal/InjectorImpl.java
index bcd653b..0ec25d3 100644
--- a/src/com/google/inject/internal/InjectorImpl.java
+++ b/src/com/google/inject/internal/InjectorImpl.java
@@ -28,7 +28,6 @@
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
 import com.google.inject.TypeLiteral;
-import static com.google.inject.internal.Annotations.findScopeAnnotation;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.ConvertedConstantBinding;
 import com.google.inject.spi.Dependency;
@@ -36,10 +35,8 @@
 import com.google.inject.spi.ProviderBinding;
 import com.google.inject.spi.ProviderKeyBinding;
 import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.Collections;
@@ -412,27 +409,7 @@
       return createProvidedByBinding(key, scoping, providedBy, errors);
     }
 
-    // We can't inject abstract classes.
-    // TODO: Method interceptors could actually enable us to implement
-    // abstract types. Should we remove this restriction?
-    if (Modifier.isAbstract(rawType.getModifiers())) {
-      throw errors.missingImplementation(key).toException();
-    }
-
-    // Error: Inner class.
-    if (Classes.isInnerClass(rawType)) {
-      throw errors.cannotInjectInnerClass(rawType).toException();
-    }
-
-    if (!scoping.isExplicitlyScoped()) {
-      Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
-      if (scopeAnnotation != null) {
-        scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
-            this, errors.withSource(rawType));
-      }
-    }
-
-    return ConstructorBindingImpl.create(this, key, source, scoping);
+    return ConstructorBindingImpl.create(this, key, null, source, scoping, errors);
   }
 
   /**
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index 3792add..7ab3e1e 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -54,25 +54,30 @@
 
   private final boolean optional;
   private final Member member;
+  private final TypeLiteral<?> declaringType;
   private final ImmutableList<Dependency<?>> dependencies;
 
-  InjectionPoint(TypeLiteral<?> type, Method method) {
+  InjectionPoint(TypeLiteral<?> declaringType, Method method) {
     this.member = method;
+    this.declaringType = declaringType;
 
     Inject inject = method.getAnnotation(Inject.class);
     this.optional = inject.optional();
 
-    this.dependencies = forMember(method, type, method.getParameterAnnotations());
+    this.dependencies = forMember(method, declaringType, method.getParameterAnnotations());
   }
 
-  InjectionPoint(TypeLiteral<?> type, Constructor<?> constructor) {
+  InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
     this.member = constructor;
+    this.declaringType = declaringType;
     this.optional = false;
-    this.dependencies = forMember(constructor, type, constructor.getParameterAnnotations());
+    this.dependencies = forMember(
+        constructor, declaringType, constructor.getParameterAnnotations());
   }
 
-  InjectionPoint(TypeLiteral<?> type, Field field) {
+  InjectionPoint(TypeLiteral<?> declaringType, Field field) {
     this.member = field;
+    this.declaringType = declaringType;
 
     Inject inject = field.getAnnotation(Inject.class);
     this.optional = inject.optional();
@@ -82,7 +87,7 @@
     Errors errors = new Errors(field);
     Key<?> key = null;
     try {
-      key = Annotations.getKey(type.getFieldType(field), field, annotations, errors);
+      key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors());
     }
@@ -148,6 +153,15 @@
     return optional;
   }
 
+  /**
+   * Returns the generic type that defines this injection point. If the member exists on a
+   * parameterized type, the result will include more type information than the member's {@link
+   * Member#getDeclaringClass() raw declaring class}.
+   */
+  public TypeLiteral<?> getDeclaringType() {
+    return declaringType;
+  }
+
   @Override public boolean equals(Object o) {
     return o instanceof InjectionPoint
         && member.equals(((InjectionPoint) o).member);
@@ -162,14 +176,13 @@
   }
 
   /**
-   * Returns a new injection point for the specified constructor. If any parameter of
-   * {@code constructor} includes a type variable (such as {@code List<T>}), prefer the overload
-   * that includes a type literal.
+   * Returns a new injection point for the specified constructor. If the declaring type of {@code
+   * constructor} is parameterized (such as {@code List<T>}), prefer the overload that includes a
+   * type literal.
    *
    * @param constructor any single constructor present on {@code type}.
    */
   public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
-    // TODO: verify that constructor is valid? (defined on a non-abstract class, etc.)
     return new InjectionPoint(TypeLiteral.get(constructor.getDeclaringClass()), constructor);
   }
 
@@ -187,7 +200,6 @@
           .throwConfigurationExceptionIfErrorsExist();
     }
 
-    // TODO: verify that constructor is valid? (defined on a non-abstract class, etc.)
     return new InjectionPoint(type, constructor);
   }
 
diff --git a/test/com/google/inject/BindingTest.java b/test/com/google/inject/BindingTest.java
index 48feea2..2bca3f7 100644
--- a/test/com/google/inject/BindingTest.java
+++ b/test/com/google/inject/BindingTest.java
@@ -21,6 +21,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.lang.reflect.Constructor;
 import junit.framework.TestCase;
 
 /**
@@ -206,4 +207,30 @@
     @Inject TooManyConstructors(Injector i) {}
     @Inject TooManyConstructors() {}
   }
+
+  public void testToConstructorBindings() throws NoSuchMethodException {
+    final Constructor<C> constructor = C.class.getConstructor(Stage.class);
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(C.class).toConstructor(constructor);
+      }
+    });
+    
+    assertEquals(Stage.DEVELOPMENT, injector.getInstance(C.class).stage);
+  }
+
+  public static class C {
+    private final Stage stage;
+
+    public C(Stage stage) {
+      this.stage = stage;
+    }
+
+    @Inject C() {
+      this.stage = null;
+    }
+  }
+
+
 }