Big API change to AssistedInject Deluxe.

I unified the API - both old and new use the same API, and the code detects which to use (by looking for the presence or absence of an @AssistedInject annotation)

This means that:
 - Upgrading from new to old is extremely easy
 - Unified Javadocs for old and new. The new Javadocs just have a section pointing out the differences when @AssistedInject is used instead of @Assisted
 - It's a little clumsier to make sure you're getting the right one. This is mitigated by some new checks on the factory interface to ensure the old constructor isn't used with the newer factories.

Also applying local variable naming fixes to InjectionPoint, as pointed out by Brian Harris.


git-svn-id: https://google-guice.googlecode.com/svn/trunk@723 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java
index 3ae7c75..941acc4 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java
@@ -32,5 +32,10 @@
  */
 @BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
 public @interface Assisted {
+
+  /**
+   * The unique name for this parameter. This is matched to the {@literal @Assisted} constructor 
+   * parameter with the same value.
+   */
   String value() default "";
 }
\ No newline at end of file
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java
index a936dd4..478e73c 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java
@@ -22,16 +22,22 @@
 import java.lang.annotation.Target;
 
 /**
- * Constructors annotated with <code>@AssistedInject</code> indicate that will
- * can be instantiated by the {@link FactoryProvider}.  Each constructor must
- * exactly one corresponding factory method within the Factory Interface.
+ * <p>Constructors annotated with {@code @AssistedInject} indicate that will can be instantiated by
+ * the {@link FactoryProvider}. Each constructor must exactly one corresponding factory method
+ * within the factory interface.
  * 
- * <p>Constructor parameters must be either supplied by the Factory Interface and
- * marked with <code>@Assisted</code>, or they must be injectable.
+ * <p>Constructor parameters must be either supplied by the factory interface and marked with
+ * <code>@Assisted</code>, or they must be injectable.
  * 
+ * @deprecated {@link FactoryProvider} now works better with the standard {@literal @Inject}
+ *     annotation. When using that annotation, parameters are matched by name and type rather than
+ *     by position. In addition, values that use the standard {@literal @Inject} constructor
+ *     annotation are eligible for method interception.
+ *
  * @author jmourits@google.com (Jerome Mourits)
  * @author jessewilson@google.com (Jesse Wilson)
  */
 @Target({CONSTRUCTOR})
 @Retention(RUNTIME)
+@Deprecated
 public @interface AssistedInject {}
