• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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