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