• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.customtestapplication;
18 
19 import static javax.lang.model.element.Modifier.FINAL;
20 import static javax.lang.model.element.Modifier.PRIVATE;
21 import static javax.lang.model.element.Modifier.VOLATILE;
22 
23 import androidx.room.compiler.processing.JavaPoetExtKt;
24 import androidx.room.compiler.processing.XFiler.Mode;
25 import androidx.room.compiler.processing.XProcessingEnv;
26 import com.squareup.javapoet.ClassName;
27 import com.squareup.javapoet.FieldSpec;
28 import com.squareup.javapoet.JavaFile;
29 import com.squareup.javapoet.MethodSpec;
30 import com.squareup.javapoet.ParameterSpec;
31 import com.squareup.javapoet.ParameterizedTypeName;
32 import com.squareup.javapoet.TypeName;
33 import com.squareup.javapoet.TypeSpec;
34 import dagger.hilt.processor.internal.ClassNames;
35 import dagger.hilt.processor.internal.Processors;
36 import java.io.IOException;
37 import javax.lang.model.element.Modifier;
38 
39 /** Generates an Android Application that holds the Singleton component. */
40 final class CustomTestApplicationGenerator {
41   private static final ParameterSpec COMPONENT_MANAGER =
42       ParameterSpec.builder(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER, "componentManager")
43           .build();
44 
45   private final XProcessingEnv processingEnv;
46   private final CustomTestApplicationMetadata metadata;
47 
CustomTestApplicationGenerator( XProcessingEnv processingEnv, CustomTestApplicationMetadata metadata)48   public CustomTestApplicationGenerator(
49       XProcessingEnv processingEnv, CustomTestApplicationMetadata metadata) {
50     this.processingEnv = processingEnv;
51     this.metadata = metadata;
52   }
53 
generate()54   public void generate() throws IOException {
55     TypeSpec.Builder generator = TypeSpec.classBuilder(metadata.appName());
56     JavaPoetExtKt.addOriginatingElement(generator, metadata.element())
57         .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
58         .superclass(metadata.baseAppName())
59         .addSuperinterface(
60             ParameterizedTypeName.get(ClassNames.GENERATED_COMPONENT_MANAGER, TypeName.OBJECT))
61         .addSuperinterface(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER_HOLDER)
62         .addField(
63             FieldSpec.builder(ClassName.OBJECT, "componentManagerLock", PRIVATE, FINAL)
64                 .initializer("new $T()", ClassName.OBJECT)
65                 .build())
66         .addField(getComponentManagerField())
67         .addMethod(getComponentManagerMethod())
68         .addMethod(getComponentMethod());
69 
70     Processors.addGeneratedAnnotation(
71         generator, processingEnv, CustomTestApplicationProcessor.class);
72 
73     JavaFile javaFile =
74         JavaFile.builder(metadata.appName().packageName(), generator.build()).build();
75     processingEnv.getFiler().write(javaFile, Mode.Isolating);
76   }
77 
78   // Initialize this in attachBaseContext to not pull it into the main dex.
79   /** private TestApplicationComponentManager componentManager; */
getComponentManagerField()80   private static FieldSpec getComponentManagerField() {
81     return FieldSpec.builder(COMPONENT_MANAGER.type, COMPONENT_MANAGER.name, PRIVATE, VOLATILE)
82         .build();
83   }
getComponentMethod()84   private static MethodSpec getComponentMethod() {
85     return MethodSpec.methodBuilder("generatedComponent")
86         .addAnnotation(Override.class)
87         .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
88         .returns(TypeName.OBJECT)
89         .addStatement("return $N().generatedComponent()", COMPONENT_MANAGER)
90         .build();
91   }
92 
getComponentManagerMethod()93   private static MethodSpec getComponentManagerMethod() {
94     return MethodSpec.methodBuilder("componentManager")
95         .addAnnotation(Override.class)
96         .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
97         .returns(ParameterizedTypeName.get(ClassNames.GENERATED_COMPONENT_MANAGER, TypeName.OBJECT))
98         // This field is initialized lazily to avoid pulling the generated component into the main
99         // dex. We could possibly avoid this by class loading TestComponentDataSupplier lazily
100         // rather than in the TestApplicationComponentManager constructor.
101         .beginControlFlow("if ($N == null)", COMPONENT_MANAGER)
102         .beginControlFlow("synchronized (componentManagerLock)")
103         .beginControlFlow("if ($N == null)", COMPONENT_MANAGER)
104         .addStatement("$N = new $T(this)", COMPONENT_MANAGER, COMPONENT_MANAGER.type)
105         .endControlFlow()
106         .endControlFlow()
107         .endControlFlow()
108         .addStatement("return $N", COMPONENT_MANAGER)
109         .build();
110   }
111 }
112