• 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 com.google.common.base.Preconditions.checkNotNull;
20 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
21 import static com.squareup.javapoet.MethodSpec.methodBuilder;
22 import static com.squareup.javapoet.TypeSpec.classBuilder;
23 import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
24 import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
25 import static javax.lang.model.element.Modifier.FINAL;
26 import static javax.lang.model.element.Modifier.PRIVATE;
27 import static javax.lang.model.element.Modifier.PUBLIC;
28 import static javax.lang.model.element.Modifier.STATIC;
29 
30 import com.google.auto.common.MoreTypes;
31 import com.squareup.javapoet.ClassName;
32 import com.squareup.javapoet.CodeBlock;
33 import com.squareup.javapoet.MethodSpec;
34 import com.squareup.javapoet.TypeName;
35 import dagger.internal.codegen.binding.BindingGraph;
36 import dagger.internal.codegen.binding.ComponentRequirement;
37 import dagger.internal.codegen.binding.ContributionBinding;
38 import dagger.internal.codegen.binding.ProvisionBinding;
39 import dagger.internal.codegen.compileroption.CompilerOptions;
40 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
41 import javax.lang.model.element.Element;
42 
43 /**
44  * A {@link javax.inject.Provider} creation expression for a provision method on a component's
45  * {@linkplain dagger.Component#dependencies()} dependency}.
46  */
47 // TODO(dpb): Resolve with DependencyMethodProducerCreationExpression.
48 final class DependencyMethodProviderCreationExpression
49     implements FrameworkInstanceCreationExpression {
50 
51   private final ComponentImplementation componentImplementation;
52   private final ComponentRequirementExpressions componentRequirementExpressions;
53   private final CompilerOptions compilerOptions;
54   private final BindingGraph graph;
55   private final ContributionBinding binding;
56 
DependencyMethodProviderCreationExpression( ContributionBinding binding, ComponentImplementation componentImplementation, ComponentRequirementExpressions componentRequirementExpressions, CompilerOptions compilerOptions, BindingGraph graph)57   DependencyMethodProviderCreationExpression(
58       ContributionBinding binding,
59       ComponentImplementation componentImplementation,
60       ComponentRequirementExpressions componentRequirementExpressions,
61       CompilerOptions compilerOptions,
62       BindingGraph graph) {
63     this.binding = checkNotNull(binding);
64     this.componentImplementation = checkNotNull(componentImplementation);
65     this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
66     this.compilerOptions = checkNotNull(compilerOptions);
67     this.graph = checkNotNull(graph);
68   }
69 
70   @Override
creationExpression()71   public CodeBlock creationExpression() {
72     // TODO(sameb): The Provider.get() throws a very vague NPE.  The stack trace doesn't
73     // help to figure out what the method or return type is.  If we include a string
74     // of the return type or method name in the error message, that can defeat obfuscation.
75     // We can easily include the raw type (no generics) + annotation type (no values),
76     // using .class & String.format -- but that wouldn't be the whole story.
77     // What should we do?
78     CodeBlock invocation =
79         ComponentProvisionBindingExpression.maybeCheckForNull(
80             (ProvisionBinding) binding,
81             compilerOptions,
82             CodeBlock.of(
83                 "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName()));
84     ClassName dependencyClassName = ClassName.get(dependency().typeElement());
85     TypeName keyType = TypeName.get(binding.key().type());
86     MethodSpec.Builder getMethod =
87         methodBuilder("get")
88             .addAnnotation(Override.class)
89             .addModifiers(PUBLIC)
90             .returns(keyType)
91             .addStatement("return $L", invocation);
92     if (binding.nullableType().isPresent()) {
93       getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get())));
94     }
95     componentImplementation.addType(
96         COMPONENT_PROVISION_FACTORY,
97         classBuilder(factoryClassName())
98             .addSuperinterface(providerOf(keyType))
99             .addModifiers(PRIVATE, STATIC, FINAL)
100             .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
101             .addMethod(
102                 constructorBuilder()
103                     .addParameter(dependencyClassName, dependency().variableName())
104                     .addStatement("this.$1L = $1L", dependency().variableName())
105                     .build())
106             .addMethod(getMethod.build())
107             .build());
108     return CodeBlock.of(
109         "new $T($L)",
110         factoryClassName(),
111         componentRequirementExpressions.getExpressionDuringInitialization(
112             dependency(), componentImplementation.name()));
113   }
114 
factoryClassName()115   private ClassName factoryClassName() {
116     String factoryName =
117         ClassName.get(dependency().typeElement()).toString().replace('.', '_')
118             + "_"
119             + binding.bindingElement().get().getSimpleName();
120     return componentImplementation.name().nestedClass(factoryName);
121   }
122 
dependency()123   private ComponentRequirement dependency() {
124     return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod());
125   }
126 
provisionMethod()127   private Element provisionMethod() {
128     return binding.bindingElement().get();
129   }
130 }
131