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