\ No newline at end of file
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Factories.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Factories.java
deleted file mode 100644
index 52cd036..0000000
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/Factories.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.inject.assistedinject;
-
-import static com.google.common.base.Preconditions.checkState;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import com.google.inject.AbstractModule;
-import com.google.inject.Binder;
-import com.google.inject.Binding;
-import com.google.inject.ConfigurationException;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import static com.google.inject.internal.Annotations.getKey;
-import com.google.inject.internal.Errors;
-import com.google.inject.internal.ErrorsException;
-import com.google.inject.spi.Message;
-import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import net.sf.cglib.proxy.Enhancer;
-import net.sf.cglib.proxy.InvocationHandler;
-
-/**
- * Static utility methods for creating and working with factory interfaces.
- *
- * @author jmourits@google.com (Jerome Mourits)
- * @author jessewilson@google.com (Jesse Wilson)
- * @author dtm@google.com (Daniel Martin)
- */
-public final class Factories {
-  private Factories() {}
-
-  private static final Class[] ONLY_RIH = { RealInvocationHandler.class };
-
-  static final Assisted DEFAULT_ASSISTED = new Assisted() {
-    public String value() {
-      return "";
-    }
-
-    public Class<? extends Annotation> annotationType() {
-      return Assisted.class;
-    }
-
-    @Override public boolean equals(Object o) {
-      return o instanceof Assisted
-          && ((Assisted) o).value().equals("");
-    }
-
-    @Override public int hashCode() {
-      return 127 * "value".hashCode() ^ "".hashCode();
-    }
-
-    @Override public String toString() {
-      return Assisted.class.getName() + "(value=)";
-    }
-  };
-
-  /**
-   * Returns a factory that combines caller-provided parameters with injector-provided values when
-   * constructing objects.
-   *
-   * <h3>Defining a Factory</h3>
-   * {@code factoryInterface} is an interface whose methods return the constructed type, or its
-   * supertypes. The method's parameters are the arguments required to build the constructed type.
-   *
-   * <pre>
-   * public interface PaymentFactory {
-   *   Payment create(Date startDate, Money amount);
-   * } </pre>
-   *
-   * You can name your factory methods whatever you like, such as <i>create</i>,
-   * <i>createPayment</i> or <i>newPayment</i>.  You may include multiple factory methods in the
-   * same interface but they must all construct the same type.
-   *
-   * <h3>Creating a type that accepts factory parameters</h3>
-   * {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
-   * constructor. In addition to injector-provided parameters, the constructor should have
-   * parameters that match each of the factory method's parameters. Each factory-provided parameter
-   * requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
-   * is not bound in the injector.
-   *
-   * <pre>
-   * public class RealPayment implements Payment {
-   *   {@literal @}Inject
-   *   public RealPayment(
-   *      CreditService creditService,
-   *      AuthService authService,
-   *      <strong>{@literal @}Assisted Date startDate</strong>,
-   *      <strong>{@literal @}Assisted Money amount</strong>) {
-   *     ...
-   *   }
-   * }</pre>
-   *
-   * <h3>Configuring factories</h3>
-   * In your {@link com.google.inject.Module module}, bind the factory interface to the returned
-   * factory:
-   *
-   * <pre>
-   * bind(PaymentFactory.class).toInstance(
-   *     Factories.create(PaymentFactory.class, RealPayment.class));</pre>
-   * As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
-   * factory cannot be used until it has been initialized.
-   *
-   * <h3>Using the Factory</h3>
-   * Inject your factory into your application classes. When you use the factory, your arguments
-   * will be combined with values from the injector to produce a concrete instance.
-   *
-   * <pre>
-   * public class PaymentAction {
-   *   {@literal @}Inject private PaymentFactory paymentFactory;
-   *
-   *   public void doPayment(Money amount) {
-   *     Payment payment = paymentFactory.create(new Date(), amount);
-   *     payment.apply();
-   *   }
-   * }</pre>
-   *
-   * <h3>Making Parameter Types Distinct</h3>
-   * The types of the factory method's parameters must be distinct. To use multiple parameters of
-   * the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
-   * parameters. The names must be applied to the factory method's parameters:
-   *
-   * <pre>
-   * public interface PaymentFactory {
-   *   Payment create(
-   *       <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
-   *       <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
-   *       Money amount);
-   * } </pre>
-   * ...and to the concrete type's constructor parameters:
-   * <pre>
-   * public class RealPayment implements Payment {
-   *   {@literal @}Inject
-   *   public RealPayment(
-   *      CreditService creditService,
-   *      AuthService authService,
-   *      <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
-   *      <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
-   *      <strong>{@literal @}Assisted</strong> Money amount) {
-   *     ...
-   *   }
-   * }</pre>
-   *
-   * <h3>MethodInterceptor support</h3>
-   * Returned factories delegate to the injector to construct returned values. The values are
-   * eligible for method interception.
-   *
-   * @param factoryInterface a Java interface that defines one or more create methods.
-   * @param constructedType a concrete type that is assignable to the return types of all factory
-   *     methods.
-   */
-  public static <F> F create(Class<F> factoryInterface, Class<?> constructedType) {
-    RealInvocationHandler<F> invocationHandler
-        = new RealInvocationHandler<F>(factoryInterface, Key.get(constructedType));
-    Enhancer enhancer = new Enhancer();
-    enhancer.setSuperclass(Base.class);
-    enhancer.setInterfaces(new Class[] { factoryInterface });
-    enhancer.setCallback(invocationHandler);
-    return factoryInterface.cast(enhancer.create(ONLY_RIH, new Object[] { invocationHandler }));
-  }
-
-  /**
-   * Generated factories extend this class, which gives us a hook to get injected by Guice. Normal
-   * Java proxies can't be injected, so we use cglib.
-   */
-  private static class Base {
-    private final RealInvocationHandler<?> invocationHandler;
-
-    protected Base(RealInvocationHandler<?> invocationHandler) {
-      this.invocationHandler = invocationHandler;
-    }
-
-    @SuppressWarnings("unused")
-    @Inject private void initialize(Injector injector) {
-      invocationHandler.initialize(injector);
-    }
-  }
-
-  // TODO: also grab methods from superinterfaces
-
-  private static class RealInvocationHandler<F> implements InvocationHandler {
-    /** the produced type, or null if all methods return concrete types */
-    private final Key<?> producedType;
-    private final ImmutableMap<Method, Key<?>> returnTypesByMethod;
-    private final ImmutableMultimap<Method, Key<?>> paramTypes;
-
-    /** the hosting injector, or null if we haven't been initialized yet */
-    private Injector injector;
-
-    private RealInvocationHandler(Class<F> factoryType, Key<?> producedType) {
-      this.producedType = producedType;
-
-      Errors errors = new Errors();
-      try {
-        ImmutableMap.Builder<Method, Key<?>> returnTypesBuilder = ImmutableMap.builder();
-        ImmutableMultimap.Builder<Method, Key<?>> paramTypesBuilder = ImmutableMultimap.builder();
-        for (Method method : factoryType.getMethods()) {
-          Key<?> returnType = getKey(TypeLiteral.get(method.getGenericReturnType()),
-              method, method.getAnnotations(), errors);
-          returnTypesBuilder.put(method, returnType);
-          Type[] params = method.getGenericParameterTypes();
-          Annotation[][] paramAnnotations = method.getParameterAnnotations();
-          int p = 0;
-          for (Type param : params) {
-            Key<?> paramKey = getKey(TypeLiteral.get(param), method, paramAnnotations[p++], errors);
-            paramTypesBuilder.put(method, assistKey(method, paramKey, errors));
-          }
-        }
-        returnTypesByMethod = returnTypesBuilder.build();
-        paramTypes = paramTypesBuilder.build();
-      } catch (ErrorsException e) {
-        throw new ConfigurationException(e.getErrors().getMessages());
-      }
-    }
-
-    /**
-     * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
-     * This fails if another binding annotation is clobbered in the process. If the key already has
-     * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
-     */
-    private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
-      if (key.getAnnotationType() == null) {
-        return Key.get(key.getTypeLiteral(), DEFAULT_ASSISTED);
-      } else if (key.getAnnotationType() == Assisted.class) {
-        return key;
-      } else {
-        errors.withSource(method).addMessage(
-            "Only @Assisted is allowed for factory parameters, but found @%s",
-            key.getAnnotationType());
-        throw errors.toException();
-      }
-    }
-
-    /**
-     * At injector-creation time, we initialize the invocation handler. At this time we make sure
-     * all factory methods will be able to build the target types.
-     */
-    void initialize(Injector injector) {
-      if (this.injector != null) {
-        throw new ConfigurationException(ImmutableList.of(new Message(Factories.class,
-            "Factories.create() factories may only be used in one Injector!")));
-      }
-
-      this.injector = injector;
-
-      for (Method method : returnTypesByMethod.keySet()) {
-        Object[] args = new Object[method.getParameterTypes().length];
-        Arrays.fill(args, "dummy object for validating Factories");
-        getBindingFromNewInjector(method, args); // throws if the binding isn't properly configured
-      }
-    }
-
-    /**
-     * Creates a child injector that binds the args, and returns the binding for the method's
-     * result.
-     */
-    public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args) {
-      checkState(injector != null,
-          "Factories.create() factories cannot be used until they're initialized by Guice.");
-
-      final Key<?> returnType = returnTypesByMethod.get(method);
-
-      Module assistedModule = new AbstractModule() {
-        @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
-        protected void configure() {
-          Binder binder = binder().withSource(method);
-
-          int p = 0;
-          for (Key<?> paramKey : paramTypes.get(method)) {
-            // Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
-            binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
-          }
-
-          if (producedType != null && !returnType.equals(producedType)) {
-            binder.bind(returnType).to((Key) producedType);
-          } else {
-            binder.bind(returnType);
-          }
-        }
-      };
-
-      Injector forCreate = injector.createChildInjector(assistedModule);
-      return forCreate.getBinding(returnType);
-    }
-
-    /**
-     * When a factory method is invoked, we create a child injector that binds all parameters, then
-     * use that to get an instance of the return type.
-     */
-    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
-      if (method.getDeclaringClass() == Object.class) {
-        return method.invoke(this, args);
-      }
-
-      Provider<?> provider = getBindingFromNewInjector(method, args).getProvider();
-      try {
-        return provider.get();
-      } catch (ProvisionException e) {
-        // if this is an exception declared by the factory method, throw it as-is
-        if (e.getErrorMessages().size() == 1) {
-          Message onlyError = getOnlyElement(e.getErrorMessages());
-          Throwable cause = onlyError.getCause();
-          if (cause != null && canRethrow(method, cause)) {
-            throw cause;
-          }
-        }
-        throw e;
-      }
-    }
-
-    @Override public String toString() {
-      return "Factory";
-    }
-
-    @Override public boolean equals(Object o) {
-      // this equals() is wacky; we pretend it's defined on the Proxy object rather than here
-      return o instanceof Base
-          && ((Base) o).invocationHandler == this;
-    }
-  }
-
-  /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
-  static boolean canRethrow(Method invoked, Throwable thrown) {
-    if (thrown instanceof Error || thrown instanceof RuntimeException) {
-      return true;
-    }
-
-    for (Class<?> declared : invoked.getExceptionTypes()) {
-      if (declared.isInstance(thrown)) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-}
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
index bded782..dcfc356 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
@@ -16,15 +16,18 @@
 
 package com.google.inject.assistedinject;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.ConfigurationException;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Key;
 import com.google.inject.Provider;
 import com.google.inject.internal.Errors;
 import com.google.inject.spi.Message;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
@@ -33,77 +36,130 @@
 import java.util.Map;
 
 /**
- * Provides a mechanism to combine user-specified paramters with
- * {@link Injector}-specified paramters when creating new objects.
+ * Provides a factory that combines caller-provided parameters with injector-provided values when
+ * constructing objects.
  *
- * <p>To use a {@link FactoryProvider}:
+ * <h3>Defining a factory</h3>
+ * Create an interface whose methods return the constructed type, or its supertypes. The method's
+ * parameters are the arguments required to build the constructed type.
+ * <pre>public interface PaymentFactory {
+ *   Payment create(Date startDate, Money amount);
+ * }</pre>
+ * You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
+ * or <i>newPayment</i>.
  *
- * <p>Annotate your implementation class' constructor with the
- * {@literal @}{@link AssistedInject} and the user-specified parameters with
- * {@literal @}{@link Assisted}:
- * <pre><code>public class RealPayment implements Payment {
- *    {@literal @}AssistedInject
- *    public RealPayment(CreditService creditService, AuthService authService,
- *      {@literal @}Assisted Date startDate, {@literal @}Assisted Money amount) {
+ * <h3>Creating a type that accepts factory parameters</h3>
+ * {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
+ * constructor. In addition to injector-provided parameters, the constructor should have
+ * parameters that match each of the factory method's parameters. Each factory-provided parameter
+ * requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
+ * is not bound by your application's modules.
+ * <pre>public class RealPayment implements Payment {
+ *   {@literal @}Inject
+ *   public RealPayment(
+ *      CreditService creditService,
+ *      AuthService authService,
+ *      <strong>{@literal @}Assisted Date startDate</strong>,
+ *      <strong>{@literal @}Assisted Money amount</strong>) {
  *     ...
- *  }
- * }</code></pre>
+ *   }
+ * }</pre>
  *
- * <p>Write an interface with a <i>create</i> method that accepts the user-specified
- * parameters in the same order as they appear in the implementation class' constructor:
- * <pre><code>public interface PaymentFactory {
- *    Payment create(Date startDate, Money amount);
- * }</code></pre>
+ * <h3>Configuring factories</h3>
+ * In your {@link com.google.inject.Module module}, bind the factory interface to the returned
+ * factory:
+ * <pre>bind(PaymentFactory.class).toInstance(
+ *     FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
+ * As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
+ * factory cannot be used until the injector has been initialized.
  *
- * <p>You can name your create methods whatever you like, such as <i>create</i>,
- * or <i>createPayment</i> or <i>newPayment</i>. The concrete class must
- * be assignable to the return type of your create method. You can also provide
- * multiple factory methods, but there must be exactly one
- * {@literal @}{@link AssistedInject} constructor on the implementation class for each.
+ * <h3>Using the factory</h3>
+ * Inject your factory into your application classes. When you use the factory, your arguments
+ * will be combined with values from the injector to produce a concrete instance.
+ * <pre>public class PaymentAction {
+ *   {@literal @}Inject private PaymentFactory paymentFactory;
  *
- * <p>In your Guice {@link com.google.inject.Module module}, bind your factory
- * interface to an instance of {@link FactoryProvider} that was created with
- * the same factory interface and implementation type:
- * <pre><code>  bind(PaymentFactory.class).toProvider(
- *     FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</code></pre>
+ *   public void doPayment(Money amount) {
+ *     Payment payment = paymentFactory.create(new Date(), amount);
+ *     payment.apply();
+ *   }
+ * }</pre>
  *
- * <p>Now you can {@literal @}{@code Inject} your factory interface into your
- * Guice-injected classes. When you invoke the create method on that factory,
- * the {@link FactoryProvider} will instantiate the implementation class using
- * parameters from the injector and the factory method.
+ * <h3>Making parameter types distinct</h3>
+ * The types of the factory method's parameters must be distinct. To use multiple parameters of
+ * the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
+ * parameters. The names must be applied to the factory method's parameters:
  *
- * <pre><code>public class PaymentAction {
- *    {@literal @}Inject private PaymentFactory paymentFactory;
+ * <pre>public interface PaymentFactory {
+ *   Payment create(
+ *       <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
+ *       <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
+ *       Money amount);
+ * } </pre>
+ * ...and to the concrete type's constructor parameters:
+ * <pre>public class RealPayment implements Payment {
+ *   {@literal @}Inject
+ *   public RealPayment(
+ *      CreditService creditService,
+ *      AuthService authService,
+ *      <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
+ *      <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
+ *      <strong>{@literal @}Assisted</strong> Money amount) {
+ *     ...
+ *   }
+ * }</pre>
  *
- *    public void doPayment(Money amount) {
- *       Payment payment = paymentFactory.create(new Date(), amount);
- *       payment.apply();
- *    }
- * }</code></pre>
+ * <h3>Values are created by Guice</h3>
+ * Returned factories use child injectors to create values. The values are eligible for method
+ * interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
+ * returned.
+ *
+ * <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
+ * Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
+ * {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
+ *
+ * <p>Instead of matching factory method arguments to constructor parameters using their names, the
+ * <strong>parameters are matched by their order</strong>. The first factory method argument is
+ * used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no
+ * effect.
+ *
+ * <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
+ * method interception. They do receive post-construction member injection.
  *
  * @param <F> The factory interface
- * @param <R> The concrete class to be created.
  *
  * @author jmourits@google.com (Jerome Mourits)
  * @author jessewilson@google.com (Jesse Wilson)
+ * @author dtm@google.com (Daniel Martin)
  */
-public class FactoryProvider<F, R> implements Provider<F> {
+public class FactoryProvider<F> implements Provider<F> {
+
+  /*
+   * This class implements the old @AssistedInject implementation that manually matches constructors
+   * to factory methods. The new child injector implementation lives in FactoryProvider2.
+   */
 
   private Injector injector;
 
   private final Class<F> factoryType;
-  private final Class<R> implementationType;
   private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
 
-  public static <X,Y> FactoryProvider<X,Y> newFactory(
-      Class<X> factoryType, Class<Y> implementationType){
-    return new FactoryProvider<X, Y>(factoryType,implementationType);
+  public static <F> Provider<F> newFactory(
+      Class<F> factoryType, Class<?> implementationType){
+    Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
+        = createMethodMapping(factoryType, implementationType);
+
+    if (!factoryMethodToConstructor.isEmpty()) {
+      return new FactoryProvider<F>(factoryType, factoryMethodToConstructor);
+    } else {
+      return new FactoryProvider2<F>(factoryType, Key.get(implementationType));
+    }
   }
 
-  private FactoryProvider(Class<F> factoryType, Class<R> implementationType) {
+  private FactoryProvider(Class<F> factoryType,
+      Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
     this.factoryType = factoryType;
-    this.implementationType = implementationType;
-    this.factoryMethodToConstructor = createMethodMapping();
+    this.factoryMethodToConstructor = factoryMethodToConstructor;
     checkDeclaredExceptionsMatch();
   }
 
@@ -150,7 +206,8 @@
   }
 
   @SuppressWarnings({"unchecked"})
-  private Map<Method, AssistedConstructor<?>> createMethodMapping() {
+  private static Map<Method, AssistedConstructor<?>> createMethodMapping(
+      Class<?> factoryType, Class<?> implementationType) {
     List<AssistedConstructor<?>> constructors = Lists.newArrayList();
 
     for (Constructor<?> c : implementationType.getDeclaredConstructors()) {
@@ -159,6 +216,10 @@
       }
     }
 
+    if (constructors.isEmpty()) {
+      return ImmutableMap.of();
+    }
+
     if (constructors.size() != factoryType.getMethods().length) {
       throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
           + "constructors, factory %s has %s creation methods", implementationType.getSimpleName(),
@@ -188,6 +249,19 @@
             + "@Assisted parameters %s in that order. @AssistInject constructors are %s",
             implementationType, methodParams, paramsToConstructor.values());
       }
+
+      method.getParameterAnnotations();
+      for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
+        for (Annotation parameterAnnotation : parameterAnnotations) {
+          if (parameterAnnotation.annotationType() == Assisted.class) {
+            throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
+                + "is incompatible with the deprecated @AssistedInject annotation. Please replace "
+                + "@AssistedInject with @Inject on the %s constructor.",
+                method, implementationType);
+          }
+        }
+      }
+
       AssistedConstructor matchingConstructor = paramsToConstructor.remove(methodParams);
 
       result.put(method, matchingConstructor);
@@ -204,8 +278,7 @@
         }
 
         AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
-        Object[] constructorArgs = gatherArgsForConstructor(
-            constructor, creationArgs);
+        Object[] constructorArgs = gatherArgsForConstructor(constructor, creationArgs);
         Object objectToReturn = constructor.newInstance(constructorArgs);
         injector.injectMembers(objectToReturn);
         return objectToReturn;
@@ -235,7 +308,7 @@
         new Class[] {factoryType}, invocationHandler));
   }
 
