• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
20 import static com.google.auto.common.MoreElements.asType;
21 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
22 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
23 import static com.google.common.base.Preconditions.checkArgument;
24 import static com.google.common.base.Preconditions.checkState;
25 import static com.google.common.base.Verify.verify;
26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
27 import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
28 import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
29 import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCED_PRODUCER;
30 import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCER_PRODUCER;
31 import static dagger.internal.codegen.javapoet.TypeNames.MAP_PRODUCER;
32 import static dagger.internal.codegen.javapoet.TypeNames.MAP_PROVIDER_FACTORY;
33 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER;
34 import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
35 import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER_OF_LAZY;
36 import static dagger.internal.codegen.javapoet.TypeNames.SET_FACTORY;
37 import static dagger.internal.codegen.javapoet.TypeNames.SET_OF_PRODUCED_PRODUCER;
38 import static dagger.internal.codegen.javapoet.TypeNames.SET_PRODUCER;
39 import static dagger.internal.codegen.xprocessing.XElements.asExecutable;
40 import static dagger.spi.model.BindingKind.ASSISTED_INJECTION;
41 import static dagger.spi.model.BindingKind.INJECTION;
42 import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
43 import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
44 import static javax.lang.model.SourceVersion.isName;
45 
46 import androidx.room.compiler.processing.XExecutableElement;
47 import androidx.room.compiler.processing.XTypeElement;
48 import com.google.auto.common.MoreElements;
49 import com.google.common.base.Joiner;
50 import com.google.common.collect.ImmutableList;
51 import com.google.common.collect.ImmutableMap;
52 import com.google.common.collect.ImmutableSet;
53 import com.google.common.collect.Iterables;
54 import com.google.common.collect.Maps;
55 import com.squareup.javapoet.ClassName;
56 import com.squareup.javapoet.CodeBlock;
57 import com.squareup.javapoet.FieldSpec;
58 import com.squareup.javapoet.ParameterizedTypeName;
59 import com.squareup.javapoet.TypeName;
60 import com.squareup.javapoet.TypeVariableName;
61 import dagger.internal.codegen.base.MapType;
62 import dagger.internal.codegen.base.SetType;
63 import dagger.internal.codegen.javapoet.TypeNames;
64 import dagger.spi.model.DependencyRequest;
65 import dagger.spi.model.RequestKind;
66 import java.util.List;
67 import javax.lang.model.SourceVersion;
68 import javax.lang.model.element.ElementKind;
69 import javax.lang.model.element.ExecutableElement;
70 import javax.lang.model.element.TypeElement;
71 import javax.lang.model.element.TypeParameterElement;
72 import javax.lang.model.element.VariableElement;
73 
74 /** Utilities for generating files. */
75 public class SourceFiles {
76 
77   private static final Joiner CLASS_FILE_NAME_JOINER = Joiner.on('_');
78 
79   /**
80    * Generates names and keys for the factory class fields needed to hold the framework classes for
81    * all of the dependencies of {@code binding}. It is responsible for choosing a name that
82    *
83    * <ul>
84    *   <li>represents all of the dependency requests for this key
85    *   <li>is <i>probably</i> associated with the type being bound
86    *   <li>is unique within the class
87    * </ul>
88    *
89    * @param binding must be an unresolved binding (type parameters must match its type element's)
90    */
91   public static ImmutableMap<DependencyRequest, FrameworkField>
generateBindingFieldsForDependencies(Binding binding)92       generateBindingFieldsForDependencies(Binding binding) {
93     checkArgument(!binding.unresolved().isPresent(), "binding must be unresolved: %s", binding);
94 
95     FrameworkTypeMapper frameworkTypeMapper =
96         FrameworkTypeMapper.forBindingType(binding.bindingType());
97 
98     return Maps.toMap(
99         binding.dependencies(),
100         dependency ->
101             FrameworkField.create(
102                 frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClassName(),
103                 TypeName.get(dependency.key().type().java()),
104                 DependencyVariableNamer.name(dependency)));
105   }
106 
frameworkTypeUsageStatement( CodeBlock frameworkTypeMemberSelect, RequestKind dependencyKind)107   public static CodeBlock frameworkTypeUsageStatement(
108       CodeBlock frameworkTypeMemberSelect, RequestKind dependencyKind) {
109     switch (dependencyKind) {
110       case LAZY:
111         return CodeBlock.of("$T.lazy($L)", DOUBLE_CHECK, frameworkTypeMemberSelect);
112       case INSTANCE:
113       case FUTURE:
114         return CodeBlock.of("$L.get()", frameworkTypeMemberSelect);
115       case PROVIDER:
116       case PRODUCER:
117         return frameworkTypeMemberSelect;
118       case PROVIDER_OF_LAZY:
119         return CodeBlock.of("$T.create($L)", PROVIDER_OF_LAZY, frameworkTypeMemberSelect);
120       default: // including PRODUCED
121         throw new AssertionError(dependencyKind);
122     }
123   }
124 
125   /**
126    * Returns a mapping of {@link DependencyRequest}s to {@link CodeBlock}s that {@linkplain
127    * #frameworkTypeUsageStatement(CodeBlock, RequestKind) use them}.
128    */
frameworkFieldUsages( ImmutableSet<DependencyRequest> dependencies, ImmutableMap<DependencyRequest, FieldSpec> fields)129   public static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
130       ImmutableSet<DependencyRequest> dependencies,
131       ImmutableMap<DependencyRequest, FieldSpec> fields) {
132     return Maps.toMap(
133         dependencies,
134         dep -> frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep)), dep.kind()));
135   }
136 
137   /** Returns the generated factory or members injector name for a binding. */
generatedClassNameForBinding(Binding binding)138   public static ClassName generatedClassNameForBinding(Binding binding) {
139     switch (binding.bindingType()) {
140       case PROVISION:
141       case PRODUCTION:
142         ContributionBinding contribution = (ContributionBinding) binding;
143         switch (contribution.kind()) {
144           case ASSISTED_INJECTION:
145           case INJECTION:
146           case PROVISION:
147           case PRODUCTION:
148             return factoryNameForElement(asExecutable(binding.bindingElement().get()));
149 
150           case ASSISTED_FACTORY:
151             return siblingClassName(asType(toJavac(binding.bindingElement().get())), "_Impl");
152 
153           default:
154             throw new AssertionError();
155         }
156 
157       case MEMBERS_INJECTION:
158         return membersInjectorNameForType(
159             ((MembersInjectionBinding) binding).membersInjectedType());
160     }
161     throw new AssertionError();
162   }
163 
164   /**
165    * Returns the generated factory name for the given element.
166    *
167    * <p>This method is useful during validation before a {@link Binding} can be created. If a
168    * binding already exists for the given element, prefer to call {@link
169    * #generatedClassNameForBinding(Binding)} instead since this method does not validate that the
170    * given element is actually a binding element or not.
171    */
factoryNameForElement(XExecutableElement element)172   public static ClassName factoryNameForElement(XExecutableElement element) {
173     return elementBasedClassName(toJavac(element), "Factory");
174   }
175 
176   /**
177    * Calculates an appropriate {@link ClassName} for a generated class that is based on {@code
178    * element}, appending {@code suffix} at the end.
179    *
180    * <p>This will always return a {@linkplain ClassName#topLevelClassName() top level class name},
181    * even if {@code element}'s enclosing class is a nested type.
182    */
elementBasedClassName(ExecutableElement element, String suffix)183   public static ClassName elementBasedClassName(ExecutableElement element, String suffix) {
184     ClassName enclosingClassName =
185         ClassName.get(MoreElements.asType(element.getEnclosingElement()));
186     String methodName =
187         element.getKind().equals(ElementKind.CONSTRUCTOR)
188             ? ""
189             : LOWER_CAMEL.to(UPPER_CAMEL, element.getSimpleName().toString());
190     return ClassName.get(
191         enclosingClassName.packageName(),
192         classFileName(enclosingClassName) + "_" + methodName + suffix);
193   }
194 
parameterizedGeneratedTypeNameForBinding(Binding binding)195   public static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
196     ClassName className = generatedClassNameForBinding(binding);
197     ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
198     return typeParameters.isEmpty()
199         ? className
200         : ParameterizedTypeName.get(className, Iterables.toArray(typeParameters, TypeName.class));
201   }
202 
membersInjectorNameForType(XTypeElement typeElement)203   public static ClassName membersInjectorNameForType(XTypeElement typeElement) {
204     return membersInjectorNameForType(toJavac(typeElement));
205   }
206 
membersInjectorNameForType(TypeElement typeElement)207   public static ClassName membersInjectorNameForType(TypeElement typeElement) {
208     return siblingClassName(typeElement, "_MembersInjector");
209   }
210 
memberInjectedFieldSignatureForVariable(VariableElement variableElement)211   public static String memberInjectedFieldSignatureForVariable(VariableElement variableElement) {
212     return MoreElements.asType(variableElement.getEnclosingElement()).getQualifiedName()
213         + "."
214         + variableElement.getSimpleName();
215   }
216 
classFileName(ClassName className)217   public static String classFileName(ClassName className) {
218     return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
219   }
220 
generatedMonitoringModuleName(XTypeElement componentElement)221   public static ClassName generatedMonitoringModuleName(XTypeElement componentElement) {
222     return siblingClassName(toJavac(componentElement), "_MonitoringModule");
223   }
224 
225   // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
226   // which could use this.
siblingClassName(TypeElement typeElement, String suffix)227   private static ClassName siblingClassName(TypeElement typeElement, String suffix) {
228     ClassName className = ClassName.get(typeElement);
229     return className.topLevelClassName().peerClass(classFileName(className) + suffix);
230   }
231 
232   /**
233    * The {@link java.util.Set} factory class name appropriate for set bindings.
234    *
235    * <ul>
236    *   <li>{@link dagger.producers.internal.SetFactory} for provision bindings.
237    *   <li>{@link dagger.producers.internal.SetProducer} for production bindings for {@code Set<T>}.
238    *   <li>{@link dagger.producers.internal.SetOfProducedProducer} for production bindings for
239    *       {@code Set<Produced<T>>}.
240    * </ul>
241    */
setFactoryClassName(ContributionBinding binding)242   public static ClassName setFactoryClassName(ContributionBinding binding) {
243     checkArgument(binding.kind().equals(MULTIBOUND_SET));
244     if (binding.bindingType().equals(BindingType.PROVISION)) {
245       return SET_FACTORY;
246     } else {
247       SetType setType = SetType.from(binding.key());
248       return setType.elementsAreTypeOf(TypeNames.PRODUCED)
249           ? SET_OF_PRODUCED_PRODUCER
250           : SET_PRODUCER;
251     }
252   }
253 
254   /** The {@link java.util.Map} factory class name appropriate for map bindings. */
mapFactoryClassName(ContributionBinding binding)255   public static ClassName mapFactoryClassName(ContributionBinding binding) {
256     checkState(binding.kind().equals(MULTIBOUND_MAP), binding.kind());
257     MapType mapType = MapType.from(binding.key());
258     switch (binding.bindingType()) {
259       case PROVISION:
260         return mapType.valuesAreTypeOf(PROVIDER) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
261       case PRODUCTION:
262         return mapType.valuesAreFrameworkType()
263             ? mapType.valuesAreTypeOf(PRODUCER)
264                 ? MAP_OF_PRODUCER_PRODUCER
265                 : MAP_OF_PRODUCED_PRODUCER
266             : MAP_PRODUCER;
267       default:
268         throw new IllegalArgumentException(binding.bindingType().toString());
269     }
270   }
271 
bindingTypeElementTypeVariableNames( Binding binding)272   public static ImmutableList<TypeVariableName> bindingTypeElementTypeVariableNames(
273       Binding binding) {
274     if (binding instanceof ContributionBinding) {
275       ContributionBinding contributionBinding = (ContributionBinding) binding;
276       if (!(contributionBinding.kind() == INJECTION
277               || contributionBinding.kind() == ASSISTED_INJECTION)
278           && !contributionBinding.requiresModuleInstance()) {
279         return ImmutableList.of();
280       }
281     }
282     List<? extends TypeParameterElement> typeParameters =
283         toJavac(binding.bindingTypeElement().get()).getTypeParameters();
284     return typeParameters.stream().map(TypeVariableName::get).collect(toImmutableList());
285   }
286 
287   /**
288    * Returns a name to be used for variables of the given {@linkplain TypeElement type}. Prefer
289    * semantically meaningful variable names, but if none can be derived, this will produce something
290    * readable.
291    */
292   // TODO(gak): maybe this should be a function of TypeMirrors instead of Elements?
simpleVariableName(TypeElement typeElement)293   public static String simpleVariableName(TypeElement typeElement) {
294     return simpleVariableName(ClassName.get(typeElement));
295   }
296 
297   /**
298    * Returns a name to be used for variables of the given {@linkplain ClassName}. Prefer
299    * semantically meaningful variable names, but if none can be derived, this will produce something
300    * readable.
301    */
simpleVariableName(ClassName className)302   public static String simpleVariableName(ClassName className) {
303     String candidateName = UPPER_CAMEL.to(LOWER_CAMEL, className.simpleName());
304     String variableName = protectAgainstKeywords(candidateName);
305     verify(isName(variableName), "'%s' was expected to be a valid variable name");
306     return variableName;
307   }
308 
protectAgainstKeywords(String candidateName)309   public static String protectAgainstKeywords(String candidateName) {
310     switch (candidateName) {
311       case "package":
312         return "pkg";
313       case "boolean":
314         return "b";
315       case "double":
316         return "d";
317       case "byte":
318         return "b";
319       case "int":
320         return "i";
321       case "short":
322         return "s";
323       case "char":
324         return "c";
325       case "void":
326         return "v";
327       case "class":
328         return "clazz";
329       case "float":
330         return "f";
331       case "long":
332         return "l";
333       default:
334         return SourceVersion.isKeyword(candidateName) ? candidateName + '_' : candidateName;
335     }
336   }
337 
SourceFiles()338   private SourceFiles() {}
339 }
340