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