-  private ConfigurationException newConfigurationException(String format, Object... args) {
+  private static ConfigurationException newConfigurationException(String format, Object... args) {
     return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
   }
 }
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
new file mode 100644
index 0000000..af55dfb
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -0,0 +1,249 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.assistedinject;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Binding;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.TypeLiteral;
+import static com.google.inject.internal.Annotations.getKey;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.Message;
+import com.google.inject.util.Providers;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+
+/**
+ * The newer implementation of factory provider. This implementation uses a child injector to
+ * create values.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ * @author dtm@google.com (Daniel Martin)
+ */
+final class FactoryProvider2<F> implements InvocationHandler, Provider<F> {
+
+  /** if a factory method parameter isn't annotated, it gets this annotation. */
+  static final Assisted DEFAULT_ANNOTATION = new Assisted() {
+    public String value() {
+      return "";
+    }
+
+    public Class<? extends Annotation> annotationType() {
+      return Assisted.class;
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof Assisted
+          && ((Assisted) o).value().equals("");
+    }
+
+    @Override public int hashCode() {
+      return 127 * "value".hashCode() ^ "".hashCode();
+    }
+
+    @Override public String toString() {
+      return Assisted.class.getName() + "(value=)";
+    }
+  };
+
+  /** the produced type, or null if all methods return concrete types */
+  private final Key<?> producedType;
+  private final ImmutableMap<Method, Key<?>> returnTypesByMethod;
+  private final ImmutableMultimap<Method, Key<?>> paramTypes;
+
+  /** the hosting injector, or null if we haven't been initialized yet */
+  private Injector injector;
+
+  /** the factory interface, implemented and provided */
+  private final F factory;
+
+  /**
+   * @param factoryType a Java interface that defines one or more create methods.
+   * @param producedType a concrete type that is assignable to the return types of all factory
+   *      methods.
+   */
+  FactoryProvider2(Class<F> factoryType, Key<?> producedType) {
+    this.producedType = producedType;
+
+    Errors errors = new Errors();
+    try {
+      ImmutableMap.Builder<Method, Key<?>> returnTypesBuilder = ImmutableMap.builder();
+      ImmutableMultimap.Builder<Method, Key<?>> paramTypesBuilder = ImmutableMultimap.builder();
+      // TODO: also grab methods from superinterfaces
+      for (Method method : factoryType.getMethods()) {
+        Key<?> returnType = getKey(TypeLiteral.get(method.getGenericReturnType()),
+            method, method.getAnnotations(), errors);
+        returnTypesBuilder.put(method, returnType);
+        Type[] params = method.getGenericParameterTypes();
+        Annotation[][] paramAnnotations = method.getParameterAnnotations();
+        int p = 0;
+        for (Type param : params) {
+          Key<?> paramKey = getKey(TypeLiteral.get(param), method, paramAnnotations[p++], errors);
+          paramTypesBuilder.put(method, assistKey(method, paramKey, errors));
+        }
+      }
+      returnTypesByMethod = returnTypesBuilder.build();
+      paramTypes = paramTypesBuilder.build();
+    } catch (ErrorsException e) {
+      throw new ConfigurationException(e.getErrors().getMessages());
+    }
+
+    factory = factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(),
+        new Class[] { factoryType }, this));
+  }
+
+  public F get() {
+    return factory;
+  }
+
+  /**
+   * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
+   * This fails if another binding annotation is clobbered in the process. If the key already has
+   * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
+   */
+  private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
+    if (key.getAnnotationType() == null) {
+      return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
+    } else if (key.getAnnotationType() == Assisted.class) {
+      return key;
+    } else {
+      errors.withSource(method).addMessage(
+          "Only @Assisted is allowed for factory parameters, but found @%s",
+          key.getAnnotationType());
+      throw errors.toException();
+    }
+  }
+
+  /**
+   * At injector-creation time, we initialize the invocation handler. At this time we make sure
+   * all factory methods will be able to build the target types.
+   */
+  @Inject
+  void initialize(Injector injector) {
+    if (this.injector != null) {
+      throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class,
+          "Factories.create() factories may only be used in one Injector!")));
+    }
+
+    this.injector = injector;
+
+    for (Method method : returnTypesByMethod.keySet()) {
+      Object[] args = new Object[method.getParameterTypes().length];
+      Arrays.fill(args, "dummy object for validating Factories");
+      getBindingFromNewInjector(method, args); // throws if the binding isn't properly configured
+    }
+  }
+
+  /**
+   * Creates a child injector that binds the args, and returns the binding for the method's result.
+   */
+  public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args) {
+    checkState(injector != null,
+        "Factories.create() factories cannot be used until they're initialized by Guice.");
+
+    final Key<?> returnType = returnTypesByMethod.get(method);
+
+    Module assistedModule = new AbstractModule() {
+      @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
+      protected void configure() {
+        Binder binder = binder().withSource(method);
+
+        int p = 0;
+        for (Key<?> paramKey : paramTypes.get(method)) {
+          // Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
+          binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
+        }
+
+        if (producedType != null && !returnType.equals(producedType)) {
+          binder.bind(returnType).to((Key) producedType);
+        } else {
+          binder.bind(returnType);
+        }
+      }
+    };
+
+    Injector forCreate = injector.createChildInjector(assistedModule);
+    return forCreate.getBinding(returnType);
+  }
+
+  /**
+   * When a factory method is invoked, we create a child injector that binds all parameters, then
+   * use that to get an instance of the return type.
+   */
+  public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
+    if (method.getDeclaringClass() == Object.class) {
+      return method.invoke(this, args);
+    }
+
+    Provider<?> provider = getBindingFromNewInjector(method, args).getProvider();
+    try {
+      return provider.get();
+    } catch (ProvisionException e) {
+      // if this is an exception declared by the factory method, throw it as-is
+      if (e.getErrorMessages().size() == 1) {
+        Message onlyError = getOnlyElement(e.getErrorMessages());
+        Throwable cause = onlyError.getCause();
+        if (cause != null && canRethrow(method, cause)) {
+          throw cause;
+        }
+      }
+      throw e;
+    }
+  }
+
+  @Override public String toString() {
+    return factory.getClass().getInterfaces()[0].getName()
+        + " for " + producedType.getTypeLiteral();
+  }
+
+  @Override public boolean equals(Object o) {
+    return o == this || o == factory;
+  }
+
+  /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
+  static boolean canRethrow(Method invoked, Throwable thrown) {
+    if (thrown instanceof Error || thrown instanceof RuntimeException) {
+      return true;
+    }
+
+    for (Class<?> declared : invoked.getExceptionTypes()) {
+      if (declared.isInstance(thrown)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoriesTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
similarity index 81%
rename from extensions/assistedinject/test/com/google/inject/assistedinject/FactoriesTest.java
rename to extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index 2b7465e..3cde5ff 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoriesTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -37,15 +37,15 @@
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
 
-public class FactoriesTest extends TestCase {
+public class FactoryProvider2Test extends TestCase {
 
   public void testAssistedFactory() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
         bind(Double.class).toInstance(5.0d);
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Mustang.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
       }
     });
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
@@ -65,8 +65,8 @@
       protected void configure() {
         bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
         bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Camaro.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Camaro.class));
       }
     });
 
