Applying ilya.firman's patch for issue 218. I tweaked the patch to use TypeLiteral's new type resolution methods like TypeLiteral.getReturnType(Method) etc., which makes it so we always just do-the-right-thing for matching type variables.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@871 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
index 4976bd0..b6024e2 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
@@ -17,6 +17,8 @@
 package com.google.inject.assistedinject;
 
 import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.Lists;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -41,18 +43,17 @@
   private final List<Parameter> allParameters;
 
   @SuppressWarnings("unchecked")
-  public AssistedConstructor(Constructor<T> constructor) {
+  public AssistedConstructor(Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
     this.constructor = constructor;
 
-    Type[] parameterTypes = constructor.getGenericParameterTypes();
     Annotation[][] annotations = constructor.getParameterAnnotations();
 
-    List<Type> typeList = new ArrayList<Type>();
+    List<Type> typeList = Lists.newArrayList();
     allParameters = new ArrayList<Parameter>();
 
     // categorize params as @Assisted or @Injected
-    for (int i = 0; i < parameterTypes.length; i++) {
-      Parameter parameter = new Parameter(parameterTypes[i], annotations[i]);
+    for (int i = 0; i < parameterTypes.size(); i++) {
+      Parameter parameter = new Parameter(parameterTypes.get(i).getType(), annotations[i]);
       allParameters.add(parameter);
       if (parameter.isProvidedByFactory()) {
         typeList.add(parameter.getType());
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
index 3e4c46a..2649188 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
@@ -21,6 +21,7 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ImmutableMap;
 import com.google.inject.internal.ImmutableSet;
@@ -34,6 +35,7 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -144,11 +146,16 @@
 
   private Injector injector;
 
-  private final Class<F> factoryType;
+  private final TypeLiteral<F> factoryType;
   private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
 
   public static <F> Provider<F> newFactory(
       Class<F> factoryType, Class<?> implementationType){
+    return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
+  }
+
+  public static <F> Provider<F> newFactory(
+      TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
     Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
         = createMethodMapping(factoryType, implementationType);
 
@@ -159,7 +166,7 @@
     }
   }
 
-  private FactoryProvider(Class<F> factoryType,
+  private FactoryProvider(TypeLiteral<F> factoryType,
       Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
     this.factoryType = factoryType;
     this.factoryMethodToConstructor = factoryMethodToConstructor;
@@ -208,14 +215,16 @@
     return parameter.isBound(injector);
   }
 
-  @SuppressWarnings({"unchecked"})
   private static Map<Method, AssistedConstructor<?>> createMethodMapping(
-      Class<?> factoryType, Class<?> implementationType) {
+      TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
     List<AssistedConstructor<?>> constructors = Lists.newArrayList();
 
-    for (Constructor<?> c : implementationType.getDeclaredConstructors()) {
-      if (c.getAnnotation(AssistedInject.class) != null) {
-        constructors.add(new AssistedConstructor(c));
+    for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
+      if (constructor.getAnnotation(AssistedInject.class) != null) {
+        @SuppressWarnings("unchecked") // the constructor type and implementation type agree
+        AssistedConstructor assistedConstructor = new AssistedConstructor(
+            constructor, implementationType.getParameterTypes(constructor));
+        constructors.add(assistedConstructor);
       }
     }
 
@@ -223,10 +232,12 @@
       return ImmutableMap.of();
     }
 
-    if (constructors.size() != factoryType.getMethods().length) {
+    Method[] factoryMethods = factoryType.getRawType().getMethods();
+
+    if (constructors.size() != factoryMethods.length) {
       throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
-          + "constructors, factory %s has %s creation methods", implementationType.getSimpleName(),
-          constructors.size(), factoryType.getSimpleName(), factoryType.getMethods().length);
+          + "constructors, factory %s has %s creation methods", implementationType,
+          constructors.size(), factoryType, factoryMethods.length);
     }
 
     Map<ParameterListKey, AssistedConstructor> paramsToConstructor = Maps.newHashMap();
@@ -239,13 +250,17 @@
     }
 
     Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
-    for (Method method : factoryType.getMethods()) {
-      if (!method.getReturnType().isAssignableFrom(implementationType)) {
-        throw new RuntimeException(String.format("Return type of method \"%s\""
-            + " is not assignable from class \"%s\"", method,
-            implementationType.getName()));
+    for (Method method : factoryMethods) {
+      if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
+        throw newConfigurationException("Return type of method %s is not assignable from %s",
+            method, implementationType);
       }
-      ParameterListKey methodParams = new ParameterListKey(method.getGenericParameterTypes());
+
+      List<Type> parameterTypes = Lists.newArrayList();
+      for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
+        parameterTypes.add(parameterType.getType());
+      }
+      ParameterListKey methodParams = new ParameterListKey(parameterTypes);
 
       if (!paramsToConstructor.containsKey(methodParams)) {
         throw newConfigurationException("%s has no @AssistInject constructor that takes the "
@@ -319,8 +334,10 @@
       }
     };
 
-    return factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(),
-        new Class[] {factoryType}, invocationHandler));
+    @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
+    Class<F> factoryRawType = (Class) factoryType.getRawType();
+    return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
+        new Class[] { factoryRawType }, invocationHandler));
   }
 
   private static ConfigurationException newConfigurationException(String format, Object... args) {
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
index 06e296b..f1cd29f 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -41,7 +41,6 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.List;
 
@@ -94,25 +93,29 @@
    * @param producedType a concrete type that is assignable to the return types of all factory
    *      methods.
    */
-  FactoryProvider2(Class<F> factoryType, Key<?> producedType) {
+  FactoryProvider2(TypeLiteral<F> factoryType, Key<?> producedType) {
     this.producedType = producedType;
 
     Errors errors = new Errors();
+
+    @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
+    Class<F> factoryRawType = (Class) factoryType.getRawType();
+
     try {
       ImmutableMap.Builder<Method, Key<?>> returnTypesBuilder = ImmutableMap.builder();
       ImmutableMap.Builder<Method, ImmutableList<Key<?>>> paramTypesBuilder
           = ImmutableMap.builder();
       // TODO: also grab methods from superinterfaces
-      for (Method method : factoryType.getMethods()) {
-        Key<?> returnType = getKey(TypeLiteral.get(method.getGenericReturnType()),
-            method, method.getAnnotations(), errors);
+      for (Method method : factoryRawType.getMethods()) {
+        Key<?> returnType = getKey(
+            factoryType.getReturnType(method), method, method.getAnnotations(), errors);
         returnTypesBuilder.put(method, returnType);
-        Type[] params = method.getGenericParameterTypes();
+        List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
         Annotation[][] paramAnnotations = method.getParameterAnnotations();
         int p = 0;
         List<Key<?>> keys = Lists.newArrayList();
-        for (Type param : params) {
-          Key<?> paramKey = getKey(TypeLiteral.get(param), method, paramAnnotations[p++], errors);
+        for (TypeLiteral<?> param : params) {
+          Key<?> paramKey = getKey(param, method, paramAnnotations[p++], errors);
           keys.add(assistKey(method, paramKey, errors));
         }
         paramTypesBuilder.put(method, ImmutableList.copyOf(keys));
@@ -123,8 +126,8 @@
       throw new ConfigurationException(e.getErrors().getMessages());
     }
 
-    factory = factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(),
-        new Class[] { factoryType }, this));
+    factory = factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
+        new Class[] { factoryRawType }, this));
   }
 
   public F get() {
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
index 2b7c8c0..d0c4e74 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
@@ -93,11 +93,10 @@
   }
 
   private boolean isBound(Injector injector, Key<?> key) {
-    /* This method is particularly lame - we really need an API that can test
-       for any binding, implicit or explicit */
+    // This method is particularly lame - we really need an API that can test
+    // for any binding, implicit or explicit
     try {
-      return injector.getBinding(key) != null
-          || injector.getProvider(key) != null;
+      return injector.getBinding(key) != null;
     } catch (ConfigurationException e) {
       return false;
     }
@@ -123,12 +122,12 @@
   }
 
   private Type getProvidedType(Type type) {
-    return ((ParameterizedType)type).getActualTypeArguments()[0];
+    return ((ParameterizedType) type).getActualTypeArguments()[0];
   }
 
   private boolean isProvider(Type type) {
     return type instanceof ParameterizedType
-        && ((ParameterizedType)type).getRawType() == Provider.class;
+        && ((ParameterizedType) type).getRawType() == Provider.class;
   }
 
   private Key<?> getBindingForType(Type type) {
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index 79bd1ae..b8aa296 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -24,6 +24,7 @@
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Key;
 import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.matcher.Matchers;
@@ -688,4 +689,194 @@
     assertEqualsBothWays(FactoryProvider2.DEFAULT_ANNOTATION, plainAssisted);
     assertEquals(FactoryProvider2.DEFAULT_ANNOTATION.toString(), plainAssisted.toString());
   }
+  
+  
+  interface GenericColoredCarFactory<T extends Car> {
+    T create(Color color);
+  }
+
+  public void testGenericAssistedFactory() {
+    final TypeLiteral<GenericColoredCarFactory<Mustang>> mustangTypeLiteral
+        = new TypeLiteral<GenericColoredCarFactory<Mustang>>() {};
+    final TypeLiteral<GenericColoredCarFactory<Camaro>> camaroTypeLiteral
+        = new TypeLiteral<GenericColoredCarFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(5.0d);
+        bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
+        bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
+        bind(mustangTypeLiteral)
+            .toProvider(FactoryProvider.newFactory(mustangTypeLiteral, TypeLiteral.get(Mustang.class)));
+        bind(camaroTypeLiteral)
+            .toProvider(FactoryProvider.newFactory(camaroTypeLiteral, TypeLiteral.get(Camaro.class)));
+      }
+    });
+
+    GenericColoredCarFactory<Mustang> mustangFactory
+        = injector.getInstance(Key.get(mustangTypeLiteral));
+    GenericColoredCarFactory<Camaro> camaroFactory
+        = injector.getInstance(Key.get(camaroTypeLiteral));
+
+    Mustang blueMustang = mustangFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueMustang.color);
+    assertEquals(5.0d, blueMustang.engineSize);
+
+    Camaro redCamaro = camaroFactory.create(Color.RED);
+    assertEquals(Color.RED, redCamaro.color);
+    assertEquals(1984, redCamaro.modelYear);
+    assertEquals(250, redCamaro.horsePower);
+  }
+  
+  public interface Insurance<T extends Car> {
+  }
+  
+  public static class MustangInsurance implements Insurance<Mustang> {
+    private final double premium;
+    private final double limit;
+    private Mustang car;
+
+    @Inject
+    public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,  
+        @Assisted double premium) {
+      this.premium = premium;
+      this.limit = limit;
+      this.car = car;
+    }
+
+    public void sell() {}
+  }
+
+  public static class CamaroInsurance implements Insurance<Camaro> {
+    private final double premium;
+    private final double limit;
+    private Camaro car;
+
+    @Inject
+    public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car, 
+        @Assisted double premium) {
+      this.premium = premium;
+      this.limit = limit;
+      this.car = car;
+    }
+
+    public void sell() {}
+  }
+  
+  public interface MustangInsuranceFactory {
+    public Insurance<Mustang> create(Mustang car, double premium);
+  }
+  
+  public interface CamaroInsuranceFactory {
+    public Insurance<Camaro> create(Camaro car, double premium);
+  }
+
+  public void testAssistedFactoryForConcreteType() {
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+        bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+        bind(MustangInsuranceFactory.class).toProvider(
+            FactoryProvider.newFactory(MustangInsuranceFactory.class, MustangInsurance.class));
+        bind(CamaroInsuranceFactory.class).toProvider(
+            FactoryProvider.newFactory(CamaroInsuranceFactory.class, CamaroInsurance.class));
+      }
+    });
+
+    MustangInsuranceFactory mustangInsuranceFactory = 
+        injector.getInstance(MustangInsuranceFactory.class);
+    CamaroInsuranceFactory camaroInsuranceFactory = 
+        injector.getInstance(CamaroInsuranceFactory.class);
+
+    Mustang mustang = new Mustang(5000d, Color.BLACK);
+    MustangInsurance mustangPolicy = 
+        (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+    assertEquals(800.0d, mustangPolicy.premium);
+    assertEquals(50000.0d, mustangPolicy.limit);
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(100000.0d, camaroPolicy.limit);
+  }
+  
+  public interface InsuranceFactory<T extends Car> {
+    public Insurance<T> create(T car, double premium);
+  }
+
+  public void testAssistedFactoryForParameterizedType() {
+    final TypeLiteral<InsuranceFactory<Mustang>> mustangInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Mustang>>() {};
+    final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+        bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+        bind(mustangInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            mustangInsuranceFactoryType, TypeLiteral.get(MustangInsurance.class)));
+        bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            camaroInsuranceFactoryType, TypeLiteral.get(CamaroInsurance.class)));
+      }
+    });
+
+    InsuranceFactory<Mustang> mustangInsuranceFactory = 
+        injector.getInstance(Key.get(mustangInsuranceFactoryType));
+    InsuranceFactory<Camaro> camaroInsuranceFactory = 
+        injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+    Mustang mustang = new Mustang(5000d, Color.BLACK);
+    MustangInsurance mustangPolicy = 
+        (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+    assertEquals(800.0d, mustangPolicy.premium);
+    assertEquals(50000.0d, mustangPolicy.limit);
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(100000.0d, camaroPolicy.limit);
+  }
+
+  public static class AutoInsurance<T extends Car> implements Insurance<T> {
+    private final double premium;
+    private final double limit;
+    private final T car;
+
+    @Inject
+    public AutoInsurance(double limit, @Assisted T car, @Assisted double premium) {
+      this.limit = limit;
+      this.car = car;
+      this.premium = premium;
+    }
+
+    public void sell() {}
+  }
+  
+  public void testAssistedFactoryForTypeVariableParameters() {
+    final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(50000.0d);
+        bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            camaroInsuranceFactoryType, new TypeLiteral<AutoInsurance<Camaro>>() {}));
+      }
+    });
+
+    InsuranceFactory<Camaro> camaroInsuranceFactory =
+        injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(50000.0d, camaroPolicy.limit);
+    assertEquals(camaro, camaroPolicy.car);
+  }
 }
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
index ea96df3..cc77001 100755
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -577,4 +577,193 @@
   interface AssistedParamsFactory {
     Car create(@Assisted Color color);
   }
