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