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 javax.lang.model.element.Modifier.ABSTRACT; 20 import static javax.lang.model.element.Modifier.PROTECTED; 21 22 import com.squareup.javapoet.ClassName; 23 import com.squareup.javapoet.CodeBlock; 24 import com.squareup.javapoet.MethodSpec; 25 import com.squareup.javapoet.TypeName; 26 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; 27 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod; 28 import dagger.internal.codegen.javapoet.Expression; 29 import dagger.internal.codegen.langmodel.Accessibility; 30 import dagger.internal.codegen.langmodel.DaggerTypes; 31 import java.util.Optional; 32 import javax.lang.model.type.TypeMirror; 33 34 /** 35 * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be 36 * satisfied when generating the abstract base class implementation of a subcomponent. The 37 * (unimplemented) method is added to the {@link ComponentImplementation} when the dependency 38 * expression is requested. The method is overridden when generating the implementation of an 39 * ancestor component. 40 */ 41 abstract class ModifiableAbstractMethodBindingExpression extends BindingExpression { 42 private final ComponentImplementation componentImplementation; 43 private final ModifiableBindingType modifiableBindingType; 44 private final BindingRequest request; 45 private final Optional<ComponentMethodDescriptor> matchingComponentMethod; 46 private final DaggerTypes types; 47 private Optional<String> methodName; 48 ModifiableAbstractMethodBindingExpression( ComponentImplementation componentImplementation, ModifiableBindingType modifiableBindingType, BindingRequest request, Optional<ModifiableBindingMethod> matchingModifiableBindingMethod, Optional<ComponentMethodDescriptor> matchingComponentMethod, DaggerTypes types)49 ModifiableAbstractMethodBindingExpression( 50 ComponentImplementation componentImplementation, 51 ModifiableBindingType modifiableBindingType, 52 BindingRequest request, 53 Optional<ModifiableBindingMethod> matchingModifiableBindingMethod, 54 Optional<ComponentMethodDescriptor> matchingComponentMethod, 55 DaggerTypes types) { 56 this.componentImplementation = componentImplementation; 57 this.modifiableBindingType = modifiableBindingType; 58 this.request = request; 59 this.matchingComponentMethod = matchingComponentMethod; 60 this.types = types; 61 this.methodName = 62 initializeMethodName(matchingComponentMethod, matchingModifiableBindingMethod); 63 } 64 65 /** 66 * If this binding corresponds to an existing component method, or a known modifiable binding 67 * method, use them to initialize the method name, which is a signal to call the existing method 68 * rather than emit an abstract method. 69 */ initializeMethodName( Optional<ComponentMethodDescriptor> matchingComponentMethod, Optional<ModifiableBindingMethod> matchingModifiableBindingMethod)70 private static Optional<String> initializeMethodName( 71 Optional<ComponentMethodDescriptor> matchingComponentMethod, 72 Optional<ModifiableBindingMethod> matchingModifiableBindingMethod) { 73 if (matchingComponentMethod.isPresent()) { 74 return Optional.of(matchingComponentMethod.get().methodElement().getSimpleName().toString()); 75 } 76 if (matchingModifiableBindingMethod.isPresent()) { 77 return Optional.of(matchingModifiableBindingMethod.get().methodSpec().name); 78 } 79 return Optional.empty(); 80 } 81 82 @Override getDependencyExpression(ClassName requestingClass)83 final Expression getDependencyExpression(ClassName requestingClass) { 84 addUnimplementedMethod(); 85 return Expression.create( 86 returnType(), 87 componentImplementation.name().equals(requestingClass) 88 ? CodeBlock.of("$N()", methodName.get()) 89 : CodeBlock.of("$T.this.$N()", componentImplementation.name(), methodName.get())); 90 } 91 addUnimplementedMethod()92 private void addUnimplementedMethod() { 93 if (!methodName.isPresent()) { 94 // Only add the method once in case of repeated references to the missing binding. 95 methodName = Optional.of(chooseMethodName()); 96 TypeMirror returnType = returnType(); 97 componentImplementation.addModifiableBindingMethod( 98 modifiableBindingType, 99 request, 100 returnType, 101 MethodSpec.methodBuilder(methodName.get()) 102 .addModifiers(PROTECTED, ABSTRACT) 103 .returns(TypeName.get(returnType)) 104 .build(), 105 false /* finalized */); 106 } 107 } 108 109 /** 110 * The return type of this abstract method expression: 111 * 112 * <ul> 113 * <li>If there's a {@code matchingComponentMethod}, use its return type. 114 * <li>Otherwise, use the {@linkplain DaggerTypes#publiclyAccessibleType(TypeMirror) publicly 115 * accessible type} of the request. We can't use the {@linkplain 116 * Accessibility#isTypeAccessibleFrom(TypeMirror, String) type accessible from the current 117 * implementation's package} because a subclass implementation may be in a different package 118 * from which the request type is not accessible. 119 * </ul> 120 */ returnType()121 private TypeMirror returnType() { 122 if (matchingComponentMethod.isPresent()) { 123 return matchingComponentMethod.get().resolvedReturnType(types); 124 } 125 126 TypeMirror requestedType = request.requestedType(contributedType(), types); 127 return types.publiclyAccessibleType(requestedType); 128 } 129 130 /** 131 * The {@link ContributionBinding#contributedType() type contributed} by the binding of this 132 * expression. For missing bindings, this will be the key type. 133 */ contributedType()134 protected abstract TypeMirror contributedType(); 135 136 /** Returns a unique 'getter' method name for the current component. */ chooseMethodName()137 abstract String chooseMethodName(); 138 } 139