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