+
+  interface GenericColoredCarFactory<T extends Car> {
+    T create(Color color);
+  }
+
+  public void testGenericAssistedFactory() {
+    final TypeLiteral<GenericColoredCarFactory<Mustang>> mustangTypeLiteral
+        = new TypeLiteral<GenericColoredCarFactory<Mustang>>() {};
+    final TypeLiteral<GenericColoredCarFactory<Camaro>> camaroTypeLiteral
+        = new TypeLiteral<GenericColoredCarFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(5.0d);
+        bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
+        bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
+        bind(mustangTypeLiteral).toProvider(
+            FactoryProvider.newFactory(mustangTypeLiteral, TypeLiteral.get(Mustang.class)));
+        bind(camaroTypeLiteral).toProvider(
+            FactoryProvider.newFactory(camaroTypeLiteral, TypeLiteral.get(Camaro.class)));
+      }
+    });
+
+    GenericColoredCarFactory<Mustang> mustangFactory
+        = injector.getInstance(Key.get(mustangTypeLiteral));
+    GenericColoredCarFactory<Camaro> camaroFactory
+        = injector.getInstance(Key.get(camaroTypeLiteral));
+
+    Mustang blueMustang = mustangFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueMustang.color);
+    assertEquals(5.0d, blueMustang.engineSize);
+
+    Camaro redCamaro = camaroFactory.create(Color.RED);
+    assertEquals(Color.RED, redCamaro.color);
+    assertEquals(1984, redCamaro.modelYear);
+    assertEquals(250, redCamaro.horsePower);
+  }
+
+  public interface Insurance<T extends Car> {
+  }
+
+  public static class MustangInsurance implements Insurance<Mustang> {
+    private final double premium;
+    private final double limit;
+    private Mustang car;
+
+    @AssistedInject
+    public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,
+        @Assisted double premium) {
+      this.premium = premium;
+      this.limit = limit;
+      this.car = car;
+    }
+
+    public void sell() {}
+  }
+
+  public static class CamaroInsurance implements Insurance<Camaro> {
+    private final double premium;
+    private final double limit;
+    private Camaro car;
+
+    @AssistedInject
+    public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car,
+        @Assisted double premium) {
+      this.premium = premium;
+      this.limit = limit;
+      this.car = car;
+    }
+
+    public void sell() {}
+  }
+
+  public interface MustangInsuranceFactory {
+    public Insurance<Mustang> create(Mustang car, double premium);
+  }
+
+  public interface CamaroInsuranceFactory {
+    public Insurance<Camaro> create(Camaro car, double premium);
+  }
+
+  public void testAssistedFactoryForConcreteType() {
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+        bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+        bind(MustangInsuranceFactory.class).toProvider(
+            FactoryProvider.newFactory(MustangInsuranceFactory.class, MustangInsurance.class));
+        bind(CamaroInsuranceFactory.class).toProvider(
+            FactoryProvider.newFactory(CamaroInsuranceFactory.class, CamaroInsurance.class));
+      }
+    });
+
+    MustangInsuranceFactory mustangInsuranceFactory =
+        injector.getInstance(MustangInsuranceFactory.class);
+    CamaroInsuranceFactory camaroInsuranceFactory =
+        injector.getInstance(CamaroInsuranceFactory.class);
+
+    Mustang mustang = new Mustang(5000d, Color.BLACK);
+    MustangInsurance mustangPolicy =
+        (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+    assertEquals(800.0d, mustangPolicy.premium);
+    assertEquals(50000.0d, mustangPolicy.limit);
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(100000.0d, camaroPolicy.limit);
+  }
+
+  public interface InsuranceFactory<T extends Car> {
+    public Insurance<T> create(T car, double premium);
+  }
+
+  public void testAssistedFactoryForParameterizedType() {
+    final TypeLiteral<InsuranceFactory<Mustang>> mustangInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Mustang>>() {};
+    final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+        bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+        bind(mustangInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            mustangInsuranceFactoryType, TypeLiteral.get(MustangInsurance.class)));
+        bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            camaroInsuranceFactoryType, TypeLiteral.get(CamaroInsurance.class)));
+      }
+    });
+
+    InsuranceFactory<Mustang> mustangInsuranceFactory =
+        injector.getInstance(Key.get(mustangInsuranceFactoryType));
+    InsuranceFactory<Camaro> camaroInsuranceFactory =
+        injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+    Mustang mustang = new Mustang(5000d, Color.BLACK);
+    MustangInsurance mustangPolicy =
+        (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+    assertEquals(800.0d, mustangPolicy.premium);
+    assertEquals(50000.0d, mustangPolicy.limit);
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(100000.0d, camaroPolicy.limit);
+  }
+
+  public static class AutoInsurance<T extends Car> implements Insurance<T> {
+    private final double premium;
+    private final double limit;
+    private final T car;
+
+    @AssistedInject
+    public AutoInsurance(double limit, @Assisted T car, @Assisted double premium) {
+      this.limit = limit;
+      this.car = car;
+      this.premium = premium;
+    }
+
+    public void sell() {}
+  }
+
+  public void testAssistedFactoryForTypeVariableParameters() {
+    final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+        new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(50000.0d);
+        bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+            camaroInsuranceFactoryType, new TypeLiteral<AutoInsurance<Camaro>>() {}));
+      }
+    });
+
+    InsuranceFactory<Camaro> camaroInsuranceFactory =
+        injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+    Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+    AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    assertEquals(800.0d, camaroPolicy.premium);
+    assertEquals(50000.0d, camaroPolicy.limit);
+    assertEquals(camaro, camaroPolicy.car);
+  }
 }
\ No newline at end of file