@@ -99,9 +99,7 @@
       this.color = color;
     }
 
-    public String drive() {
-      return "vroom!";
-    }
+    public void drive() {}
   }
 
   public static class Camaro implements Car {
@@ -124,13 +122,13 @@
     Car create(Color color, boolean convertable);
   }
 
-  public void testFactoryWithMultipleMethods() {
+  public void testFactoryUsesInjectedConstructor() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
         bind(float.class).toInstance(140f);
-        bind(SummerCarFactory.class).toInstance(
-            Factories.create(SummerCarFactory.class, Corvette.class));
+        bind(SummerCarFactory.class).toProvider(
+            FactoryProvider.newFactory(SummerCarFactory.class, Corvette.class));
       }
     });
 
@@ -162,8 +160,8 @@
   public void testConstructorDoesntNeedAllFactoryMethodArguments() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       protected void configure() {
-        bind(SummerCarFactory.class).toInstance(
-            Factories.create(SummerCarFactory.class, Beetle.class));
+        bind(SummerCarFactory.class).toProvider(
+            FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class));
       }
     });
     SummerCarFactory factory = injector.getInstance(SummerCarFactory.class);
@@ -187,8 +185,8 @@
         bind(String.class).toInstance("turbo");
         bind(int.class).toInstance(911);
         bind(double.class).toInstance(50000d);
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Porshe.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Porshe.class));
       }
     });
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
@@ -222,8 +220,8 @@
       @Override
       protected void configure() {
         bind(String.class).toInstance("trans am");
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Firebird.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Firebird.class));
       }
     });
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
@@ -250,8 +248,8 @@
       protected void configure() {
         bind(new TypeLiteral<Set<String>>() {}).toInstance(Collections.singleton("Flux Capacitor"));
         bind(new TypeLiteral<Set<Integer>>() {}).toInstance(Collections.singleton(88));
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, DeLorean.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, DeLorean.class));
       }
     });
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
@@ -281,8 +279,8 @@
       @Override
       protected void configure() {
         bind(new TypeLiteral<Set<String>>() { }).toInstance(Collections.singleton("Datsun"));
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Z.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Z.class));
       }
     });
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
@@ -317,8 +315,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Prius.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class));
       }
     });
     Car car = injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
