• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Dagger Authors.
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 dagger.android.processor;
18 
19 import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
20 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
21 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
22 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
23 import static com.squareup.javapoet.MethodSpec.methodBuilder;
24 import static com.squareup.javapoet.TypeSpec.classBuilder;
25 import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
26 import static dagger.android.processor.DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS;
27 import static javax.lang.model.element.Modifier.ABSTRACT;
28 import static javax.lang.model.element.Modifier.PRIVATE;
29 import static javax.lang.model.element.Modifier.PUBLIC;
30 import static javax.lang.model.element.Modifier.STATIC;
31 import static javax.tools.Diagnostic.Kind.ERROR;
32 
33 import androidx.room.compiler.processing.XElement;
34 import androidx.room.compiler.processing.XFiler;
35 import androidx.room.compiler.processing.XProcessingEnv;
36 import androidx.room.compiler.processing.XTypeElement;
37 import com.google.common.base.Ascii;
38 import com.google.common.base.Joiner;
39 import com.google.common.collect.ImmutableSet;
40 import com.squareup.javapoet.AnnotationSpec;
41 import com.squareup.javapoet.ClassName;
42 import com.squareup.javapoet.JavaFile;
43 import com.squareup.javapoet.MethodSpec;
44 import com.squareup.javapoet.ParameterizedTypeName;
45 import com.squareup.javapoet.TypeName;
46 import com.squareup.javapoet.TypeSpec;
47 import com.squareup.javapoet.WildcardTypeName;
48 import dagger.internal.codegen.xprocessing.XElements;
49 
50 /** Generates the implementation specified in {@code ContributesAndroidInjector}. */
51 final class ContributesAndroidInjectorProcessingStep extends BaseProcessingStep {
52   private final AndroidInjectorDescriptor.Validator validator;
53   private final XProcessingEnv processingEnv;
54 
ContributesAndroidInjectorProcessingStep(XProcessingEnv processingEnv)55   ContributesAndroidInjectorProcessingStep(XProcessingEnv processingEnv) {
56     this.processingEnv = processingEnv;
57     this.validator = new AndroidInjectorDescriptor.Validator(processingEnv.getMessager());
58   }
59 
60   @Override
annotationClassNames()61   public ImmutableSet<ClassName> annotationClassNames() {
62     return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
63   }
64 
65   @Override
process(XElement element, ImmutableSet<ClassName> annotationNames)66   public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
67     validator.createIfValid(XElements.asMethod(element)).ifPresent(this::generate);
68   }
69 
generate(AndroidInjectorDescriptor descriptor)70   private void generate(AndroidInjectorDescriptor descriptor) {
71     ClassName moduleName =
72         descriptor
73             .enclosingModule()
74             .topLevelClassName()
75             .peerClass(
76                 Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
77                     + "_"
78                     + LOWER_CAMEL.to(UPPER_CAMEL, XElements.getSimpleName(descriptor.method())));
79 
80     String baseName = descriptor.injectedType().simpleName();
81     ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
82     ClassName subcomponentFactoryName = subcomponentName.nestedClass("Factory");
83 
84     TypeSpec.Builder module =
85         classBuilder(moduleName)
86             .addAnnotation(
87                 AnnotationSpec.builder(TypeNames.MODULE)
88                     .addMember("subcomponents", "$T.class", subcomponentName)
89                     .build())
90             .addModifiers(PUBLIC, ABSTRACT)
91             .addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
92             .addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
93             .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
94 
95     addOriginatingElement(module, descriptor.method());
96 
97     XTypeElement generatedAnnotation = processingEnv.findGeneratedAnnotation();
98     if (generatedAnnotation != null) {
99       module.addAnnotation(
100           AnnotationSpec.builder(generatedAnnotation.getClassName())
101               .addMember(
102                   "value", "$S", ClassName.get("dagger.android.processor", "AndroidProcessor"))
103               .build());
104     }
105 
106     processingEnv
107         .getFiler()
108         .write(
109             JavaFile.builder(moduleName.packageName(), module.build())
110                 .skipJavaLangImports(true)
111                 .build(),
112             XFiler.Mode.Isolating);
113   }
114 
useStringKeys(XProcessingEnv processingEnv)115   private static boolean useStringKeys(XProcessingEnv processingEnv) {
116     if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
117       return false;
118     }
119     String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
120     if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
121       return true;
122     } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
123       return false;
124     } else {
125       processingEnv
126           .getMessager()
127           .printMessage(
128               ERROR,
129               String.format(
130                   "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
131                   flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
132       return false;
133     }
134   }
135 
bindAndroidInjectorFactory( AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName)136   private MethodSpec bindAndroidInjectorFactory(
137       AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName) {
138     return methodBuilder("bindAndroidInjectorFactory")
139         .addAnnotation(TypeNames.BINDS)
140         .addAnnotation(TypeNames.INTO_MAP)
141         .addAnnotation(androidInjectorMapKey(descriptor))
142         .addModifiers(ABSTRACT)
143         .returns(
144             ParameterizedTypeName.get(
145                 TypeNames.ANDROID_INJECTOR_FACTORY, WildcardTypeName.subtypeOf(TypeName.OBJECT)))
146         .addParameter(subcomponentBuilderName, "builder")
147         .build();
148   }
149 
androidInjectorMapKey(AndroidInjectorDescriptor descriptor)150   private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
151     if (useStringKeys(processingEnv)) {
152       return AnnotationSpec.builder(TypeNames.ANDROID_INJECTION_KEY)
153           .addMember("value", "$S", descriptor.injectedType().toString())
154           .build();
155     }
156     return AnnotationSpec.builder(TypeNames.CLASS_KEY)
157         .addMember("value", "$T.class", descriptor.injectedType())
158         .build();
159   }
160 
subcomponent( AndroidInjectorDescriptor descriptor, ClassName subcomponentName, ClassName subcomponentFactoryName)161   private TypeSpec subcomponent(
162       AndroidInjectorDescriptor descriptor,
163       ClassName subcomponentName,
164       ClassName subcomponentFactoryName) {
165     AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(TypeNames.SUBCOMPONENT);
166     for (ClassName module : descriptor.modules()) {
167       subcomponentAnnotation.addMember("modules", "$T.class", module);
168     }
169 
170     return interfaceBuilder(subcomponentName)
171         .addModifiers(PUBLIC)
172         .addAnnotation(subcomponentAnnotation.build())
173         .addAnnotations(descriptor.scopes())
174         .addSuperinterface(
175             ParameterizedTypeName.get(TypeNames.ANDROID_INJECTOR, descriptor.injectedType()))
176         .addType(subcomponentFactory(descriptor, subcomponentFactoryName))
177         .build();
178   }
179 
subcomponentFactory( AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName)180   private TypeSpec subcomponentFactory(
181       AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName) {
182     return interfaceBuilder(subcomponentFactoryName)
183         .addAnnotation(TypeNames.SUBCOMPONENT_FACTORY)
184         .addModifiers(PUBLIC, STATIC)
185         .addSuperinterface(
186             ParameterizedTypeName.get(
187                 TypeNames.ANDROID_INJECTOR_FACTORY, descriptor.injectedType()))
188         .build();
189   }
190 }
191