| /* |
| * Copyright (C) 2014 The Dagger Authors. |
| * |
| * 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 dagger.internal.codegen; |
| |
| import static com.google.testing.compile.CompilationSubject.assertThat; |
| import static dagger.internal.codegen.Compilers.compilerWithOptions; |
| import static dagger.internal.codegen.Compilers.daggerCompiler; |
| import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS; |
| |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| import java.util.Collection; |
| import javax.tools.JavaFileObject; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| @RunWith(Parameterized.class) |
| public class MapBindingComponentProcessorTest { |
| @Parameters(name = "{0}") |
| public static Collection<Object[]> parameters() { |
| return CompilerMode.TEST_PARAMETERS; |
| } |
| |
| private final CompilerMode compilerMode; |
| |
| public MapBindingComponentProcessorTest(CompilerMode compilerMode) { |
| this.compilerMode = compilerMode; |
| } |
| |
| @Test |
| public void mapBindingsWithEnumKey() { |
| JavaFileObject mapModuleOneFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleOne", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleOne {", |
| " @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {", |
| " return new AdminHandler();", |
| " }", |
| "}"); |
| JavaFileObject mapModuleTwoFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleTwo", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleTwo {", |
| " @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {", |
| " return new LoginHandler();", |
| " }", |
| "}"); |
| JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey", |
| "package test;", |
| "import dagger.MapKey;", |
| "import java.lang.annotation.Retention;", |
| "import static java.lang.annotation.RetentionPolicy.RUNTIME;", |
| "", |
| "@MapKey(unwrapValue = true)", |
| "@Retention(RUNTIME)", |
| "public @interface PathKey {", |
| " PathEnum value();", |
| "}"); |
| JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum", |
| "package test;", |
| "", |
| "public enum PathEnum {", |
| " ADMIN,", |
| " LOGIN;", |
| "}"); |
| |
| JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler", |
| "package test;", |
| "", |
| "interface Handler {}"); |
| JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler", |
| "package test;", |
| "", |
| "class LoginHandler implements Handler {", |
| " public LoginHandler() {}", |
| "}"); |
| JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler", |
| "package test;", |
| "", |
| "class AdminHandler implements Handler {", |
| " public AdminHandler() {}", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})", |
| "interface TestComponent {", |
| " Provider<Map<PathEnum, Provider<Handler>>> dispatcher();", |
| "}"); |
| JavaFileObject generatedComponent; |
| switch (compilerMode) { |
| case FAST_INIT_MODE: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private final MapModuleOne mapModuleOne;", |
| " private final MapModuleTwo mapModuleTwo;", |
| " private volatile Provider<Handler> provideAdminHandlerProvider;", |
| " private volatile Provider<Handler> provideLoginHandlerProvider;", |
| " private volatile Provider<Map<PathEnum, Provider<Handler>>>", |
| " mapOfPathEnumAndProviderOfHandlerProvider;", |
| "", |
| " private Provider<Handler> provideAdminHandlerProvider() {", |
| " Object local = provideAdminHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(1);", |
| " provideAdminHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Provider<Handler> provideLoginHandlerProvider() {", |
| " Object local = provideLoginHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(2);", |
| " provideLoginHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Map<PathEnum, Provider<Handler>>", |
| " mapOfPathEnumAndProviderOfHandler() {", |
| " return ImmutableMap.<PathEnum, Provider<Handler>>of(", |
| " PathEnum.ADMIN, provideAdminHandlerProvider(),", |
| " PathEnum.LOGIN, provideLoginHandlerProvider());", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {", |
| " Object local = mapOfPathEnumAndProviderOfHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(0);", |
| " mapOfPathEnumAndProviderOfHandlerProvider =", |
| " (Provider<Map<PathEnum, Provider<Handler>>>) local;", |
| " }", |
| " return (Provider<Map<PathEnum, Provider<Handler>>>) local;", |
| " }", |
| "", |
| " private final class SwitchingProvider<T> implements Provider<T> {", |
| " private final int id;", |
| "", |
| " SwitchingProvider(int id) {", |
| " this.id = id;", |
| " }", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " @Override", |
| " public T get() {", |
| " switch (id) {", |
| " case 0:", |
| " return (T) DaggerTestComponent.this", |
| " .mapOfPathEnumAndProviderOfHandler();", |
| " case 1:", |
| " return (T) MapModuleOne_ProvideAdminHandlerFactory", |
| " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);", |
| " case 2:", |
| " return (T) MapModuleTwo_ProvideLoginHandlerFactory", |
| " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);", |
| " default: throw new AssertionError(id);", |
| " }", |
| " }", |
| " }", |
| "}"); |
| break; |
| default: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private Provider<Handler> provideAdminHandlerProvider;", |
| " private Provider<Handler> provideLoginHandlerProvider;", |
| " private Provider<Map<PathEnum, Provider<Handler>>>", |
| " mapOfPathEnumAndProviderOfHandlerProvider;", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize(", |
| " final MapModuleOne mapModuleOneParam,", |
| " final MapModuleTwo mapModuleTwoParam) {", |
| " this.provideAdminHandlerProvider =", |
| " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);", |
| " this.provideLoginHandlerProvider =", |
| " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);", |
| " this.mapOfPathEnumAndProviderOfHandlerProvider =", |
| " MapProviderFactory.<PathEnum, Handler>builder(2)", |
| " .put(PathEnum.ADMIN, provideAdminHandlerProvider)", |
| " .put(PathEnum.LOGIN, provideLoginHandlerProvider)", |
| " .build();", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {", |
| " return mapOfPathEnumAndProviderOfHandlerProvider;", |
| " }", |
| "}"); |
| } |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile( |
| mapModuleOneFile, |
| mapModuleTwoFile, |
| enumKeyFile, |
| pathEnumFile, |
| HandlerFile, |
| LoginHandlerFile, |
| AdminHandlerFile, |
| componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| |
| @Test |
| public void mapBindingsWithInaccessibleKeys() { |
| JavaFileObject mapKeys = |
| JavaFileObjects.forSourceLines( |
| "mapkeys.MapKeys", |
| "package mapkeys;", |
| "", |
| "import dagger.MapKey;", |
| "import dagger.multibindings.ClassKey;", |
| "", |
| "public class MapKeys {", |
| " @MapKey(unwrapValue = false)", |
| " public @interface ComplexKey {", |
| " Class<?>[] manyClasses();", |
| " Class<?> oneClass();", |
| " ClassKey annotation();", |
| " }", |
| "", |
| " @MapKey", |
| " @interface EnumKey {", |
| " PackagePrivateEnum value();", |
| " }", |
| "", |
| " enum PackagePrivateEnum { INACCESSIBLE }", |
| "", |
| " interface Inaccessible {}", |
| "}"); |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "mapkeys.MapModule", |
| "package mapkeys;", |
| "", |
| "import dagger.Binds;", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.ClassKey;", |
| "import dagger.multibindings.IntoMap;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "", |
| "@Module", |
| "public interface MapModule {", |
| " @Provides @IntoMap @ClassKey(MapKeys.Inaccessible.class)", |
| " static int classKey() { return 1; }", |
| "", |
| " @Provides @IntoMap @MapKeys.EnumKey(MapKeys.PackagePrivateEnum.INACCESSIBLE)", |
| " static int enumKey() { return 1; }", |
| "", |
| " @Binds Object bindInaccessibleEnumMapToAccessibleTypeForComponent(", |
| " Map<MapKeys.PackagePrivateEnum, Integer> map);", |
| "", |
| " @Provides @IntoMap", |
| " @MapKeys.ComplexKey(", |
| " manyClasses = {java.lang.Object.class, java.lang.String.class},", |
| " oneClass = MapKeys.Inaccessible.class,", |
| " annotation = @ClassKey(java.lang.Object.class)", |
| " )", |
| " static int complexKeyWithInaccessibleValue() { return 1; }", |
| "", |
| " @Provides @IntoMap", |
| " @MapKeys.ComplexKey(", |
| " manyClasses = {MapKeys.Inaccessible.class, java.lang.String.class},", |
| " oneClass = java.lang.String.class,", |
| " annotation = @ClassKey(java.lang.Object.class)", |
| " )", |
| " static int complexKeyWithInaccessibleArrayValue() { return 1; }", |
| "", |
| " @Provides @IntoMap", |
| " @MapKeys.ComplexKey(", |
| " manyClasses = {java.lang.String.class},", |
| " oneClass = java.lang.String.class,", |
| " annotation = @ClassKey(MapKeys.Inaccessible.class)", |
| " )", |
| " static int complexKeyWithInaccessibleAnnotationValue() { return 1; }", |
| "}"); |
| JavaFileObject componentFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "import mapkeys.MapKeys;", |
| "import mapkeys.MapModule;", |
| "", |
| "@Component(modules = MapModule.class)", |
| "interface TestComponent {", |
| " Map<Class<?>, Integer> classKey();", |
| " Provider<Map<Class<?>, Integer>> classKeyProvider();", |
| "", |
| " Object inaccessibleEnum();", |
| " Provider<Object> inaccessibleEnumProvider();", |
| "", |
| " Map<MapKeys.ComplexKey, Integer> complexKey();", |
| " Provider<Map<MapKeys.ComplexKey, Integer>> complexKeyProvider();", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(mapKeys, moduleFile, componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn( |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private Provider<Map<Class<?>, Integer>> mapOfClassOfAndIntegerProvider;", |
| "", |
| " @SuppressWarnings(\"rawtypes\")", |
| " private Provider mapOfPackagePrivateEnumAndIntegerProvider;", |
| "", |
| " private Provider<Map<MapKeys.ComplexKey, Integer>>", |
| " mapOfComplexKeyAndIntegerProvider;", |
| "", |
| " private Map mapOfPackagePrivateEnumAndInteger() {", |
| " return ImmutableMap.of(", |
| " MapModule_EnumKeyMapKey.create(), MapModule.enumKey());", |
| " }", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize() {", |
| " this.mapOfClassOfAndIntegerProvider =", |
| " MapFactory.<Class<?>, Integer>builder(1)", |
| " .put(MapModule_ClassKeyMapKey.create(),", |
| " MapModule_ClassKeyFactory.create())", |
| " .build();", |
| " this.mapOfPackagePrivateEnumAndIntegerProvider =", |
| " MapFactory.builder(1)", |
| " .put(MapModule_EnumKeyMapKey.create(), ", |
| " (Provider) MapModule_EnumKeyFactory.create())", |
| " .build();", |
| " this.mapOfComplexKeyAndIntegerProvider =", |
| " MapFactory.<MapKeys.ComplexKey, Integer>builder(3)", |
| " .put(", |
| " MapModule_ComplexKeyWithInaccessibleValueMapKey.create(),", |
| " MapModule_ComplexKeyWithInaccessibleValueFactory.create())", |
| " .put(", |
| " MapModule_ComplexKeyWithInaccessibleArrayValueMapKey.create(),", |
| " MapModule_ComplexKeyWithInaccessibleArrayValueFactory.create())", |
| " .put(", |
| " MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey.create(),", |
| " MapModule_ComplexKeyWithInaccessibleAnnotationValueFactory.create())", |
| " .build();", |
| " }", |
| "", |
| " @Override", |
| " public Map<Class<?>, Integer> classKey() {", |
| " return ImmutableMap.<Class<?>, Integer>of(", |
| " MapModule_ClassKeyMapKey.create(), MapModule.classKey());", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<Class<?>, Integer>> classKeyProvider() {", |
| " return mapOfClassOfAndIntegerProvider;", |
| " }", |
| "", |
| " @Override", |
| " public Object inaccessibleEnum() {", |
| " return mapOfPackagePrivateEnumAndInteger();", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Object> inaccessibleEnumProvider() {", |
| " return mapOfPackagePrivateEnumAndIntegerProvider;", |
| " }", |
| "", |
| " @Override", |
| " public Map<MapKeys.ComplexKey, Integer> complexKey() {", |
| " return ImmutableMap.<MapKeys.ComplexKey, Integer>of(", |
| " MapModule_ComplexKeyWithInaccessibleValueMapKey.create(),", |
| " MapModule.complexKeyWithInaccessibleValue(),", |
| " MapModule_ComplexKeyWithInaccessibleArrayValueMapKey.create(),", |
| " MapModule.complexKeyWithInaccessibleArrayValue(),", |
| " MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey.create(),", |
| " MapModule.complexKeyWithInaccessibleAnnotationValue());", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<MapKeys.ComplexKey, Integer>> complexKeyProvider() {", |
| " return mapOfComplexKeyAndIntegerProvider;", |
| " }", |
| "}")); |
| assertThat(compilation) |
| .generatedSourceFile( |
| "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey") |
| .containsElementsIn( |
| JavaFileObjects.forSourceLines( |
| "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey", |
| "package mapkeys;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey {", |
| " public static MapKeys.ComplexKey create() {", |
| " return MapKeys_ComplexKeyCreator.createComplexKey(", |
| " new Class[] {String.class},", |
| " String.class,", |
| " MapKeys_ComplexKeyCreator.createClassKey(MapKeys.Inaccessible.class));", |
| " }", |
| "}")); |
| assertThat(compilation) |
| .generatedSourceFile("mapkeys.MapModule_ClassKeyMapKey") |
| .containsElementsIn( |
| JavaFileObjects.forSourceLines( |
| "mapkeys.MapModule_ClassKeyMapKey", |
| "package mapkeys;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class MapModule_ClassKeyMapKey {", |
| " public static Class<?> create() {", |
| " return MapKeys.Inaccessible.class;", |
| " }", |
| "}")); |
| } |
| |
| @Test |
| public void mapBindingsWithStringKey() { |
| JavaFileObject mapModuleOneFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleOne", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.StringKey;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleOne {", |
| " @Provides @IntoMap @StringKey(\"Admin\") Handler provideAdminHandler() {", |
| " return new AdminHandler();", |
| " }", |
| "}"); |
| JavaFileObject mapModuleTwoFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleTwo", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "import dagger.multibindings.StringKey;", |
| "", |
| "@Module", |
| "final class MapModuleTwo {", |
| " @Provides @IntoMap @StringKey(\"Login\") Handler provideLoginHandler() {", |
| " return new LoginHandler();", |
| " }", |
| "}"); |
| JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler", |
| "package test;", |
| "", |
| "interface Handler {}"); |
| JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler", |
| "package test;", |
| "", |
| "class LoginHandler implements Handler {", |
| " public LoginHandler() {}", |
| "}"); |
| JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler", |
| "package test;", |
| "", |
| "class AdminHandler implements Handler {", |
| " public AdminHandler() {}", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})", |
| "interface TestComponent {", |
| " Provider<Map<String, Provider<Handler>>> dispatcher();", |
| "}"); |
| JavaFileObject generatedComponent; |
| switch (compilerMode) { |
| case FAST_INIT_MODE: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private final MapModuleOne mapModuleOne;", |
| " private final MapModuleTwo mapModuleTwo;", |
| " private volatile Provider<Handler> provideAdminHandlerProvider;", |
| " private volatile Provider<Handler> provideLoginHandlerProvider;", |
| " private volatile Provider<Map<String, Provider<Handler>>>", |
| " mapOfStringAndProviderOfHandlerProvider;", |
| "", |
| " private Provider<Handler> provideAdminHandlerProvider() {", |
| " Object local = provideAdminHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(1);", |
| " provideAdminHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Provider<Handler> provideLoginHandlerProvider() {", |
| " Object local = provideLoginHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(2);", |
| " provideLoginHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Map<String, Provider<Handler>>", |
| " mapOfStringAndProviderOfHandler() {", |
| " return ImmutableMap.<String, Provider<Handler>>of(", |
| " \"Admin\", provideAdminHandlerProvider(),", |
| " \"Login\", provideLoginHandlerProvider());", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<String, Provider<Handler>>> dispatcher() {", |
| " Object local = mapOfStringAndProviderOfHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(0);", |
| " mapOfStringAndProviderOfHandlerProvider =", |
| " (Provider<Map<String, Provider<Handler>>>) local;", |
| " }", |
| " return (Provider<Map<String, Provider<Handler>>>) local;", |
| " }", |
| "", |
| " private final class SwitchingProvider<T> implements Provider<T> {", |
| " private final int id;", |
| "", |
| " SwitchingProvider(int id) {", |
| " this.id = id;", |
| " }", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " @Override", |
| " public T get() {", |
| " switch (id) {", |
| " case 0:", |
| " return (T) DaggerTestComponent.this", |
| " .mapOfStringAndProviderOfHandler();", |
| " case 1:", |
| " return (T) MapModuleOne_ProvideAdminHandlerFactory", |
| " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);", |
| " case 2:", |
| " return (T) MapModuleTwo_ProvideLoginHandlerFactory", |
| " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);", |
| " default: throw new AssertionError(id);", |
| " }", |
| " }", |
| " }", |
| "}"); |
| break; |
| default: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private Provider<Handler> provideAdminHandlerProvider;", |
| " private Provider<Handler> provideLoginHandlerProvider;", |
| " private Provider<Map<String, Provider<Handler>>>", |
| " mapOfStringAndProviderOfHandlerProvider;", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize(", |
| " final MapModuleOne mapModuleOneParam,", |
| " final MapModuleTwo mapModuleTwoParam) {", |
| " this.provideAdminHandlerProvider =", |
| " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);", |
| " this.provideLoginHandlerProvider =", |
| " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);", |
| " this.mapOfStringAndProviderOfHandlerProvider =", |
| " MapProviderFactory.<String, Handler>builder(2)", |
| " .put(\"Admin\", provideAdminHandlerProvider)", |
| " .put(\"Login\", provideLoginHandlerProvider)", |
| " .build();", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<String, Provider<Handler>>> dispatcher() {", |
| " return mapOfStringAndProviderOfHandlerProvider;", |
| " }", |
| "}"); |
| } |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile( |
| mapModuleOneFile, |
| mapModuleTwoFile, |
| HandlerFile, |
| LoginHandlerFile, |
| AdminHandlerFile, |
| componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| |
| @Test |
| public void mapBindingsWithWrappedKey() { |
| JavaFileObject mapModuleOneFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleOne", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleOne {", |
| " @Provides @IntoMap", |
| " @WrappedClassKey(Integer.class) Handler provideAdminHandler() {", |
| " return new AdminHandler();", |
| " }", |
| "}"); |
| JavaFileObject mapModuleTwoFile = |
| JavaFileObjects |
| .forSourceLines("test.MapModuleTwo", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleTwo {", |
| " @Provides @IntoMap", |
| " @WrappedClassKey(Long.class) Handler provideLoginHandler() {", |
| " return new LoginHandler();", |
| " }", |
| "}"); |
| JavaFileObject wrappedClassKeyFile = JavaFileObjects.forSourceLines("test.WrappedClassKey", |
| "package test;", |
| "import dagger.MapKey;", |
| "import java.lang.annotation.Retention;", |
| "import static java.lang.annotation.RetentionPolicy.RUNTIME;", |
| "", |
| "@MapKey(unwrapValue = false)", |
| "@Retention(RUNTIME)", |
| "public @interface WrappedClassKey {", |
| " Class<?> value();", |
| "}"); |
| JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler", |
| "package test;", |
| "", |
| "interface Handler {}"); |
| JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler", |
| "package test;", |
| "", |
| "class LoginHandler implements Handler {", |
| " public LoginHandler() {}", |
| "}"); |
| JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler", |
| "package test;", |
| "", |
| "class AdminHandler implements Handler {", |
| " public AdminHandler() {}", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})", |
| "interface TestComponent {", |
| " Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher();", |
| "}"); |
| JavaFileObject generatedComponent; |
| switch (compilerMode) { |
| case FAST_INIT_MODE: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private final MapModuleOne mapModuleOne;", |
| " private final MapModuleTwo mapModuleTwo;", |
| " private volatile Provider<Handler> provideAdminHandlerProvider;", |
| " private volatile Provider<Handler> provideLoginHandlerProvider;", |
| " private volatile Provider<Map<WrappedClassKey, Provider<Handler>>>", |
| " mapOfWrappedClassKeyAndProviderOfHandlerProvider;", |
| "", |
| " private DaggerTestComponent(", |
| " MapModuleOne mapModuleOneParam,", |
| " MapModuleTwo mapModuleTwoParam) {", |
| " this.mapModuleOne = mapModuleOneParam;", |
| " this.mapModuleTwo = mapModuleTwoParam;", |
| " }", |
| "", |
| " private Provider<Handler> provideAdminHandlerProvider() {", |
| " Object local = provideAdminHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(1);", |
| " provideAdminHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Provider<Handler> provideLoginHandlerProvider() {", |
| " Object local = provideLoginHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(2);", |
| " provideLoginHandlerProvider = (Provider<Handler>) local;", |
| " }", |
| " return (Provider<Handler>) local;", |
| " }", |
| "", |
| " private Map<WrappedClassKey, Provider<Handler>>", |
| " mapOfWrappedClassKeyAndProviderOfHandler() {", |
| " return ImmutableMap.<WrappedClassKey, Provider<Handler>>of(", |
| " WrappedClassKeyCreator.createWrappedClassKey(Integer.class),", |
| " provideAdminHandlerProvider(),", |
| " WrappedClassKeyCreator.createWrappedClassKey(Long.class),", |
| " provideLoginHandlerProvider());", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {", |
| " Object local = mapOfWrappedClassKeyAndProviderOfHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(0);", |
| " mapOfWrappedClassKeyAndProviderOfHandlerProvider =", |
| " (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;", |
| " }", |
| " return (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;", |
| " }", |
| "", |
| " private final class SwitchingProvider<T> implements Provider<T> {", |
| " private final int id;", |
| "", |
| " SwitchingProvider(int id) {", |
| " this.id = id;", |
| " }", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " @Override", |
| " public T get() {", |
| " switch (id) {", |
| " case 0:", |
| " return (T) DaggerTestComponent.this", |
| " .mapOfWrappedClassKeyAndProviderOfHandler();", |
| " case 1:", |
| " return (T) MapModuleOne_ProvideAdminHandlerFactory", |
| " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);", |
| " case 2:", |
| " return (T) MapModuleTwo_ProvideLoginHandlerFactory", |
| " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);", |
| " default: throw new AssertionError(id);", |
| " }", |
| " }", |
| " }", |
| "}"); |
| break; |
| default: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private Provider<Handler> provideAdminHandlerProvider;", |
| " private Provider<Handler> provideLoginHandlerProvider;", |
| " private Provider<Map<WrappedClassKey, Provider<Handler>>>", |
| " mapOfWrappedClassKeyAndProviderOfHandlerProvider;", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize(", |
| " final MapModuleOne mapModuleOneParam,", |
| " final MapModuleTwo mapModuleTwoParam) {", |
| " this.provideAdminHandlerProvider =", |
| " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);", |
| " this.provideLoginHandlerProvider =", |
| " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);", |
| " this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =", |
| " MapProviderFactory.<WrappedClassKey, Handler>builder(2)", |
| " .put(WrappedClassKeyCreator.createWrappedClassKey(Integer.class),", |
| " provideAdminHandlerProvider)", |
| " .put(WrappedClassKeyCreator.createWrappedClassKey(Long.class),", |
| " provideLoginHandlerProvider)", |
| " .build();", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {", |
| " return mapOfWrappedClassKeyAndProviderOfHandlerProvider;", |
| " }", |
| "}"); |
| } |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile( |
| mapModuleOneFile, |
| mapModuleTwoFile, |
| wrappedClassKeyFile, |
| HandlerFile, |
| LoginHandlerFile, |
| AdminHandlerFile, |
| componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| |
| @Test |
| public void mapBindingsWithNonProviderValue() { |
| JavaFileObject mapModuleOneFile = JavaFileObjects.forSourceLines("test.MapModuleOne", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleOne {", |
| " @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {", |
| " return new AdminHandler();", |
| " }", |
| "}"); |
| JavaFileObject mapModuleTwoFile = JavaFileObjects.forSourceLines("test.MapModuleTwo", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoMap;", |
| "", |
| "@Module", |
| "final class MapModuleTwo {", |
| " @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {", |
| " return new LoginHandler();", |
| " }", |
| "}"); |
| JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey", |
| "package test;", |
| "import dagger.MapKey;", |
| "import java.lang.annotation.Retention;", |
| "import static java.lang.annotation.RetentionPolicy.RUNTIME;", |
| "", |
| "@MapKey(unwrapValue = true)", |
| "@Retention(RUNTIME)", |
| "public @interface PathKey {", |
| " PathEnum value();", |
| "}"); |
| JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum", |
| "package test;", |
| "", |
| "public enum PathEnum {", |
| " ADMIN,", |
| " LOGIN;", |
| "}"); |
| JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler", |
| "package test;", |
| "", |
| "interface Handler {}"); |
| JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler", |
| "package test;", |
| "", |
| "class LoginHandler implements Handler {", |
| " public LoginHandler() {}", |
| "}"); |
| JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler", |
| "package test;", |
| "", |
| "class AdminHandler implements Handler {", |
| " public AdminHandler() {}", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})", |
| "interface TestComponent {", |
| " Provider<Map<PathEnum, Handler>> dispatcher();", |
| "}"); |
| JavaFileObject generatedComponent; |
| switch (compilerMode) { |
| case FAST_INIT_MODE: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private final MapModuleOne mapModuleOne;", |
| " private final MapModuleTwo mapModuleTwo;", |
| " private volatile Provider<Map<PathEnum, Handler>>", |
| " mapOfPathEnumAndHandlerProvider;", |
| "", |
| " private Map<PathEnum, Handler> mapOfPathEnumAndHandler() {", |
| " return ImmutableMap.<PathEnum, Handler>of(", |
| " PathEnum.ADMIN,", |
| " MapModuleOne_ProvideAdminHandlerFactory.provideAdminHandler(", |
| " mapModuleOne),", |
| " PathEnum.LOGIN,", |
| " MapModuleTwo_ProvideLoginHandlerFactory.provideLoginHandler(", |
| " mapModuleTwo));", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<PathEnum, Handler>> dispatcher() {", |
| " Object local = mapOfPathEnumAndHandlerProvider;", |
| " if (local == null) {", |
| " local = new SwitchingProvider<>(0);", |
| " mapOfPathEnumAndHandlerProvider = (Provider<Map<PathEnum, Handler>>) local;", |
| " }", |
| " return (Provider<Map<PathEnum, Handler>>) local;", |
| " }", |
| "", |
| " private final class SwitchingProvider<T> implements Provider<T> {", |
| " private final int id;", |
| "", |
| " SwitchingProvider(int id) {", |
| " this.id = id;", |
| " }", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " @Override", |
| " public T get() {", |
| " switch (id) {", |
| " case 0: return (T) DaggerTestComponent.this.mapOfPathEnumAndHandler();", |
| " default: throw new AssertionError(id);", |
| " }", |
| " }", |
| " }", |
| "}"); |
| break; |
| default: |
| generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private Provider<Handler> provideAdminHandlerProvider;", |
| " private Provider<Handler> provideLoginHandlerProvider;", |
| " private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;", |
| "", |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize(", |
| " final MapModuleOne mapModuleOneParam,", |
| " final MapModuleTwo mapModuleTwoParam) {", |
| " this.provideAdminHandlerProvider =", |
| " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);", |
| " this.provideLoginHandlerProvider =", |
| " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);", |
| " this.mapOfPathEnumAndHandlerProvider =", |
| " MapFactory.<PathEnum, Handler>builder(2)", |
| " .put(PathEnum.ADMIN, provideAdminHandlerProvider)", |
| " .put(PathEnum.LOGIN, provideLoginHandlerProvider)", |
| " .build();", |
| " }", |
| "", |
| " @Override", |
| " public Provider<Map<PathEnum, Handler>> dispatcher() {", |
| " return mapOfPathEnumAndHandlerProvider;", |
| " }", |
| "}"); |
| } |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile( |
| mapModuleOneFile, |
| mapModuleTwoFile, |
| enumKeyFile, |
| pathEnumFile, |
| HandlerFile, |
| LoginHandlerFile, |
| AdminHandlerFile, |
| componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| |
| @Test |
| public void injectMapWithoutMapBinding() { |
| JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import java.util.HashMap;", |
| "import java.util.Map;", |
| "", |
| "@Module", |
| "final class MapModule {", |
| " @Provides Map<String, String> provideAMap() {", |
| " Map<String, String> map = new HashMap<String, String>();", |
| " map.put(\"Hello\", \"World\");", |
| " return map;", |
| " }", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.Map;", |
| "", |
| "@Component(modules = {MapModule.class})", |
| "interface TestComponent {", |
| " Map<String, String> dispatcher();", |
| "}"); |
| JavaFileObject generatedComponent = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerTestComponent", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "final class DaggerTestComponent implements TestComponent {", |
| " private final MapModule mapModule;", |
| "", |
| " @Override", |
| " public Map<String, String> dispatcher() {", |
| " return MapModule_ProvideAMapFactory.provideAMap(mapModule);", |
| " }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(mapModuleFile, componentFile); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerTestComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| } |