Skip to content

Commit 4fc43df

Browse files
authored
Fix conditional factories (#785)
* Fix conditional factories resolves #784 * Update MetaData.java * Update MetaData.java
1 parent c0db86d commit 4fc43df

File tree

9 files changed

+86
-35
lines changed

9 files changed

+86
-35
lines changed

blackbox-test-inject/src/main/java/org/example/myapp/conditional/BirdFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ public class BirdFactory {
1717
@Bean
1818
@NoKiwi
1919
@RequiresProperty(missing = "finch-time")
20-
public BlueJay jay() {
20+
public Bird jay() {
2121
return new BlueJay();
2222
}
2323

2424
@Bean
2525
@Secondary
26-
public Cassowary dinosaur() {
26+
public Bird dinosaur() {
2727
return new Cassowary();
2828
}
2929

3030
@Bean
3131
@Finches
32-
public StrawberryFinch finch() {
32+
public Bird finch() {
3333
return new StrawberryFinch();
3434
}
3535
}

blackbox-test-inject/src/test/java/org/example/myapp/conditional/ConditionalTests.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
package org.example.myapp.conditional;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertTrue;
56

6-
import java.io.IOException;
7-
8-
import org.example.myapp.conditional.Bird;
97
import org.example.myapp.conditional.Bird.BlueJay;
108
import org.example.myapp.conditional.Bird.StrawberryFinch;
11-
import org.example.myapp.conditional.BirdFactory;
12-
import org.example.myapp.conditional.BirdWatcher;
13-
import org.example.myapp.conditional.QualifiedBirdWatcher;
149
import org.junit.jupiter.api.BeforeEach;
1510
import org.junit.jupiter.api.Test;
1611

@@ -44,7 +39,7 @@ void jay() {
4439
final BeanScope beanScope = BeanScope.builder().build();
4540

4641
assertTrue(beanScope.getOptional(BirdFactory.class).isPresent());
47-
assertTrue(beanScope.getOptional(BlueJay.class).isPresent());
42+
assertThat(beanScope.get(Bird.class)).isInstanceOf(BlueJay.class);
4843
assertTrue(beanScope.getOptional(BirdWatcher.class).isEmpty());
4944
}
5045

inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package io.avaje.inject.generator;
22

33
import static io.avaje.inject.generator.APContext.typeElement;
4-
import java.util.*;
5-
import java.util.stream.Collectors;
64

5+
import java.util.ArrayList;
6+
import java.util.Comparator;
7+
import java.util.HashMap;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Set;
12+
import java.util.stream.Collectors;
713

814
/**
915
* Holds the data as per <code>@DependencyMeta</code>
@@ -15,13 +21,17 @@ final class MetaData implements Comparable<MetaData> {
1521
.thenComparing(MetaData::name, Comparator.nullsFirst(Comparator.naturalOrder()))
1622
.thenComparing(MetaData::compareProvides);
1723

24+
private static final Map<String, Integer> FACTORY_FREQUENCY = new HashMap<>();
25+
1826
private static final String INDENT = " ";
1927
private static final String NEWLINE = "\n";
2028

2129
private final String type;
2230
private final String shortType;
2331
private final String name;
24-
private String method;
32+
private final String buildName;
33+
private final String method;
34+
private final String key;
2535
private boolean wired;
2636
private String providesAspect;
2737

@@ -55,19 +65,28 @@ final class MetaData implements Comparable<MetaData> {
5565
this.provides = Util.addQualifierSuffix(meta.provides(), name);
5666
this.autoProvides = Util.addQualifierSuffix(meta.autoProvides(), name);
5767
this.importedComponent = meta.importedComponent();
68+
this.key = createKey();
69+
this.buildName = createBuildName();
5870
}
5971

6072
MetaData(String type, String name) {
73+
this(type, name, null);
74+
}
75+
76+
MetaData(String type, String name, String method) {
6177
this.type = type;
6278
this.name = trimName(name);
6379
this.shortType = Util.shortName(type);
6480
this.provides = new ArrayList<>();
6581
this.dependsOn = new ArrayList<>();
82+
this.method = method;
83+
this.key = createKey();
84+
this.buildName = createBuildName();
6685
}
6786

6887
@Override
6988
public String toString() {
70-
return (name == null) ? type : type + ":" + name;
89+
return name == null ? type : type + ":" + name;
7190
}
7291

7392
boolean importedComponent() {
@@ -90,18 +109,21 @@ private String trimName(String name) {
90109
}
91110

92111
String buildName() {
112+
return buildName;
113+
}
114+
115+
private String createBuildName() {
93116
if (Util.isVoid(type)) {
94117
return "void_" + Util.trimMethod(method);
95118
} else {
96119
final String trimType = Util.trimMethod(Util.unwrapProvider(type));
120+
97121
if (name != null) {
98122
return trimType + "_" + name.replaceAll("[^a-zA-Z0-9_$]+", "_");
99-
} else {
100-
if (buildNameIncludeMethod()) {
101-
return trimType + "__" + Util.trimMethod(method);
102-
}
103-
return trimType;
123+
} else if (buildNameIncludeMethod() || hasMethod() && FACTORY_FREQUENCY.get(type) > 0) {
124+
return trimType + "__" + Util.trimMethod(method);
104125
}
126+
return trimType;
105127
}
106128
}
107129

@@ -110,15 +132,20 @@ private boolean buildNameIncludeMethod() {
110132
return type.contains("<") && hasMethod();
111133
}
112134

113-
public String key() {
135+
String key() {
136+
return key;
137+
}
138+
139+
private String createKey() {
114140
if (Util.isVoid(type)) {
115141
return "method:" + method;
116142
}
117-
if (name != null) {
118-
return type + ":" + name;
119-
} else {
120-
return type;
143+
String keyString = name != null ? type + ":" + name : type;
144+
if (hasMethod() && FACTORY_FREQUENCY.compute(type, (k, v) -> v == null ? 0 : ++v) > 0) {
145+
// allow multiple factory methods that produce the same types, typically conditional
146+
keyString += FACTORY_FREQUENCY.get(type);
121147
}
148+
return keyString;
122149
}
123150

124151
boolean noDepends() {
@@ -294,10 +321,6 @@ void setDependsOn(List<String> dependsOn) {
294321
this.dependsOn = dependsOn.stream().map(Dependency::new).collect(Collectors.toList());
295322
}
296323

297-
void setMethod(String method) {
298-
this.method = method;
299-
}
300-
301324
void setAutoProvides(List<String> autoProvides) {
302325
this.autoProvides = autoProvides;
303326
}

inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ final class MethodReader {
8484
this.factoryType = beanType.getQualifiedName().toString();
8585
this.factoryShortName = Util.shortName(factoryType);
8686
this.isVoid = Util.isVoid(mainType);
87-
String initMethod = (bean == null) ? null : bean.initMethod();
88-
String destroyMethod = (bean == null) ? null : bean.destroyMethod();
89-
this.destroyPriority = (bean == null) ? null : bean.destroyPriority();
90-
this.beanCloseable = (bean != null) && bean.autoCloseable();
87+
String initMethod = bean == null ? null : bean.initMethod();
88+
String destroyMethod = bean == null ? null : bean.destroyMethod();
89+
this.destroyPriority = bean == null ? null : bean.destroyPriority();
90+
this.beanCloseable = bean != null && bean.autoCloseable();
9191
// for multiRegister we desire a qualifier name such that builder.isBeanAbsent() uses it and allows
9292
// other beans of the same type to also register, otherwise it defaults to slightly confusing behaviour
9393
this.name = multiRegister && qualifierName == null ? "multi" : qualifierName;
@@ -151,8 +151,7 @@ List<MethodParam> params() {
151151
}
152152

153153
MetaData createMeta() {
154-
MetaData metaData = new MetaData(returnTypeRaw, name);
155-
metaData.setMethod(fullBuildMethod());
154+
MetaData metaData = new MetaData(returnTypeRaw, name, fullBuildMethod());
156155

157156
List<String> dependsOn = new ArrayList<>(params.size() + 1);
158157
dependsOn.add(factoryType);
@@ -374,6 +373,10 @@ Set<UType> genericTypes() {
374373
return typeReader == null ? Collections.emptySet() : typeReader.genericTypes();
375374
}
376375

376+
boolean hasConditions() {
377+
return !conditions.isEmpty();
378+
}
379+
377380
void buildConditional(Append writer) {
378381
new ConditionalWriter(writer, conditions).write();
379382
}

inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ void validate() {
262262
void validateFactoryMethodDuplicates() {
263263
var map = new HashMap<String, MethodReader>();
264264
for (MethodReader method : factoryMethods) {
265-
if (!method.isVoid()) {
265+
if (!method.isVoid() && !method.hasConditions()) {
266266
var clashMethod = map.put(method.qualifiedKey(), method);
267267
if (clashMethod != null) {
268268
var msg = String.format("@Bean method %s() returns the same type as with method %s() without a unique name qualifier." +
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.avaje.inject.generator.models.valid.conditions;
2+
3+
public interface MyService {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.avaje.inject.generator.models.valid.conditions;
2+
3+
import io.avaje.inject.Bean;
4+
import io.avaje.inject.Factory;
5+
import io.avaje.inject.RequiresProperty;
6+
7+
@Factory
8+
class MyServiceFactory {
9+
10+
@Bean
11+
@RequiresProperty(value = "service.type", equalTo = "one")
12+
MyService myServiceOne() {
13+
return new MyServiceOne();
14+
}
15+
16+
@Bean
17+
@RequiresProperty(value = "service.type", equalTo = "two")
18+
MyService myServiceTwo() {
19+
return new MyServiceTwo();
20+
}
21+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.avaje.inject.generator.models.valid.conditions;
2+
3+
public class MyServiceOne implements MyService {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.avaje.inject.generator.models.valid.conditions;
2+
3+
public class MyServiceTwo implements MyService {}

0 commit comments

Comments
 (0)