1 /* 2 * Copyright (C) 2016 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.writing; 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.CaseFormat.UPPER_UNDERSCORE; 22 import static com.google.common.base.Preconditions.checkArgument; 23 import static com.google.common.base.Preconditions.checkState; 24 import static com.google.common.base.Suppliers.memoize; 25 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 26 import static com.squareup.javapoet.MethodSpec.methodBuilder; 27 import static com.squareup.javapoet.TypeSpec.classBuilder; 28 import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER; 29 import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName; 30 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf; 31 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 32 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 33 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 34 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings; 35 import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames; 36 import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD; 37 import static dagger.internal.codegen.xprocessing.MethodSpecs.overriding; 38 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 39 import static javax.lang.model.element.Modifier.FINAL; 40 import static javax.lang.model.element.Modifier.PRIVATE; 41 import static javax.lang.model.element.Modifier.PUBLIC; 42 import static javax.lang.model.element.Modifier.STATIC; 43 import static javax.tools.Diagnostic.Kind.ERROR; 44 45 import androidx.room.compiler.processing.XExecutableParameterElement; 46 import androidx.room.compiler.processing.XMessager; 47 import androidx.room.compiler.processing.XMethodElement; 48 import androidx.room.compiler.processing.XProcessingEnv; 49 import androidx.room.compiler.processing.XType; 50 import androidx.room.compiler.processing.XTypeElement; 51 import androidx.room.compiler.processing.XVariableElement; 52 import com.google.common.base.Function; 53 import com.google.common.base.Supplier; 54 import com.google.common.collect.ImmutableList; 55 import com.google.common.collect.ImmutableMap; 56 import com.google.common.collect.ImmutableSet; 57 import com.google.common.collect.Iterables; 58 import com.google.common.collect.ListMultimap; 59 import com.google.common.collect.Lists; 60 import com.google.common.collect.MultimapBuilder; 61 import com.google.common.collect.Sets; 62 import com.squareup.javapoet.ClassName; 63 import com.squareup.javapoet.CodeBlock; 64 import com.squareup.javapoet.FieldSpec; 65 import com.squareup.javapoet.MethodSpec; 66 import com.squareup.javapoet.ParameterSpec; 67 import com.squareup.javapoet.TypeName; 68 import com.squareup.javapoet.TypeSpec; 69 import dagger.internal.Preconditions; 70 import dagger.internal.codegen.base.ComponentCreatorKind; 71 import dagger.internal.codegen.base.UniqueNameSet; 72 import dagger.internal.codegen.binding.Binding; 73 import dagger.internal.codegen.binding.BindingGraph; 74 import dagger.internal.codegen.binding.BindingNode; 75 import dagger.internal.codegen.binding.BindingRequest; 76 import dagger.internal.codegen.binding.CancellationPolicy; 77 import dagger.internal.codegen.binding.ComponentCreatorDescriptor; 78 import dagger.internal.codegen.binding.ComponentDescriptor; 79 import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor; 80 import dagger.internal.codegen.binding.ComponentRequirement; 81 import dagger.internal.codegen.binding.KeyVariableNamer; 82 import dagger.internal.codegen.binding.MethodSignature; 83 import dagger.internal.codegen.compileroption.CompilerOptions; 84 import dagger.internal.codegen.javapoet.CodeBlocks; 85 import dagger.internal.codegen.javapoet.TypeNames; 86 import dagger.internal.codegen.javapoet.TypeSpecs; 87 import dagger.internal.codegen.langmodel.Accessibility; 88 import dagger.internal.codegen.model.BindingGraph.Node; 89 import dagger.internal.codegen.model.Key; 90 import dagger.internal.codegen.model.RequestKind; 91 import dagger.internal.codegen.xprocessing.XTypeElements; 92 import java.util.ArrayList; 93 import java.util.HashMap; 94 import java.util.LinkedHashMap; 95 import java.util.List; 96 import java.util.Map; 97 import java.util.Optional; 98 import java.util.Set; 99 import javax.inject.Inject; 100 import javax.inject.Provider; 101 import javax.lang.model.element.Modifier; 102 103 /** The implementation of a component type. */ 104 @PerComponentImplementation 105 public final class ComponentImplementation { 106 /** A factory for creating a {@link ComponentImplementation}. */ 107 public interface ChildComponentImplementationFactory { 108 /** Creates a {@link ComponentImplementation} for the given {@code childGraph}. */ create(BindingGraph childGraph)109 ComponentImplementation create(BindingGraph childGraph); 110 } 111 112 /** Compiler Modes. */ 113 public enum CompilerMode { 114 DEFAULT, 115 FAST_INIT; 116 isFastInit()117 public boolean isFastInit() { 118 return this == CompilerMode.FAST_INIT; 119 } 120 } 121 122 /** A type of field that this component can contain. */ 123 public enum FieldSpecKind { 124 /** A field for a component shard. */ 125 COMPONENT_SHARD_FIELD, 126 127 /** A field required by the component, e.g. module instances. */ 128 COMPONENT_REQUIREMENT_FIELD, 129 130 /** A framework field for type T, e.g. {@code Provider<T>}. */ 131 FRAMEWORK_FIELD, 132 133 /** A static field that always returns an absent {@code Optional} value for the binding. */ 134 ABSENT_OPTIONAL_FIELD 135 } 136 137 /** A type of method that this component can contain. */ 138 // TODO(bcorso, dpb): Change the oder to constructor, initialize, component, then private 139 // (including MIM and AOM—why treat those separately?). 140 public enum MethodSpecKind { 141 /** The component constructor. */ 142 CONSTRUCTOR, 143 144 /** A builder method for the component. (Only used by the root component.) */ 145 BUILDER_METHOD, 146 147 /** A private method that wraps dependency expressions. */ 148 PRIVATE_METHOD, 149 150 /** An initialization method that initializes component requirements and framework types. */ 151 INITIALIZE_METHOD, 152 153 /** An implementation of a component interface method. */ 154 COMPONENT_METHOD, 155 156 /** A private method that encapsulates members injection logic for a binding. */ 157 MEMBERS_INJECTION_METHOD, 158 159 /** A static method that always returns an absent {@code Optional} value for the binding. */ 160 ABSENT_OPTIONAL_METHOD, 161 162 /** 163 * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)} 164 * method for a production component. 165 */ 166 CANCELLATION_LISTENER_METHOD 167 } 168 169 /** A type of nested class that this component can contain. */ 170 public enum TypeSpecKind { 171 /** A factory class for a present optional binding. */ 172 PRESENT_FACTORY, 173 174 /** A class for the component creator (only used by the root component.) */ 175 COMPONENT_CREATOR, 176 177 /** A provider class for a component provision. */ 178 COMPONENT_PROVISION_FACTORY, 179 180 /** A class for the component/subcomponent or subcomponent builder implementation. */ 181 COMPONENT_IMPL, 182 183 /** A class for a component shard. */ 184 COMPONENT_SHARD_TYPE 185 } 186 187 /** 188 * Returns the {@link ShardImplementation} for each binding in this graph. 189 * 190 * <p>Each shard contains approximately {@link CompilerOptions#keysPerComponentShard()} bindings. 191 * 192 * <p>If more than 1 shard is needed, we iterate the strongly connected nodes to make sure of two 193 * things: 1) bindings are put in shards in reverse topological order (i.e., bindings in Shard{i} 194 * do not depend on bindings in Shard{i+j}) and 2) bindings belonging to the same cycle are put in 195 * the same shard. These two guarantees allow us to initialize each shard in a well defined order. 196 */ createShardsByBinding( ShardImplementation componentShard, BindingGraph graph, CompilerOptions compilerOptions)197 private static ImmutableMap<Binding, ShardImplementation> createShardsByBinding( 198 ShardImplementation componentShard, BindingGraph graph, CompilerOptions compilerOptions) { 199 ImmutableList<ImmutableList<Binding>> partitions = bindingPartitions(graph, compilerOptions); 200 ImmutableMap.Builder<Binding, ShardImplementation> builder = ImmutableMap.builder(); 201 for (int i = 0; i < partitions.size(); i++) { 202 ShardImplementation shard = i == 0 ? componentShard : componentShard.createShard(); 203 partitions.get(i).forEach(binding -> builder.put(binding, shard)); 204 } 205 return builder.build(); 206 } 207 bindingPartitions( BindingGraph graph, CompilerOptions compilerOptions)208 private static ImmutableList<ImmutableList<Binding>> bindingPartitions( 209 BindingGraph graph, CompilerOptions compilerOptions) { 210 int bindingsPerShard = compilerOptions.keysPerComponentShard(graph.componentTypeElement()); 211 int maxPartitions = (graph.localBindingNodes().size() / bindingsPerShard) + 1; 212 if (maxPartitions <= 1) { 213 return ImmutableList.of( 214 graph.localBindingNodes().stream().map(BindingNode::delegate).collect(toImmutableList())); 215 } 216 217 // Iterate through all SCCs in order until all bindings local to this component are partitioned. 218 List<Binding> currPartition = new ArrayList<>(bindingsPerShard); 219 ImmutableList.Builder<ImmutableList<Binding>> partitions = 220 ImmutableList.builderWithExpectedSize(maxPartitions); 221 for (ImmutableSet<Node> nodes : graph.topLevelBindingGraph().stronglyConnectedNodes()) { 222 nodes.stream() 223 .flatMap(instancesOf(BindingNode.class)) 224 .filter(bindingNode -> bindingNode.componentPath().equals(graph.componentPath())) 225 .map(BindingNode::delegate) 226 .forEach(currPartition::add); 227 if (currPartition.size() >= bindingsPerShard) { 228 partitions.add(ImmutableList.copyOf(currPartition)); 229 currPartition = new ArrayList<>(bindingsPerShard); 230 } 231 } 232 if (!currPartition.isEmpty()) { 233 partitions.add(ImmutableList.copyOf(currPartition)); 234 } 235 return partitions.build(); 236 } 237 238 /** The boolean parameter of the onProducerFutureCancelled method. */ 239 public static final ParameterSpec MAY_INTERRUPT_IF_RUNNING_PARAM = 240 ParameterSpec.builder(boolean.class, "mayInterruptIfRunning").build(); 241 242 private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled"; 243 244 /** 245 * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method 246 * before they get partitioned. 247 */ 248 private static final int STATEMENTS_PER_METHOD = 100; 249 250 private final ShardImplementation componentShard; 251 private final Supplier<ImmutableMap<Binding, ShardImplementation>> shardsByBinding; 252 private final Map<ShardImplementation, FieldSpec> shardFieldsByImplementation = new HashMap<>(); 253 private final List<CodeBlock> shardInitializations = new ArrayList<>(); 254 private final List<CodeBlock> shardCancellations = new ArrayList<>(); 255 private final Optional<ComponentImplementation> parent; 256 private final ChildComponentImplementationFactory childComponentImplementationFactory; 257 private final Provider<GeneratedImplementation> topLevelImplementationProvider; 258 private final Provider<ComponentRequestRepresentations> componentRequestRepresentationsProvider; 259 private final Provider<ComponentCreatorImplementationFactory> 260 componentCreatorImplementationFactoryProvider; 261 private final BindingGraph graph; 262 private final ComponentNames componentNames; 263 private final CompilerOptions compilerOptions; 264 private final ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation; 265 private final XMessager messager; 266 private final CompilerMode compilerMode; 267 private final XProcessingEnv processingEnv; 268 269 @Inject ComponentImplementation( @arentComponent Optional<ComponentImplementation> parent, ChildComponentImplementationFactory childComponentImplementationFactory, @TopLevel Provider<GeneratedImplementation> topLevelImplementationProvider, Provider<ComponentRequestRepresentations> componentRequestRepresentationsProvider, Provider<ComponentCreatorImplementationFactory> componentCreatorImplementationFactoryProvider, BindingGraph graph, ComponentNames componentNames, CompilerOptions compilerOptions, XMessager messager, XProcessingEnv processingEnv)270 ComponentImplementation( 271 @ParentComponent Optional<ComponentImplementation> parent, 272 ChildComponentImplementationFactory childComponentImplementationFactory, 273 // Inject as Provider<> to prevent a cycle. 274 @TopLevel Provider<GeneratedImplementation> topLevelImplementationProvider, 275 Provider<ComponentRequestRepresentations> componentRequestRepresentationsProvider, 276 Provider<ComponentCreatorImplementationFactory> componentCreatorImplementationFactoryProvider, 277 BindingGraph graph, 278 ComponentNames componentNames, 279 CompilerOptions compilerOptions, 280 XMessager messager, 281 XProcessingEnv processingEnv) { 282 this.parent = parent; 283 this.childComponentImplementationFactory = childComponentImplementationFactory; 284 this.topLevelImplementationProvider = topLevelImplementationProvider; 285 this.componentRequestRepresentationsProvider = componentRequestRepresentationsProvider; 286 this.componentCreatorImplementationFactoryProvider = 287 componentCreatorImplementationFactoryProvider; 288 this.graph = graph; 289 this.componentNames = componentNames; 290 this.compilerOptions = compilerOptions; 291 this.processingEnv = processingEnv; 292 293 // The first group of keys belong to the component itself. We call this the componentShard. 294 this.componentShard = new ShardImplementation(componentNames.get(graph.componentPath())); 295 296 // Claim the method names for all local and inherited methods on the component type. 297 XTypeElements.getAllNonPrivateInstanceMethods(graph.componentTypeElement()).stream() 298 .forEach(method -> componentShard.componentMethodNames.claim(getSimpleName(method))); 299 300 // Create the shards for this component, indexed by binding. 301 this.shardsByBinding = 302 memoize(() -> createShardsByBinding(componentShard, graph, compilerOptions)); 303 304 // Create and claim the fields for this and all ancestor components stored as fields. 305 this.componentFieldsByImplementation = 306 createComponentFieldsByImplementation(this, compilerOptions); 307 this.messager = messager; 308 XTypeElement typeElement = rootComponentImplementation().componentDescriptor().typeElement(); 309 this.compilerMode = 310 compilerOptions.fastInit(typeElement) ? CompilerMode.FAST_INIT : CompilerMode.DEFAULT; 311 } 312 313 /** 314 * Returns the shard for a given {@link Binding}. 315 * 316 * <p>Each set of {@link CompilerOptions#keysPerShard()} will get its own shard instance. 317 */ shardImplementation(Binding binding)318 public ShardImplementation shardImplementation(Binding binding) { 319 checkState( 320 shardsByBinding.get().containsKey(binding), "No shard in %s for: %s", name(), binding); 321 return shardsByBinding.get().get(binding); 322 } 323 324 /** Returns the {@link GeneratedImplementation} for the top-level generated class. */ topLevelImplementation()325 private GeneratedImplementation topLevelImplementation() { 326 return topLevelImplementationProvider.get(); 327 } 328 329 /** Returns the root {@link ComponentImplementation}. */ rootComponentImplementation()330 public ComponentImplementation rootComponentImplementation() { 331 return parent.map(ComponentImplementation::rootComponentImplementation).orElse(this); 332 } 333 334 /** Returns a reference to this implementation when called from a different class. */ componentFieldReference()335 public CodeBlock componentFieldReference() { 336 // TODO(bcorso): This currently relies on all requesting classes having a reference to the 337 // component with the same name, which is kind of sketchy. Try to think of a better way that 338 // can accomodate the component missing in some classes if it's not used. 339 return CodeBlock.of("$N", componentFieldsByImplementation.get(this)); 340 } 341 342 /** Returns the fields for all components in the component path. */ componentFields()343 public ImmutableList<FieldSpec> componentFields() { 344 return ImmutableList.copyOf(componentFieldsByImplementation.values()); 345 } 346 347 /** Returns the fields for all components in the component path except the current component. */ creatorComponentFields()348 public ImmutableList<FieldSpec> creatorComponentFields() { 349 return componentFieldsByImplementation.entrySet().stream() 350 .filter(entry -> !this.equals(entry.getKey())) 351 .map(Map.Entry::getValue) 352 .collect(toImmutableList()); 353 } 354 355 private static ImmutableMap<ComponentImplementation, FieldSpec> createComponentFieldsByImplementation( ComponentImplementation componentImplementation, CompilerOptions compilerOptions)356 createComponentFieldsByImplementation( 357 ComponentImplementation componentImplementation, CompilerOptions compilerOptions) { 358 checkArgument( 359 componentImplementation.componentShard != null, 360 "The component shard must be set before computing the component fields."); 361 ImmutableList.Builder<ComponentImplementation> builder = ImmutableList.builder(); 362 for (ComponentImplementation curr = componentImplementation; 363 curr != null; 364 curr = curr.parent.orElse(null)) { 365 builder.add(curr); 366 } 367 // For better readability when adding these fields/parameters to generated code, we collect the 368 // component implementations in reverse order so that parents appear before children. 369 return builder.build().reverse().stream() 370 .collect( 371 toImmutableMap( 372 componentImpl -> componentImpl, 373 componentImpl -> { 374 ClassName component = 375 componentImpl.graph.componentPath().currentComponent().className(); 376 ClassName fieldType = componentImpl.name(); 377 String fieldName = 378 componentImpl.isNested() 379 ? simpleVariableName(componentImpl.name()) 380 : simpleVariableName(component); 381 FieldSpec.Builder field = 382 FieldSpec.builder( 383 fieldType, 384 fieldName.equals(componentImpl.name().simpleName()) 385 ? "_" + fieldName 386 : fieldName, 387 PRIVATE, 388 FINAL); 389 componentImplementation.componentShard.componentFieldNames.claim(fieldName); 390 391 return field.build(); 392 })); 393 } 394 /** Returns the shard representing the {@link ComponentImplementation} itself. */ getComponentShard()395 public ShardImplementation getComponentShard() { 396 return componentShard; 397 } 398 399 /** Returns the binding graph for the component being generated. */ graph()400 public BindingGraph graph() { 401 return componentShard.graph(); 402 } 403 404 /** Returns the descriptor for the component being generated. */ componentDescriptor()405 public ComponentDescriptor componentDescriptor() { 406 return componentShard.componentDescriptor(); 407 } 408 409 /** Returns the name of the component. */ name()410 public ClassName name() { 411 return componentShard.name; 412 } 413 414 /** Returns if the current compile mode is fast init. */ compilerMode()415 public CompilerMode compilerMode() { 416 return compilerMode; 417 } 418 419 /** Returns whether or not the implementation is nested within another class. */ isNested()420 private boolean isNested() { 421 return name().enclosingClassName() != null; 422 } 423 424 /** 425 * Returns the name of the creator class for this component. It will be a sibling of this 426 * generated class unless this is a top-level component, in which case it will be nested. 427 */ getCreatorName()428 public ClassName getCreatorName() { 429 return componentNames.getCreatorName(graph.componentPath()); 430 } 431 432 /** Generates the component and returns the resulting {@link TypeSpec}. */ generate()433 public TypeSpec generate() { 434 return componentShard.generate(); 435 } 436 437 /** 438 * The implementation of a shard. 439 * 440 * <p>The purpose of a shard is to allow a component implemenation to be split into multiple 441 * classes, where each class owns the creation logic for a set of keys. Sharding is useful for 442 * large component implementations, where a single component implementation class can reach size 443 * limitations, such as the constant pool size. 444 * 445 * <p>When generating the actual sources, the creation logic within the first instance of {@link 446 * ShardImplementation} will go into the component implementation class itself (e.g. {@code 447 * MySubcomponentImpl}). Each subsequent instance of {@link ShardImplementation} will generate a 448 * nested "shard" class within the component implementation (e.g. {@code 449 * MySubcomponentImpl.Shard1}, {@code MySubcomponentImpl.Shard2}, etc). 450 */ 451 public final class ShardImplementation implements GeneratedImplementation { 452 private final ClassName name; 453 private final UniqueNameSet componentFieldNames = new UniqueNameSet(); 454 private final UniqueNameSet componentMethodNames = new UniqueNameSet(); 455 private final UniqueNameSet componentClassNames = new UniqueNameSet(); 456 private final UniqueNameSet assistedParamNames = new UniqueNameSet(); 457 private final List<CodeBlock> initializations = new ArrayList<>(); 458 private final SwitchingProviders switchingProviders; 459 private final LazyClassKeyProviders lazyClassKeyProviders; 460 private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>(); 461 private final Map<XVariableElement, String> uniqueAssistedName = new LinkedHashMap<>(); 462 private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>(); 463 private final ImmutableMap<ComponentRequirement, ParameterSpec> constructorParameters; 464 private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap = 465 MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build(); 466 private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap = 467 MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build(); 468 private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap = 469 MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build(); 470 private final List<Supplier<TypeSpec>> typeSuppliers = new ArrayList<>(); 471 private boolean initialized = false; // This is used for initializing assistedParamNames. 472 ShardImplementation(ClassName name)473 private ShardImplementation(ClassName name) { 474 this.name = name; 475 this.switchingProviders = new SwitchingProviders(this, processingEnv); 476 this.lazyClassKeyProviders = new LazyClassKeyProviders(this); 477 if (graph.componentDescriptor().isProduction()) { 478 claimMethodName(CANCELLATION_LISTENER_METHOD_NAME); 479 } 480 481 // Build the map of constructor parameters for this shard and claim the field names to prevent 482 // collisions between the constructor parameters and fields. 483 constructorParameters = 484 constructorRequirements(graph).stream() 485 .collect( 486 toImmutableMap( 487 requirement -> requirement, 488 requirement -> 489 ParameterSpec.builder( 490 requirement.type().getTypeName(), 491 getUniqueFieldName(requirement.variableName() + "Param")) 492 .build())); 493 } 494 createShard()495 private ShardImplementation createShard() { 496 checkState(isComponentShard(), "Only the componentShard can create other shards."); 497 return new ShardImplementation( 498 topLevelImplementation() 499 .name() 500 .nestedClass( 501 topLevelImplementation() 502 .getUniqueClassName(getComponentShard().name().simpleName() + "Shard"))); 503 } 504 505 /** Returns the {@link SwitchingProviders} class for this shard. */ getSwitchingProviders()506 public SwitchingProviders getSwitchingProviders() { 507 return switchingProviders; 508 } 509 getLazyClassKeyProviders()510 public LazyClassKeyProviders getLazyClassKeyProviders() { 511 return lazyClassKeyProviders; 512 } 513 514 /** Returns the {@link ComponentImplementation} that owns this shard. */ getComponentImplementation()515 public ComponentImplementation getComponentImplementation() { 516 return ComponentImplementation.this; 517 } 518 519 /** 520 * Returns {@code true} if this shard represents the component implementation rather than a 521 * separate {@code Shard} class. 522 */ isComponentShard()523 public boolean isComponentShard() { 524 return this == componentShard; 525 } 526 527 /** Returns the fields for all components in the component path by component implementation. */ componentFieldsByImplementation()528 public ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation() { 529 return componentFieldsByImplementation; 530 } 531 532 /** Returns a reference to this implementation when called from a different class. */ shardFieldReference()533 public CodeBlock shardFieldReference() { 534 if (!isComponentShard() && !shardFieldsByImplementation.containsKey(this)) { 535 // Add the shard if this is the first time it's requested by something. 536 String shardFieldName = 537 componentShard.getUniqueFieldName(UPPER_CAMEL.to(LOWER_CAMEL, name.simpleName())); 538 FieldSpec shardField = FieldSpec.builder(name, shardFieldName, PRIVATE).build(); 539 540 shardFieldsByImplementation.put(this, shardField); 541 } 542 // TODO(bcorso): This currently relies on all requesting classes having a reference to the 543 // component with the same name, which is kind of sketchy. Try to think of a better way that 544 // can accomodate the component missing in some classes if it's not used. 545 return isComponentShard() 546 ? componentFieldReference() 547 : CodeBlock.of("$L.$N", componentFieldReference(), shardFieldsByImplementation.get(this)); 548 } 549 550 // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that 551 // need it. 552 /** Returns the binding graph for the component being generated. */ graph()553 public BindingGraph graph() { 554 return graph; 555 } 556 557 /** Returns the descriptor for the component being generated. */ componentDescriptor()558 public ComponentDescriptor componentDescriptor() { 559 return graph.componentDescriptor(); 560 } 561 562 @Override name()563 public ClassName name() { 564 return name; 565 } 566 567 /** 568 * Returns the name of the creator implementation class for the given subcomponent creator 569 * {@link Key}. 570 */ getSubcomponentCreatorSimpleName(Key creatorKey)571 ClassName getSubcomponentCreatorSimpleName(Key creatorKey) { 572 return componentNames.getSubcomponentCreatorName(graph.componentPath(), creatorKey); 573 } 574 575 /** 576 * Returns an accessible type for this shard implementation, returns Object if the type is not 577 * accessible. 578 * 579 * <p>This method checks accessibility for public types and package private types. 580 */ accessibleTypeName(XType type)581 TypeName accessibleTypeName(XType type) { 582 return Accessibility.accessibleTypeName(type, name(), processingEnv); 583 } 584 585 /** 586 * Returns {@code true} if {@code type} is accessible from the generated component. 587 * 588 * <p>This method checks accessibility for public types and package private types. 589 */ isTypeAccessible(XType type)590 boolean isTypeAccessible(XType type) { 591 return Accessibility.isTypeAccessibleFrom(type, name.packageName()); 592 } 593 594 // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name? 595 /** Adds the given field to the component. */ 596 @Override addField(FieldSpecKind fieldKind, FieldSpec fieldSpec)597 public void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) { 598 fieldSpecsMap.put(fieldKind, fieldSpec); 599 } 600 601 // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name? 602 /** Adds the given method to the component. */ 603 @Override addMethod(MethodSpecKind methodKind, MethodSpec methodSpec)604 public void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) { 605 methodSpecsMap.put(methodKind, methodSpec); 606 } 607 608 /** Adds the given type to the component. */ 609 @Override addType(TypeSpecKind typeKind, TypeSpec typeSpec)610 public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) { 611 typeSpecsMap.put(typeKind, typeSpec); 612 } 613 614 /** Adds a {@link Supplier} for the SwitchingProvider for the component. */ addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier)615 void addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier) { 616 typeSuppliers.add(typeSpecSupplier); 617 } 618 619 /** Adds the given code block to the initialize methods of the component. */ addInitialization(CodeBlock codeBlock)620 void addInitialization(CodeBlock codeBlock) { 621 initializations.add(codeBlock); 622 } 623 624 /** Adds the given code block that initializes a {@link ComponentRequirement}. */ addComponentRequirementInitialization(CodeBlock codeBlock)625 void addComponentRequirementInitialization(CodeBlock codeBlock) { 626 componentRequirementInitializations.add(codeBlock); 627 } 628 629 /** 630 * Adds the given cancellation statement to the cancellation listener method of the component. 631 */ addCancellation(Key key, CodeBlock codeBlock)632 void addCancellation(Key key, CodeBlock codeBlock) { 633 // Store cancellations by key to avoid adding the same cancellation twice. 634 cancellations.putIfAbsent(key, codeBlock); 635 } 636 637 /** Returns a new, unique field name for the component based on the given name. */ getUniqueFieldName(String name)638 String getUniqueFieldName(String name) { 639 return componentFieldNames.getUniqueName(name); 640 } 641 getUniqueAssistedParamName(String name)642 String getUniqueAssistedParamName(String name) { 643 if (!initialized) { 644 // Assisted params will be used in switching provider, so they can't conflict with component 645 // field names in switching provider. {@link UniqueNameSet#getUniqueName} will add the 646 // component field names to the unique set if it does not exists. If the name already exists 647 // in the set, then a dedupe will be performed automatically on the passed in name, and the 648 // newly created unique name will then be added to the set. 649 componentFieldsByImplementation() 650 .values() 651 .forEach(fieldSpec -> assistedParamNames.getUniqueName(fieldSpec.name)); 652 initialized = true; 653 } 654 return assistedParamNames.getUniqueName(name); 655 } 656 getUniqueFieldNameForAssistedParam(XExecutableParameterElement parameter)657 public String getUniqueFieldNameForAssistedParam(XExecutableParameterElement parameter) { 658 if (uniqueAssistedName.containsKey(parameter)) { 659 return uniqueAssistedName.get(parameter); 660 } 661 String name = getUniqueAssistedParamName(parameter.getJvmName()); 662 uniqueAssistedName.put(parameter, name); 663 return name; 664 } 665 666 /** Returns a new, unique nested class name for the component based on the given name. */ getUniqueMethodName(String name)667 public String getUniqueMethodName(String name) { 668 return componentMethodNames.getUniqueName(name); 669 } 670 671 /** Returns a new, unique method name for a getter method for the given request. */ getUniqueMethodName(BindingRequest request)672 String getUniqueMethodName(BindingRequest request) { 673 return uniqueMethodName(request, KeyVariableNamer.name(request.key())); 674 } 675 676 @Override getUniqueClassName(String name)677 public String getUniqueClassName(String name) { 678 return componentClassNames.getUniqueName(name); 679 } 680 uniqueMethodName(BindingRequest request, String bindingName)681 private String uniqueMethodName(BindingRequest request, String bindingName) { 682 // This name is intentionally made to match the name for fields in fastInit 683 // in order to reduce the constant pool size. b/162004246 684 String baseMethodName = 685 bindingName 686 + (request.isRequestKind(RequestKind.INSTANCE) 687 ? "" 688 : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName())); 689 return getUniqueMethodName(baseMethodName); 690 } 691 692 /** 693 * Gets the parameter name to use for the given requirement for this component, starting with 694 * the given base name if no parameter name has already been selected for the requirement. 695 */ getParameterName(ComponentRequirement requirement)696 public String getParameterName(ComponentRequirement requirement) { 697 return constructorParameters.get(requirement).name; 698 } 699 700 /** Claims a new method name for the component. Does nothing if method name already exists. */ claimMethodName(CharSequence name)701 public void claimMethodName(CharSequence name) { 702 componentMethodNames.claim(name); 703 } 704 705 @Override generate()706 public TypeSpec generate() { 707 TypeSpec.Builder builder = classBuilder(name); 708 709 if (isComponentShard()) { 710 TypeSpecs.addSupertype(builder, graph.componentTypeElement()); 711 addCreator(); 712 addFactoryMethods(); 713 addInterfaceMethods(); 714 addChildComponents(); 715 addShards(); 716 } 717 718 addConstructorAndInitializationMethods(); 719 720 if (graph.componentDescriptor().isProduction()) { 721 if (isComponentShard() || !cancellations.isEmpty()) { 722 TypeSpecs.addSupertype( 723 builder, processingEnv.requireTypeElement(TypeNames.CANCELLATION_LISTENER)); 724 addCancellationListenerImplementation(); 725 } 726 } 727 728 modifiers().forEach(builder::addModifiers); 729 fieldSpecsMap.asMap().values().forEach(builder::addFields); 730 methodSpecsMap.asMap().values().forEach(builder::addMethods); 731 typeSpecsMap.asMap().values().forEach(builder::addTypes); 732 typeSuppliers.stream().map(Supplier::get).forEach(builder::addType); 733 734 if (!compilerOptions.generatedClassExtendsComponent() 735 && isComponentShard() 736 && graph.componentPath().atRoot()) { 737 topLevelImplementation().addType(TypeSpecKind.COMPONENT_IMPL, builder.build()); 738 return topLevelImplementation().generate(); 739 } 740 741 return builder.build(); 742 } 743 modifiers()744 private ImmutableSet<Modifier> modifiers() { 745 return isNested() || !isComponentShard() 746 ? ImmutableSet.of(PRIVATE, STATIC, FINAL) 747 : graph.componentTypeElement().isPublic() 748 // TODO(ronshapiro): perhaps all generated components should be non-public? 749 ? ImmutableSet.of(PUBLIC, FINAL) 750 : ImmutableSet.of(FINAL); 751 } 752 addCreator()753 private void addCreator() { 754 componentCreatorImplementationFactoryProvider 755 .get() 756 .create() 757 .map(ComponentCreatorImplementation::spec) 758 .ifPresent( 759 creator -> topLevelImplementation().addType(TypeSpecKind.COMPONENT_CREATOR, creator)); 760 } 761 addFactoryMethods()762 private void addFactoryMethods() { 763 if (parent.isPresent()) { 764 graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod); 765 } else { 766 createRootComponentFactoryMethod(); 767 } 768 } 769 createRootComponentFactoryMethod()770 private void createRootComponentFactoryMethod() { 771 checkState(!parent.isPresent()); 772 // Top-level components have a static method that returns a builder or factory for the 773 // component. If the user defined a @Component.Builder or @Component.Factory, an 774 // implementation of their type is returned. Otherwise, an autogenerated Builder type is 775 // returned. 776 // TODO(cgdecker): Replace this abomination with a small class? 777 // Better yet, change things so that an autogenerated builder type has a descriptor of sorts 778 // just like a user-defined creator type. 779 ComponentCreatorKind creatorKind; 780 ClassName creatorType; 781 String factoryMethodName; 782 boolean noArgFactoryMethod; 783 Optional<ComponentCreatorDescriptor> creatorDescriptor = 784 graph.componentDescriptor().creatorDescriptor(); 785 if (creatorDescriptor.isPresent()) { 786 ComponentCreatorDescriptor descriptor = creatorDescriptor.get(); 787 creatorKind = descriptor.kind(); 788 creatorType = descriptor.typeElement().getClassName(); 789 factoryMethodName = getSimpleName(descriptor.factoryMethod()); 790 noArgFactoryMethod = descriptor.factoryParameters().isEmpty(); 791 } else { 792 creatorKind = BUILDER; 793 creatorType = getCreatorName(); 794 factoryMethodName = "build"; 795 noArgFactoryMethod = true; 796 } 797 validateMethodNameDoesNotOverrideGeneratedCreator(creatorKind.methodName()); 798 claimMethodName(creatorKind.methodName()); 799 topLevelImplementation() 800 .addMethod( 801 MethodSpecKind.BUILDER_METHOD, 802 methodBuilder(creatorKind.methodName()) 803 .addModifiers(PUBLIC, STATIC) 804 .returns(creatorType) 805 .addStatement("return new $T()", getCreatorName()) 806 .build()); 807 if (noArgFactoryMethod && canInstantiateAllRequirements()) { 808 validateMethodNameDoesNotOverrideGeneratedCreator("create"); 809 claimMethodName("create"); 810 topLevelImplementation() 811 .addMethod( 812 MethodSpecKind.BUILDER_METHOD, 813 methodBuilder("create") 814 .returns(graph.componentTypeElement().getClassName()) 815 .addModifiers(PUBLIC, STATIC) 816 .addStatement("return new $L().$L()", creatorKind.typeName(), factoryMethodName) 817 .build()); 818 } 819 } 820 821 // TODO(bcorso): This can be removed once we delete generatedClassExtendsComponent flag. validateMethodNameDoesNotOverrideGeneratedCreator(String creatorName)822 private void validateMethodNameDoesNotOverrideGeneratedCreator(String creatorName) { 823 // Check if there is any client added method has the same signature as generated creatorName. 824 XTypeElements.getAllMethods(graph.componentTypeElement()).stream() 825 .filter(method -> getSimpleName(method).contentEquals(creatorName)) 826 .filter(method -> method.getParameters().isEmpty()) 827 .filter(method -> !method.isStatic()) 828 .forEach( 829 (XMethodElement method) -> 830 messager.printMessage( 831 ERROR, 832 String.format( 833 "The method %s.%s() conflicts with a method of the same name Dagger is " 834 + "trying to generate as a way to instantiate the component. Please " 835 + "choose a different name for your method.", 836 method.getEnclosingElement().getClassName().canonicalName(), 837 getSimpleName(method)))); 838 } 839 840 /** {@code true} if all of the graph's required dependencies can be automatically constructed */ canInstantiateAllRequirements()841 private boolean canInstantiateAllRequirements() { 842 return !Iterables.any( 843 graph.componentRequirements(), ComponentRequirement::requiresAPassedInstance); 844 } 845 createSubcomponentFactoryMethod(XMethodElement factoryMethod)846 private void createSubcomponentFactoryMethod(XMethodElement factoryMethod) { 847 checkState(parent.isPresent()); 848 XType parentType = parent.get().graph().componentTypeElement().getType(); 849 MethodSpec.Builder method = overriding(factoryMethod, parentType); 850 // Use the parameter names from the overriding method, which may be different from the 851 // parameter names at the declaration site if it is pulled in as a class dependency from a 852 // separate build unit (see https://github.com/google/dagger/issues/3401). 853 method.parameters.forEach( 854 param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param)); 855 method.addStatement( 856 "return new $T($L)", 857 name(), 858 parameterNames( 859 ImmutableList.<ParameterSpec>builder() 860 .addAll( 861 creatorComponentFields().stream() 862 .map(field -> ParameterSpec.builder(field.type, field.name).build()) 863 .collect(toImmutableList())) 864 .addAll(method.parameters) 865 .build())); 866 867 parent.get().getComponentShard().addMethod(COMPONENT_METHOD, method.build()); 868 } 869 addInterfaceMethods()870 private void addInterfaceMethods() { 871 // Each component method may have been declared by several supertypes. We want to implement 872 // only one method for each distinct signature. 873 XType componentType = graph.componentTypeElement().getType(); 874 Set<MethodSignature> signatures = Sets.newHashSet(); 875 for (ComponentMethodDescriptor method : graph.componentDescriptor().entryPointMethods()) { 876 if (signatures.add( 877 MethodSignature.forComponentMethod(method, componentType, processingEnv))) { 878 addMethod( 879 COMPONENT_METHOD, 880 componentRequestRepresentationsProvider.get().getComponentMethod(method)); 881 } 882 } 883 } 884 addChildComponents()885 private void addChildComponents() { 886 for (BindingGraph subgraph : graph.subgraphs()) { 887 topLevelImplementation() 888 .addType( 889 TypeSpecKind.COMPONENT_IMPL, 890 childComponentImplementationFactory.create(subgraph).generate()); 891 } 892 } 893 addShards()894 private void addShards() { 895 // Generate all shards and add them to this component implementation. 896 for (ShardImplementation shard : ImmutableSet.copyOf(shardsByBinding.get().values())) { 897 if (shardFieldsByImplementation.containsKey(shard)) { 898 addField(FieldSpecKind.COMPONENT_SHARD_FIELD, shardFieldsByImplementation.get(shard)); 899 TypeSpec shardTypeSpec = shard.generate(); 900 topLevelImplementation().addType(TypeSpecKind.COMPONENT_SHARD_TYPE, shardTypeSpec); 901 } 902 } 903 } 904 905 /** Creates and adds the constructor and methods needed for initializing the component. */ addConstructorAndInitializationMethods()906 private void addConstructorAndInitializationMethods() { 907 MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE); 908 ImmutableList<ParameterSpec> parameters = constructorParameters.values().asList(); 909 910 // Add a constructor parameter and initialization for each component field. We initialize 911 // these fields immediately so that we don't need to be pass them to each initialize method 912 // and shard constructor. 913 componentFieldsByImplementation() 914 .forEach( 915 (componentImplementation, field) -> { 916 if (isComponentShard() 917 && componentImplementation.equals(ComponentImplementation.this)) { 918 // For the self-referenced component field, 919 // just initialize it in the initializer. 920 addField( 921 FieldSpecKind.COMPONENT_REQUIREMENT_FIELD, 922 field.toBuilder().initializer("this").build()); 923 } else { 924 addField(FieldSpecKind.COMPONENT_REQUIREMENT_FIELD, field); 925 constructor.addStatement("this.$1N = $1N", field); 926 constructor.addParameter(field.type, field.name); 927 } 928 }); 929 if (isComponentShard()) { 930 constructor.addCode(CodeBlocks.concat(componentRequirementInitializations)); 931 } 932 constructor.addParameters(parameters); 933 934 // TODO(cgdecker): It's not the case that each initialize() method has need for all of the 935 // given parameters. In some cases, those parameters may have already been assigned to fields 936 // which could be referenced instead. In other cases, an initialize method may just not need 937 // some of the parameters because the set of initializations in that partition does not 938 // include any reference to them. Right now, the Dagger code has no way of getting that 939 // information because, among other things, componentImplementation.getImplementations() just 940 // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know 941 // yet whether a field will end up needing to be created for a specific requirement, and we 942 // don't want to create a field that ends up only being used during initialization. 943 CodeBlock args = parameterNames(parameters); 944 ImmutableList<MethodSpec> initializationMethods = 945 createPartitionedMethods( 946 "initialize", 947 // TODO(bcorso): Rather than passing in all of the constructor parameters, keep track 948 // of which parameters are used during initialization and only pass those. This could 949 // be useful for FastInit, where most of the initializations are just calling 950 // SwitchingProvider with no parameters. 951 makeFinal(parameters), 952 initializations, 953 methodName -> 954 methodBuilder(methodName) 955 /* TODO(gak): Strictly speaking, we only need the suppression here if we are 956 * also initializing a raw field in this method, but the structure of this 957 * code makes it awkward to pass that bit through. This will be cleaned up 958 * when we no longer separate fields and initialization as we do now. */ 959 .addAnnotation(suppressWarnings(UNCHECKED))); 960 961 for (MethodSpec initializationMethod : initializationMethods) { 962 constructor.addStatement("$N($L)", initializationMethod, args); 963 addMethod(MethodSpecKind.INITIALIZE_METHOD, initializationMethod); 964 } 965 966 if (isComponentShard()) { 967 constructor.addCode(CodeBlocks.concat(shardInitializations)); 968 } else { 969 // This initialization is called from the componentShard, so we need to use those args. 970 CodeBlock componentArgs = 971 parameterNames(componentShard.constructorParameters.values().asList()); 972 CodeBlock componentFields = 973 componentFieldsByImplementation().values().stream() 974 .map(field -> CodeBlock.of("$N", field)) 975 .collect(CodeBlocks.toParametersCodeBlock()); 976 shardInitializations.add( 977 CodeBlock.of( 978 "$N = new $T($L);", 979 shardFieldsByImplementation.get(this), 980 name, 981 componentArgs.isEmpty() 982 ? componentFields 983 : CodeBlocks.makeParametersCodeBlock( 984 ImmutableList.of(componentFields, componentArgs)))); 985 } 986 987 addMethod(MethodSpecKind.CONSTRUCTOR, constructor.build()); 988 } 989 addCancellationListenerImplementation()990 private void addCancellationListenerImplementation() { 991 MethodSpec.Builder methodBuilder = 992 methodBuilder(CANCELLATION_LISTENER_METHOD_NAME) 993 .addModifiers(PUBLIC) 994 .addAnnotation(Override.class) 995 .addParameter(MAY_INTERRUPT_IF_RUNNING_PARAM); 996 997 // Reversing should order cancellations starting from entry points and going down to leaves 998 // rather than the other way around. This shouldn't really matter but seems *slightly* 999 // preferable because: 1000 // When a future that another future depends on is cancelled, that cancellation will propagate 1001 // up the future graph toward the entry point. Cancelling in reverse order should ensure that 1002 // everything that depends on a particular node has already been cancelled when that node is 1003 // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might 1004 // propagate through most of the graph, making most of the cancel calls that follow in the 1005 // onProducerFutureCancelled method do nothing. 1006 if (isComponentShard()) { 1007 methodBuilder.addCode( 1008 CodeBlocks.concat(ImmutableList.copyOf(shardCancellations).reverse())); 1009 } else if (!cancellations.isEmpty()) { 1010 shardCancellations.add( 1011 CodeBlock.of( 1012 "$N.$N($N);", 1013 shardFieldsByImplementation.get(this), 1014 CANCELLATION_LISTENER_METHOD_NAME, 1015 MAY_INTERRUPT_IF_RUNNING_PARAM)); 1016 } 1017 1018 ImmutableList<CodeBlock> cancellationStatements = 1019 ImmutableList.copyOf(cancellations.values()).reverse(); 1020 if (cancellationStatements.size() < STATEMENTS_PER_METHOD) { 1021 methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build(); 1022 } else { 1023 ImmutableList<MethodSpec> cancelProducersMethods = 1024 createPartitionedMethods( 1025 "cancelProducers", 1026 ImmutableList.of(MAY_INTERRUPT_IF_RUNNING_PARAM), 1027 cancellationStatements, 1028 methodName -> methodBuilder(methodName).addModifiers(PRIVATE)); 1029 for (MethodSpec cancelProducersMethod : cancelProducersMethods) { 1030 methodBuilder.addStatement( 1031 "$N($N)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING_PARAM); 1032 addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, cancelProducersMethod); 1033 } 1034 } 1035 1036 if (isComponentShard()) { 1037 cancelParentStatement().ifPresent(methodBuilder::addCode); 1038 } 1039 1040 addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, methodBuilder.build()); 1041 } 1042 cancelParentStatement()1043 private Optional<CodeBlock> cancelParentStatement() { 1044 if (!shouldPropagateCancellationToParent()) { 1045 return Optional.empty(); 1046 } 1047 return Optional.of( 1048 CodeBlock.builder() 1049 .addStatement( 1050 "$L.$N($N)", 1051 parent.get().componentFieldReference(), 1052 CANCELLATION_LISTENER_METHOD_NAME, 1053 MAY_INTERRUPT_IF_RUNNING_PARAM) 1054 .build()); 1055 } 1056 shouldPropagateCancellationToParent()1057 private boolean shouldPropagateCancellationToParent() { 1058 return parent.isPresent() 1059 && parent 1060 .get() 1061 .componentDescriptor() 1062 .cancellationPolicy() 1063 .map(policy -> policy.equals(CancellationPolicy.PROPAGATE)) 1064 .orElse(false); 1065 } 1066 1067 /** 1068 * Creates one or more methods, all taking the given {@code parameters}, which partition the 1069 * given list of {@code statements} among themselves such that no method has more than {@code 1070 * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in 1071 * order, will execute the {@code statements} in the given order. 1072 */ createPartitionedMethods( String methodName, Iterable<ParameterSpec> parameters, List<CodeBlock> statements, Function<String, MethodSpec.Builder> methodBuilderCreator)1073 private ImmutableList<MethodSpec> createPartitionedMethods( 1074 String methodName, 1075 Iterable<ParameterSpec> parameters, 1076 List<CodeBlock> statements, 1077 Function<String, MethodSpec.Builder> methodBuilderCreator) { 1078 return Lists.partition(statements, STATEMENTS_PER_METHOD).stream() 1079 .map( 1080 partition -> 1081 methodBuilderCreator 1082 .apply(getUniqueMethodName(methodName)) 1083 .addModifiers(PRIVATE) 1084 .addParameters(parameters) 1085 .addCode(CodeBlocks.concat(partition)) 1086 .build()) 1087 .collect(toImmutableList()); 1088 } 1089 } 1090 constructorRequirements(BindingGraph graph)1091 private static ImmutableList<ComponentRequirement> constructorRequirements(BindingGraph graph) { 1092 if (graph.componentDescriptor().hasCreator()) { 1093 return graph.componentRequirements().asList(); 1094 } else if (graph.factoryMethod().isPresent()) { 1095 return graph.factoryMethodParameters().keySet().asList(); 1096 } else { 1097 throw new AssertionError( 1098 "Expected either a component creator or factory method but found neither."); 1099 } 1100 } 1101 makeFinal(List<ParameterSpec> parameters)1102 private static ImmutableList<ParameterSpec> makeFinal(List<ParameterSpec> parameters) { 1103 return parameters.stream() 1104 .map(param -> param.toBuilder().addModifiers(FINAL).build()) 1105 .collect(toImmutableList()); 1106 } 1107 } 1108