2626import static dagger .internal .codegen .binding .SourceFiles .generatedClassNameForBinding ;
2727import static dagger .internal .codegen .binding .SourceFiles .parameterizedGeneratedTypeNameForBinding ;
2828import static dagger .internal .codegen .extension .DaggerStreams .presentValues ;
29+ import static dagger .internal .codegen .extension .DaggerStreams .toImmutableList ;
2930import static dagger .internal .codegen .extension .DaggerStreams .toImmutableMap ;
3031import static dagger .internal .codegen .javapoet .AnnotationSpecs .Suppression .RAWTYPES ;
3132import static dagger .internal .codegen .javapoet .AnnotationSpecs .Suppression .UNCHECKED ;
3233import static dagger .internal .codegen .javapoet .AnnotationSpecs .suppressWarnings ;
33- import static dagger .internal .codegen .javapoet .CodeBlocks .makeParametersCodeBlock ;
34+ import static dagger .internal .codegen .javapoet .CodeBlocks .parameterNames ;
3435import static dagger .internal .codegen .javapoet .TypeNames .factoryOf ;
35- import static dagger .internal .codegen .model .BindingKind .INJECTION ;
3636import static dagger .internal .codegen .model .BindingKind .PROVISION ;
3737import static dagger .internal .codegen .writing .GwtCompatibility .gwtIncompatibleAnnotation ;
3838import static javax .lang .model .element .Modifier .FINAL ;
4747import com .google .common .collect .ImmutableList ;
4848import com .google .common .collect .ImmutableMap ;
4949import com .google .common .collect .ImmutableSet ;
50- import com .google .common .collect .Lists ;
5150import com .squareup .javapoet .AnnotationSpec ;
5251import com .squareup .javapoet .ClassName ;
5352import com .squareup .javapoet .CodeBlock ;
5958import dagger .internal .Factory ;
6059import dagger .internal .codegen .base .SourceFileGenerator ;
6160import dagger .internal .codegen .base .UniqueNameSet ;
62- import dagger .internal .codegen .binding .Binding ;
6361import dagger .internal .codegen .binding .ProvisionBinding ;
6462import dagger .internal .codegen .binding .SourceFiles ;
6563import dagger .internal .codegen .compileroption .CompilerOptions ;
7169import dagger .internal .codegen .model .Scope ;
7270import dagger .internal .codegen .writing .InjectionMethods .InjectionSiteMethod ;
7371import dagger .internal .codegen .writing .InjectionMethods .ProvisionMethod ;
74- import java .util .List ;
7572import java .util .Optional ;
7673import java .util .stream .Stream ;
7774import javax .inject .Inject ;
@@ -123,113 +120,124 @@ private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
123120 .addAnnotation (qualifierMetadataAnnotation (binding ));
124121
125122 factoryTypeName (binding ).ifPresent (factoryBuilder ::addSuperinterface );
126- addConstructorAndFields (binding , factoryBuilder );
127- factoryBuilder .addMethod (getMethod (binding ));
128- addCreateMethod (binding , factoryBuilder );
129-
130- factoryBuilder .addMethod (ProvisionMethod .create (binding , compilerOptions ));
123+ FactoryFields factoryFields = FactoryFields .create (binding );
124+ // If the factory has no input fields we can use a static instance holder to create a
125+ // singleton instance of the factory. Otherwise, we create a new instance via the constructor.
126+ if (factoryFields .isEmpty ()) {
127+ factoryBuilder .addType (staticInstanceHolderType (binding ));
128+ } else {
129+ factoryBuilder
130+ .addFields (factoryFields .getAll ())
131+ .addMethod (constructorMethod (factoryFields ));
132+ }
131133 gwtIncompatibleAnnotation (binding ).ifPresent (factoryBuilder ::addAnnotation );
132134
133- return factoryBuilder ;
135+ return factoryBuilder
136+ .addMethod (getMethod (binding , factoryFields ))
137+ .addMethod (staticCreateMethod (binding , factoryFields ))
138+ .addMethod (staticProvisionMethod (binding ));
134139 }
135140
136- private void addConstructorAndFields (ProvisionBinding binding , TypeSpec .Builder factoryBuilder ) {
137- if (FactoryCreationStrategy .of (binding ) == FactoryCreationStrategy .SINGLETON_INSTANCE ) {
138- return ;
141+ // private static final class InstanceHolder {
142+ // private static final FooModule_ProvidesFooFactory INSTANCE =
143+ // new FooModule_ProvidesFooFactory();
144+ // }
145+ private TypeSpec staticInstanceHolderType (ProvisionBinding binding ) {
146+ ClassName generatedClassName = generatedClassNameForBinding (binding );
147+ FieldSpec .Builder instanceHolderFieldBuilder =
148+ FieldSpec .builder (generatedClassName , "INSTANCE" , PRIVATE , STATIC , FINAL )
149+ .initializer ("new $T()" , generatedClassName );
150+ if (!bindingTypeElementTypeVariableNames (binding ).isEmpty ()) {
151+ // If the factory has type parameters, ignore them in the field declaration & initializer
152+ instanceHolderFieldBuilder .addAnnotation (suppressWarnings (RAWTYPES ));
139153 }
140- // TODO(bcorso): Make the constructor private?
141- MethodSpec .Builder constructor = constructorBuilder ().addModifiers (PUBLIC );
142- constructorParams (binding ).forEach (
143- param -> {
144- constructor .addParameter (param ).addStatement ("this.$1N = $1N" , param );
145- factoryBuilder .addField (
146- FieldSpec .builder (param .type , param .name , PRIVATE , FINAL ).build ());
147- });
148- factoryBuilder .addMethod (constructor .build ());
149- }
150-
151- private ImmutableList <ParameterSpec > constructorParams (ProvisionBinding binding ) {
152- ImmutableList .Builder <ParameterSpec > params = ImmutableList .builder ();
153- moduleParameter (binding ).ifPresent (params ::add );
154- frameworkFields (binding ).values ().forEach (field -> params .add (toParameter (field )));
155- return params .build ();
154+ return TypeSpec .classBuilder (instanceHolderClassName (binding ))
155+ .addModifiers (PRIVATE , STATIC , FINAL )
156+ .addField (instanceHolderFieldBuilder .build ())
157+ .build ();
156158 }
157159
158- private Optional <ParameterSpec > moduleParameter (ProvisionBinding binding ) {
159- if (binding .requiresModuleInstance ()) {
160- // TODO(bcorso, dpb): Should this use contributingModule()?
161- TypeName type = binding .bindingTypeElement ().get ().getType ().getTypeName ();
162- return Optional .of (ParameterSpec .builder (type , "module" ).build ());
163- }
164- return Optional .empty ();
160+ private static ClassName instanceHolderClassName (ProvisionBinding binding ) {
161+ return generatedClassNameForBinding (binding ).nestedClass ("InstanceHolder" );
165162 }
166163
167- private ImmutableMap <DependencyRequest , FieldSpec > frameworkFields (ProvisionBinding binding ) {
168- UniqueNameSet uniqueFieldNames = new UniqueNameSet ();
169- // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
170- moduleParameter (binding ).ifPresent (module -> uniqueFieldNames .claim (module .name ));
171- // We avoid Maps.transformValues here because it would implicitly depend on the order in which
172- // the transform function is evaluated on each entry in the map.
173- ImmutableMap .Builder <DependencyRequest , FieldSpec > builder = ImmutableMap .builder ();
174- generateBindingFieldsForDependencies (binding ).forEach (
175- (dependency , field ) ->
176- builder .put (dependency ,
177- FieldSpec .builder (
178- field .type (), uniqueFieldNames .getUniqueName (field .name ()), PRIVATE , FINAL )
179- .build ()));
180- return builder .build ();
164+ // public FooModule_ProvidesFooFactory(
165+ // FooModule module,
166+ // Provider<Bar> barProvider,
167+ // Provider<Baz> bazProvider) {
168+ // this.module = module;
169+ // this.barProvider = barProvider;
170+ // this.bazProvider = bazProvider;
171+ // }
172+ private MethodSpec constructorMethod (FactoryFields factoryFields ) {
173+ // TODO(bcorso): Make the constructor private?
174+ MethodSpec .Builder constructor = constructorBuilder ().addModifiers (PUBLIC );
175+ factoryFields .getAll ().forEach (
176+ field ->
177+ constructor
178+ .addParameter (field .type , field .name )
179+ .addStatement ("this.$1N = $1N" , field ));
180+ return constructor .build ();
181181 }
182182
183- private void addCreateMethod (ProvisionBinding binding , TypeSpec .Builder factoryBuilder ) {
184- // If constructing a factory for @Inject or @Provides bindings, we use a static create method
185- // so that generated components can avoid having to refer to the generic types
186- // of the factory. (Otherwise they may have visibility problems referring to the types.)
183+ // Example 1: no dependencies.
184+ // public static FooModule_ProvidesFooFactory create() {
185+ // return InstanceHolder.INSTANCE;
186+ // }
187+ //
188+ // Example 2: with dependencies.
189+ // public static FooModule_ProvidesFooFactory create(
190+ // FooModule module,
191+ // Provider<Bar> barProvider,
192+ // Provider<Baz> bazProvider) {
193+ // return new FooModule_ProvidesFooFactory(module, barProvider, bazProvider);
194+ // }
195+ private MethodSpec staticCreateMethod (ProvisionBinding binding , FactoryFields factoryFields ) {
196+ // We use a static create method so that generated components can avoid having to refer to the
197+ // generic types of the factory. (Otherwise they may have visibility problems referring to the
198+ // types.)
187199 MethodSpec .Builder createMethodBuilder =
188200 methodBuilder ("create" )
189201 .addModifiers (PUBLIC , STATIC )
190202 .returns (parameterizedGeneratedTypeNameForBinding (binding ))
191203 .addTypeVariables (bindingTypeElementTypeVariableNames (binding ));
192204
193- switch (FactoryCreationStrategy .of (binding )) {
194- case SINGLETON_INSTANCE :
195- FieldSpec .Builder instanceFieldBuilder =
196- FieldSpec .builder (
197- generatedClassNameForBinding (binding ), "INSTANCE" , PRIVATE , STATIC , FINAL )
198- .initializer ("new $T()" , generatedClassNameForBinding (binding ));
199-
200- if (!bindingTypeElementTypeVariableNames (binding ).isEmpty ()) {
201- // If the factory has type parameters, ignore them in the field declaration & initializer
202- instanceFieldBuilder .addAnnotation (suppressWarnings (RAWTYPES ));
203- createMethodBuilder .addAnnotation (suppressWarnings (UNCHECKED ));
204- }
205-
206- ClassName instanceHolderName =
207- generatedClassNameForBinding (binding ).nestedClass ("InstanceHolder" );
208- createMethodBuilder .addStatement ("return $T.INSTANCE" , instanceHolderName );
209- factoryBuilder .addType (
210- TypeSpec .classBuilder (instanceHolderName )
211- .addModifiers (PRIVATE , STATIC , FINAL )
212- .addField (instanceFieldBuilder .build ())
213- .build ());
214- break ;
215- case CLASS_CONSTRUCTOR :
216- List <ParameterSpec > params = constructorParams (binding );
217- createMethodBuilder .addParameters (params );
218- createMethodBuilder .addStatement (
219- "return new $T($L)" ,
220- parameterizedGeneratedTypeNameForBinding (binding ),
221- makeParametersCodeBlock (Lists .transform (params , input -> CodeBlock .of ("$N" , input ))));
222- break ;
223- default :
224- throw new AssertionError ();
205+ if (factoryFields .isEmpty ()) {
206+ if (!bindingTypeElementTypeVariableNames (binding ).isEmpty ()) {
207+ createMethodBuilder .addAnnotation (suppressWarnings (UNCHECKED ));
208+ }
209+ createMethodBuilder .addStatement ("return $T.INSTANCE" , instanceHolderClassName (binding ));
210+ } else {
211+ ImmutableList <ParameterSpec > parameters =
212+ factoryFields .getAll ().stream ()
213+ .map (field -> ParameterSpec .builder (field .type , field .name ).build ())
214+ .collect (toImmutableList ());
215+ createMethodBuilder
216+ .addParameters (parameters )
217+ .addStatement (
218+ "return new $T($L)" ,
219+ parameterizedGeneratedTypeNameForBinding (binding ),
220+ parameterNames (parameters ));
225221 }
226- factoryBuilder . addMethod ( createMethodBuilder .build () );
222+ return createMethodBuilder .build ();
227223 }
228224
229- private MethodSpec getMethod (ProvisionBinding binding ) {
225+ // Example 1: Provision binding.
226+ // @Override
227+ // public Foo get() {
228+ // return provideFoo(module, barProvider.get(), bazProvider.get());
229+ // }
230+ //
231+ // Example 2: Injection binding with some inject field.
232+ // @Override
233+ // public Foo get() {
234+ // Foo instance = newInstance(barProvider.get(), bazProvider.get());
235+ // Foo_MembersInjector.injectSomeField(instance, someFieldProvider.get());
236+ // return instance;
237+ // }
238+ private MethodSpec getMethod (ProvisionBinding binding , FactoryFields factoryFields ) {
230239 UniqueNameSet uniqueFieldNames = new UniqueNameSet ();
231- ImmutableMap <DependencyRequest , FieldSpec > frameworkFields = frameworkFields (binding );
232- frameworkFields .values ().forEach (field -> uniqueFieldNames .claim (field .name ));
240+ factoryFields .getAll ().forEach (field -> uniqueFieldNames .claim (field .name ));
233241 ImmutableMap <XExecutableParameterElement , ParameterSpec > assistedParameters =
234242 assistedParameters (binding ).stream ()
235243 .collect (
@@ -254,10 +262,10 @@ private MethodSpec getMethod(ProvisionBinding binding) {
254262 binding ,
255263 request ->
256264 sourceFiles .frameworkTypeUsageStatement (
257- CodeBlock .of ("$N" , frameworkFields .get (request )), request .kind ()),
265+ CodeBlock .of ("$N" , factoryFields .get (request )), request .kind ()),
258266 param -> assistedParameters .get (param ).name ,
259267 generatedClassNameForBinding (binding ),
260- moduleParameter ( binding ) .map (module -> CodeBlock .of ("$N" , module )),
268+ factoryFields . moduleField .map (module -> CodeBlock .of ("$N" , module )),
261269 compilerOptions );
262270
263271 if (binding .kind ().equals (PROVISION )) {
@@ -278,7 +286,8 @@ private MethodSpec getMethod(ProvisionBinding binding) {
278286 generatedClassNameForBinding (binding ),
279287 instance ,
280288 binding .key ().type ().xprocessing (),
281- sourceFiles .frameworkFieldUsages (binding .dependencies (), frameworkFields )::get ))
289+ sourceFiles .frameworkFieldUsages (
290+ binding .dependencies (), factoryFields .frameworkFields )::get ))
282291 .addStatement ("return $L" , instance );
283292
284293 } else {
@@ -289,6 +298,19 @@ private MethodSpec getMethod(ProvisionBinding binding) {
289298 return getMethod .build ();
290299 }
291300
301+ // Example 1: Provision binding
302+ // public static Foo provideFoo(FooModule module, Bar bar, Baz baz) {
303+ // return Preconditions.checkNotNullFromProvides(module.provideFoo(bar, baz));
304+ // }
305+ //
306+ // Example 2: Injection binding
307+ // public static Foo newInstance(Bar bar, Baz baz) {
308+ // return new Foo(bar, baz);
309+ // }
310+ private MethodSpec staticProvisionMethod (ProvisionBinding binding ) {
311+ return ProvisionMethod .create (binding , compilerOptions );
312+ }
313+
292314 private AnnotationSpec scopeMetadataAnnotation (ProvisionBinding binding ) {
293315 AnnotationSpec .Builder builder = AnnotationSpec .builder (TypeNames .SCOPE_METADATA );
294316 binding .scope ()
@@ -324,31 +346,57 @@ private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
324346 : Optional .of (factoryOf (providedTypeName (binding )));
325347 }
326348
327- private static ParameterSpec toParameter (FieldSpec field ) {
328- return ParameterSpec .builder (field .type , field .name ).build ();
329- }
349+ /** Represents the available fields in the generated factory class. */
350+ private static final class FactoryFields {
351+ static FactoryFields create (ProvisionBinding binding ) {
352+ UniqueNameSet nameSet = new UniqueNameSet ();
353+ // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
354+ Optional <FieldSpec > moduleField =
355+ binding .requiresModuleInstance ()
356+ ? Optional .of (
357+ createField (
358+ binding .bindingTypeElement ().get ().getType ().getTypeName (),
359+ nameSet .getUniqueName ("module" )))
360+ : Optional .empty ();
361+
362+ ImmutableMap .Builder <DependencyRequest , FieldSpec > frameworkFields = ImmutableMap .builder ();
363+ generateBindingFieldsForDependencies (binding ).forEach (
364+ (dependency , field ) ->
365+ frameworkFields .put (
366+ dependency , createField (field .type (), nameSet .getUniqueName (field .name ()))));
367+
368+ return new FactoryFields (moduleField , frameworkFields .buildOrThrow ());
369+ }
330370
331- /** The strategy for getting an instance of a factory for a {@link Binding}. */
332- private enum FactoryCreationStrategy {
333- /** The factory class is a single instance. */
334- SINGLETON_INSTANCE ,
335- /** The factory must be created by calling the constructor. */
336- CLASS_CONSTRUCTOR ;
337-
338- static FactoryCreationStrategy of (Binding binding ) {
339- switch (binding .kind ()) {
340- case PROVISION :
341- return binding .dependencies ().isEmpty () && !binding .requiresModuleInstance ()
342- ? SINGLETON_INSTANCE
343- : CLASS_CONSTRUCTOR ;
344- case INJECTION :
345- case ASSISTED_INJECTION :
346- return binding .dependencies ().isEmpty ()
347- ? SINGLETON_INSTANCE
348- : CLASS_CONSTRUCTOR ;
349- default :
350- throw new AssertionError ("Unexpected binding kind: " + binding .kind ());
351- }
371+ private static FieldSpec createField (TypeName type , String name ) {
372+ return FieldSpec .builder (type , name , PRIVATE , FINAL ).build ();
373+ }
374+
375+ private final Optional <FieldSpec > moduleField ;
376+ private final ImmutableMap <DependencyRequest , FieldSpec > frameworkFields ;
377+
378+ private FactoryFields (
379+ Optional <FieldSpec > moduleField ,
380+ ImmutableMap <DependencyRequest , FieldSpec > frameworkFields ) {
381+ this .moduleField = moduleField ;
382+ this .frameworkFields = frameworkFields ;
383+ }
384+
385+ FieldSpec get (DependencyRequest request ) {
386+ return frameworkFields .get (request );
387+ }
388+
389+ ImmutableList <FieldSpec > getAll () {
390+ return moduleField .isPresent ()
391+ ? ImmutableList .<FieldSpec >builder ()
392+ .add (moduleField .get ())
393+ .addAll (frameworkFields .values ())
394+ .build ()
395+ : frameworkFields .values ().asList ();
396+ }
397+
398+ boolean isEmpty () {
399+ return getAll ().isEmpty ();
352400 }
353401 }
354402}
0 commit comments