/* * Copyright (C) 2015 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.writing; import static com.google.common.base.Preconditions.checkNotNull; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf; import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.TypeName; import dagger.internal.codegen.binding.BindingGraph; import dagger.internal.codegen.binding.ComponentRequirement; import dagger.internal.codegen.binding.ContributionBinding; import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; /** * A {@link dagger.producers.Producer} creation expression for a production method on a production * component's {@linkplain dagger.producers.ProductionComponent#dependencies()} dependency} that * returns a {@link com.google.common.util.concurrent.ListenableFuture}. */ // TODO(dpb): Resolve with DependencyMethodProviderCreationExpression. final class DependencyMethodProducerCreationExpression implements FrameworkInstanceCreationExpression { private final ContributionBinding binding; private final ComponentImplementation componentImplementation; private final ComponentRequirementExpressions componentRequirementExpressions; private final BindingGraph graph; DependencyMethodProducerCreationExpression( ContributionBinding binding, ComponentImplementation componentImplementation, ComponentRequirementExpressions componentRequirementExpressions, BindingGraph graph) { this.binding = checkNotNull(binding); this.componentImplementation = checkNotNull(componentImplementation); this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions); this.graph = checkNotNull(graph); } @Override public CodeBlock creationExpression() { ComponentRequirement dependency = graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get()); FieldSpec dependencyField = FieldSpec.builder( ClassName.get(dependency.typeElement()), dependency.variableName(), PRIVATE, FINAL) .initializer( componentRequirementExpressions.getExpressionDuringInitialization( dependency, // This isn't a real class name, but we want the requesting class for the // expression to *not* be the same class as the component implementation, // because it isn't... it's an anonymous inner class. // TODO(cgdecker): If we didn't use an anonymous inner class here but instead // generated a named nested class as with // DependencyMethodProviderCreationExpression, we wouldn't need to deal with // this and might be able to avoid potentially creating an extra field in the // component? componentImplementation.name().nestedClass("Anonymous"))) .build(); // TODO(b/70395982): Explore using a private static type instead of an anonymous class. TypeName keyType = TypeName.get(binding.key().type()); return CodeBlock.of( "$L", anonymousClassBuilder("") .superclass(dependencyMethodProducerOf(keyType)) .addField(dependencyField) .addMethod( methodBuilder("callDependencyMethod") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(listenableFutureOf(keyType)) .addStatement( "return $N.$L()", dependencyField, binding.bindingElement().get().getSimpleName()) .build()) .build()); } }