• 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 com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
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 javax.lang.model.element.Modifier.ABSTRACT;
27 import static javax.lang.model.element.Modifier.PRIVATE;
28 import static javax.lang.model.element.Modifier.PUBLIC;
29 import static javax.lang.model.element.Modifier.STATIC;
30 import static javax.lang.model.util.ElementFilter.methodsIn;
31 
32 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
33 import com.google.common.base.Joiner;
34 import com.google.common.collect.ImmutableSet;
35 import com.google.common.collect.SetMultimap;
36 import com.squareup.javapoet.AnnotationSpec;
37 import com.squareup.javapoet.ClassName;
38 import com.squareup.javapoet.JavaFile;
39 import com.squareup.javapoet.MethodSpec;
40 import com.squareup.javapoet.ParameterizedTypeName;
41 import com.squareup.javapoet.TypeName;
42 import com.squareup.javapoet.TypeSpec;
43 import com.squareup.javapoet.WildcardTypeName;
44 import dagger.Binds;
45 import dagger.Module;
46 import dagger.Subcomponent;
47 import dagger.android.AndroidInjectionKey;
48 import dagger.android.AndroidInjector;
49 import dagger.android.ContributesAndroidInjector;
50 import dagger.android.processor.AndroidInjectorDescriptor.Validator;
51 import dagger.multibindings.ClassKey;
52 import dagger.multibindings.IntoMap;
53 import java.io.IOException;
54 import java.lang.annotation.Annotation;
55 import java.util.Set;
56 import javax.annotation.processing.Filer;
57 import javax.lang.model.SourceVersion;
58 import javax.lang.model.element.Element;
59 import javax.lang.model.element.ExecutableElement;
60 import javax.lang.model.util.Elements;
61 
62 /** Generates the implementation specified in {@link ContributesAndroidInjector}. */
63 final class ContributesAndroidInjectorGenerator implements ProcessingStep {
64 
65   private final AndroidInjectorDescriptor.Validator validator;
66   private final Filer filer;
67   private final Elements elements;
68   private final boolean useStringKeys;
69   private final SourceVersion sourceVersion;
70 
ContributesAndroidInjectorGenerator( Validator validator, boolean useStringKeys, Filer filer, Elements elements, SourceVersion sourceVersion)71   ContributesAndroidInjectorGenerator(
72       Validator validator,
73       boolean useStringKeys,
74       Filer filer,
75       Elements elements,
76       SourceVersion sourceVersion) {
77     this.validator = validator;
78     this.useStringKeys = useStringKeys;
79     this.filer = filer;
80     this.elements = elements;
81     this.sourceVersion = sourceVersion;
82   }
83 
84   @Override
annotations()85   public Set<? extends Class<? extends Annotation>> annotations() {
86     return ImmutableSet.of(ContributesAndroidInjector.class);
87   }
88 
89   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)90   public Set<Element> process(
91       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
92     ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
93     for (ExecutableElement method : methodsIn(elementsByAnnotation.values())) {
94       try {
95         validator.createIfValid(method).ifPresent(this::generate);
96       } catch (TypeNotPresentException e) {
97         deferredElements.add(method);
98       }
99     }
100     return deferredElements.build();
101   }
102 
generate(AndroidInjectorDescriptor descriptor)103   private void generate(AndroidInjectorDescriptor descriptor) {
104     ClassName moduleName =
105         descriptor
106             .enclosingModule()
107             .topLevelClassName()
108             .peerClass(
109                 Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
110                     + "_"
111                     + LOWER_CAMEL.to(UPPER_CAMEL, descriptor.method().getSimpleName().toString()));
112 
113     String baseName = descriptor.injectedType().simpleName();
114     ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
115     ClassName subcomponentFactoryName = subcomponentName.nestedClass("Factory");
116 
117     TypeSpec.Builder module =
118         classBuilder(moduleName)
119             .addOriginatingElement(descriptor.method())
120             .addAnnotation(
121                 AnnotationSpec.builder(Module.class)
122                     .addMember("subcomponents", "$T.class", subcomponentName)
123                     .build())
124             .addModifiers(PUBLIC, ABSTRACT)
125             .addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
126             .addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
127             .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
128     generatedAnnotationSpec(elements, sourceVersion, AndroidProcessor.class)
129         .ifPresent(module::addAnnotation);
130 
131     try {
132       JavaFile.builder(moduleName.packageName(), module.build())
133           .skipJavaLangImports(true)
134           .build()
135           .writeTo(filer);
136     } catch (IOException e) {
137       throw new AssertionError(e);
138     }
139   }
140 
bindAndroidInjectorFactory( AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName)141   private MethodSpec bindAndroidInjectorFactory(
142       AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName) {
143     return methodBuilder("bindAndroidInjectorFactory")
144         .addAnnotation(Binds.class)
145         .addAnnotation(IntoMap.class)
146         .addAnnotation(androidInjectorMapKey(descriptor))
147         .addModifiers(ABSTRACT)
148         .returns(
149             parameterizedTypeName(
150                 AndroidInjector.Factory.class,
151                 WildcardTypeName.subtypeOf(TypeName.OBJECT)))
152         .addParameter(subcomponentBuilderName, "builder")
153         .build();
154   }
155 
androidInjectorMapKey(AndroidInjectorDescriptor descriptor)156   private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
157     if (useStringKeys) {
158       return AnnotationSpec.builder(AndroidInjectionKey.class)
159           .addMember("value", "$S", descriptor.injectedType().toString())
160           .build();
161     }
162     return AnnotationSpec.builder(ClassKey.class)
163         .addMember("value", "$T.class", descriptor.injectedType())
164         .build();
165   }
166 
subcomponent( AndroidInjectorDescriptor descriptor, ClassName subcomponentName, ClassName subcomponentFactoryName)167   private TypeSpec subcomponent(
168       AndroidInjectorDescriptor descriptor,
169       ClassName subcomponentName,
170       ClassName subcomponentFactoryName) {
171     AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(Subcomponent.class);
172     for (ClassName module : descriptor.modules()) {
173       subcomponentAnnotation.addMember("modules", "$T.class", module);
174     }
175 
176     return interfaceBuilder(subcomponentName)
177         .addModifiers(PUBLIC)
178         .addAnnotation(subcomponentAnnotation.build())
179         .addAnnotations(descriptor.scopes())
180         .addSuperinterface(parameterizedTypeName(AndroidInjector.class, descriptor.injectedType()))
181         .addType(subcomponentFactory(descriptor, subcomponentFactoryName))
182         .build();
183   }
184 
subcomponentFactory( AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName)185   private TypeSpec subcomponentFactory(
186       AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName) {
187     return interfaceBuilder(subcomponentFactoryName)
188         .addAnnotation(Subcomponent.Factory.class)
189         .addModifiers(PUBLIC, STATIC)
190         .addSuperinterface(
191             parameterizedTypeName(AndroidInjector.Factory.class, descriptor.injectedType()))
192         .build();
193   }
194 
parameterizedTypeName( Class<?> clazz, TypeName... typeArguments)195   private static ParameterizedTypeName parameterizedTypeName(
196       Class<?> clazz, TypeName... typeArguments) {
197     return ParameterizedTypeName.get(ClassName.get(clazz), typeArguments);
198   }
199 }
200