/** * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.inject.assistedinject; import com.google.inject.AbstractModule; import static com.google.inject.Asserts.assertContains; import static com.google.inject.Asserts.assertEqualsBothWays; import com.google.inject.ConfigurationException; import com.google.inject.CreationException; 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.Stage; import com.google.inject.TypeLiteral; import com.google.inject.matcher.Matchers; import com.google.inject.name.Named; import com.google.inject.name.Names; import junit.framework.TestCase; import java.awt.*; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings("deprecation") public class FactoryProvider2Test extends TestCase { public void testAssistedFactory() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Double.class).toInstance(5.0d); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE); assertEquals(Color.BLUE, blueMustang.color); assertEquals(5.0d, blueMustang.engineSize); Mustang redMustang = (Mustang) carFactory.create(Color.RED); assertEquals(Color.RED, redMustang.color); assertEquals(5.0d, redMustang.engineSize); } public void testAssistedFactoryWithAnnotations() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250); bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Camaro.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Camaro blueCamaro = (Camaro) carFactory.create(Color.BLUE); assertEquals(Color.BLUE, blueCamaro.color); assertEquals(1984, blueCamaro.modelYear); assertEquals(250, blueCamaro.horsePower); Camaro redCamaro = (Camaro) carFactory.create(Color.RED); assertEquals(Color.RED, redCamaro.color); assertEquals(1984, redCamaro.modelYear); assertEquals(250, redCamaro.horsePower); } public interface Car {} interface ColoredCarFactory { Car create(Color color); } public static class Mustang implements Car { private final double engineSize; private final Color color; @Inject public Mustang(double engineSize, @Assisted Color color) { this.engineSize = engineSize; this.color = color; } public void drive() {} } public static class Camaro implements Car { private final int horsePower; private final int modelYear; private final Color color; @Inject public Camaro( @Named("horsePower") int horsePower, @Named("modelYear") int modelYear, @Assisted Color color) { this.horsePower = horsePower; this.modelYear = modelYear; this.color = color; } } interface SummerCarFactory { Car create(Color color, boolean convertable); } public void testFactoryUsesInjectedConstructor() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(float.class).toInstance(140f); bind(SummerCarFactory.class).toProvider( FactoryProvider.newFactory(SummerCarFactory.class, Corvette.class)); } }); SummerCarFactory carFactory = injector.getInstance(SummerCarFactory.class); Corvette redCorvette = (Corvette) carFactory.create(Color.RED, false); assertEquals(Color.RED, redCorvette.color); assertEquals(140f, redCorvette.maxMph); assertFalse(redCorvette.isConvertable); } public static class Corvette implements Car { private boolean isConvertable; private Color color; private float maxMph; public Corvette(Color color, boolean isConvertable) { throw new IllegalStateException("Not an @AssistedInject constructor"); } @Inject public Corvette(@Assisted Color color, Float maxMph, @Assisted boolean isConvertable) { this.isConvertable = isConvertable; this.color = color; this.maxMph = maxMph; } } public void testConstructorDoesntNeedAllFactoryMethodArguments() { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(SummerCarFactory.class).toProvider( FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class)); } }); SummerCarFactory factory = injector.getInstance(SummerCarFactory.class); Beetle beetle = (Beetle) factory.create(Color.RED, true); assertSame(Color.RED, beetle.color); } public static class Beetle implements Car { private final Color color; @Inject public Beetle(@Assisted Color color) { this.color = color; } } public void testMethodsAndFieldsGetInjected() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("turbo"); bind(int.class).toInstance(911); bind(double.class).toInstance(50000d); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Porsche.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Porsche grayPorsche = (Porsche) carFactory.create(Color.GRAY); assertEquals(Color.GRAY, grayPorsche.color); assertEquals(50000d, grayPorsche.price); assertEquals(911, grayPorsche.model); assertEquals("turbo", grayPorsche.name); } public static class Porsche implements Car { private final Color color; private final double price; private @Inject String name; private int model; @Inject public Porsche(@Assisted Color color, double price) { this.color = color; this.price = price; } @Inject void setModel(int model) { this.model = model; } } public void testProviderInjection() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("trans am"); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Firebird.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Firebird blackFirebird = (Firebird) carFactory.create(Color.BLACK); assertEquals(Color.BLACK, blackFirebird.color); assertEquals("trans am", blackFirebird.modifiersProvider.get()); } public static class Firebird implements Car { private final Provider<String> modifiersProvider; private final Color color; @Inject public Firebird(Provider<String> modifiersProvider, @Assisted Color color) { this.modifiersProvider = modifiersProvider; this.color = color; } } public void testAssistedProviderInjection() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("trans am"); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Flamingbird.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Flamingbird flamingbird = (Flamingbird) carFactory.create(Color.BLACK); assertEquals(Color.BLACK, flamingbird.colorProvider.get()); assertEquals("trans am", flamingbird.modifiersProvider.get()); Flamingbird flamingbird2 = (Flamingbird) carFactory.create(Color.RED); assertEquals(Color.RED, flamingbird2.colorProvider.get()); assertEquals("trans am", flamingbird2.modifiersProvider.get()); // Make sure the original flamingbird is black still. assertEquals(Color.BLACK, flamingbird.colorProvider.get()); } public static class Flamingbird implements Car { private final Provider<String> modifiersProvider; private final Provider<Color> colorProvider; @Inject public Flamingbird(Provider<String> modifiersProvider, @Assisted Provider<Color> colorProvider) { this.modifiersProvider = modifiersProvider; this.colorProvider = colorProvider; } } public void testTypeTokenInjection() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(new TypeLiteral<Set<String>>() {}).toInstance(Collections.singleton("Flux Capacitor")); bind(new TypeLiteral<Set<Integer>>() {}).toInstance(Collections.singleton(88)); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, DeLorean.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); DeLorean deLorean = (DeLorean) carFactory.create(Color.GRAY); assertEquals(Color.GRAY, deLorean.color); assertEquals("Flux Capacitor", deLorean.features.iterator().next()); assertEquals(new Integer(88), deLorean.featureActivationSpeeds.iterator().next()); } public static class DeLorean implements Car { private final Set<String> features; private final Set<Integer> featureActivationSpeeds; private final Color color; @Inject public DeLorean( Set<String> extraFeatures, Set<Integer> featureActivationSpeeds, @Assisted Color color) { this.features = extraFeatures; this.featureActivationSpeeds = featureActivationSpeeds; this.color = color; } } public void testTypeTokenProviderInjection() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(new TypeLiteral<Set<String>>() { }).toInstance(Collections.singleton("Datsun")); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Z.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Z orangeZ = (Z) carFactory.create(Color.ORANGE); assertEquals(Color.ORANGE, orangeZ.color); assertEquals("Datsun", orangeZ.manufacturersProvider.get().iterator().next()); } public static class Z implements Car { private final Provider<Set<String>> manufacturersProvider; private final Color color; @Inject public Z(Provider<Set<String>> manufacturersProvider, @Assisted Color color) { this.manufacturersProvider = manufacturersProvider; this.color = color; } } public static class Prius implements Car { final Color color; @Inject private Prius(@Assisted Color color) { this.color = color; } } public void testAssistInjectionInNonPublicConstructor() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class)); } }); Prius prius = (Prius) injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE); assertEquals(prius.color, Color.ORANGE); } public static class ExplodingCar implements Car { @Inject public ExplodingCar(@Assisted Color color) { throw new IllegalStateException("kaboom!"); } } public void testExceptionDuringConstruction() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, ExplodingCar.class)); } }); try { injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE); fail(); } catch (IllegalStateException e) { assertEquals("kaboom!", e.getMessage()); } } public static class DefectiveCar implements Car { @Inject public DefectiveCar() throws ExplosionException, FireException { throw new ExplosionException(); } } public static class ExplosionException extends Exception { } public static class FireException extends Exception { } public interface DefectiveCarFactoryWithNoExceptions { Car createCar(); } public interface DefectiveCarFactory { Car createCar() throws FireException; } public interface CorrectDefectiveCarFactory { Car createCar() throws FireException, ExplosionException; } public void testConstructorExceptionsAreThrownByFactory() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(CorrectDefectiveCarFactory.class).toProvider( FactoryProvider.newFactory(CorrectDefectiveCarFactory.class, DefectiveCar.class)); } }); try { injector.getInstance(CorrectDefectiveCarFactory.class).createCar(); fail(); } catch (FireException e) { fail(); } catch (ExplosionException expected) { } } public static class WildcardCollection { public interface Factory { WildcardCollection create(Collection<?> items); } @Inject public WildcardCollection(@Assisted Collection<?> items) { } } public void testWildcardGenerics() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(WildcardCollection.Factory.class).toProvider( FactoryProvider.newFactory(WildcardCollection.Factory.class, WildcardCollection.class)); } }); WildcardCollection.Factory factory = injector.getInstance(WildcardCollection.Factory.class); factory.create(Collections.emptyList()); } public static class SteeringWheel {} public static class Fiat implements Car { private final SteeringWheel steeringWheel; private final Color color; @Inject public Fiat(SteeringWheel steeringWheel, @Assisted Color color) { this.steeringWheel = steeringWheel; this.color = color; } } public void testFactoryWithImplicitBindings() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Fiat.class)); } }); ColoredCarFactory coloredCarFactory = injector.getInstance(ColoredCarFactory.class); Fiat fiat = (Fiat) coloredCarFactory.create(Color.GREEN); assertEquals(Color.GREEN, fiat.color); assertNotNull(fiat.steeringWheel); } public void testFactoryFailsWithMissingBinding() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "Could not find a suitable constructor in java.lang.Double.", "at " + ColoredCarFactory.class.getName() + ".create(FactoryProvider2Test.java"); } } public void testFactoryFailsWithMissingBindingInToolStage() { try { Guice.createInjector(Stage.TOOL, new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "Could not find a suitable constructor in java.lang.Double.", "at " + ColoredCarFactory.class.getName() + ".create(FactoryProvider2Test.java"); } } public void testMethodsDeclaredInObject() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Double.class).toInstance(5.0d); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); assertEqualsBothWays(carFactory, carFactory); } static class Subaru implements Car { @Inject @Assisted Provider<Color> colorProvider; } public void testInjectingProviderOfParameter() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Subaru subaru = (Subaru) carFactory.create(Color.RED); assertSame(Color.RED, subaru.colorProvider.get()); assertSame(Color.RED, subaru.colorProvider.get()); Subaru sedan = (Subaru) carFactory.create(Color.BLUE); assertSame(Color.BLUE, sedan.colorProvider.get()); assertSame(Color.BLUE, sedan.colorProvider.get()); // and make sure the subaru is still red assertSame(Color.RED, subaru.colorProvider.get()); } public void testInjectingNullParameter() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Subaru subaru = (Subaru) carFactory.create(null); assertNull(subaru.colorProvider.get()); assertNull(subaru.colorProvider.get()); } interface ProviderBasedColoredCarFactory { Car createCar(Provider<Color> colorProvider, Provider<String> stringProvider); Mustang createMustang(@Assisted("color") Provider<Color> colorProvider); } public void testAssistedProviderIsDisallowed() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ProviderBasedColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ProviderBasedColoredCarFactory.class, Subaru.class)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "1) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [1] with key" + " [com.google.inject.Provider<java.awt.Color>] on method [" + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]", "2) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [2] with key" + " [com.google.inject.Provider<java.lang.String>] on method [" + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]", "3) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [1] with key" + " [com.google.inject.Provider<java.awt.Color>" + " annotated with @com.google.inject.assistedinject.Assisted(value=color)]" + " on method [" + ProviderBasedColoredCarFactory.class.getName() + ".createMustang()]" ); } } interface JavaxProviderBasedColoredCarFactory { Car createCar(javax.inject.Provider<Color> colorProvider, javax.inject.Provider<String> stringProvider); Mustang createMustang(@Assisted("color") javax.inject.Provider<Color> colorProvider); } public void testAssistedJavaxProviderIsDisallowed() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(JavaxProviderBasedColoredCarFactory.class).toProvider( FactoryProvider.newFactory(JavaxProviderBasedColoredCarFactory.class, Subaru.class)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "1) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [1] with key" + " [com.google.inject.Provider<java.awt.Color>] on method [" + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]", "2) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [2] with key" + " [com.google.inject.Provider<java.lang.String>] on method [" + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]", "3) A Provider may not be a type in a factory method of an AssistedInject." + "\n Offending instance is parameter [1] with key" + " [com.google.inject.Provider<java.awt.Color>" + " annotated with @com.google.inject.assistedinject.Assisted(value=color)]" + " on method [" + JavaxProviderBasedColoredCarFactory.class.getName() + ".createMustang()]" ); } } public void testFactoryUseBeforeInitialization() { ColoredCarFactory carFactory = FactoryProvider.newFactory(ColoredCarFactory.class, Subaru.class) .get(); try { carFactory.create(Color.RED); fail(); } catch (IllegalStateException expected) { assertContains(expected.getMessage(), "Factories.create() factories cannot be used until they're initialized by Guice."); } } interface MustangFactory { Mustang create(Color color); } public void testFactoryBuildingConcreteTypes() { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(double.class).toInstance(5.0d); // note there is no 'thatMakes()' call here: bind(MustangFactory.class).toProvider( FactoryProvider.newFactory(MustangFactory.class, Mustang.class)); } }); MustangFactory factory = injector.getInstance(MustangFactory.class); Mustang mustang = factory.create(Color.RED); assertSame(Color.RED, mustang.color); assertEquals(5.0d, mustang.engineSize); } static class Fleet { @Inject Mustang mustang; @Inject Camaro camaro; } interface FleetFactory { Fleet createFleet(Color color); } public void testInjectDeepIntoConstructedObjects() { 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(FleetFactory.class).toProvider(FactoryProvider.newFactory(FleetFactory.class, Fleet.class)); } }); FleetFactory fleetFactory = injector.getInstance(FleetFactory.class); Fleet fleet = fleetFactory.createFleet(Color.RED); assertSame(Color.RED, fleet.mustang.color); assertEquals(5.0d, fleet.mustang.engineSize); assertSame(Color.RED, fleet.camaro.color); assertEquals(250, fleet.camaro.horsePower); assertEquals(1984, fleet.camaro.modelYear); } interface TwoToneCarFactory { Car create(@Assisted("paint") Color paint, @Assisted("fabric") Color fabric); } static class Maxima implements Car { @Inject @Assisted("paint") Color paint; @Inject @Assisted("fabric") Color fabric; } public void testDistinctKeys() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(TwoToneCarFactory.class).toProvider( FactoryProvider.newFactory(TwoToneCarFactory.class, Maxima.class)); } }); TwoToneCarFactory factory = injector.getInstance(TwoToneCarFactory.class); Maxima maxima = (Maxima) factory.create(Color.BLACK, Color.GRAY); assertSame(Color.BLACK, maxima.paint); assertSame(Color.GRAY, maxima.fabric); } interface DoubleToneCarFactory { Car create(@Assisted("paint") Color paint, @Assisted("paint") Color morePaint); } public void testDuplicateKeys() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(DoubleToneCarFactory.class).toProvider( FactoryProvider.newFactory(DoubleToneCarFactory.class, Maxima.class)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "A binding to java.awt.Color annotated with @" + Assisted.class.getName() + "(value=paint) was already configured at"); } } /*if[AOP]*/ public void testMethodInterceptorsOnAssistedTypes() { final AtomicInteger invocationCount = new AtomicInteger(); final org.aopalliance.intercept.MethodInterceptor interceptor = new org.aopalliance.intercept.MethodInterceptor() { public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) throws Throwable { invocationCount.incrementAndGet(); return methodInvocation.proceed(); } }; Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bindInterceptor(Matchers.any(), Matchers.any(), interceptor); bind(Double.class).toInstance(5.0d); bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class)); } }); ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class); Mustang mustang = (Mustang) factory.create(Color.GREEN); assertEquals(0, invocationCount.get()); mustang.drive(); assertEquals(1, invocationCount.get()); } /*end[AOP]*/ /** * Our factories aren't reusable across injectors. Although this behaviour isn't something we * like, I have a test case to make sure the error message is pretty. */ public void testFactoryReuseErrorMessageIsPretty() { final Provider<ColoredCarFactory> factoryProvider = FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class); Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Double.class).toInstance(5.0d); bind(ColoredCarFactory.class).toProvider(factoryProvider); } }); try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Double.class).toInstance(5.0d); bind(ColoredCarFactory.class).toProvider(factoryProvider); } }); fail(); } catch(CreationException expected) { assertContains(expected.getMessage(), "Factories.create() factories may only be used in one Injector!"); } } public void testNonAssistedFactoryMethodParameter() { try { FactoryProvider.newFactory(NamedParameterFactory.class, Mustang.class); fail(); } catch(ConfigurationException expected) { assertContains(expected.getMessage(), "Only @Assisted is allowed for factory parameters, but found @" + Named.class.getName()); } } interface NamedParameterFactory { Car create(@Named("seats") int seats, double engineSize); } public void testDefaultAssistedAnnotation() throws NoSuchFieldException { Assisted plainAssisted = Subaru.class.getDeclaredField("colorProvider").getAnnotation(Assisted.class); 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); } public void testInjectingAndUsingInjector() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(ColoredCarFactory.class).toProvider( FactoryProvider.newFactory(ColoredCarFactory.class, Segway.class)); } }); ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class); Segway green = (Segway)carFactory.create(Color.GREEN); assertSame(Color.GREEN, green.getColor()); assertSame(Color.GREEN, green.getColor()); Segway pink = (Segway)carFactory.create(Color.PINK); assertSame(Color.PINK, pink.getColor()); assertSame(Color.PINK, pink.getColor()); assertSame(Color.GREEN, green.getColor()); } static class Segway implements Car { @Inject Injector injector; Color getColor() { return injector.getInstance(Key.get(Color.class, FactoryProvider2.DEFAULT_ANNOTATION)); } } }