@@ -335,8 +333,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, ExplodingCar.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, ExplodingCar.class));
       }
     });
     try {
@@ -373,8 +371,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(CorrectDefectiveCarFactory.class).toInstance(
-            Factories.create(CorrectDefectiveCarFactory.class, DefectiveCar.class));
+        bind(CorrectDefectiveCarFactory.class).toProvider(
+            FactoryProvider.newFactory(CorrectDefectiveCarFactory.class, DefectiveCar.class));
       }
     });
     try {
@@ -386,13 +384,6 @@
     }
   }
 
-  public static class MultipleConstructorDefectiveCar implements Car {
-    @Inject
-    public MultipleConstructorDefectiveCar(@Assisted Color c) throws FireException {
-      throw new FireException();
-    }
-  }
-
   public static class WildcardCollection {
 
     public interface Factory {
@@ -407,8 +398,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(WildcardCollection.Factory.class).toInstance(
-            Factories.create(WildcardCollection.Factory.class, WildcardCollection.class));
+        bind(WildcardCollection.Factory.class).toProvider(
+            FactoryProvider.newFactory(WildcardCollection.Factory.class, WildcardCollection.class));
       }
     });
     WildcardCollection.Factory factory = injector.getInstance(WildcardCollection.Factory.class);
@@ -434,8 +425,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Fiat.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Fiat.class));
       }
     });
 
