1 /*
2  * Copyright (C) 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.assistedinject;
18 
19 import static com.google.inject.Asserts.assertContains;
20 
21 import com.google.common.collect.ImmutableSet;
22 import com.google.inject.AbstractModule;
23 import com.google.inject.Binding;
24 import com.google.inject.ConfigurationException;
25 import com.google.inject.CreationException;
26 import com.google.inject.Guice;
27 import com.google.inject.Inject;
28 import com.google.inject.Injector;
29 import com.google.inject.Key;
30 import com.google.inject.Provider;
31 import com.google.inject.TypeLiteral;
32 import com.google.inject.assistedinject.FactoryProviderTest.Equals.ComparisonMethod;
33 import com.google.inject.assistedinject.FactoryProviderTest.Equals.Impl;
34 import com.google.inject.name.Named;
35 import com.google.inject.name.Names;
36 import com.google.inject.spi.Dependency;
37 import com.google.inject.spi.HasDependencies;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Set;
41 import junit.framework.TestCase;
42 
43 /**
44  * @author jmourits@google.com (Jerome Mourits)
45  * @author jessewilson@google.com (Jesse Wilson)
46  */
47 @SuppressWarnings("deprecation")
48 public class FactoryProviderTest extends TestCase {
49 
50   private enum Color {
51     BLUE,
52     GREEN,
53     RED,
54     GRAY,
55     BLACK,
56     ORANGE,
57     PINK
58   }
59 
testAssistedFactory()60   public void testAssistedFactory() {
61     Injector injector =
62         Guice.createInjector(
63             new AbstractModule() {
64               @Override
65               protected void configure() {
66                 bind(Double.class).toInstance(5.0d);
67                 bind(ColoredCarFactory.class)
68                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
69               }
70             });
71     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
72 
73     Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
74     assertEquals(Color.BLUE, blueMustang.color);
75     assertEquals(5.0d, blueMustang.engineSize, 0.0);
76 
77     Mustang redMustang = (Mustang) carFactory.create(Color.RED);
78     assertEquals(Color.RED, redMustang.color);
79     assertEquals(5.0d, redMustang.engineSize, 0.0);
80   }
81 
testFactoryBindingDependencies()82   public void testFactoryBindingDependencies() {
83     Injector injector =
84         Guice.createInjector(
85             new AbstractModule() {
86               @Override
87               protected void configure() {
88                 bind(Double.class).toInstance(5.0d);
89                 bind(ColoredCarFactory.class)
90                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
91               }
92             });
93 
94     Binding<?> binding = injector.getBinding(ColoredCarFactory.class);
95     HasDependencies hasDependencies = (HasDependencies) binding;
96     assertEquals(
97         ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(double.class))),
98         hasDependencies.getDependencies());
99   }
100 
testAssistedFactoryWithAnnotations()101   public void testAssistedFactoryWithAnnotations() {
102     Injector injector =
103         Guice.createInjector(
104             new AbstractModule() {
105               @Override
106               protected void configure() {
107                 bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
108                 bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
109                 bind(ColoredCarFactory.class)
110                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Camaro.class));
111               }
112             });
113 
114     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
115 
116     Camaro blueCamaro = (Camaro) carFactory.create(Color.BLUE);
117     assertEquals(Color.BLUE, blueCamaro.color);
118     assertEquals(1984, blueCamaro.modelYear);
119     assertEquals(250, blueCamaro.horsePower);
120 
121     Camaro redCamaro = (Camaro) carFactory.create(Color.RED);
122     assertEquals(Color.RED, redCamaro.color);
123     assertEquals(1984, redCamaro.modelYear);
124     assertEquals(250, redCamaro.horsePower);
125   }
126 
127   interface Car {}
128 
129   interface ColoredCarFactory {
create(Color color)130     Car create(Color color);
131   }
132 
133   public static class Mustang implements Car {
134     private final double engineSize;
135     private final Color color;
136 
137     @AssistedInject
Mustang(double engineSize, @Assisted Color color)138     public Mustang(double engineSize, @Assisted Color color) {
139       this.engineSize = engineSize;
140       this.color = color;
141     }
142   }
143 
144   public static class Camaro implements Car {
145     private final int horsePower;
146     private final int modelYear;
147     private final Color color;
148 
149     @AssistedInject
Camaro( @amed"horsePower") int horsePower, @Named("modelYear") int modelYear, @Assisted Color color)150     public Camaro(
151         @Named("horsePower") int horsePower,
152         @Named("modelYear") int modelYear,
153         @Assisted Color color) {
154       this.horsePower = horsePower;
155       this.modelYear = modelYear;
156       this.color = color;
157     }
158   }
159 
160   interface SummerCarFactory {
create(Color color, boolean convertable)161     Car create(Color color, boolean convertable);
162 
createConvertible(Color color)163     Car createConvertible(Color color);
164   }
165 
testFactoryWithMultipleMethods()166   public void testFactoryWithMultipleMethods() {
167     Injector injector =
168         Guice.createInjector(
169             new AbstractModule() {
170               @Override
171               protected void configure() {
172                 bind(float.class).toInstance(140f);
173                 bind(SummerCarFactory.class)
174                     .toProvider(FactoryProvider.newFactory(SummerCarFactory.class, Corvette.class));
175               }
176             });
177 
178     SummerCarFactory carFactory = injector.getInstance(SummerCarFactory.class);
179 
180     Corvette blueCorvette = (Corvette) carFactory.createConvertible(Color.BLUE);
181     assertEquals(Color.BLUE, blueCorvette.color);
182     assertEquals(100f, blueCorvette.maxMph, 0.0f);
183     assertTrue(blueCorvette.isConvertable);
184 
185     Corvette redCorvette = (Corvette) carFactory.create(Color.RED, false);
186     assertEquals(Color.RED, redCorvette.color);
187     assertEquals(140f, redCorvette.maxMph, 0.0f);
188     assertFalse(redCorvette.isConvertable);
189   }
190 
191   public static class Corvette implements Car {
192     private boolean isConvertable;
193     private Color color;
194     private float maxMph;
195 
196     @AssistedInject
Corvette(@ssisted Color color)197     public Corvette(@Assisted Color color) {
198       this(color, 100f, true);
199     }
200 
201     @SuppressWarnings("unused")
Corvette(@ssisted Color color, @Assisted boolean isConvertable)202     public Corvette(@Assisted Color color, @Assisted boolean isConvertable) {
203       throw new IllegalStateException("Not an @AssistedInject constructor");
204     }
205 
206     @AssistedInject
Corvette(@ssisted Color color, Float maxMph, @Assisted boolean isConvertable)207     public Corvette(@Assisted Color color, Float maxMph, @Assisted boolean isConvertable) {
208       this.isConvertable = isConvertable;
209       this.color = color;
210       this.maxMph = maxMph;
211     }
212   }
213 
testFactoryMethodsMismatch()214   public void testFactoryMethodsMismatch() {
215     try {
216       FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class);
217       fail();
218     } catch (ConfigurationException e) {
219       assertContains(e.getMessage(), "Constructor mismatch");
220     }
221   }
222 
223   public static class Beetle implements Car {
224     @AssistedInject
225     @SuppressWarnings("unused")
Beetle(@ssisted Color color)226     public Beetle(@Assisted Color color) {
227       throw new IllegalStateException("Conflicting constructors");
228     }
229 
230     @AssistedInject
231     @SuppressWarnings("unused")
Beetle(@ssisted Color color, @Assisted boolean isConvertable)232     public Beetle(@Assisted Color color, @Assisted boolean isConvertable) {
233       throw new IllegalStateException("Conflicting constructors");
234     }
235 
236     @AssistedInject
237     @SuppressWarnings("unused")
Beetle(@ssisted Color color, @Assisted boolean isConvertable, float maxMph)238     public Beetle(@Assisted Color color, @Assisted boolean isConvertable, float maxMph) {
239       throw new IllegalStateException("Conflicting constructors");
240     }
241   }
242 
testMethodsAndFieldsGetInjected()243   public void testMethodsAndFieldsGetInjected() {
244     Injector injector =
245         Guice.createInjector(
246             new AbstractModule() {
247               @Override
248               protected void configure() {
249                 bind(String.class).toInstance("turbo");
250                 bind(int.class).toInstance(911);
251                 bind(double.class).toInstance(50000d);
252                 bind(ColoredCarFactory.class)
253                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Porshe.class));
254               }
255             });
256     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
257 
258     Porshe grayPorshe = (Porshe) carFactory.create(Color.GRAY);
259     assertEquals(Color.GRAY, grayPorshe.color);
260     assertEquals(50000d, grayPorshe.price, 0.0);
261     assertEquals(911, grayPorshe.model);
262     assertEquals("turbo", grayPorshe.name);
263   }
264 
265   public static class Porshe implements Car {
266     private final Color color;
267     private final double price;
268     private @Inject String name;
269     private int model;
270 
271     @AssistedInject
Porshe(@ssisted Color color, double price)272     public Porshe(@Assisted Color color, double price) {
273       this.color = color;
274       this.price = price;
275     }
276 
277     @Inject
setModel(int model)278     void setModel(int model) {
279       this.model = model;
280     }
281   }
282 
testProviderInjection()283   public void testProviderInjection() {
284     Injector injector =
285         Guice.createInjector(
286             new AbstractModule() {
287               @Override
288               protected void configure() {
289                 bind(String.class).toInstance("trans am");
290                 bind(ColoredCarFactory.class)
291                     .toProvider(
292                         FactoryProvider.newFactory(ColoredCarFactory.class, Firebird.class));
293               }
294             });
295     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
296 
297     Firebird blackFirebird = (Firebird) carFactory.create(Color.BLACK);
298     assertEquals(Color.BLACK, blackFirebird.color);
299     assertEquals("trans am", blackFirebird.modifiersProvider.get());
300   }
301 
302   public static class Firebird implements Car {
303     private final Provider<String> modifiersProvider;
304     private final Color color;
305 
306     @AssistedInject
Firebird(Provider<String> modifiersProvider, @Assisted Color color)307     public Firebird(Provider<String> modifiersProvider, @Assisted Color color) {
308       this.modifiersProvider = modifiersProvider;
309       this.color = color;
310     }
311   }
312 
testTypeTokenInjection()313   public void testTypeTokenInjection() {
314     Injector injector =
315         Guice.createInjector(
316             new AbstractModule() {
317               @Override
318               protected void configure() {
319                 bind(new TypeLiteral<Set<String>>() {})
320                     .toInstance(Collections.singleton("Flux Capacitor"));
321                 bind(new TypeLiteral<Set<Integer>>() {}).toInstance(Collections.singleton(88));
322                 bind(ColoredCarFactory.class)
323                     .toProvider(
324                         FactoryProvider.newFactory(ColoredCarFactory.class, DeLorean.class));
325               }
326             });
327     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
328 
329     DeLorean deLorean = (DeLorean) carFactory.create(Color.GRAY);
330     assertEquals(Color.GRAY, deLorean.color);
331     assertEquals("Flux Capacitor", deLorean.features.iterator().next());
332     assertEquals(Integer.valueOf(88), deLorean.featureActivationSpeeds.iterator().next());
333   }
334 
335   public static class DeLorean implements Car {
336     private final Set<String> features;
337     private final Set<Integer> featureActivationSpeeds;
338     private final Color color;
339 
340     @AssistedInject
DeLorean( Set<String> extraFeatures, Set<Integer> featureActivationSpeeds, @Assisted Color color)341     public DeLorean(
342         Set<String> extraFeatures, Set<Integer> featureActivationSpeeds, @Assisted Color color) {
343       this.features = extraFeatures;
344       this.featureActivationSpeeds = featureActivationSpeeds;
345       this.color = color;
346     }
347   }
348 
testTypeTokenProviderInjection()349   public void testTypeTokenProviderInjection() {
350     Injector injector =
351         Guice.createInjector(
352             new AbstractModule() {
353               @Override
354               protected void configure() {
355                 bind(new TypeLiteral<Set<String>>() {}).toInstance(Collections.singleton("Datsun"));
356                 bind(ColoredCarFactory.class)
357                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Z.class));
358               }
359             });
360     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
361 
362     Z orangeZ = (Z) carFactory.create(Color.ORANGE);
363     assertEquals(Color.ORANGE, orangeZ.color);
364     assertEquals("Datsun", orangeZ.manufacturersProvider.get().iterator().next());
365   }
366 
367   public static class Z implements Car {
368     private final Provider<Set<String>> manufacturersProvider;
369     private final Color color;
370 
371     @AssistedInject
Z(Provider<Set<String>> manufacturersProvider, @Assisted Color color)372     public Z(Provider<Set<String>> manufacturersProvider, @Assisted Color color) {
373       this.manufacturersProvider = manufacturersProvider;
374       this.color = color;
375     }
376   }
377 
378   public static class Prius implements Car {
379     @SuppressWarnings("unused")
380     private final Color color;
381 
382     @AssistedInject
Prius(@ssisted Color color)383     private Prius(@Assisted Color color) {
384       this.color = color;
385     }
386   }
387 
testAssistInjectionInNonPublicConstructor()388   public void testAssistInjectionInNonPublicConstructor() {
389     Injector injector =
390         Guice.createInjector(
391             new AbstractModule() {
392               @Override
393               protected void configure() {
394                 bind(ColoredCarFactory.class)
395                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class));
396               }
397             });
398     injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
399   }
400 
401   public static class ExplodingCar implements Car {
402     @AssistedInject
ExplodingCar(@uppressWarnings"unused") @ssisted Color color)403     public ExplodingCar(@SuppressWarnings("unused") @Assisted Color color) {
404       throw new IllegalStateException("kaboom!");
405     }
406   }
407 
testExceptionDuringConstruction()408   public void testExceptionDuringConstruction() {
409     Injector injector =
410         Guice.createInjector(
411             new AbstractModule() {
412               @Override
413               protected void configure() {
414                 bind(ColoredCarFactory.class)
415                     .toProvider(
416                         FactoryProvider.newFactory(ColoredCarFactory.class, ExplodingCar.class));
417               }
418             });
419     try {
420       injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
421       fail();
422     } catch (IllegalStateException e) {
423       assertEquals("kaboom!", e.getMessage());
424     }
425   }
426 
427   public static class DefectiveCar implements Car {
428     @AssistedInject
DefectiveCar()429     public DefectiveCar() throws ExplosionException {
430       throw new ExplosionException();
431     }
432   }
433 
434   public static class ExplosionException extends Exception {}
435 
436   public static class FireException extends Exception {}
437 
438   public interface DefectiveCarFactoryWithNoExceptions {
createCar()439     Car createCar();
440   }
441 
442   public interface DefectiveCarFactory {
createCar()443     Car createCar() throws FireException;
444   }
445 
testFactoryMethodMustDeclareAllConstructorExceptions()446   public void testFactoryMethodMustDeclareAllConstructorExceptions() {
447     try {
448       FactoryProvider.newFactory(DefectiveCarFactoryWithNoExceptions.class, DefectiveCar.class);
449       fail();
450     } catch (ConfigurationException expected) {
451       assertContains(expected.getMessage(), "no compatible exception is thrown");
452     }
453   }
454 
455   public interface CorrectDefectiveCarFactory {
createCar()456     Car createCar() throws FireException, ExplosionException;
457   }
458 
testConstructorExceptionsAreThrownByFactory()459   public void testConstructorExceptionsAreThrownByFactory() {
460     Injector injector =
461         Guice.createInjector(
462             new AbstractModule() {
463               @Override
464               protected void configure() {
465                 bind(CorrectDefectiveCarFactory.class)
466                     .toProvider(
467                         FactoryProvider.newFactory(
468                             CorrectDefectiveCarFactory.class, DefectiveCar.class));
469               }
470             });
471     try {
472       injector.getInstance(CorrectDefectiveCarFactory.class).createCar();
473       fail();
474     } catch (FireException e) {
475       fail();
476     } catch (ExplosionException expected) {
477     }
478   }
479 
480   public static class MultipleConstructorDefectiveCar implements Car {
481     @AssistedInject
MultipleConstructorDefectiveCar()482     public MultipleConstructorDefectiveCar() throws ExplosionException {
483       throw new ExplosionException();
484     }
485 
486     @AssistedInject
MultipleConstructorDefectiveCar(@uppressWarnings"unused") @ssisted Color c)487     public MultipleConstructorDefectiveCar(@SuppressWarnings("unused") @Assisted Color c)
488         throws FireException {
489       throw new FireException();
490     }
491   }
492 
493   public interface MultipleConstructorDefectiveCarFactory {
createCar()494     Car createCar() throws ExplosionException;
495 
createCar(Color r)496     Car createCar(Color r) throws FireException;
497   }
498 
testMultipleConstructorExceptionMatching()499   public void testMultipleConstructorExceptionMatching() {
500     Injector injector =
501         Guice.createInjector(
502             new AbstractModule() {
503               @Override
504               protected void configure() {
505                 bind(MultipleConstructorDefectiveCarFactory.class)
506                     .toProvider(
507                         FactoryProvider.newFactory(
508                             MultipleConstructorDefectiveCarFactory.class,
509                             MultipleConstructorDefectiveCar.class));
510               }
511             });
512     MultipleConstructorDefectiveCarFactory factory =
513         injector.getInstance(MultipleConstructorDefectiveCarFactory.class);
514     try {
515       factory.createCar();
516       fail();
517     } catch (ExplosionException expected) {
518     }
519 
520     try {
521       factory.createCar(Color.RED);
522       fail();
523     } catch (FireException expected) {
524     }
525   }
526 
527   public static class WildcardCollection {
528 
529     public interface Factory {
create(Collection<?> items)530       WildcardCollection create(Collection<?> items);
531     }
532 
533     @AssistedInject
WildcardCollection(@uppressWarnings"unused") @ssisted Collection<?> items)534     public WildcardCollection(@SuppressWarnings("unused") @Assisted Collection<?> items) {}
535   }
536 
testWildcardGenerics()537   public void testWildcardGenerics() {
538     Injector injector =
539         Guice.createInjector(
540             new AbstractModule() {
541               @Override
542               protected void configure() {
543                 bind(WildcardCollection.Factory.class)
544                     .toProvider(
545                         FactoryProvider.newFactory(
546                             WildcardCollection.Factory.class, WildcardCollection.class));
547               }
548             });
549     WildcardCollection.Factory factory = injector.getInstance(WildcardCollection.Factory.class);
550     factory.create(Collections.emptyList());
551   }
552 
553   public static class SteeringWheel {}
554 
555   public static class Fiat implements Car {
556     @SuppressWarnings("unused")
557     private final SteeringWheel steeringWheel;
558 
559     @SuppressWarnings("unused")
560     private final Color color;
561 
562     @AssistedInject
Fiat(SteeringWheel steeringWheel, @Assisted Color color)563     public Fiat(SteeringWheel steeringWheel, @Assisted Color color) {
564       this.steeringWheel = steeringWheel;
565       this.color = color;
566     }
567   }
568 
testFactoryWithImplicitBindings()569   public void testFactoryWithImplicitBindings() {
570     Injector injector =
571         Guice.createInjector(
572             new AbstractModule() {
573               @Override
574               protected void configure() {
575                 bind(ColoredCarFactory.class)
576                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Fiat.class));
577               }
578             });
579 
580     ColoredCarFactory coloredCarFactory = injector.getInstance(ColoredCarFactory.class);
581     Fiat fiat = (Fiat) coloredCarFactory.create(Color.GREEN);
582     assertEquals(Color.GREEN, fiat.color);
583     assertNotNull(fiat.steeringWheel);
584   }
585 
testFactoryFailsWithMissingBinding()586   public void testFactoryFailsWithMissingBinding() {
587     try {
588       Guice.createInjector(
589           new AbstractModule() {
590             @Override
591             protected void configure() {
592               bind(ColoredCarFactory.class)
593                   .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
594             }
595           });
596       fail();
597     } catch (CreationException expected) {
598       assertContains(
599           expected.getMessage(),
600           "1) Parameter of type 'double' is not injectable or annotated with @Assisted");
601     }
602   }
603 
604   @SuppressWarnings("SelfEquals")
testMethodsDeclaredInObject()605   public void testMethodsDeclaredInObject() {
606     Injector injector =
607         Guice.createInjector(
608             new AbstractModule() {
609               @Override
610               protected void configure() {
611                 bind(Double.class).toInstance(5.0d);
612                 bind(ColoredCarFactory.class)
613                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
614               }
615             });
616 
617     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
618 
619     carFactory.equals(carFactory);
620     carFactory.hashCode();
621     carFactory.toString();
622   }
623 
testAssistedInjectConstructorAndAssistedFactoryParameterMustNotMix()624   public void testAssistedInjectConstructorAndAssistedFactoryParameterMustNotMix() {
625     try {
626       Guice.createInjector(
627           new AbstractModule() {
628             @Override
629             protected void configure() {
630               bind(Double.class).toInstance(5.0d);
631               bind(AssistedParamsFactory.class)
632                   .toProvider(
633                       FactoryProvider.newFactory(AssistedParamsFactory.class, Mustang.class));
634             }
635           });
636       fail();
637     } catch (CreationException expected) {
638       assertContains(
639           expected.getMessage(),
640           "Factory method "
641               + AssistedParamsFactory.class.getName()
642               + ".create() has an @Assisted parameter, which "
643               + "is incompatible with the deprecated @AssistedInject annotation.");
644     }
645   }
646 
647   interface AssistedParamsFactory {
create(@ssisted Color color)648     Car create(@Assisted Color color);
649   }
650 
651   interface GenericColoredCarFactory<T extends Car> {
create(Color color)652     T create(Color color);
653   }
654 
testGenericAssistedFactory()655   public void testGenericAssistedFactory() {
656     final TypeLiteral<GenericColoredCarFactory<Mustang>> mustangTypeLiteral =
657         new TypeLiteral<GenericColoredCarFactory<Mustang>>() {};
658     final TypeLiteral<GenericColoredCarFactory<Camaro>> camaroTypeLiteral =
659         new TypeLiteral<GenericColoredCarFactory<Camaro>>() {};
660 
661     Injector injector =
662         Guice.createInjector(
663             new AbstractModule() {
664               @Override
665               protected void configure() {
666                 bind(Double.class).toInstance(5.0d);
667                 bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
668                 bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
669                 bind(mustangTypeLiteral)
670                     .toProvider(
671                         FactoryProvider.newFactory(
672                             mustangTypeLiteral, TypeLiteral.get(Mustang.class)));
673                 bind(camaroTypeLiteral)
674                     .toProvider(
675                         FactoryProvider.newFactory(
676                             camaroTypeLiteral, TypeLiteral.get(Camaro.class)));
677               }
678             });
679 
680     GenericColoredCarFactory<Mustang> mustangFactory =
681         injector.getInstance(Key.get(mustangTypeLiteral));
682     GenericColoredCarFactory<Camaro> camaroFactory =
683         injector.getInstance(Key.get(camaroTypeLiteral));
684 
685     Mustang blueMustang = mustangFactory.create(Color.BLUE);
686     assertEquals(Color.BLUE, blueMustang.color);
687     assertEquals(5.0d, blueMustang.engineSize, 0.0);
688 
689     Camaro redCamaro = camaroFactory.create(Color.RED);
690     assertEquals(Color.RED, redCamaro.color);
691     assertEquals(1984, redCamaro.modelYear);
692     assertEquals(250, redCamaro.horsePower);
693   }
694 
695   @SuppressWarnings("unused")
696   public interface Insurance<T extends Car> {}
697 
698   public static class MustangInsurance implements Insurance<Mustang> {
699     private final double premium;
700     private final double limit;
701 
702     @SuppressWarnings("unused")
703     private Mustang car;
704 
705     @AssistedInject
MustangInsurance( @amed"lowLimit") double limit, @Assisted Mustang car, @Assisted double premium)706     public MustangInsurance(
707         @Named("lowLimit") double limit, @Assisted Mustang car, @Assisted double premium) {
708       this.premium = premium;
709       this.limit = limit;
710       this.car = car;
711     }
712 
sell()713     public void sell() {}
714   }
715 
716   public static class CamaroInsurance implements Insurance<Camaro> {
717     private final double premium;
718     private final double limit;
719 
720     @SuppressWarnings("unused")
721     private Camaro car;
722 
723     @AssistedInject
CamaroInsurance( @amed"highLimit") double limit, @Assisted Camaro car, @Assisted double premium)724     public CamaroInsurance(
725         @Named("highLimit") double limit, @Assisted Camaro car, @Assisted double premium) {
726       this.premium = premium;
727       this.limit = limit;
728       this.car = car;
729     }
730 
sell()731     public void sell() {}
732   }
733 
734   public interface MustangInsuranceFactory {
create(Mustang car, double premium)735     public Insurance<Mustang> create(Mustang car, double premium);
736   }
737 
738   public interface CamaroInsuranceFactory {
create(Camaro car, double premium)739     public Insurance<Camaro> create(Camaro car, double premium);
740   }
741 
testAssistedFactoryForConcreteType()742   public void testAssistedFactoryForConcreteType() {
743 
744     Injector injector =
745         Guice.createInjector(
746             new AbstractModule() {
747               @Override
748               protected void configure() {
749                 bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
750                 bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
751                 bind(MustangInsuranceFactory.class)
752                     .toProvider(
753                         FactoryProvider.newFactory(
754                             MustangInsuranceFactory.class, MustangInsurance.class));
755                 bind(CamaroInsuranceFactory.class)
756                     .toProvider(
757                         FactoryProvider.newFactory(
758                             CamaroInsuranceFactory.class, CamaroInsurance.class));
759               }
760             });
761 
762     MustangInsuranceFactory mustangInsuranceFactory =
763         injector.getInstance(MustangInsuranceFactory.class);
764     CamaroInsuranceFactory camaroInsuranceFactory =
765         injector.getInstance(CamaroInsuranceFactory.class);
766 
767     Mustang mustang = new Mustang(5000d, Color.BLACK);
768     MustangInsurance mustangPolicy =
769         (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
770     assertEquals(800.0d, mustangPolicy.premium, 0.0);
771     assertEquals(50000.0d, mustangPolicy.limit, 0.0);
772 
773     Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
774     CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
775     assertEquals(800.0d, camaroPolicy.premium, 0.0);
776     assertEquals(100000.0d, camaroPolicy.limit, 0.0);
777   }
778 
779   public interface InsuranceFactory<T extends Car> {
create(T car, double premium)780     public Insurance<T> create(T car, double premium);
781   }
782 
testAssistedFactoryForParameterizedType()783   public void testAssistedFactoryForParameterizedType() {
784     final TypeLiteral<InsuranceFactory<Mustang>> mustangInsuranceFactoryType =
785         new TypeLiteral<InsuranceFactory<Mustang>>() {};
786     final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
787         new TypeLiteral<InsuranceFactory<Camaro>>() {};
788 
789     Injector injector =
790         Guice.createInjector(
791             new AbstractModule() {
792               @Override
793               protected void configure() {
794                 bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
795                 bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
796                 bind(mustangInsuranceFactoryType)
797                     .toProvider(
798                         FactoryProvider.newFactory(
799                             mustangInsuranceFactoryType, TypeLiteral.get(MustangInsurance.class)));
800                 bind(camaroInsuranceFactoryType)
801                     .toProvider(
802                         FactoryProvider.newFactory(
803                             camaroInsuranceFactoryType, TypeLiteral.get(CamaroInsurance.class)));
804               }
805             });
806 
807     InsuranceFactory<Mustang> mustangInsuranceFactory =
808         injector.getInstance(Key.get(mustangInsuranceFactoryType));
809     InsuranceFactory<Camaro> camaroInsuranceFactory =
810         injector.getInstance(Key.get(camaroInsuranceFactoryType));
811 
812     Mustang mustang = new Mustang(5000d, Color.BLACK);
813     MustangInsurance mustangPolicy =
814         (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
815     assertEquals(800.0d, mustangPolicy.premium, 0.0);
816     assertEquals(50000.0d, mustangPolicy.limit, 0.0);
817 
818     Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
819     CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
820     assertEquals(800.0d, camaroPolicy.premium, 0.0);
821     assertEquals(100000.0d, camaroPolicy.limit, 0.0);
822   }
823 
824   public static class AutoInsurance<T extends Car> implements Insurance<T> {
825     private final double premium;
826     private final double limit;
827     private final T car;
828 
829     @AssistedInject
AutoInsurance(double limit, @Assisted T car, @Assisted double premium)830     public AutoInsurance(double limit, @Assisted T car, @Assisted double premium) {
831       this.limit = limit;
832       this.car = car;
833       this.premium = premium;
834     }
835 
sell()836     public void sell() {}
837   }
838 
testAssistedFactoryForTypeVariableParameters()839   public void testAssistedFactoryForTypeVariableParameters() {
840     final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
841         new TypeLiteral<InsuranceFactory<Camaro>>() {};
842 
843     Injector injector =
844         Guice.createInjector(
845             new AbstractModule() {
846               @Override
847               protected void configure() {
848                 bind(Double.class).toInstance(50000.0d);
849                 bind(camaroInsuranceFactoryType)
850                     .toProvider(
851                         FactoryProvider.newFactory(
852                             camaroInsuranceFactoryType,
853                             new TypeLiteral<AutoInsurance<Camaro>>() {}));
854               }
855             });
856 
857     InsuranceFactory<Camaro> camaroInsuranceFactory =
858         injector.getInstance(Key.get(camaroInsuranceFactoryType));
859 
860     Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
861     AutoInsurance<?> camaroPolicy =
862         (AutoInsurance<?>) camaroInsuranceFactory.create(camaro, 800.0d);
863     assertEquals(800.0d, camaroPolicy.premium, 0.0);
864     assertEquals(50000.0d, camaroPolicy.limit, 0.0);
865     assertEquals(camaro, camaroPolicy.car);
866   }
867 
testDuplicateAssistedFactoryBinding()868   public void testDuplicateAssistedFactoryBinding() {
869     Injector injector =
870         Guice.createInjector(
871             new AbstractModule() {
872               @Override
873               protected void configure() {
874                 bind(Double.class).toInstance(5.0d);
875                 bind(ColoredCarFactory.class)
876                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
877                 bind(ColoredCarFactory.class)
878                     .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
879               }
880             });
881     ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
882 
883     Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
884     assertEquals(Color.BLUE, blueMustang.color);
885     assertEquals(5.0d, blueMustang.engineSize, 0.0);
886 
887     Mustang redMustang = (Mustang) carFactory.create(Color.RED);
888     assertEquals(Color.RED, redMustang.color);
889     assertEquals(5.0d, redMustang.engineSize, 0.0);
890   }
891 
892   public interface Equals {
893 
894     enum ComparisonMethod {
895       SHALLOW,
896       DEEP;
897     }
898 
899     interface Factory {
equals(Equals.ComparisonMethod comparisonMethod)900       Equals equals(Equals.ComparisonMethod comparisonMethod);
901     }
902 
903     public static class Impl implements Equals {
904       private final double sigma;
905       private final ComparisonMethod comparisonMethod;
906 
907       @Inject
Impl(double sigma, @Assisted ComparisonMethod comparisonMethod)908       public Impl(double sigma, @Assisted ComparisonMethod comparisonMethod) {
909         this.sigma = sigma;
910         this.comparisonMethod = comparisonMethod;
911       }
912     }
913   }
914 
testFactoryMethodCalledEquals()915   public void testFactoryMethodCalledEquals() {
916     Injector injector =
917         Guice.createInjector(
918             new AbstractModule() {
919               @Override
920               protected void configure() {
921                 bind(Double.class).toInstance(0.01d);
922                 bind(Equals.Factory.class)
923                     .toProvider(
924                         FactoryProvider.newFactory(Equals.Factory.class, Equals.Impl.class));
925               }
926             });
927     Equals.Factory equalsFactory = injector.getInstance(Equals.Factory.class);
928     Equals.Impl shallowEquals = (Impl) equalsFactory.equals(ComparisonMethod.SHALLOW);
929     assertEquals(ComparisonMethod.SHALLOW, shallowEquals.comparisonMethod);
930     assertEquals(0.01d, shallowEquals.sigma, 0.0);
931   }
932 }
933