Skip to content

Commit f40a391

Browse files
committed
Fix handling of reflection target name in TypeReference
This commit adds a `getName` to `TypeReference` that provides a way to generate the reflection target name of a type. This typically handle primitives (omitting the `java.lang` packages) and arrays. Closes gh-28347
1 parent 7820804 commit f40a391

File tree

11 files changed

+214
-97
lines changed

11 files changed

+214
-97
lines changed

spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeReference.java

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public final class GeneratedTypeReference extends AbstractTypeReference {
3232

3333
private final ClassName className;
3434

35-
@Nullable
36-
private final TypeReference enclosingType;
37-
3835
private GeneratedTypeReference(ClassName className) {
36+
super(className.packageName(), className.simpleName(), safeCreate(className.enclosingClassName()));
3937
this.className = className;
40-
this.enclosingType = (className.enclosingClassName() != null
41-
? new GeneratedTypeReference(className.enclosingClassName())
42-
: null);
38+
}
39+
40+
@Nullable
41+
private static GeneratedTypeReference safeCreate(@Nullable ClassName className) {
42+
return (className != null ? new GeneratedTypeReference(className) : null);
4343
}
4444

4545
public static GeneratedTypeReference of(ClassName className) {
@@ -53,18 +53,8 @@ public String getCanonicalName() {
5353
}
5454

5555
@Override
56-
public String getPackageName() {
57-
return this.className.packageName();
58-
}
59-
60-
@Override
61-
public String getSimpleName() {
62-
return this.className.simpleName();
63-
}
64-
65-
@Override
66-
public TypeReference getEnclosingType() {
67-
return this.enclosingType;
56+
protected boolean isPrimitive() {
57+
return this.className.isPrimitive();
6858
}
6959

7060
}

spring-core/src/main/java/org/springframework/aot/hint/AbstractTypeReference.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.util.Objects;
2020

21+
import org.springframework.lang.Nullable;
22+
2123
/**
2224
* Base {@link TypeReference} implementation that ensures consistent behaviour
2325
* for {@code equals()}, {@code hashCode()}, and {@code toString()} based on
@@ -28,6 +30,54 @@
2830
*/
2931
public abstract class AbstractTypeReference implements TypeReference {
3032

33+
private final String packageName;
34+
35+
private final String simpleName;
36+
37+
@Nullable
38+
private final TypeReference enclosingType;
39+
40+
protected AbstractTypeReference(String packageName, String simpleName, @Nullable TypeReference enclosingType) {
41+
this.packageName = packageName;
42+
this.simpleName = simpleName;
43+
this.enclosingType = enclosingType;
44+
}
45+
46+
@Override
47+
public String getName() {
48+
TypeReference enclosingType = getEnclosingType();
49+
String simpleName = getSimpleName();
50+
return (enclosingType != null
51+
? (enclosingType.getName() + '$' + simpleName)
52+
: addPackageIfNecessary(simpleName));
53+
}
54+
55+
@Override
56+
public String getPackageName() {
57+
return this.packageName;
58+
}
59+
60+
@Override
61+
public String getSimpleName() {
62+
return this.simpleName;
63+
}
64+
65+
@Nullable
66+
@Override
67+
public TypeReference getEnclosingType() {
68+
return this.enclosingType;
69+
}
70+
71+
protected abstract boolean isPrimitive();
72+
73+
protected String addPackageIfNecessary(String part) {
74+
if (this.packageName.isEmpty() ||
75+
this.packageName.equals("java.lang") && isPrimitive()) {
76+
return part;
77+
}
78+
return this.packageName + '.' + part;
79+
}
80+
3181
@Override
3282
public int hashCode() {
3383
return Objects.hash(getCanonicalName());

spring-core/src/main/java/org/springframework/aot/hint/ReflectionTypeReference.java

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ final class ReflectionTypeReference extends AbstractTypeReference {
2727

2828
private final Class<?> type;
2929

30-
@Nullable
31-
private final TypeReference enclosing;
32-
33-
3430
private ReflectionTypeReference(Class<?> type) {
31+
super(type.getPackageName(), type.getSimpleName(), safeCreate(type.getEnclosingClass()));
3532
this.type = type;
36-
this.enclosing = (type.getEnclosingClass() != null
37-
? TypeReference.of(type.getEnclosingClass()) : null);
33+
}
34+
35+
@Nullable
36+
private static ReflectionTypeReference safeCreate(@Nullable Class<?> type) {
37+
return (type != null ? new ReflectionTypeReference(type) : null);
3838
}
3939

4040
static ReflectionTypeReference of(Class<?> type) {
@@ -47,18 +47,9 @@ public String getCanonicalName() {
4747
}
4848

4949
@Override
50-
public String getPackageName() {
51-
return this.type.getPackageName();
52-
}
53-
54-
@Override
55-
public String getSimpleName() {
56-
return this.type.getSimpleName();
57-
}
58-
59-
@Override
60-
public TypeReference getEnclosingType() {
61-
return this.enclosing;
50+
protected boolean isPrimitive() {
51+
return this.type.isPrimitive() ||
52+
(this.type.isArray() && this.type.getComponentType().isPrimitive());
6253
}
6354

6455
}

spring-core/src/main/java/org/springframework/aot/hint/ResourceHints.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,7 @@ private ResourcePatternHint typesPatternResourceHint() {
131131
}
132132

133133
private String toIncludePattern(TypeReference type) {
134-
StringBuilder names = new StringBuilder();
135-
buildName(type, names);
136-
String candidate = type.getPackageName() + "." + names;
137-
return candidate.replace(".", "/") + ".class";
138-
}
139-
140-
private void buildName(@Nullable TypeReference type, StringBuilder sb) {
141-
if (type == null) {
142-
return;
143-
}
144-
String typeName = (type.getEnclosingType() != null) ? "$" + type.getSimpleName() : type.getSimpleName();
145-
sb.insert(0, typeName);
146-
buildName(type.getEnclosingType(), sb);
134+
return type.getName().replace(".", "/") + ".class";
147135
}
148136

149137
}

spring-core/src/main/java/org/springframework/aot/hint/SimpleTypeReference.java

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.aot.hint;
1818

19+
import java.util.List;
20+
1921
import javax.lang.model.SourceVersion;
2022

2123
import org.springframework.lang.Nullable;
@@ -28,21 +30,14 @@
2830
*/
2931
final class SimpleTypeReference extends AbstractTypeReference {
3032

31-
@Nullable
32-
private String canonicalName;
33-
34-
private final String packageName;
35-
36-
private final String simpleName;
33+
private static final List<String> PRIMITIVE_NAMES = List.of("boolean", "byte",
34+
"short", "int", "long", "char", "float", "double", "void");
3735

3836
@Nullable
39-
private final TypeReference enclosingType;
40-
37+
private String canonicalName;
4138

4239
SimpleTypeReference(String packageName, String simpleName, @Nullable TypeReference enclosingType) {
43-
this.packageName = packageName;
44-
this.simpleName = simpleName;
45-
this.enclosingType = enclosingType;
40+
super(packageName, simpleName, enclosingType);
4641
}
4742

4843
static SimpleTypeReference of(String className) {
@@ -63,7 +58,8 @@ static SimpleTypeReference of(String className) {
6358

6459
private static boolean isValidClassName(String className) {
6560
for (String s : className.split("\\.", -1)) {
66-
if (!SourceVersion.isIdentifier(s)) {
61+
String candidate = s.replace("[", "").replace("]", "");
62+
if (!SourceVersion.isIdentifier(candidate)) {
6763
return false;
6864
}
6965
}
@@ -72,21 +68,34 @@ private static boolean isValidClassName(String className) {
7268

7369
private static SimpleTypeReference createTypeReference(String className) {
7470
int i = className.lastIndexOf('.');
75-
return (i != -1 ? new SimpleTypeReference(className.substring(0, i), className.substring(i + 1), null)
76-
: new SimpleTypeReference("", className, null));
71+
if (i != -1) {
72+
return new SimpleTypeReference(className.substring(0, i), className.substring(i + 1), null);
73+
}
74+
else {
75+
String packageName = isPrimitive(className) ? "java.lang" : "";
76+
return new SimpleTypeReference(packageName, className, null);
77+
}
7778
}
7879

7980
@Override
8081
public String getCanonicalName() {
8182
if (this.canonicalName == null) {
8283
StringBuilder names = new StringBuilder();
8384
buildName(this, names);
84-
this.canonicalName = (this.packageName.isEmpty()
85-
? names.toString() : this.packageName + "." + names);
85+
this.canonicalName = addPackageIfNecessary(names.toString());
8686
}
8787
return this.canonicalName;
8888
}
8989

90+
@Override
91+
protected boolean isPrimitive() {
92+
return isPrimitive(getSimpleName());
93+
}
94+
95+
private static boolean isPrimitive(String name) {
96+
return PRIMITIVE_NAMES.stream().anyMatch(name::startsWith);
97+
}
98+
9099
private static void buildName(@Nullable TypeReference type, StringBuilder sb) {
91100
if (type == null) {
92101
return;
@@ -96,19 +105,4 @@ private static void buildName(@Nullable TypeReference type, StringBuilder sb) {
96105
buildName(type.getEnclosingType(), sb);
97106
}
98107

99-
@Override
100-
public String getPackageName() {
101-
return this.packageName;
102-
}
103-
104-
@Override
105-
public String getSimpleName() {
106-
return this.simpleName;
107-
}
108-
109-
@Override
110-
public TypeReference getEnclosingType() {
111-
return this.enclosingType;
112-
}
113-
114108
}

spring-core/src/main/java/org/springframework/aot/hint/TypeReference.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
*/
2828
public interface TypeReference {
2929

30+
/**
31+
* Return the fully qualified name of this type reference.
32+
* @return the reflection target name
33+
*/
34+
String getName();
35+
3036
/**
3137
* Return the {@linkplain Class#getCanonicalName() canonical name} of this
3238
* type reference.

spring-core/src/main/java/org/springframework/aot/nativex/BasicJsonWriter.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.function.Consumer;
2525

2626
import org.springframework.aot.hint.TypeReference;
27-
import org.springframework.lang.Nullable;
2827

2928
/**
3029
* Very basic json writer for the purposes of translating runtime hints to native
@@ -133,7 +132,7 @@ else if (value instanceof List<?> list) {
133132
writeArray(list, false);
134133
}
135134
else if (value instanceof TypeReference typeReference) {
136-
this.writer.print(quote(toName(typeReference)));
135+
this.writer.print(quote(typeReference.getName()));
137136
}
138137
else if (value instanceof CharSequence string) {
139138
this.writer.print(quote(escape(string)));
@@ -150,21 +149,6 @@ private String quote(String name) {
150149
return "\"" + name + "\"";
151150
}
152151

153-
private String toName(TypeReference typeReference) {
154-
StringBuilder names = new StringBuilder();
155-
buildName(typeReference, names);
156-
return typeReference.getPackageName() + "." + names;
157-
}
158-
159-
private void buildName(@Nullable TypeReference type, StringBuilder sb) {
160-
if (type == null) {
161-
return;
162-
}
163-
String typeName = (type.getEnclosingType() != null) ? "$" + type.getSimpleName() : type.getSimpleName();
164-
sb.insert(0, typeName);
165-
buildName(type.getEnclosingType(), sb);
166-
}
167-
168152

169153
private static String escape(CharSequence input) {
170154
StringBuilder builder = new StringBuilder();

spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeReferenceTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616

1717
package org.springframework.aot.generator;
1818

19+
import java.util.stream.Stream;
20+
1921
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.Arguments;
24+
import org.junit.jupiter.params.provider.MethodSource;
2025

2126
import org.springframework.aot.hint.TypeReference;
2227
import org.springframework.javapoet.ClassName;
@@ -30,6 +35,19 @@
3035
*/
3136
class GeneratedTypeReferenceTests {
3237

38+
39+
@ParameterizedTest
40+
@MethodSource("reflectionTargetNames")
41+
void hasSuitableReflectionTargetName(TypeReference typeReference, String binaryName) {
42+
assertThat(typeReference.getName()).isEqualTo(binaryName);
43+
}
44+
45+
static Stream<Arguments> reflectionTargetNames() {
46+
return Stream.of(
47+
Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test")), "com.example.Test"),
48+
Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test", "Inner")), "com.example.Test$Inner"));
49+
}
50+
3351
@Test
3452
void createWithClassName() {
3553
GeneratedTypeReference typeReference = GeneratedTypeReference.of(

0 commit comments

Comments
 (0)