• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.binding;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.collect.Iterables.getOnlyElement;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
23 import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
24 import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
25 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
26 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
27 import static kotlin.streams.jdk8.StreamsKt.asStream;
28 
29 import androidx.room.compiler.processing.XConstructorElement;
30 import androidx.room.compiler.processing.XConstructorType;
31 import androidx.room.compiler.processing.XElement;
32 import androidx.room.compiler.processing.XHasModifiers;
33 import androidx.room.compiler.processing.XMethodElement;
34 import androidx.room.compiler.processing.XMethodType;
35 import androidx.room.compiler.processing.XType;
36 import androidx.room.compiler.processing.XTypeElement;
37 import androidx.room.compiler.processing.XVariableElement;
38 import com.google.auto.common.MoreElements;
39 import com.google.auto.value.AutoValue;
40 import com.google.auto.value.extension.memoized.Memoized;
41 import com.google.common.collect.ImmutableList;
42 import com.google.common.collect.ImmutableMap;
43 import com.google.common.collect.ImmutableSet;
44 import com.squareup.javapoet.ParameterSpec;
45 import com.squareup.javapoet.TypeName;
46 import dagger.assisted.Assisted;
47 import dagger.assisted.AssistedFactory;
48 import dagger.internal.codegen.javapoet.TypeNames;
49 import dagger.spi.model.BindingKind;
50 import java.util.List;
51 import java.util.Optional;
52 import javax.lang.model.element.VariableElement;
53 
54 /** Assisted injection utility methods. */
55 public final class AssistedInjectionAnnotations {
56   /** Returns the factory method for the given factory {@link XTypeElement}. */
assistedFactoryMethod(XTypeElement factory)57   public static XMethodElement assistedFactoryMethod(XTypeElement factory) {
58     return getOnlyElement(assistedFactoryMethods(factory));
59   }
60 
61   /** Returns the list of abstract factory methods for the given factory {@link XTypeElement}. */
assistedFactoryMethods(XTypeElement factory)62   public static ImmutableSet<XMethodElement> assistedFactoryMethods(XTypeElement factory) {
63     return asStream(factory.getAllNonPrivateInstanceMethods())
64         .filter(XHasModifiers::isAbstract)
65         .filter(method -> !method.isJavaDefault())
66         .collect(toImmutableSet());
67   }
68 
69   /** Returns {@code true} if the element uses assisted injection. */
isAssistedInjectionType(XTypeElement typeElement)70   public static boolean isAssistedInjectionType(XTypeElement typeElement) {
71     return assistedInjectedConstructors(typeElement).stream()
72         .anyMatch(constructor -> constructor.hasAnnotation(TypeNames.ASSISTED_INJECT));
73   }
74 
75   /** Returns {@code true} if this binding is an assisted factory. */
isAssistedFactoryType(XElement element)76   public static boolean isAssistedFactoryType(XElement element) {
77     return element.hasAnnotation(TypeNames.ASSISTED_FACTORY);
78   }
79 
80   /**
81    * Returns the list of assisted parameters as {@link ParameterSpec}s.
82    *
83    * <p>The type of each parameter will be the resolved type given by the binding key, and the name
84    * of each parameter will be the name given in the {@link
85    * dagger.assisted.AssistedInject}-annotated constructor.
86    */
assistedParameterSpecs(Binding binding)87   public static ImmutableList<ParameterSpec> assistedParameterSpecs(Binding binding) {
88     checkArgument(binding.kind() == BindingKind.ASSISTED_INJECTION);
89     XConstructorElement constructor = asConstructor(binding.bindingElement().get());
90     XConstructorType constructorType = constructor.asMemberOf(binding.key().type().xprocessing());
91     return assistedParameterSpecs(constructor.getParameters(), constructorType.getParameterTypes());
92   }
93 
assistedParameterSpecs( List<? extends XVariableElement> paramElements, List<XType> paramTypes)94   private static ImmutableList<ParameterSpec> assistedParameterSpecs(
95       List<? extends XVariableElement> paramElements, List<XType> paramTypes) {
96     ImmutableList.Builder<ParameterSpec> assistedParameterSpecs = ImmutableList.builder();
97     for (int i = 0; i < paramElements.size(); i++) {
98       XVariableElement paramElement = paramElements.get(i);
99       XType paramType = paramTypes.get(i);
100       if (isAssistedParameter(paramElement)) {
101         assistedParameterSpecs.add(
102             ParameterSpec.builder(paramType.getTypeName(), getSimpleName(paramElement)).build());
103       }
104     }
105     return assistedParameterSpecs.build();
106   }
107 
108   /**
109    * Returns the list of assisted factory parameters as {@link ParameterSpec}s.
110    *
111    * <p>The type of each parameter will be the resolved type given by the binding key, and the name
112    * of each parameter will be the name given in the {@link
113    * dagger.assisted.AssistedInject}-annotated constructor.
114    */
assistedFactoryParameterSpecs(Binding binding)115   public static ImmutableList<ParameterSpec> assistedFactoryParameterSpecs(Binding binding) {
116     checkArgument(binding.kind() == BindingKind.ASSISTED_FACTORY);
117 
118     XTypeElement factory = asTypeElement(binding.bindingElement().get());
119     AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType());
120     XMethodType factoryMethodType =
121         metadata.factoryMethod().asMemberOf(binding.key().type().xprocessing());
122     return assistedParameterSpecs(
123         // Use the order of the parameters from the @AssistedFactory method but use the parameter
124         // names of the @AssistedInject constructor.
125         metadata.assistedFactoryAssistedParameters().stream()
126             .map(metadata.assistedInjectAssistedParametersMap()::get)
127             .collect(toImmutableList()),
128         factoryMethodType.getParameterTypes());
129   }
130 
131   /** Returns the constructors in {@code type} that are annotated with {@link AssistedInject}. */
assistedInjectedConstructors(XTypeElement type)132   public static ImmutableSet<XConstructorElement> assistedInjectedConstructors(XTypeElement type) {
133     return type.getConstructors().stream()
134         .filter(constructor -> constructor.hasAnnotation(TypeNames.ASSISTED_INJECT))
135         .collect(toImmutableSet());
136   }
137 
assistedParameters(Binding binding)138   public static ImmutableList<XVariableElement> assistedParameters(Binding binding) {
139     return binding.kind() == BindingKind.ASSISTED_INJECTION
140         ? asConstructor(binding.bindingElement().get()).getParameters().stream()
141             .filter(AssistedInjectionAnnotations::isAssistedParameter)
142             .collect(toImmutableList())
143         : ImmutableList.of();
144   }
145 
146   /** Returns {@code true} if this binding is uses assisted injection. */
isAssistedParameter(XVariableElement param)147   public static boolean isAssistedParameter(XVariableElement param) {
148     return param.hasAnnotation(TypeNames.ASSISTED);
149   }
150 
151   /** Returns {@code true} if this binding is uses assisted injection. */
isAssistedParameter(VariableElement param)152   public static boolean isAssistedParameter(VariableElement param) {
153     return isAnnotationPresent(MoreElements.asVariable(param), TypeNames.ASSISTED);
154   }
155 
156   /** Metadata about an {@link dagger.assisted.AssistedFactory} annotated type. */
157   @AutoValue
158   public abstract static class AssistedFactoryMetadata {
create(XType factoryType)159     public static AssistedFactoryMetadata create(XType factoryType) {
160       XTypeElement factoryElement = factoryType.getTypeElement();
161       XMethodElement factoryMethod = assistedFactoryMethod(factoryElement);
162       XMethodType factoryMethodType = factoryMethod.asMemberOf(factoryType);
163       XType assistedInjectType = factoryMethodType.getReturnType();
164       XTypeElement assistedInjectElement = assistedInjectType.getTypeElement();
165       return new AutoValue_AssistedInjectionAnnotations_AssistedFactoryMetadata(
166           factoryElement,
167           factoryType,
168           factoryMethod,
169           factoryMethodType,
170           assistedInjectElement,
171           assistedInjectType,
172           AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType),
173           AssistedInjectionAnnotations.assistedFactoryAssistedParameters(
174               factoryMethod, factoryMethodType));
175     }
176 
factory()177     public abstract XTypeElement factory();
178 
factoryType()179     public abstract XType factoryType();
180 
factoryMethod()181     public abstract XMethodElement factoryMethod();
182 
factoryMethodType()183     public abstract XMethodType factoryMethodType();
184 
assistedInjectElement()185     public abstract XTypeElement assistedInjectElement();
186 
assistedInjectType()187     public abstract XType assistedInjectType();
188 
assistedInjectAssistedParameters()189     public abstract ImmutableList<AssistedParameter> assistedInjectAssistedParameters();
190 
assistedFactoryAssistedParameters()191     public abstract ImmutableList<AssistedParameter> assistedFactoryAssistedParameters();
192 
193     @Memoized
assistedInjectAssistedParametersMap()194     public ImmutableMap<AssistedParameter, XVariableElement> assistedInjectAssistedParametersMap() {
195       ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
196       for (AssistedParameter assistedParameter : assistedInjectAssistedParameters()) {
197         builder.put(assistedParameter, assistedParameter.element());
198       }
199       return builder.build();
200     }
201 
202     @Memoized
203     public ImmutableMap<AssistedParameter, XVariableElement>
assistedFactoryAssistedParametersMap()204         assistedFactoryAssistedParametersMap() {
205       ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
206       for (AssistedParameter assistedParameter : assistedFactoryAssistedParameters()) {
207         builder.put(assistedParameter, assistedParameter.element());
208       }
209       return builder.build();
210     }
211   }
212 
213   /**
214    * Metadata about an {@link Assisted} annotated parameter.
215    *
216    * <p>This parameter can represent an {@link Assisted} annotated parameter from an {@link
217    * AssistedInject} constructor or an {@link AssistedFactory} method.
218    */
219   @AutoValue
220   public abstract static class AssistedParameter {
create(XVariableElement parameter, XType parameterType)221     public static AssistedParameter create(XVariableElement parameter, XType parameterType) {
222       AssistedParameter assistedParameter =
223           new AutoValue_AssistedInjectionAnnotations_AssistedParameter(
224               Optional.ofNullable(parameter.getAnnotation(TypeNames.ASSISTED))
225                   .map(assisted -> assisted.getAsString("value"))
226                   .orElse(""),
227               parameterType.getTypeName());
228       assistedParameter.parameterElement = parameter;
229       assistedParameter.parameterType = parameterType;
230       return assistedParameter;
231     }
232 
233     private XVariableElement parameterElement;
234     private XType parameterType;
235 
236     /** Returns the string qualifier from the {@link Assisted#value()}. */
qualifier()237     public abstract String qualifier();
238 
239     /** Returns the type annotated with {@link Assisted}. */
typeName()240     abstract TypeName typeName();
241 
242     /** Returns the type annotated with {@link Assisted}. */
type()243     public final XType type() {
244       return parameterType;
245     }
246 
element()247     public final XVariableElement element() {
248       return parameterElement;
249     }
250 
251     @Override
toString()252     public final String toString() {
253       return qualifier().isEmpty()
254           ? String.format("@Assisted %s", type())
255           : String.format("@Assisted(\"%s\") %s", qualifier(), type());
256     }
257   }
258 
assistedInjectAssistedParameters( XType assistedInjectType)259   public static ImmutableList<AssistedParameter> assistedInjectAssistedParameters(
260       XType assistedInjectType) {
261     // We keep track of the constructor both as an ExecutableElement to access @Assisted
262     // parameters and as an ExecutableType to access the resolved parameter types.
263     XConstructorElement assistedInjectConstructor =
264         getOnlyElement(assistedInjectedConstructors(assistedInjectType.getTypeElement()));
265     XConstructorType assistedInjectConstructorType =
266         assistedInjectConstructor.asMemberOf(assistedInjectType);
267 
268     ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
269     for (int i = 0; i < assistedInjectConstructor.getParameters().size(); i++) {
270       XVariableElement parameter = assistedInjectConstructor.getParameters().get(i);
271       XType parameterType = assistedInjectConstructorType.getParameterTypes().get(i);
272       if (parameter.hasAnnotation(TypeNames.ASSISTED)) {
273         builder.add(AssistedParameter.create(parameter, parameterType));
274       }
275     }
276     return builder.build();
277   }
278 
assistedFactoryAssistedParameters( XMethodElement factoryMethod, XMethodType factoryMethodType)279   private static ImmutableList<AssistedParameter> assistedFactoryAssistedParameters(
280       XMethodElement factoryMethod, XMethodType factoryMethodType) {
281     ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
282     for (int i = 0; i < factoryMethod.getParameters().size(); i++) {
283       XVariableElement parameter = factoryMethod.getParameters().get(i);
284       XType parameterType = factoryMethodType.getParameterTypes().get(i);
285       builder.add(AssistedParameter.create(parameter, parameterType));
286     }
287     return builder.build();
288   }
289 
AssistedInjectionAnnotations()290   private AssistedInjectionAnnotations() {}
291 }
292