Use proper format for TypeReference

This commit updates BasicJsonWriter to handle TypeReferences and
generate an appropriate format for a class name. Specifically, an
inner class should be separated by a dollar sign, not a dot.

Closes gh-28312
This commit is contained in:
Stephane Nicoll 2022-04-11 16:57:22 +02:00
parent c8a7a187a2
commit 069aab37cd
4 changed files with 63 additions and 13 deletions

View File

@ -23,6 +23,9 @@ import java.util.List;
import java.util.Map;
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
* configuration.
@ -129,6 +132,9 @@ class BasicJsonWriter {
else if (value instanceof List<?> list) {
writeArray(list, false);
}
else if (value instanceof TypeReference typeReference) {
this.writer.print(quote(toName(typeReference)));
}
else if (value instanceof CharSequence string) {
this.writer.print(quote(escape(string)));
}
@ -144,6 +150,22 @@ class BasicJsonWriter {
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();
input.chars().forEach(c -> {

View File

@ -42,7 +42,7 @@ class JavaSerializationHintsWriter {
private Map<String, Object> toAttributes(TypeReference typeReference) {
LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", typeReference.getCanonicalName());
attributes.put("name", typeReference);
return attributes;
}

View File

@ -52,7 +52,7 @@ class ReflectionHintsWriter {
private Map<String, Object> toAttributes(TypeHint hint) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", hint.getType().getCanonicalName());
attributes.put("name", hint.getType());
handleCondition(attributes, hint);
handleCategories(attributes, hint.getMemberCategories());
handleFields(attributes, hint.fields());
@ -63,7 +63,7 @@ class ReflectionHintsWriter {
private void handleCondition(Map<String, Object> attributes, TypeHint hint) {
if (hint.getReachableType() != null) {
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
conditionAttributes.put("typeReachable", hint.getReachableType().getCanonicalName());
conditionAttributes.put("typeReachable", hint.getReachableType());
attributes.put("condition", conditionAttributes);
}
}

View File

@ -24,6 +24,9 @@ import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.nativex.BasicJsonWriterTests.Nested.Inner;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -136,49 +139,66 @@ class BasicJsonWriterTests {
@Test
void writeWithEscapeDoubleQuote() {
assertEscapedValue("foo\"bar", "foo\\\"bar");
assertStringAttribute("foo\"bar", "foo\\\"bar");
}
@Test
void writeWithEscapeBackslash() {
assertEscapedValue("foo\"bar", "foo\\\"bar");
assertStringAttribute("foo\"bar", "foo\\\"bar");
}
@Test
void writeWithEscapeBackspace() {
assertEscapedValue("foo\bbar", "foo\\bbar");
assertStringAttribute("foo\bbar", "foo\\bbar");
}
@Test
void writeWithEscapeFormFeed() {
assertEscapedValue("foo\fbar", "foo\\fbar");
assertStringAttribute("foo\fbar", "foo\\fbar");
}
@Test
void writeWithEscapeNewline() {
assertEscapedValue("foo\nbar", "foo\\nbar");
assertStringAttribute("foo\nbar", "foo\\nbar");
}
@Test
void writeWithEscapeCarriageReturn() {
assertEscapedValue("foo\rbar", "foo\\rbar");
assertStringAttribute("foo\rbar", "foo\\rbar");
}
@Test
void writeWithEscapeTab() {
assertEscapedValue("foo\tbar", "foo\\tbar");
assertStringAttribute("foo\tbar", "foo\\tbar");
}
@Test
void writeWithEscapeUnicode() {
assertEscapedValue("foo\u001Fbar", "foo\\u001fbar");
assertStringAttribute("foo\u001Fbar", "foo\\u001fbar");
}
void assertEscapedValue(String value, String expectedEscapedValue) {
@Test
void writeWithTypeReferenceForSimpleClass() {
assertStringAttribute(TypeReference.of(String.class), "java.lang.String");
}
@Test
void writeWithTypeReferenceForInnerClass() {
assertStringAttribute(TypeReference.of(Nested.class),
"org.springframework.aot.nativex.BasicJsonWriterTests$Nested");
}
@Test
void writeWithTypeReferenceForDoubleInnerClass() {
assertStringAttribute(TypeReference.of(Inner.class),
"org.springframework.aot.nativex.BasicJsonWriterTests$Nested$Inner");
}
void assertStringAttribute(Object value, String expectedValue) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("test", value);
this.json.writeObject(attributes);
assertThat(out.toString()).contains("\"test\": \"" + expectedEscapedValue + "\"");
assertThat(out.toString()).contains("\"test\": \"" + expectedValue + "\"");
}
private static LinkedHashMap<String, Object> orderedMap(String key, Object value) {
@ -187,4 +207,12 @@ class BasicJsonWriterTests {
return map;
}
static class Nested {
static class Inner {
}
}
}