@@ -449,15 +440,15 @@
     try {
       Guice.createInjector(new AbstractModule() {
         @Override protected void configure() {
-          bind(ColoredCarFactory.class).toInstance(
-              Factories.create(ColoredCarFactory.class, Mustang.class));
+          bind(ColoredCarFactory.class).toProvider(
+              FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
         }
       });
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Could not find a suitable constructor in java.lang.Double.",
-          "at " + ColoredCarFactory.class.getName() + ".create(FactoriesTest.java");
+          "at " + ColoredCarFactory.class.getName() + ".create(FactoryProvider2Test.java");
     }
   }
   
@@ -465,15 +456,16 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
         @Override protected void configure() {
           bind(Double.class).toInstance(5.0d);
-          bind(ColoredCarFactory.class).toInstance(
-              Factories.create(ColoredCarFactory.class, Mustang.class));
+          bind(ColoredCarFactory.class).toProvider(
+              FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
         }
       });
 
     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
 
     assertEqualsBothWays(carFactory, carFactory);
-    assertEquals("Factory", carFactory.toString());
+    assertEquals(ColoredCarFactory.class.getName() + " for " + Mustang.class.getName(),
+        carFactory.toString());
   }
 
   static class Subaru implements Car {
@@ -483,8 +475,8 @@
   public void testInjectingProviderOfParameter() {
     Injector injector = Guice.createInjector(new AbstractModule() {
         @Override protected void configure() {
-          bind(ColoredCarFactory.class).toInstance(
-              Factories.create(ColoredCarFactory.class, Subaru.class));
+          bind(ColoredCarFactory.class).toProvider(
+              FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class));
         }
       });
 
@@ -498,8 +490,8 @@
   public void testInjectingNullParameter() {
     Injector injector = Guice.createInjector(new AbstractModule() {
         @Override protected void configure() {
-          bind(ColoredCarFactory.class).toInstance(
-              Factories.create(ColoredCarFactory.class, Subaru.class));
+          bind(ColoredCarFactory.class).toProvider(
+              FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class));
         }
       });
 
@@ -511,7 +503,8 @@
   }
 
   public void testFactoryUseBeforeInitialization() {
-    ColoredCarFactory carFactory = Factories.create(ColoredCarFactory.class, Subaru.class);
+    ColoredCarFactory carFactory = FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class)
+        .get();
     try {
       carFactory.create(Color.RED);
       fail();
@@ -530,8 +523,8 @@
       protected void configure() {
         bind(double.class).toInstance(5.0d);
         // note there is no 'thatMakes()' call here:
-        bind(MustangFactory.class).toInstance(
-            Factories.create(MustangFactory.class, Mustang.class));
+        bind(MustangFactory.class).toProvider(
+            FactoryProvider.newFactory(MustangFactory.class, Mustang.class));
       }
     });
     MustangFactory factory = injector.getInstance(MustangFactory.class);
