1 /* 2 * Copyright (C) 2019 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.hilt.android.processor.internal.androidentrypoint; 18 19 20 import com.squareup.javapoet.ClassName; 21 import com.squareup.javapoet.CodeBlock; 22 import com.squareup.javapoet.FieldSpec; 23 import com.squareup.javapoet.JavaFile; 24 import com.squareup.javapoet.MethodSpec; 25 import com.squareup.javapoet.ParameterSpec; 26 import com.squareup.javapoet.TypeName; 27 import com.squareup.javapoet.TypeSpec; 28 import com.squareup.javapoet.TypeVariableName; 29 import dagger.hilt.android.processor.internal.AndroidClassNames; 30 import dagger.hilt.processor.internal.ComponentNames; 31 import dagger.hilt.processor.internal.Processors; 32 import java.io.IOException; 33 import javax.annotation.processing.ProcessingEnvironment; 34 import javax.lang.model.element.Modifier; 35 36 /** Generates an Hilt Application for an @AndroidEntryPoint app class. */ 37 public final class ApplicationGenerator { 38 private final ProcessingEnvironment env; 39 private final AndroidEntryPointMetadata metadata; 40 private final ClassName wrapperClassName; 41 ApplicationGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata)42 public ApplicationGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) { 43 this.env = env; 44 this.metadata = metadata; 45 wrapperClassName = metadata.generatedClassName(); 46 } 47 48 // @Generated("ApplicationGenerator") 49 // abstract class Hilt_$APP extends $BASE implements ComponentManager<ApplicationComponent> { 50 // ... 51 // } generate()52 public void generate() throws IOException { 53 TypeSpec.Builder typeSpecBuilder = 54 TypeSpec.classBuilder(wrapperClassName.simpleName()) 55 .addOriginatingElement(metadata.element()) 56 .superclass(metadata.baseClassName()) 57 .addModifiers(metadata.generatedClassModifiers()) 58 .addField(componentManagerField()) 59 .addMethod(componentManagerMethod()); 60 61 Generators.addGeneratedBaseClassJavadoc(typeSpecBuilder, AndroidClassNames.HILT_ANDROID_APP); 62 Processors.addGeneratedAnnotation(typeSpecBuilder, env, getClass()); 63 64 metadata.baseElement().getTypeParameters().stream() 65 .map(TypeVariableName::get) 66 .forEachOrdered(typeSpecBuilder::addTypeVariable); 67 68 Generators.copyLintAnnotations(metadata.element(), typeSpecBuilder); 69 Generators.addComponentOverride(metadata, typeSpecBuilder); 70 71 typeSpecBuilder.addMethod(onCreateMethod()); 72 73 JavaFile.builder(metadata.elementClassName().packageName(), typeSpecBuilder.build()) 74 .build() 75 .writeTo(env.getFiler()); 76 } 77 78 // private final ApplicationComponentManager<ApplicationComponent> componentManager = 79 // new ApplicationComponentManager(/* creatorType */); componentManagerField()80 private FieldSpec componentManagerField() { 81 ParameterSpec managerParam = metadata.componentManagerParam(); 82 return FieldSpec.builder(managerParam.type, managerParam.name) 83 .addModifiers(Modifier.PRIVATE, Modifier.FINAL) 84 .initializer("new $T($L)", AndroidClassNames.APPLICATION_COMPONENT_MANAGER, creatorType()) 85 .build(); 86 } 87 88 // protected ApplicationComponentManager<ApplicationComponent> componentManager() { 89 // return componentManager(); 90 // } componentManagerMethod()91 private MethodSpec componentManagerMethod() { 92 return MethodSpec.methodBuilder("componentManager") 93 .addAnnotation(Override.class) 94 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 95 .returns(metadata.componentManagerParam().type) 96 .addStatement("return $N", metadata.componentManagerParam()) 97 .build(); 98 } 99 100 // new Supplier<ApplicationComponent>() { 101 // @Override 102 // public ApplicationComponent get() { 103 // return DaggerApplicationComponent.builder() 104 // .applicationContextModule(new ApplicationContextModule(Hilt_$APP.this)) 105 // .build(); 106 // } 107 // } creatorType()108 private TypeSpec creatorType() { 109 ClassName component = 110 ComponentNames.generatedComponent( 111 metadata.elementClassName(), AndroidClassNames.SINGLETON_COMPONENT); 112 return TypeSpec.anonymousClassBuilder("") 113 .addSuperinterface(AndroidClassNames.COMPONENT_SUPPLIER) 114 .addMethod( 115 MethodSpec.methodBuilder("get") 116 .addAnnotation(Override.class) 117 .addModifiers(Modifier.PUBLIC) 118 .returns(TypeName.OBJECT) 119 .addStatement( 120 "return $T.builder()\n" 121 + ".applicationContextModule(new $T($T.this))\n" 122 + ".build()", 123 Processors.prepend(Processors.getEnclosedClassName(component), "Dagger"), 124 AndroidClassNames.APPLICATION_CONTEXT_MODULE, 125 wrapperClassName) 126 .build()) 127 .build(); 128 } 129 130 // @CallSuper 131 // @Override 132 // public void onCreate() { 133 // // This is a known unsafe cast but should be fine if the only use is 134 // // $APP extends Hilt_$APP 135 // generatedComponent().inject(($APP) this); 136 // super.onCreate(); 137 // } onCreateMethod()138 private MethodSpec onCreateMethod() { 139 return MethodSpec.methodBuilder("onCreate") 140 .addAnnotation(AndroidClassNames.CALL_SUPER) 141 .addAnnotation(Override.class) 142 .addModifiers(Modifier.PUBLIC) 143 .addCode(injectCodeBlock()) 144 .addStatement("super.onCreate()") 145 .build(); 146 } 147 148 // // This is a known unsafe cast but should be fine if the only use is 149 // // $APP extends Hilt_$APP 150 // generatedComponent().inject$APP(($APP) this); injectCodeBlock()151 private CodeBlock injectCodeBlock() { 152 return CodeBlock.builder() 153 .add("// This is a known unsafe cast, but is safe in the only correct use case:\n") 154 .add("// $T extends $T\n", metadata.elementClassName(), metadata.generatedClassName()) 155 .addStatement( 156 "(($T) generatedComponent()).$L($L)", 157 metadata.injectorClassName(), 158 metadata.injectMethodName(), 159 Generators.unsafeCastThisTo(metadata.elementClassName())) 160 .build(); 161 } 162 } 163