• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.internal.codegen.writing;
18 
19 import static androidx.room.compiler.processing.XElementKt.isMethod;
20 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
21 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.base.Preconditions.checkNotNull;
24 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
25 import static com.squareup.javapoet.MethodSpec.methodBuilder;
26 import static com.squareup.javapoet.TypeSpec.classBuilder;
27 import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
28 import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
29 import static dagger.internal.codegen.xprocessing.XElements.asMethod;
30 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
31 import static javax.lang.model.element.Modifier.FINAL;
32 import static javax.lang.model.element.Modifier.PRIVATE;
33 import static javax.lang.model.element.Modifier.PUBLIC;
34 import static javax.lang.model.element.Modifier.STATIC;
35 
36 import androidx.room.compiler.processing.XMethodElement;
37 import com.squareup.javapoet.ClassName;
38 import com.squareup.javapoet.CodeBlock;
39 import com.squareup.javapoet.MethodSpec;
40 import com.squareup.javapoet.TypeName;
41 import dagger.assisted.Assisted;
42 import dagger.assisted.AssistedFactory;
43 import dagger.assisted.AssistedInject;
44 import dagger.internal.codegen.binding.BindingGraph;
45 import dagger.internal.codegen.binding.ComponentRequirement;
46 import dagger.internal.codegen.binding.ProvisionBinding;
47 import dagger.internal.codegen.compileroption.CompilerOptions;
48 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
49 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
50 
51 /**
52  * A {@link javax.inject.Provider} creation expression for a provision method on a component's
53  * {@linkplain dagger.Component#dependencies()} dependency}.
54  */
55 // TODO(dpb): Resolve with DependencyMethodProducerCreationExpression.
56 final class DependencyMethodProviderCreationExpression
57     implements FrameworkInstanceCreationExpression {
58 
59   private final ShardImplementation shardImplementation;
60   private final ComponentRequirementExpressions componentRequirementExpressions;
61   private final CompilerOptions compilerOptions;
62   private final BindingGraph graph;
63   private final ProvisionBinding binding;
64   private final XMethodElement provisionMethod;
65 
66   @AssistedInject
DependencyMethodProviderCreationExpression( @ssisted ProvisionBinding binding, ComponentImplementation componentImplementation, ComponentRequirementExpressions componentRequirementExpressions, CompilerOptions compilerOptions, BindingGraph graph)67   DependencyMethodProviderCreationExpression(
68       @Assisted ProvisionBinding binding,
69       ComponentImplementation componentImplementation,
70       ComponentRequirementExpressions componentRequirementExpressions,
71       CompilerOptions compilerOptions,
72       BindingGraph graph) {
73     this.binding = checkNotNull(binding);
74     this.shardImplementation = componentImplementation.shardImplementation(binding);
75     this.componentRequirementExpressions = componentRequirementExpressions;
76     this.compilerOptions = compilerOptions;
77     this.graph = graph;
78 
79     checkArgument(binding.bindingElement().isPresent());
80     checkArgument(isMethod(binding.bindingElement().get()));
81     provisionMethod = asMethod(binding.bindingElement().get());
82   }
83 
84   @Override
creationExpression()85   public CodeBlock creationExpression() {
86     // TODO(sameb): The Provider.get() throws a very vague NPE.  The stack trace doesn't
87     // help to figure out what the method or return type is.  If we include a string
88     // of the return type or method name in the error message, that can defeat obfuscation.
89     // We can easily include the raw type (no generics) + annotation type (no values),
90     // using .class & String.format -- but that wouldn't be the whole story.
91     // What should we do?
92     CodeBlock invocation =
93         ComponentProvisionRequestRepresentation.maybeCheckForNull(
94             binding,
95             compilerOptions,
96             CodeBlock.of("$N.$N()", dependency().variableName(), provisionMethod.getJvmName()));
97     ClassName dependencyClassName = dependency().typeElement().getClassName();
98     TypeName keyType = binding.key().type().xprocessing().getTypeName();
99     MethodSpec.Builder getMethod =
100         methodBuilder("get")
101             .addAnnotation(Override.class)
102             .addModifiers(PUBLIC)
103             .returns(keyType)
104             .addStatement("return $L", invocation);
105 
106     binding
107         .nullability()
108         .nullableAnnotations()
109         .forEach(getMethod::addAnnotation);
110 
111     // We need to use the componentShard here since the generated type is static and shards are
112     // not static classes so it can't be nested inside the shard.
113     ShardImplementation componentShard =
114         shardImplementation.getComponentImplementation().getComponentShard();
115     ClassName factoryClassName =
116         componentShard
117             .name()
118             .nestedClass(
119                 componentShard.getUniqueClassName(
120                     LOWER_CAMEL.to(UPPER_CAMEL, getSimpleName(provisionMethod) + "Provider")));
121     componentShard.addType(
122         COMPONENT_PROVISION_FACTORY,
123         classBuilder(factoryClassName)
124             .addSuperinterface(daggerProviderOf(keyType))
125             .addModifiers(PRIVATE, STATIC, FINAL)
126             .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
127             .addMethod(
128                 constructorBuilder()
129                     .addParameter(dependencyClassName, dependency().variableName())
130                     .addStatement("this.$1L = $1L", dependency().variableName())
131                     .build())
132             .addMethod(getMethod.build())
133             .build());
134     return CodeBlock.of(
135         "new $T($L)",
136         factoryClassName,
137         componentRequirementExpressions.getExpressionDuringInitialization(
138             dependency(), shardImplementation.name()));
139   }
140 
dependency()141   private ComponentRequirement dependency() {
142     return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod);
143   }
144 
145   @AssistedFactory
146   static interface Factory {
create(ProvisionBinding binding)147     DependencyMethodProviderCreationExpression create(ProvisionBinding binding);
148   }
149 }
150