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.methodBuilder; 21 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; 22 import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf; 23 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf; 24 import static dagger.internal.codegen.xprocessing.XElements.asMethod; 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 29 import com.squareup.javapoet.CodeBlock; 30 import com.squareup.javapoet.FieldSpec; 31 import com.squareup.javapoet.TypeName; 32 import dagger.assisted.Assisted; 33 import dagger.assisted.AssistedFactory; 34 import dagger.assisted.AssistedInject; 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.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; 39 40 /** 41 * A {@link dagger.producers.Producer} creation expression for a production method on a production 42 * component's {@linkplain dagger.producers.ProductionComponent#dependencies()} dependency} that 43 * returns a {@link com.google.common.util.concurrent.ListenableFuture}. 44 */ 45 // TODO(dpb): Resolve with DependencyMethodProviderCreationExpression. 46 final class DependencyMethodProducerCreationExpression 47 implements FrameworkInstanceCreationExpression { 48 private final ContributionBinding binding; 49 private final ComponentImplementation componentImplementation; 50 private final ComponentRequirementExpressions componentRequirementExpressions; 51 private final BindingGraph graph; 52 53 @AssistedInject DependencyMethodProducerCreationExpression( @ssisted ContributionBinding binding, ComponentImplementation componentImplementation, ComponentRequirementExpressions componentRequirementExpressions, BindingGraph graph)54 DependencyMethodProducerCreationExpression( 55 @Assisted ContributionBinding binding, 56 ComponentImplementation componentImplementation, 57 ComponentRequirementExpressions componentRequirementExpressions, 58 BindingGraph graph) { 59 this.binding = checkNotNull(binding); 60 this.componentImplementation = componentImplementation; 61 this.componentRequirementExpressions = componentRequirementExpressions; 62 this.graph = graph; 63 } 64 65 @Override creationExpression()66 public CodeBlock creationExpression() { 67 ComponentRequirement dependency = 68 graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get()); 69 FieldSpec dependencyField = 70 FieldSpec.builder( 71 dependency.typeElement().getClassName(), dependency.variableName(), PRIVATE, FINAL) 72 .initializer( 73 componentRequirementExpressions.getExpressionDuringInitialization( 74 dependency, 75 // This isn't a real class name, but we want the requesting class for the 76 // expression to *not* be the same class as the component implementation, 77 // because it isn't... it's an anonymous inner class. 78 // TODO(cgdecker): If we didn't use an anonymous inner class here but instead 79 // generated a named nested class as with 80 // DependencyMethodProviderCreationExpression, we wouldn't need to deal with 81 // this and might be able to avoid potentially creating an extra field in the 82 // component? 83 componentImplementation.name().nestedClass("Anonymous"))) 84 .build(); 85 // TODO(b/70395982): Explore using a private static type instead of an anonymous class. 86 TypeName keyType = binding.key().type().xprocessing().getTypeName(); 87 return CodeBlock.of( 88 "$L", 89 anonymousClassBuilder("") 90 .superclass(dependencyMethodProducerOf(keyType)) 91 .addField(dependencyField) 92 .addMethod( 93 methodBuilder("callDependencyMethod") 94 .addAnnotation(Override.class) 95 .addModifiers(PUBLIC) 96 .returns(listenableFutureOf(keyType)) 97 .addStatement( 98 "return $N.$L()", 99 dependencyField, 100 asMethod(binding.bindingElement().get()).getJvmName()) 101 .build()) 102 .build()); 103 } 104 105 @AssistedFactory 106 static interface Factory { create(ContributionBinding binding)107 DependencyMethodProducerCreationExpression create(ContributionBinding binding); 108 } 109 } 110