• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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