• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 Google, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package dagger.internal.codegen;
15 
16 import com.google.common.base.CaseFormat;
17 import com.google.common.collect.ComparisonChain;
18 import com.google.common.collect.FluentIterable;
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.ImmutableSetMultimap;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Ordering;
25 import dagger.internal.DoubleCheckLazy;
26 import dagger.internal.codegen.writer.ClassName;
27 import dagger.internal.codegen.writer.ParameterizedTypeName;
28 import dagger.internal.codegen.writer.Snippet;
29 import dagger.internal.codegen.writer.TypeName;
30 import dagger.internal.codegen.writer.TypeNames;
31 import java.util.Collection;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.type.TypeMirror;
38 import javax.lang.model.util.Types;
39 
40 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
41 import static com.google.common.base.Preconditions.checkArgument;
42 
43 /**
44  * Utilities for generating files.
45  *
46  * @author Gregory Kick
47  * @since 2.0
48  */
49 class SourceFiles {
50   /**
51    * Sorts {@link DependencyRequest} instances in an order likely to reflect their logical
52    * importance.
53    */
54   static final Ordering<DependencyRequest> DEPENDENCY_ORDERING = new Ordering<DependencyRequest>() {
55     @Override
56     public int compare(DependencyRequest left, DependencyRequest right) {
57       return ComparisonChain.start()
58       // put fields before parameters
59           .compare(left.requestElement().getKind(), right.requestElement().getKind())
60           // order by dependency kind
61           .compare(left.kind(), right.kind())
62           // then sort by name
63           .compare(left.requestElement().getSimpleName().toString(),
64               right.requestElement().getSimpleName().toString()).result();
65     }
66   };
67 
68   /**
69    * A variant of {@link #indexDependenciesByKey} that maps from unresolved keys
70    * to requests.  This is used when generating component's initialize()
71    * methods (and in members injectors) in order to instantiate dependent
72    * providers.  Consider a generic type of {@code Foo<T>} with a constructor
73    * of {@code Foo(T t, T t1, A a, A a1)}.  That will be collapsed to a factory
74    * taking a {@code Provider<T> tProvider, Provider<A> aProvider}. However,
75    * if it was referenced as {@code Foo<A>}, we need to make sure we still
76    * pass two providers.  Naively (if we just referenced by resolved BindingKey),
77    * we would have passed a single {@code aProvider}.
78    */
79   // TODO(user): Refactor these indexing methods so that the binding itself knows what sort of
80   // binding keys and framework classes that it needs.
indexDependenciesByUnresolvedKey( Types types, Iterable<? extends DependencyRequest> dependencies)81   static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByUnresolvedKey(
82       Types types, Iterable<? extends DependencyRequest> dependencies) {
83     ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder =
84         new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
85             .orderValuesBy(DEPENDENCY_ORDERING);
86     for (DependencyRequest dependency : dependencies) {
87       BindingKey resolved = dependency.bindingKey();
88       // To get the proper unresolved type, we have to extract the proper type from the
89       // request type again (because we're looking at the actual element's type).
90       TypeMirror unresolvedType =
91           DependencyRequest.Factory.extractKindAndType(dependency.requestElement().asType()).type();
92       BindingKey unresolved =
93           BindingKey.create(resolved.kind(), resolved.key().withType(types, unresolvedType));
94       dependenciesByKeyBuilder.put(unresolved, dependency);
95     }
96     return dependenciesByKeyBuilder.build();
97   }
98 
99   /**
100    * Allows dependency requests to be grouped by the key they're requesting.
101    * This is used by factory generation in order to minimize the number of parameters
102    * required in the case where a given key is requested more than once.  This expects
103    * unresolved dependency requests, otherwise we may generate factories based on
104    * a particular usage of a class as opposed to the generic types of the class.
105    */
indexDependenciesByKey( Iterable<? extends DependencyRequest> dependencies)106   static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByKey(
107       Iterable<? extends DependencyRequest> dependencies) {
108     ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder =
109         new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
110             .orderValuesBy(DEPENDENCY_ORDERING);
111     for (DependencyRequest dependency : dependencies) {
112       dependenciesByKeyBuilder.put(dependency.bindingKey(), dependency);
113     }
114     return dependenciesByKeyBuilder.build();
115   }
116 
117   /**
118    * This method generates names and keys for the framework classes necessary for all of the
119    * bindings. It is responsible for the following:
120    * <ul>
121    * <li>Choosing a name that associates the binding with all of the dependency requests for this
122    * type.
123    * <li>Choosing a name that is <i>probably</i> associated with the type being bound.
124    * <li>Ensuring that no two bindings end up with the same name.
125    * </ul>
126    *
127    * @return Returns the mapping from {@link BindingKey} to field, sorted by the name of the field.
128    */
generateBindingFieldsForDependencies( DependencyRequestMapper dependencyRequestMapper, Iterable<? extends DependencyRequest> dependencies)129   static ImmutableMap<BindingKey, FrameworkField> generateBindingFieldsForDependencies(
130       DependencyRequestMapper dependencyRequestMapper,
131       Iterable<? extends DependencyRequest> dependencies) {
132     ImmutableSetMultimap<BindingKey, DependencyRequest> dependenciesByKey =
133         indexDependenciesByKey(dependencies);
134     Map<BindingKey, Collection<DependencyRequest>> dependenciesByKeyMap =
135         dependenciesByKey.asMap();
136     ImmutableMap.Builder<BindingKey, FrameworkField> bindingFields = ImmutableMap.builder();
137     for (Entry<BindingKey, Collection<DependencyRequest>> entry
138         : dependenciesByKeyMap.entrySet()) {
139       BindingKey bindingKey = entry.getKey();
140       Collection<DependencyRequest> requests = entry.getValue();
141       Class<?> frameworkClass =
142           dependencyRequestMapper.getFrameworkClass(requests.iterator().next());
143       // collect together all of the names that we would want to call the provider
144       ImmutableSet<String> dependencyNames =
145           FluentIterable.from(requests).transform(new DependencyVariableNamer()).toSet();
146 
147       if (dependencyNames.size() == 1) {
148         // if there's only one name, great! use it!
149         String name = Iterables.getOnlyElement(dependencyNames);
150         bindingFields.put(bindingKey,
151             FrameworkField.createWithTypeFromKey(frameworkClass, bindingKey, name));
152       } else {
153         // in the event that a field is being used for a bunch of deps with different names,
154         // add all the names together with "And"s in the middle. E.g.: stringAndS
155         Iterator<String> namesIterator = dependencyNames.iterator();
156         String first = namesIterator.next();
157         StringBuilder compositeNameBuilder = new StringBuilder(first);
158         while (namesIterator.hasNext()) {
159           compositeNameBuilder.append("And").append(
160               CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, namesIterator.next()));
161         }
162         bindingFields.put(bindingKey, FrameworkField.createWithTypeFromKey(
163             frameworkClass, bindingKey, compositeNameBuilder.toString()));
164       }
165     }
166     return bindingFields.build();
167   }
168 
frameworkTypeUsageStatement(Snippet frameworkTypeMemberSelect, DependencyRequest.Kind dependencyKind)169   static Snippet frameworkTypeUsageStatement(Snippet frameworkTypeMemberSelect,
170       DependencyRequest.Kind dependencyKind) {
171     switch (dependencyKind) {
172       case LAZY:
173         return Snippet.format("%s.create(%s)", ClassName.fromClass(DoubleCheckLazy.class),
174             frameworkTypeMemberSelect);
175       case INSTANCE:
176       case FUTURE:
177         return Snippet.format("%s.get()", frameworkTypeMemberSelect);
178       case PROVIDER:
179       case PRODUCER:
180       case MEMBERS_INJECTOR:
181         return Snippet.format("%s", frameworkTypeMemberSelect);
182       default:
183         throw new AssertionError();
184     }
185   }
186 
187   /**
188    * Returns the generated factory or members injector name for a binding.
189    */
generatedClassNameForBinding(Binding binding)190   static ClassName generatedClassNameForBinding(Binding binding) {
191     switch (binding.bindingType()) {
192       case PROVISION:
193       case PRODUCTION:
194         ContributionBinding contribution = (ContributionBinding) binding;
195         checkArgument(!contribution.isSyntheticBinding());
196         ClassName enclosingClassName = ClassName.fromTypeElement(contribution.bindingTypeElement());
197         switch (contribution.bindingKind()) {
198           case INJECTION:
199           case PROVISION:
200           case IMMEDIATE:
201           case FUTURE_PRODUCTION:
202             return enclosingClassName
203                 .topLevelClassName()
204                 .peerNamed(
205                     enclosingClassName.classFileName()
206                         + "_"
207                         + factoryPrefix(contribution)
208                         + "Factory");
209 
210           default:
211             throw new AssertionError();
212         }
213 
214       case MEMBERS_INJECTION:
215         return membersInjectorNameForType(binding.bindingTypeElement());
216 
217       default:
218         throw new AssertionError();
219     }
220   }
221 
222   /**
223    * Returns the generated factory or members injector name parameterized with the proper type
224    * parameters if necessary.
225    */
parameterizedGeneratedTypeNameForBinding(Binding binding)226   static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
227     return generatedClassNameForBinding(binding).withTypeParameters(bindingTypeParameters(binding));
228   }
229 
bindingTypeParameters(Binding binding)230   private static ImmutableList<TypeName> bindingTypeParameters(Binding binding)
231       throws AssertionError {
232     TypeMirror bindingType;
233     switch (binding.bindingType()) {
234       case PROVISION:
235       case PRODUCTION:
236         ContributionBinding contributionBinding = (ContributionBinding) binding;
237         if (contributionBinding.contributionType().isMultibinding()) {
238           return ImmutableList.of();
239         }
240         switch (contributionBinding.bindingKind()) {
241           case INJECTION:
242             bindingType = contributionBinding.key().type();
243             break;
244 
245           case PROVISION:
246             // For provision bindings, we parameterize creation on the types of
247             // the module, not the types of the binding.
248             // Consider: Module<A, B, C> { @Provides List<B> provideB(B b) { .. }}
249             // The binding is just parameterized on <B>, but we need all of <A, B, C>.
250             bindingType = contributionBinding.bindingTypeElement().asType();
251             break;
252 
253           case IMMEDIATE:
254           case FUTURE_PRODUCTION:
255             // TODO(beder): Can these be treated just like PROVISION?
256             throw new UnsupportedOperationException();
257 
258           default:
259             return ImmutableList.of();
260         }
261         break;
262 
263       case MEMBERS_INJECTION:
264         bindingType = binding.key().type();
265         break;
266 
267       default:
268         throw new AssertionError();
269     }
270     TypeName bindingTypeName = TypeNames.forTypeMirror(bindingType);
271     return bindingTypeName instanceof ParameterizedTypeName
272         ? ((ParameterizedTypeName) bindingTypeName).parameters()
273         : ImmutableList.<TypeName>of();
274   }
275 
membersInjectorNameForType(TypeElement typeElement)276   static ClassName membersInjectorNameForType(TypeElement typeElement) {
277     ClassName injectedClassName = ClassName.fromTypeElement(typeElement);
278     return injectedClassName
279         .topLevelClassName()
280         .peerNamed(injectedClassName.classFileName() + "_MembersInjector");
281   }
282 
generatedMonitoringModuleName(TypeElement componentElement)283   static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
284     ClassName componentName = ClassName.fromTypeElement(componentElement);
285     return componentName
286         .topLevelClassName()
287         .peerNamed(componentName.classFileName() + "_MonitoringModule");
288   }
289 
factoryPrefix(ContributionBinding binding)290   private static String factoryPrefix(ContributionBinding binding) {
291     switch (binding.bindingKind()) {
292       case INJECTION:
293         return "";
294 
295       case PROVISION:
296       case IMMEDIATE:
297       case FUTURE_PRODUCTION:
298         return CaseFormat.LOWER_CAMEL.to(
299             UPPER_CAMEL, ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
300 
301       default:
302         throw new IllegalArgumentException();
303     }
304   }
305 
SourceFiles()306   private SourceFiles() {}
307 }
308