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