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
This commit is contained in:
@@ -27,8 +27,8 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Generate unique class names based on a target {@link Class} and a feature
|
||||
* name.
|
||||
* Generate unique class names based on a target {@link ClassName} and a
|
||||
* feature name.
|
||||
*
|
||||
* <p>This class is stateful, so the same instance should be used for all name
|
||||
* generation.
|
||||
@@ -43,7 +43,7 @@ public final class ClassNameGenerator {
|
||||
|
||||
private static final String AOT_FEATURE = "Aot";
|
||||
|
||||
private final Class<?> defaultTarget;
|
||||
private final ClassName defaultTarget;
|
||||
|
||||
private final String featureNamePrefix;
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class ClassNameGenerator {
|
||||
* feature name prefix.
|
||||
* @param defaultTarget the default target class to use
|
||||
*/
|
||||
public ClassNameGenerator(Class<?> defaultTarget) {
|
||||
public ClassNameGenerator(ClassName defaultTarget) {
|
||||
this(defaultTarget, "");
|
||||
}
|
||||
|
||||
@@ -65,11 +65,11 @@ public final class ClassNameGenerator {
|
||||
* @param defaultTarget the default target class to use
|
||||
* @param featureNamePrefix the prefix to use to qualify feature names
|
||||
*/
|
||||
public ClassNameGenerator(Class<?> defaultTarget, String featureNamePrefix) {
|
||||
public ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix) {
|
||||
this(defaultTarget, featureNamePrefix, new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
private ClassNameGenerator(Class<?> defaultTarget, String featureNamePrefix,
|
||||
private ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix,
|
||||
Map<String, AtomicInteger> sequenceGenerator) {
|
||||
Assert.notNull(defaultTarget, "'defaultTarget' must not be null");
|
||||
this.defaultTarget = defaultTarget;
|
||||
@@ -98,16 +98,16 @@ public final class ClassNameGenerator {
|
||||
* {@code null} to use the main target
|
||||
* @return a unique generated class name
|
||||
*/
|
||||
public ClassName generateClassName(String featureName, @Nullable Class<?> target) {
|
||||
public ClassName generateClassName(String featureName, @Nullable ClassName target) {
|
||||
return generateSequencedClassName(getRootName(featureName, target));
|
||||
}
|
||||
|
||||
private String getRootName(String featureName, @Nullable Class<?> target) {
|
||||
private String getRootName(String featureName, @Nullable ClassName target) {
|
||||
Assert.hasLength(featureName, "'featureName' must not be empty");
|
||||
featureName = clean(featureName);
|
||||
Class<?> targetToUse = (target != null ? target : this.defaultTarget);
|
||||
ClassName targetToUse = (target != null ? target : this.defaultTarget);
|
||||
String featureNameToUse = this.featureNamePrefix + featureName;
|
||||
return targetToUse.getName().replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse);
|
||||
return toName(targetToUse).replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse);
|
||||
}
|
||||
|
||||
private String clean(String name) {
|
||||
@@ -147,4 +147,8 @@ public final class ClassNameGenerator {
|
||||
this.sequenceGenerator);
|
||||
}
|
||||
|
||||
private static String toName(ClassName className) {
|
||||
return GeneratedTypeReference.of(className).getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class GeneratedClasses {
|
||||
* @return an existing or newly generated class
|
||||
*/
|
||||
public GeneratedClass getOrAddForFeatureComponent(String featureName,
|
||||
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
|
||||
Assert.hasLength(featureName, "'featureName' must not be empty");
|
||||
Assert.notNull(targetComponent, "'targetComponent' must not be null");
|
||||
@@ -111,6 +111,24 @@ public class GeneratedClasses {
|
||||
return generatedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or add a generated class for the specified {@code featureName}
|
||||
* targeting the specified {@code component}. If this method has previously
|
||||
* been called with the given {@code featureName}/{@code target} the
|
||||
* existing class will be returned, otherwise a new class will be generated,
|
||||
* otherwise a new class will be generated.
|
||||
* @param featureName the name of the feature to associate with the
|
||||
* generated class
|
||||
* @param targetComponent the target component
|
||||
* @param type a {@link Consumer} used to build the type
|
||||
* @return an existing or newly generated class
|
||||
*/
|
||||
public GeneratedClass getOrAddForFeatureComponent(String featureName,
|
||||
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
|
||||
return getOrAddForFeatureComponent(featureName, ClassName.get(targetComponent), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new generated class for the specified {@code featureName} and no
|
||||
* particular component.
|
||||
@@ -135,7 +153,7 @@ public class GeneratedClasses {
|
||||
* @return the newly generated class
|
||||
*/
|
||||
public GeneratedClass addForFeatureComponent(String featureName,
|
||||
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
|
||||
Assert.hasLength(featureName, "'featureName' must not be empty");
|
||||
Assert.notNull(targetComponent, "'targetComponent' must not be null");
|
||||
@@ -143,8 +161,23 @@ public class GeneratedClasses {
|
||||
return createAndAddGeneratedClass(featureName, targetComponent, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new generated class for the specified {@code featureName} targeting
|
||||
* the specified {@code component}.
|
||||
* @param featureName the name of the feature to associate with the
|
||||
* generated class
|
||||
* @param targetComponent the target component
|
||||
* @param type a {@link Consumer} used to build the type
|
||||
* @return the newly generated class
|
||||
*/
|
||||
public GeneratedClass addForFeatureComponent(String featureName,
|
||||
Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
|
||||
return addForFeatureComponent(featureName, ClassName.get(targetComponent), type);
|
||||
}
|
||||
|
||||
private GeneratedClass createAndAddGeneratedClass(String featureName,
|
||||
@Nullable Class<?> targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
@Nullable ClassName targetComponent, Consumer<TypeSpec.Builder> type) {
|
||||
|
||||
ClassName className = this.classNameGenerator.generateClassName(featureName, targetComponent);
|
||||
GeneratedClass generatedClass = new GeneratedClass(className, type);
|
||||
@@ -171,7 +204,7 @@ public class GeneratedClasses {
|
||||
this.classes, this.classesByOwner);
|
||||
}
|
||||
|
||||
private record Owner(String featureNamePrefix, String featureName, @Nullable Class<?> target) {
|
||||
private record Owner(String featureNamePrefix, String featureName, @Nullable ClassName target) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,71 +32,69 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*/
|
||||
class ClassNameGeneratorTests {
|
||||
|
||||
private final ClassNameGenerator generator = new ClassNameGenerator(Object.class);
|
||||
private static final ClassName TEST_TARGET = ClassName.get("com.example", "Test");
|
||||
|
||||
private final ClassNameGenerator generator = new ClassNameGenerator(TEST_TARGET);
|
||||
|
||||
@Test
|
||||
void generateClassNameWhenTargetClassIsNullUsesMainTarget() {
|
||||
ClassName generated = this.generator.generateClassName("test", null);
|
||||
assertThat(generated).hasToString("java.lang.Object__Test");
|
||||
assertThat(generated).hasToString("com.example.Test__Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClassNameUseFeatureNamePrefix() {
|
||||
ClassName generated = new ClassNameGenerator(Object.class, "One")
|
||||
.generateClassName("test", InputStream.class);
|
||||
ClassName generated = new ClassNameGenerator(TEST_TARGET, "One")
|
||||
.generateClassName("test", ClassName.get(InputStream.class));
|
||||
assertThat(generated).hasToString("java.io.InputStream__OneTest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClassNameWithNoTextFeatureNamePrefix() {
|
||||
ClassName generated = new ClassNameGenerator(Object.class, " ")
|
||||
.generateClassName("test", InputStream.class);
|
||||
ClassName generated = new ClassNameGenerator(TEST_TARGET, " ")
|
||||
.generateClassName("test", ClassName.get(InputStream.class));
|
||||
assertThat(generated).hasToString("java.io.InputStream__Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedClassNameWhenFeatureIsEmptyThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.generator.generateClassName("", InputStream.class))
|
||||
.isThrownBy(() -> this.generator.generateClassName("", ClassName.get(InputStream.class)))
|
||||
.withMessage("'featureName' must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedClassNameWhenFeatureIsNotAllLettersThrowsException() {
|
||||
assertThat(this.generator.generateClassName("name!", InputStream.class))
|
||||
assertThat(this.generator.generateClassName("name!", ClassName.get(InputStream.class)))
|
||||
.hasToString("java.io.InputStream__Name");
|
||||
assertThat(this.generator.generateClassName("1NameHere", InputStream.class))
|
||||
assertThat(this.generator.generateClassName("1NameHere", ClassName.get(InputStream.class)))
|
||||
.hasToString("java.io.InputStream__NameHere");
|
||||
assertThat(this.generator.generateClassName("Y0pe", InputStream.class))
|
||||
assertThat(this.generator.generateClassName("Y0pe", ClassName.get(InputStream.class)))
|
||||
.hasToString("java.io.InputStream__YPe");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClassNameWithClassWhenLowercaseFeatureNameGeneratesName() {
|
||||
ClassName generated = this.generator.generateClassName("bytes", InputStream.class);
|
||||
ClassName generated = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
|
||||
assertThat(generated).hasToString("java.io.InputStream__Bytes");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClassNameWithClassWhenInnerClassGeneratesName() {
|
||||
ClassName generated = this.generator.generateClassName("EventListener", TestBean.class);
|
||||
ClassName innerBean = ClassName.get("com.example", "Test", "InnerBean");
|
||||
ClassName generated = this.generator.generateClassName("EventListener", innerBean);
|
||||
assertThat(generated)
|
||||
.hasToString("org.springframework.aot.generate.ClassNameGeneratorTests_TestBean__EventListener");
|
||||
.hasToString("com.example.Test_InnerBean__EventListener");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClassWithClassWhenMultipleCallsGeneratesSequencedName() {
|
||||
ClassName generated1 = this.generator.generateClassName("bytes", InputStream.class);
|
||||
ClassName generated2 = this.generator.generateClassName("bytes", InputStream.class);
|
||||
ClassName generated3 = this.generator.generateClassName("bytes", InputStream.class);
|
||||
ClassName generated1 = this.generator.generateClassName("bytes",ClassName.get(InputStream.class));
|
||||
ClassName generated2 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
|
||||
ClassName generated3 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class));
|
||||
assertThat(generated1).hasToString("java.io.InputStream__Bytes");
|
||||
assertThat(generated2).hasToString("java.io.InputStream__Bytes1");
|
||||
assertThat(generated3).hasToString("java.io.InputStream__Bytes2");
|
||||
}
|
||||
|
||||
|
||||
static class TestBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedFiles.Kind;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -35,10 +36,12 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*/
|
||||
class DefaultGenerationContextTests {
|
||||
|
||||
private static final ClassName SAMPLE_TARGET = ClassName.get("com.example", "SampleTarget");
|
||||
|
||||
private static final Consumer<TypeSpec.Builder> typeSpecCustomizer = type -> {};
|
||||
|
||||
private final GeneratedClasses generatedClasses = new GeneratedClasses(
|
||||
new ClassNameGenerator(SampleTarget.class));
|
||||
new ClassNameGenerator(SAMPLE_TARGET));
|
||||
|
||||
private final InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
|
||||
|
||||
@@ -48,7 +51,7 @@ class DefaultGenerationContextTests {
|
||||
@Test
|
||||
void createWithOnlyGeneratedFilesCreatesContext() {
|
||||
DefaultGenerationContext context = new DefaultGenerationContext(
|
||||
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
|
||||
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
|
||||
assertThat(context.getGeneratedFiles()).isSameAs(this.generatedFiles);
|
||||
assertThat(context.getRuntimeHints()).isInstanceOf(RuntimeHints.class);
|
||||
}
|
||||
@@ -109,7 +112,7 @@ class DefaultGenerationContextTests {
|
||||
@Test
|
||||
void withNameUpdateNamingConvention() {
|
||||
DefaultGenerationContext context = new DefaultGenerationContext(
|
||||
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
|
||||
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
|
||||
GenerationContext anotherContext = context.withName("Another");
|
||||
GeneratedClass generatedClass = anotherContext.getGeneratedClasses()
|
||||
.addForFeature("Test", typeSpecCustomizer);
|
||||
@@ -119,7 +122,7 @@ class DefaultGenerationContextTests {
|
||||
@Test
|
||||
void withNameKeepsTrackOfAllGeneratedFiles() {
|
||||
DefaultGenerationContext context = new DefaultGenerationContext(
|
||||
new ClassNameGenerator(SampleTarget.class), this.generatedFiles);
|
||||
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
|
||||
context.getGeneratedClasses().addForFeature("Test", typeSpecCustomizer);
|
||||
GenerationContext anotherContext = context.withName("Another");
|
||||
assertThat(anotherContext.getGeneratedClasses()).isNotSameAs(context.getGeneratedClasses());
|
||||
@@ -133,7 +136,7 @@ class DefaultGenerationContextTests {
|
||||
@Test
|
||||
void withNameGeneratesUniqueName() {
|
||||
DefaultGenerationContext context = new DefaultGenerationContext(
|
||||
new ClassNameGenerator(Object.class), this.generatedFiles);
|
||||
new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles);
|
||||
context.withName("Test").getGeneratedClasses()
|
||||
.addForFeature("Feature", typeSpecCustomizer);
|
||||
context.withName("Test").getGeneratedClasses()
|
||||
@@ -142,11 +145,9 @@ class DefaultGenerationContextTests {
|
||||
.addForFeature("Feature", typeSpecCustomizer);
|
||||
context.writeGeneratedContent();
|
||||
assertThat(this.generatedFiles.getGeneratedFiles(Kind.SOURCE)).containsOnlyKeys(
|
||||
"java/lang/Object__TestFeature.java",
|
||||
"java/lang/Object__Test1Feature.java",
|
||||
"java/lang/Object__Test2Feature.java");
|
||||
"com/example/SampleTarget__TestFeature.java",
|
||||
"com/example/SampleTarget__Test1Feature.java",
|
||||
"com/example/SampleTarget__Test2Feature.java");
|
||||
}
|
||||
|
||||
static class SampleTarget {}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.function.Consumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedFiles.Kind;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -42,7 +43,7 @@ class GeneratedClassesTests {
|
||||
private static final Consumer<TypeSpec.Builder> emptyTypeCustomizer = type -> {};
|
||||
|
||||
private final GeneratedClasses generatedClasses = new GeneratedClasses(
|
||||
new ClassNameGenerator(Object.class));
|
||||
new ClassNameGenerator(ClassName.get("com.example", "Test")));
|
||||
|
||||
@Test
|
||||
void createWhenClassNameGeneratorIsNullThrowsException() {
|
||||
@@ -76,7 +77,7 @@ class GeneratedClassesTests {
|
||||
@Test
|
||||
void addForFeatureUsesDefaultTarget() {
|
||||
GeneratedClass generatedClass = this.generatedClasses.addForFeature("Test", emptyTypeCustomizer);
|
||||
assertThat(generatedClass.getName()).hasToString("java.lang.Object__Test");
|
||||
assertThat(generatedClass.getName()).hasToString("com.example.Test__Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user