@@ -557,8 +550,8 @@
         bind(double.class).toInstance(5.0d);
         bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
         bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
-        FleetFactory fleetFactory = Factories.create(FleetFactory.class, Fleet.class);
-        bind(FleetFactory.class).toInstance(fleetFactory);
+        bind(FleetFactory.class).toProvider(FactoryProvider.newFactory(FleetFactory.class,
+            Fleet.class));
       }
     });
 
@@ -585,8 +578,8 @@
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(TwoToneCarFactory.class).toInstance(
-            Factories.create(TwoToneCarFactory.class, Maxima.class));
+        bind(TwoToneCarFactory.class).toProvider(
+            FactoryProvider.newFactory(TwoToneCarFactory.class, Maxima.class));
       }
     });
 
@@ -596,6 +589,25 @@
     assertSame(Color.GRAY, maxima.fabric);
   }
 
+  interface DoubleToneCarFactory {
+    Car create(@Assisted("paint") Color paint, @Assisted("paint") Color morePaint);
+  }
+
+  public void testDuplicateKeys() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override protected void configure() {
+          bind(DoubleToneCarFactory.class).toProvider(
+              FactoryProvider.newFactory(DoubleToneCarFactory.class, Maxima.class));
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "A binding to java.awt.Color annotated with @"
+          + Assisted.class.getName() + "(value=paint) was already configured at");
+    }
+  }
+
   public void testMethodInterceptorsOnAssistedTypes() {
     final AtomicInteger invocationCount = new AtomicInteger();
     final MethodInterceptor interceptor = new MethodInterceptor() {
@@ -610,8 +622,8 @@
       protected void configure() {
         bindInterceptor(Matchers.any(), Matchers.any(), interceptor);
         bind(Double.class).toInstance(5.0d);
-        bind(ColoredCarFactory.class).toInstance(
-            Factories.create(ColoredCarFactory.class, Mustang.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
       }
     });
 
@@ -623,7 +635,7 @@
   }
 
   public void testDefaultAssistedAnnotation() throws NoSuchFieldException {
-    assertEqualsBothWays(Factories.DEFAULT_ASSISTED,
+    assertEqualsBothWays(FactoryProvider2.DEFAULT_ANNOTATION,
         Subaru.class.getDeclaredField("colorProvider").getAnnotation(Assisted.class));
   }
 }
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
index 7f649f4..e085f08 100755
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -536,5 +536,24 @@
     carFactory.toString();
   }
 
