Home | History | Annotate | Download | only in codegen
      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.
     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    */
    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    */
    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 
    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    */
    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    */
    226   static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
    227     return generatedClassNameForBinding(binding).withTypeParameters(bindingTypeParameters(binding));
    228   }
    229 
    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 
    276   static ClassName membersInjectorNameForType(TypeElement typeElement) {
    277     ClassName injectedClassName = ClassName.fromTypeElement(typeElement);
    278     return injectedClassName
    279         .topLevelClassName()
    280         .peerNamed(injectedClassName.classFileName() + "_MembersInjector");
    281   }
    282 
    283   static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
    284     ClassName componentName = ClassName.fromTypeElement(componentElement);
    285     return componentName
    286         .topLevelClassName()
    287         .peerNamed(componentName.classFileName() + "_MonitoringModule");
    288   }
    289 
    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 
    306   private SourceFiles() {}
    307 }
    308