1 /* 2 * Copyright (C) 2018 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 dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD; 20 import static javax.lang.model.element.Modifier.PRIVATE; 21 22 import com.squareup.javapoet.CodeBlock; 23 import com.squareup.javapoet.FieldSpec; 24 import com.squareup.javapoet.TypeName; 25 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; 26 import dagger.internal.codegen.javapoet.Expression; 27 import dagger.internal.codegen.langmodel.DaggerTypes; 28 import dagger.model.RequestKind; 29 import dagger.producers.Producer; 30 import dagger.producers.internal.CancellationListener; 31 import dagger.producers.internal.Producers; 32 import java.util.Optional; 33 import javax.lang.model.type.TypeMirror; 34 35 /** 36 * A factory of {@linkplain Producers#entryPointViewOf(Producer, CancellationListener) entry point 37 * views} of {@link Producer}s. 38 */ 39 final class ProducerEntryPointView { 40 private final DaggerTypes types; 41 ProducerEntryPointView(DaggerTypes types)42 ProducerEntryPointView(DaggerTypes types) { 43 this.types = types; 44 } 45 46 /** 47 * Returns an expression for an {@linkplain Producers#entryPointViewOf(Producer, 48 * CancellationListener) entry point view} of a producer if the component method returns a {@link 49 * Producer} or {@link com.google.common.util.concurrent.ListenableFuture}. 50 * 51 * <p>This is intended to be a replacement implementation for {@link 52 * BindingExpression#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor, 53 * ComponentImplementation)}, and in cases where {@link Optional#empty()} is returned, callers 54 * should call {@code super.getDependencyExpressionForComponentMethod()}. 55 */ getProducerEntryPointField( BindingExpression producerExpression, ComponentMethodDescriptor componentMethod, ComponentImplementation component)56 Optional<Expression> getProducerEntryPointField( 57 BindingExpression producerExpression, 58 ComponentMethodDescriptor componentMethod, 59 ComponentImplementation component) { 60 if (component.componentDescriptor().isProduction() 61 && (componentMethod.dependencyRequest().get().kind().equals(RequestKind.FUTURE) 62 || componentMethod.dependencyRequest().get().kind().equals(RequestKind.PRODUCER))) { 63 return Optional.of( 64 Expression.create( 65 fieldType(componentMethod), 66 "$N", 67 createField(producerExpression, componentMethod, component))); 68 } else { 69 // If the component isn't a production component, it won't implement CancellationListener and 70 // as such we can't create an entry point. But this binding must also just be a Producer from 71 // Provider anyway in that case, so there shouldn't be an issue. 72 // TODO(b/116855531): Is it really intended that a non-production component can have Producer 73 // entry points? 74 return Optional.empty(); 75 } 76 } 77 createField( BindingExpression producerExpression, ComponentMethodDescriptor componentMethod, ComponentImplementation component)78 private FieldSpec createField( 79 BindingExpression producerExpression, 80 ComponentMethodDescriptor componentMethod, 81 ComponentImplementation component) { 82 // TODO(cgdecker): Use a FrameworkFieldInitializer for this? 83 // Though I don't think we need the once-only behavior of that, since I think 84 // getComponentMethodImplementation will only be called once anyway 85 String methodName = componentMethod.methodElement().getSimpleName().toString(); 86 FieldSpec field = 87 FieldSpec.builder( 88 TypeName.get(fieldType(componentMethod)), 89 component.getUniqueFieldName(methodName + "EntryPoint"), 90 PRIVATE) 91 .build(); 92 component.addField(FRAMEWORK_FIELD, field); 93 94 CodeBlock fieldInitialization = 95 CodeBlock.of( 96 "this.$N = $T.entryPointViewOf($L, this);", 97 field, 98 Producers.class, 99 producerExpression.getDependencyExpression(component.name()).codeBlock()); 100 component.addInitialization(fieldInitialization); 101 102 return field; 103 } 104 105 // TODO(cgdecker): Can we use producerExpression.getDependencyExpression().type() instead of 106 // needing to (re)compute this? fieldType(ComponentMethodDescriptor componentMethod)107 private TypeMirror fieldType(ComponentMethodDescriptor componentMethod) { 108 return types.wrapType(componentMethod.dependencyRequest().get().key().type(), Producer.class); 109 } 110 } 111