-  // TODO(jessewilson): test for duplicate constructors
+  public void testAssistedInjectConstructorAndAssistedFactoryParameterMustNotMix() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override protected void configure() {
+          bind(Double.class).toInstance(5.0d);
+          bind(AssistedParamsFactory.class)
+              .toProvider(FactoryProvider.newFactory(AssistedParamsFactory.class, Mustang.class));
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "Factory method "
+          + AssistedParamsFactory.class.getName() + ".create() has an @Assisted parameter, which "
+          + "is incompatible with the deprecated @AssistedInject annotation.");
+    }
+  }
+
+  interface AssistedParamsFactory {
+    Car create(@Assisted Color color);
+  }
 }
\ No newline at end of file
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index d8b4ece..64ec59f 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -356,9 +356,9 @@
   }
 
   private static <M extends Member & AnnotatedElement> void addInjectorsForMembers(
-      TypeLiteral<?> typeResolver, Factory<M> factory, boolean statics,
+      TypeLiteral<?> typeLiteral, Factory<M> factory, boolean statics,
       Collection<InjectionPoint> injectionPoints, Errors errors) {
-    for (M member : factory.getMembers(getRawType(typeResolver.getType()))) {
+    for (M member : factory.getMembers(getRawType(typeLiteral.getType()))) {
       if (isStatic(member) != statics) {
         continue;
       }
@@ -369,7 +369,7 @@
       }
 
       try {
-        injectionPoints.add(factory.create(typeResolver, member, errors));
+        injectionPoints.add(factory.create(typeLiteral, member, errors));
       } catch (ConfigurationException ignorable) {
         if (!inject.optional()) {
           errors.merge(ignorable.getErrorMessages());
@@ -387,8 +387,8 @@
       public Field[] getMembers(Class<?> type) {
         return type.getDeclaredFields();
       }
-      public InjectionPoint create(TypeLiteral<?> typeResolver, Field member, Errors errors) {
-        return new InjectionPoint(typeResolver, member);
+      public InjectionPoint create(TypeLiteral<?> typeLiteral, Field member, Errors errors) {
+        return new InjectionPoint(typeLiteral, member);
       }
     };
 
@@ -396,14 +396,14 @@
       public Method[] getMembers(Class<?> type) {
         return type.getDeclaredMethods();
       }
-      public InjectionPoint create(TypeLiteral<?> typeResolver, Method member, Errors errors) {
+      public InjectionPoint create(TypeLiteral<?> typeLiteral, Method member, Errors errors) {
         checkForMisplacedBindingAnnotations(member, errors);
-        return new InjectionPoint(typeResolver, member);
+        return new InjectionPoint(typeLiteral, member);
       }
     };
 
     M[] getMembers(Class<?> type);
-    InjectionPoint create(TypeLiteral<?> typeResolver, M member, Errors errors);
+    InjectionPoint create(TypeLiteral<?> typeLiteral, M member, Errors errors);
   }
 
   private static final long serialVersionUID = 0;