Merge branch '5.3.x'

# Conflicts:
#	spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java
#	src/eclipse/org.eclipse.jdt.ui.prefs
This commit is contained in:
Sam Brannen 2022-02-11 15:01:47 +01:00
commit 5ae9217271
3 changed files with 95 additions and 11 deletions

View File

@ -177,7 +177,7 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
private String annotationToString() {
String string = this.string;
if (string == null) {
StringBuilder builder = new StringBuilder("@").append(this.type.getName()).append('(');
StringBuilder builder = new StringBuilder("@").append(getName(this.type)).append('(');
for (int i = 0; i < this.attributes.size(); i++) {
Method attribute = this.attributes.get(i);
if (i > 0) {
@ -194,15 +194,43 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
return string;
}
/**
* This method currently does not address the following issues which we may
* choose to address at a later point in time.
*
* <ul>
* <li>non-ASCII, non-visible, and non-printable characters within a character
* or String literal are not escaped.</li>
* <li>formatting for float and double values does not take into account whether
* a value is not a number (NaN) or infinite.</li>
* </ul>
* @param value the attribute value to format
* @return the formatted string representation
*/
private String toString(Object value) {
if (value instanceof String str) {
return '"' + str + '"';
}
if (value instanceof Character) {
return '\'' + value.toString() + '\'';
}
if (value instanceof Byte) {
return String.format("(byte) 0x%02X", value);
}
if (value instanceof Long longValue) {
return Long.toString(longValue) + 'L';
}
if (value instanceof Float floatValue) {
return Float.toString(floatValue) + 'f';
}
if (value instanceof Double doubleValue) {
return Double.toString(doubleValue) + 'd';
}
if (value instanceof Enum<?> e) {
return e.name();
}
if (value instanceof Class<?> clazz) {
return clazz.getName() + ".class";
return getName(clazz) + ".class";
}
if (value.getClass().isArray()) {
StringBuilder builder = new StringBuilder("{");
@ -277,6 +305,11 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
private static String getName(Class<?> clazz) {
String canonicalName = clazz.getCanonicalName();
return (canonicalName != null ? canonicalName : clazz.getName());
}
private static boolean isVisible(ClassLoader classLoader, Class<?> interfaceClass) {
if (classLoader == interfaceClass.getClassLoader()) {

View File

@ -37,6 +37,7 @@ import java.util.stream.Stream;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.JRE;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.MergedAnnotation.Adapt;
@ -1883,15 +1884,44 @@ class MergedAnnotationsTests {
// Formatting common to Spring and JDK 9+
assertThat(string)
.startsWith("@" + RequestMapping.class.getName() + "(")
.contains("value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"", "clazz=java.lang.Object.class")
.contains("value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"", "ch='X'", "chars={'X'}")
.endsWith(")");
if (webMapping instanceof SynthesizedAnnotation) {
assertThat(string).as("Spring uses Enum#name()").contains("method={GET, POST}");
assertThat(string).as("Spring formatting")
.startsWith("@org.springframework.core.annotation.MergedAnnotationsTests.RequestMapping(")
.contains("method={GET, POST}",
"clazz=org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod.class",
"classes={int[][].class, org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod[].class}",
"byteValue=(byte) 0xFF", "bytes={(byte) 0xFF}",
"shortValue=9876", "shorts={9876}",
"longValue=42L", "longs={42L}",
"floatValue=3.14f", "floats={3.14f}",
"doubleValue=99.999d", "doubles={99.999d}"
);
}
else {
assertThat(string).as("JDK uses Enum#toString()").contains("method={method: get, method: post}");
assertThat(string).as("JDK 9-18 formatting")
.startsWith("@org.springframework.core.annotation.MergedAnnotationsTests$RequestMapping(")
.contains("method={method: get, method: post}",
"clazz=org.springframework.core.annotation.MergedAnnotationsTests$RequestMethod.class",
"classes={int[][].class, org.springframework.core.annotation.MergedAnnotationsTests$RequestMethod[].class}",
"shortValue=9876", "shorts={9876}",
"floatValue=3.14f", "floats={3.14f}",
"doubleValue=99.999", "doubles={99.999}"
);
if (JRE.currentVersion().ordinal() < JRE.JAVA_14.ordinal()) {
assertThat(string).as("JDK 9-13 formatting")
.contains("longValue=42", "longs={42}",
"byteValue=-1", "bytes={-1}"
);
}
else {
assertThat(string).as("JDK 14+ formatting")
.contains("longValue=42L", "longs={42L}",
"byteValue=(byte)0xff", "bytes={(byte)0xff}"
);
}
}
}
@ -2985,8 +3015,29 @@ class MergedAnnotationsTests {
RequestMethod[] method() default {};
// clazz is only used for testing annotation toString() implementations
Class<?> clazz() default Object.class;
// ---------------------------------------------------------------------
// All remaining attributes declare default values that are used solely
// for the purpose of testing the toString() implementations for annotations.
Class<?> clazz() default RequestMethod.class;
Class<?>[] classes() default {int[][].class, RequestMethod[].class};
char ch() default 'X';
char[] chars() default {'X'};
byte byteValue() default (byte) 0xFF;
byte[] bytes() default {(byte) 0xFF};
short shortValue() default 9876;
short[] shorts() default {9876};
long longValue() default 42L;
long[] longs() default {42L};
float floatValue() default 3.14F;
float[] floats() default {3.14F};
double doubleValue() default 99.999D;
double[] doubles() default {99.999D};
}
@Retention(RetentionPolicy.RUNTIME)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -72,8 +72,8 @@ class BootstrapUtilsTests {
assertThatIllegalStateException().isThrownBy(() ->
resolveTestContextBootstrapper(bootstrapContext))
.withMessageContaining("Configuration error: found multiple declarations of @BootstrapWith")
.withMessageContaining(FooBootstrapper.class.getName())
.withMessageContaining(BarBootstrapper.class.getName());
.withMessageContaining(FooBootstrapper.class.getCanonicalName())
.withMessageContaining(BarBootstrapper.class.getCanonicalName());
}
@Test