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