Skip to content

Commit 8ef850f

Browse files
committed
Update ClassNameGenerator to work with a ClassName target
This commit updates ClassNameGenerator so that it uses a ClassName for its default target. This makes sure that a target that has been generated can be used. See gh-29027
1 parent 1707a22 commit 8ef850f

File tree

9 files changed

+99
-81
lines changed

9 files changed

+99
-81
lines changed

spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.springframework.aot.generate.MethodReference;
3434
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
3535
import org.springframework.aot.test.generate.TestGenerationContext;
36-
import org.springframework.aot.test.generate.TestTarget;
3736
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3837
import org.springframework.beans.factory.support.RegisteredBean;
3938
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -97,7 +96,7 @@ void applyToAppliesContribution() {
9796
@Test
9897
void applyToWhenHasNameGeneratesPrefixedFeatureName() {
9998
this.generationContext = new TestGenerationContext(
100-
new ClassNameGenerator(TestTarget.class, "Management"));
99+
new ClassNameGenerator(TestGenerationContext.TEST_TARGET, "Management"));
101100
this.beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(this.generationContext);
102101
Map<String, BeanDefinitionMethodGenerator> registrations = new LinkedHashMap<>();
103102
RegisteredBean registeredBean = registerBean(

spring-core-test/src/main/java/org/springframework/aot/test/generate/TestGenerationContext.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.aot.generate.GenerationContext;
2424
import org.springframework.aot.generate.InMemoryGeneratedFiles;
2525
import org.springframework.core.test.tools.TestCompiler;
26+
import org.springframework.javapoet.ClassName;
2627

2728
/**
2829
* {@link GenerationContext} test implementation that uses
@@ -35,6 +36,11 @@
3536
*/
3637
public class TestGenerationContext extends DefaultGenerationContext implements UnaryOperator<TestCompiler> {
3738

39+
/**
40+
* The default test target {@link ClassName}.
41+
*/
42+
public static final ClassName TEST_TARGET = ClassName.get("com.example", "TestTarget");
43+
3844
/**
3945
* Create an instance using the specified {@link ClassNameGenerator}.
4046
* @param classNameGenerator the class name generator to use
@@ -47,15 +53,15 @@ public TestGenerationContext(ClassNameGenerator classNameGenerator) {
4753
* Create an instance using the specified {@code target}.
4854
* @param target the default target class to use
4955
*/
50-
public TestGenerationContext(Class<?> target) {
56+
public TestGenerationContext(ClassName target) {
5157
this(new ClassNameGenerator(target));
5258
}
5359

5460
/**
55-
* Create an instance using {@link TestTarget} as the {@code target}.
61+
* Create an instance using {@link #TEST_TARGET} as the {@code target}.
5662
*/
5763
public TestGenerationContext() {
58-
this(TestTarget.class);
64+
this(TEST_TARGET);
5965
}
6066

6167

spring-core-test/src/main/java/org/springframework/aot/test/generate/TestTarget.java

Lines changed: 0 additions & 26 deletions
This file was deleted.

spring-core/src/main/java/org/springframework/aot/generate/ClassNameGenerator.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import org.springframework.util.StringUtils;
2828

2929
/**
30-
* Generate unique class names based on a target {@link Class} and a feature
31-
* name.
30+
* Generate unique class names based on a target {@link ClassName} and a
31+
* feature name.
3232
*
3333
* <p>This class is stateful, so the same instance should be used for all name
3434
* generation.
@@ -43,7 +43,7 @@ public final class ClassNameGenerator {
4343

4444
private static final String AOT_FEATURE = "Aot";
4545

46-
private final Class<?> defaultTarget;
46+
private final ClassName defaultTarget;
4747

4848
private final String featureNamePrefix;
4949

@@ -55,7 +55,7 @@ public final class ClassNameGenerator {
5555
* feature name prefix.
5656
* @param defaultTarget the default target class to use
5757
*/
58-
public ClassNameGenerator(Class<?> defaultTarget) {
58+
public ClassNameGenerator(ClassName defaultTarget) {
5959
this(defaultTarget, "");
6060
}
6161

@@ -65,11 +65,11 @@ public ClassNameGenerator(Class<?> defaultTarget) {
6565
* @param defaultTarget the default target class to use
6666
* @param featureNamePrefix the prefix to use to qualify feature names
6767
*/
68-
public ClassNameGenerator(Class<?> defaultTarget, String featureNamePrefix) {
68+
public ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix) {
6969
this(defaultTarget, featureNamePrefix, new ConcurrentHashMap<>());
7070
}
7171

72-
private ClassNameGenerator(Class<?> defaultTarget, String featureNamePrefix,
72+
private ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix,
7373
Map<String, AtomicInteger> sequenceGenerator) {
7474
Assert.notNull(defaultTarget, "'defaultTarget' must not be null");
7575
this.defaultTarget = defaultTarget;
@@ -98,16 +98,16 @@ String getFeatureNamePrefix() {
9898
* {@code null} to use the main target
9999
* @return a unique generated class name
100100
*/
101-
public ClassName generateClassName(String featureName, @Nullable Class<?> target) {
101+
public ClassName generateClassName(String featureName, @Nullable ClassName target) {
102102
return generateSequencedClassName(getRootName(featureName, target));
103103
}
104104

105-
private String getRootName(String featureName, @Nullable Class<?> target) {
105+
private String getRootName(String featureName, @Nullable ClassName target) {
106106
Assert.hasLength(featureName, "'featureName' must not be empty");
107107
featureName = clean(featureName);
108-
Class<?> targetToUse = (target != null ? target : this.defaultTarget);
108+
ClassName targetToUse = (target != null ? target : this.defaultTarget);
109109
String featureNameToUse = this.featureNamePrefix + featureName;
110-
return targetToUse.getName().replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse);
110+
return toName(targetToUse).replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse);
111111
}
112112

113113
private String clean(String name) {
@@ -147,4 +147,8 @@ ClassNameGenerator withFeatureNamePrefix(String featureNamePrefix) {
147147
this.sequenceGenerator);
148148
}
149149

150+
private static String toName(ClassName className) {
151+
return GeneratedTypeReference.of(className).getName();
152+
}
153+
150154
}

spring-core/src/main/java/org/springframework/aot/generate/GeneratedClasses.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public GeneratedClass getOrAddForFeature(String featureName,
9999
* @return an existing or newly generated class
100100
*/
101101
public GeneratedClass getOrAddForFeatureComponent(String featureName,
102-
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
102+
ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
103103

104104
Assert.hasLength(featureName, "'featureName' must not be empty");
105105
Assert.notNull(targetComponent, "'targetComponent' must not be null");
@@ -111,6 +111,24 @@ public GeneratedClass getOrAddForFeatureComponent(String featureName,
111111
return generatedClass;
112112
}
113113

114+
/**
115+
* Get or add a generated class for the specified {@code featureName}
116+
* targeting the specified {@code component}. If this method has previously
117+
* been called with the given {@code featureName}/{@code target} the
118+
* existing class will be returned, otherwise a new class will be generated,
119+
* otherwise a new class will be generated.
120+
* @param featureName the name of the feature to associate with the
121+
* generated class
122+
* @param targetComponent the target component
123+
* @param type a {@link Consumer} used to build the type
124+
* @return an existing or newly generated class
125+
*/
126+
public GeneratedClass getOrAddForFeatureComponent(String featureName,
127+
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
128+
129+
return getOrAddForFeatureComponent(featureName, ClassName.get(targetComponent), type);
130+
}
131+
114132
/**
115133
* Add a new generated class for the specified {@code featureName} and no
116134
* particular component.
@@ -135,16 +153,31 @@ public GeneratedClass addForFeature(String featureName, Consumer<TypeSpec.Builde
135153
* @return the newly generated class
136154
*/
137155
public GeneratedClass addForFeatureComponent(String featureName,
138-
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
156+
ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
139157

140158
Assert.hasLength(featureName, "'featureName' must not be empty");
141159
Assert.notNull(targetComponent, "'targetComponent' must not be null");
142160
Assert.notNull(type, "'type' must not be null");
143161
return createAndAddGeneratedClass(featureName, targetComponent, type);
144162
}
145163

164+
/**
165+
* Add a new generated class for the specified {@code featureName} targeting
166+
* the specified {@code component}.
167+
* @param featureName the name of the feature to associate with the
168+
* generated class
169+
* @param targetComponent the target component
170+
* @param type a {@link Consumer} used to build the type
171+
* @return the newly generated class
172+
*/
173+
public GeneratedClass addForFeatureComponent(String featureName,
174+
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
175+
176+
return addForFeatureComponent(featureName, ClassName.get(targetComponent), type);
177+
}
178+
146179
private GeneratedClass createAndAddGeneratedClass(String featureName,
147-
@Nullable Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
180+
@Nullable ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
148181

149182
ClassName className = this.classNameGenerator.generateClassName(featureName, targetComponent);
150183
GeneratedClass generatedClass = new GeneratedClass(className, type);
@@ -171,7 +204,7 @@ GeneratedClasses withFeatureNamePrefix(String name) {
171204
this.classes, this.classesByOwner);
172205
}
173206

174-
private record Owner(String featureNamePrefix, String featureName, @Nullable Class<?> target) {
207+
private record Owner(String featureNamePrefix, String featureName, @Nullable ClassName target) {
175208
}
176209

177210
}

spring-core/src/test/java/org/springframework/aot/generate/ClassNameGeneratorTests.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,71 +32,69 @@
3232
*/
3333
class ClassNameGeneratorTests {
3434

35-
private final ClassNameGenerator generator = new ClassNameGenerator(Object.class);
35+
private static final ClassName TEST_TARGET = ClassName.get("com.example", "Test");
36+
37+
private final ClassNameGenerator generator = new ClassNameGenerator(TEST_TARGET);
3638

3739
@Test
3840
void generateClassNameWhenTargetClassIsNullUsesMainTarget() {
3941
ClassName generated = this.generator.generateClassName("test", null);
40-
assertThat(generated).hasToString("java.lang.Object__Test");
42+
assertThat(generated).hasToString("com.example.Test__Test");
4143
}
4244

4345
@Test
4446
void generateClassNameUseFeatureNamePrefix() {
45-
ClassName generated = new ClassNameGenerator(Object.class, "One")
46-
.generateClassName("test", InputStream.class);
47+
ClassName generated = new ClassNameGenerator(TEST_TARGET, "One")
48+
.generateClassName("test", ClassName.get(InputStream.class));
4749
assertThat(generated).hasToString("java.io.InputStream__OneTest");
4850
}
4951

5052
@Test
5153
void generateClassNameWithNoTextFeatureNamePrefix() {
52-
ClassName generated = new ClassNameGenerator(Object.class, " ")
53-
.generateClassName("test", InputStream.class);
54+
ClassName generated = new ClassNameGenerator(TEST_TARGET, " ")
55+
.generateClassName("test", ClassName.get(InputStream.class));
5456
assertThat(generated).hasToString("java.io.InputStream__Test");
5557
}
5658

5759
@Test
5860
void generatedClassNameWhenFeatureIsEmptyThrowsException() {
5961
assertThatIllegalArgumentException()
60-
.isThrownBy(() -> this.generator.generateClassName("", InputStream.class))
62+
.isThrownBy(() -> this.generator.generateClassName("", ClassName.get(InputStream.class)))
6163
.withMessage("'featureName' must not be empty");
6264
}
6365

6466
@Test
6567
void generatedClassNameWhenFeatureIsNotAllLettersThrowsException() {
66-
assertThat(this.generator.generateClassName("name!", InputStream.class))
68+
assertThat(this.generator.generateClassName("name!", ClassName.get(InputStream.class)))
6769
.hasToString("java.io.InputStream__Name");
68-
assertThat(this.generator.generateClassName("1NameHere", InputStream.class))
70+
assertThat(this.generator.generateClassName("1NameHere", ClassName.get(InputStream.class)))
6971
.hasToString("java.io.InputStream__NameHere");
70-
assertThat(this.generator.generateClassName("Y0pe", InputStream.class))
72+
assertThat(this.generator.generateClassName("Y0pe", ClassName.get(InputStream.class)))
7173
.hasToString("java.io.InputStream__YPe");
7274
}
7375

7476
@Test
7577
void generateClassNameWithClassWhenLowercaseFeatureNameGeneratesName() {
76-
ClassName generated = this.generator.generateClassName("bytes", InputStream.class);
78+
ClassName generated = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
7779
assertThat(generated).hasToString("java.io.InputStream__Bytes");
7880
}
7981

8082
@Test
8183
void generateClassNameWithClassWhenInnerClassGeneratesName() {
82-
ClassName generated = this.generator.generateClassName("EventListener", TestBean.class);
84+
ClassName innerBean = ClassName.get("com.example", "Test", "InnerBean");
85+
ClassName generated = this.generator.generateClassName("EventListener", innerBean);
8386
assertThat(generated)
84-
.hasToString("org.springframework.aot.generate.ClassNameGeneratorTests_TestBean__EventListener");
87+
.hasToString("com.example.Test_InnerBean__EventListener");
8588
}
8689

8790
@Test
8891
void generateClassWithClassWhenMultipleCallsGeneratesSequencedName() {
89-
ClassName generated1 = this.generator.generateClassName("bytes", InputStream.class);
90-
ClassName generated2 = this.generator.generateClassName("bytes", InputStream.class);
91-
ClassName generated3 = this.generator.generateClassName("bytes", InputStream.class);
92+
ClassName generated1 = this.generator.generateClassName("bytes",ClassName.get(InputStream.class));
93+
ClassName generated2 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
94+
ClassName generated3 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
9295
assertThat(generated1).hasToString("java.io.InputStream__Bytes");
9396
assertThat(generated2).hasToString("java.io.InputStream__Bytes1");
9497
assertThat(generated3).hasToString("java.io.InputStream__Bytes2");
9598
}
9699

97-
98-
static class TestBean {
99-
100-
}
101-
102100
}

spring-core/src/test/java/org/springframework/aot/generate/DefaultGenerationContextTests.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.springframework.aot.generate.GeneratedFiles.Kind;
2424
import org.springframework.aot.hint.RuntimeHints;
25+
import org.springframework.javapoet.ClassName;
2526
import org.springframework.javapoet.TypeSpec;
2627

2728
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,10 +36,12 @@
3536
*/
3637
class DefaultGenerationContextTests {
3738

39+
private static final ClassName SAMPLE_TARGET = ClassName.get("com.example", "SampleTarget");
40+
3841
private static final Consumer<TypeSpec.Builder> typeSpecCustomizer = type -> {};
3942

4043
private final GeneratedClasses generatedClasses = new GeneratedClasses(
41-
new ClassNameGenerator(SampleTarget.class));
44+
new ClassNameGenerator(SAMPLE_TARGET));
4245

4346
private final InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
4447

@@ -48,7 +51,7 @@ class DefaultGenerationContextTests {
4851
@Test
4952
void createWithOnlyGeneratedFilesCreatesContext() {
5053
DefaultGenerationContext context = new DefaultGenerationContext(
51-
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
54+
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
5255
assertThat(context.getGeneratedFiles()).isSameAs(this.generatedFiles);
5356
assertThat(context.getRuntimeHints()).isInstanceOf(RuntimeHints.class);
5457
}
@@ -109,7 +112,7 @@ void getRuntimeHintsReturnsRuntimeHints() {
109112
@Test
110113
void withNameUpdateNamingConvention() {
111114
DefaultGenerationContext context = new DefaultGenerationContext(
112-
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
115+
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
113116
GenerationContext anotherContext = context.withName("Another");
114117
GeneratedClass generatedClass = anotherContext.getGeneratedClasses()
115118
.addForFeature("Test", typeSpecCustomizer);
@@ -119,7 +122,7 @@ void withNameUpdateNamingConvention() {
119122
@Test
120123
void withNameKeepsTrackOfAllGeneratedFiles() {
121124
DefaultGenerationContext context = new DefaultGenerationContext(
122-
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
125+
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
123126
context.getGeneratedClasses().addForFeature("Test", typeSpecCustomizer);
124127
GenerationContext anotherContext = context.withName("Another");
125128
assertThat(anotherContext.getGeneratedClasses()).isNotSameAs(context.getGeneratedClasses());
@@ -133,7 +136,7 @@ void withNameKeepsTrackOfAllGeneratedFiles() {
133136
@Test
134137
void withNameGeneratesUniqueName() {
135138
DefaultGenerationContext context = new DefaultGenerationContext(
136-
new ClassNameGenerator(Object.class), this.generatedFiles);
139+
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
137140
context.withName("Test").getGeneratedClasses()
138141
.addForFeature("Feature", typeSpecCustomizer);
139142
context.withName("Test").getGeneratedClasses()
@@ -142,11 +145,9 @@ void withNameGeneratesUniqueName() {
142145
.addForFeature("Feature", typeSpecCustomizer);
143146
context.writeGeneratedContent();
144147
assertThat(this.generatedFiles.getGeneratedFiles(Kind.SOURCE)).containsOnlyKeys(
145-
"java/lang/Object__TestFeature.java",
146-
"java/lang/Object__Test1Feature.java",
147-
"java/lang/Object__Test2Feature.java");
148+
"com/example/SampleTarget__TestFeature.java",
149+
"com/example/SampleTarget__Test1Feature.java",
150+
"com/example/SampleTarget__Test2Feature.java");
148151
}
149152

150-
static class SampleTarget {}
151-
152153
}

0 commit comments

Comments
 (0)