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