Skip to content

Commit

Permalink
Fix handling of reflection target name in TypeReference
Browse files Browse the repository at this point in the history
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
  • Loading branch information
snicoll committed Apr 15, 2022
1 parent 7820804 commit f40a391
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ public final class GeneratedTypeReference extends AbstractTypeReference {

private final ClassName className;

@Nullable
private final TypeReference enclosingType;

private GeneratedTypeReference(ClassName className) {
super(className.packageName(), className.simpleName(), safeCreate(className.enclosingClassName()));
this.className = className;
this.enclosingType = (className.enclosingClassName() != null
? new GeneratedTypeReference(className.enclosingClassName())
: null);
}

@Nullable
private static GeneratedTypeReference safeCreate(@Nullable ClassName className) {
return (className != null ? new GeneratedTypeReference(className) : null);
}

public static GeneratedTypeReference of(ClassName className) {
Expand All @@ -53,18 +53,8 @@ public String getCanonicalName() {
}

@Override
public String getPackageName() {
return this.className.packageName();
}

@Override
public String getSimpleName() {
return this.className.simpleName();
}

@Override
public TypeReference getEnclosingType() {
return this.enclosingType;
protected boolean isPrimitive() {
return this.className.isPrimitive();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import java.util.Objects;

import org.springframework.lang.Nullable;

/**
* Base {@link TypeReference} implementation that ensures consistent behaviour
* for {@code equals()}, {@code hashCode()}, and {@code toString()} based on
Expand All @@ -28,6 +30,54 @@
*/
public abstract class AbstractTypeReference implements TypeReference {

private final String packageName;

private final String simpleName;

@Nullable
private final TypeReference enclosingType;

protected AbstractTypeReference(String packageName, String simpleName, @Nullable TypeReference enclosingType) {
this.packageName = packageName;
this.simpleName = simpleName;
this.enclosingType = enclosingType;
}

@Override
public String getName() {
TypeReference enclosingType = getEnclosingType();
String simpleName = getSimpleName();
return (enclosingType != null
? (enclosingType.getName() + '$' + simpleName)
: addPackageIfNecessary(simpleName));
}

@Override
public String getPackageName() {
return this.packageName;
}

@Override
public String getSimpleName() {
return this.simpleName;
}

@Nullable
@Override
public TypeReference getEnclosingType() {
return this.enclosingType;
}

protected abstract boolean isPrimitive();

protected String addPackageIfNecessary(String part) {
if (this.packageName.isEmpty() ||
this.packageName.equals("java.lang") && isPrimitive()) {
return part;
}
return this.packageName + '.' + part;
}

@Override
public int hashCode() {
return Objects.hash(getCanonicalName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ final class ReflectionTypeReference extends AbstractTypeReference {

private final Class<?> type;

@Nullable
private final TypeReference enclosing;


private ReflectionTypeReference(Class<?> type) {
super(type.getPackageName(), type.getSimpleName(), safeCreate(type.getEnclosingClass()));
this.type = type;
this.enclosing = (type.getEnclosingClass() != null
? TypeReference.of(type.getEnclosingClass()) : null);
}

@Nullable
private static ReflectionTypeReference safeCreate(@Nullable Class<?> type) {
return (type != null ? new ReflectionTypeReference(type) : null);
}

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

@Override
public String getPackageName() {
return this.type.getPackageName();
}

@Override
public String getSimpleName() {
return this.type.getSimpleName();
}

@Override
public TypeReference getEnclosingType() {
return this.enclosing;
protected boolean isPrimitive() {
return this.type.isPrimitive() ||
(this.type.isArray() && this.type.getComponentType().isPrimitive());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,7 @@ private ResourcePatternHint typesPatternResourceHint() {
}

private String toIncludePattern(TypeReference type) {
StringBuilder names = new StringBuilder();
buildName(type, names);
String candidate = type.getPackageName() + "." + names;
return candidate.replace(".", "/") + ".class";
}

private void buildName(@Nullable TypeReference type, StringBuilder sb) {
if (type == null) {
return;
}
String typeName = (type.getEnclosingType() != null) ? "$" + type.getSimpleName() : type.getSimpleName();
sb.insert(0, typeName);
buildName(type.getEnclosingType(), sb);
return type.getName().replace(".", "/") + ".class";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.aot.hint;

import java.util.List;

import javax.lang.model.SourceVersion;

import org.springframework.lang.Nullable;
Expand All @@ -28,21 +30,14 @@
*/
final class SimpleTypeReference extends AbstractTypeReference {

@Nullable
private String canonicalName;

private final String packageName;

private final String simpleName;
private static final List<String> PRIMITIVE_NAMES = List.of("boolean", "byte",
"short", "int", "long", "char", "float", "double", "void");

@Nullable
private final TypeReference enclosingType;

private String canonicalName;

SimpleTypeReference(String packageName, String simpleName, @Nullable TypeReference enclosingType) {
this.packageName = packageName;
this.simpleName = simpleName;
this.enclosingType = enclosingType;
super(packageName, simpleName, enclosingType);
}

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

private static boolean isValidClassName(String className) {
for (String s : className.split("\\.", -1)) {
if (!SourceVersion.isIdentifier(s)) {
String candidate = s.replace("[", "").replace("]", "");
if (!SourceVersion.isIdentifier(candidate)) {
return false;
}
}
Expand All @@ -72,21 +68,34 @@ private static boolean isValidClassName(String className) {

private static SimpleTypeReference createTypeReference(String className) {
int i = className.lastIndexOf('.');
return (i != -1 ? new SimpleTypeReference(className.substring(0, i), className.substring(i + 1), null)
: new SimpleTypeReference("", className, null));
if (i != -1) {
return new SimpleTypeReference(className.substring(0, i), className.substring(i + 1), null);
}
else {
String packageName = isPrimitive(className) ? "java.lang" : "";
return new SimpleTypeReference(packageName, className, null);
}
}

@Override
public String getCanonicalName() {
if (this.canonicalName == null) {
StringBuilder names = new StringBuilder();
buildName(this, names);
this.canonicalName = (this.packageName.isEmpty()
? names.toString() : this.packageName + "." + names);
this.canonicalName = addPackageIfNecessary(names.toString());
}
return this.canonicalName;
}

@Override
protected boolean isPrimitive() {
return isPrimitive(getSimpleName());
}

private static boolean isPrimitive(String name) {
return PRIMITIVE_NAMES.stream().anyMatch(name::startsWith);
}

private static void buildName(@Nullable TypeReference type, StringBuilder sb) {
if (type == null) {
return;
Expand All @@ -96,19 +105,4 @@ private static void buildName(@Nullable TypeReference type, StringBuilder sb) {
buildName(type.getEnclosingType(), sb);
}

@Override
public String getPackageName() {
return this.packageName;
}

@Override
public String getSimpleName() {
return this.simpleName;
}

@Override
public TypeReference getEnclosingType() {
return this.enclosingType;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
*/
public interface TypeReference {

/**
* Return the fully qualified name of this type reference.
* @return the reflection target name
*/
String getName();

/**
* Return the {@linkplain Class#getCanonicalName() canonical name} of this
* type reference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.function.Consumer;

import org.springframework.aot.hint.TypeReference;
import org.springframework.lang.Nullable;

/**
* Very basic json writer for the purposes of translating runtime hints to native
Expand Down Expand Up @@ -133,7 +132,7 @@ else if (value instanceof List<?> list) {
writeArray(list, false);
}
else if (value instanceof TypeReference typeReference) {
this.writer.print(quote(toName(typeReference)));
this.writer.print(quote(typeReference.getName()));
}
else if (value instanceof CharSequence string) {
this.writer.print(quote(escape(string)));
Expand All @@ -150,21 +149,6 @@ private String quote(String name) {
return "\"" + name + "\"";
}

private String toName(TypeReference typeReference) {
StringBuilder names = new StringBuilder();
buildName(typeReference, names);
return typeReference.getPackageName() + "." + names;
}

private void buildName(@Nullable TypeReference type, StringBuilder sb) {
if (type == null) {
return;
}
String typeName = (type.getEnclosingType() != null) ? "$" + type.getSimpleName() : type.getSimpleName();
sb.insert(0, typeName);
buildName(type.getEnclosingType(), sb);
}


private static String escape(CharSequence input) {
StringBuilder builder = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@

package org.springframework.aot.generator;

import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import org.springframework.aot.hint.TypeReference;
import org.springframework.javapoet.ClassName;
Expand All @@ -30,6 +35,19 @@
*/
class GeneratedTypeReferenceTests {


@ParameterizedTest
@MethodSource("reflectionTargetNames")
void hasSuitableReflectionTargetName(TypeReference typeReference, String binaryName) {
assertThat(typeReference.getName()).isEqualTo(binaryName);
}

static Stream<Arguments> reflectionTargetNames() {
return Stream.of(
Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test")), "com.example.Test"),
Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test", "Inner")), "com.example.Test$Inner"));
}

@Test
void createWithClassName() {
GeneratedTypeReference typeReference = GeneratedTypeReference.of(
Expand Down
Loading

0 comments on commit f40a391

Please sign in to comment.