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