Polishing

See gh-26540
This commit is contained in:
Sam Brannen 2021-02-12 11:27:44 +01:00
parent 77b7e49fb2
commit 566ff54782
4 changed files with 68 additions and 60 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -74,8 +74,8 @@ public abstract class ClassUtils {
/** The path separator character: {@code '/'}. */
private static final char PATH_SEPARATOR = '/';
/** The inner class separator character: {@code '$'}. */
private static final char INNER_CLASS_SEPARATOR = '$';
/** The nested class separator character: {@code '$'}. */
private static final char NESTED_CLASS_SEPARATOR = '$';
/** The CGLIB class separator: {@code "$$"}. */
public static final String CGLIB_CLASS_SEPARATOR = "$$";
@ -232,7 +232,7 @@ public abstract class ClassUtils {
/**
* Replacement for {@code Class.forName()} that also returns Class instances
* for primitives (e.g. "int") and array class names (e.g. "String[]").
* Furthermore, it is also capable of resolving inner class names in Java source
* Furthermore, it is also capable of resolving nested class names in Java source
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
* @param name the name of the Class
* @param classLoader the class loader to use
@ -286,10 +286,10 @@ public abstract class ClassUtils {
catch (ClassNotFoundException ex) {
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
String innerClassName =
name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
String nestedClassName =
name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return Class.forName(innerClassName, false, clToUse);
return Class.forName(nestedClassName, false, clToUse);
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
@ -953,7 +953,7 @@ public abstract class ClassUtils {
nameEndIndex = className.length();
}
String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
shortName = shortName.replace(NESTED_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
return shortName;
}
@ -968,7 +968,7 @@ public abstract class ClassUtils {
/**
* Return the short string name of a Java class in uncapitalized JavaBeans
* property format. Strips the outer class name in case of an inner class.
* property format. Strips the outer class name in case of a nested class.
* @param clazz the class
* @return the short name rendered in a standard JavaBeans property format
* @see java.beans.Introspector#decapitalize(String)

View File

@ -75,7 +75,4 @@ public class TestObject implements ITestObject, ITestInterface, Comparable<Objec
return 1;
}
}
public static class NestedObject {
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -33,6 +33,7 @@ import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@ -59,14 +60,6 @@ class ClassUtilsTests {
private final ClassLoader classLoader = getClass().getClassLoader();
@BeforeEach
void clearStatics() {
InnerClass.noArgCalled = false;
InnerClass.argCalled = false;
InnerClass.overloadedCalled = false;
}
@Test
void isPresent() {
assertThat(ClassUtils.isPresent("java.lang.String", classLoader)).isTrue();
@ -86,8 +79,12 @@ class ClassUtilsTests {
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject[][]", classLoader)).isEqualTo(TestObject[][].class);
assertThat(ClassUtils.forName(TestObject[][].class.getName(), classLoader)).isEqualTo(TestObject[][].class);
assertThat(ClassUtils.forName("[[[S", classLoader)).isEqualTo(short[][][].class);
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject$NestedObject", classLoader)).isEqualTo(TestObject.NestedObject.class);
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject.NestedObject", classLoader)).isEqualTo(TestObject.NestedObject.class);
}
@Test
void forNameWithNestedType() throws ClassNotFoundException {
assertThat(ClassUtils.forName("org.springframework.util.ClassUtilsTests$NestedClass", classLoader)).isEqualTo(NestedClass.class);
assertThat(ClassUtils.forName("org.springframework.util.ClassUtilsTests.NestedClass", classLoader)).isEqualTo(NestedClass.class);
}
@Test
@ -145,11 +142,11 @@ class ClassUtilsTests {
assertThat(ClassUtils.isCacheSafe(String.class, childLoader1)).isTrue();
assertThat(ClassUtils.isCacheSafe(String.class, childLoader2)).isTrue();
assertThat(ClassUtils.isCacheSafe(String.class, childLoader3)).isTrue();
assertThat(ClassUtils.isCacheSafe(InnerClass.class, null)).isFalse();
assertThat(ClassUtils.isCacheSafe(InnerClass.class, classLoader)).isTrue();
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader1)).isTrue();
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader2)).isTrue();
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader3)).isTrue();
assertThat(ClassUtils.isCacheSafe(NestedClass.class, null)).isFalse();
assertThat(ClassUtils.isCacheSafe(NestedClass.class, classLoader)).isTrue();
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader1)).isTrue();
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader2)).isTrue();
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader3)).isTrue();
assertThat(ClassUtils.isCacheSafe(composite, null)).isFalse();
assertThat(ClassUtils.isCacheSafe(composite, classLoader)).isFalse();
assertThat(ClassUtils.isCacheSafe(composite, childLoader1)).isTrue();
@ -211,9 +208,9 @@ class ClassUtilsTests {
}
@Test
void getShortNameForInnerClass() {
String className = ClassUtils.getShortName(InnerClass.class);
assertThat(className).as("Class name did not match").isEqualTo("ClassUtilsTests.InnerClass");
void getShortNameForNestedClass() {
String className = ClassUtils.getShortName(NestedClass.class);
assertThat(className).as("Class name did not match").isEqualTo("ClassUtilsTests.NestedClass");
}
@Test
@ -301,27 +298,6 @@ class ClassUtilsTests {
assertThat(ClassUtils.hasAtLeastOneMethodWithName(TestObject.class, "setAge")).isTrue();
}
@Test
void noArgsStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(InnerClass.class, "staticMethod");
method.invoke(null, (Object[]) null);
assertThat(InnerClass.noArgCalled).as("no argument method was not invoked.").isTrue();
}
@Test
void argsStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(InnerClass.class, "argStaticMethod", String.class);
method.invoke(null, "test");
assertThat(InnerClass.argCalled).as("argument method was not invoked.").isTrue();
}
@Test
void overloadedStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(InnerClass.class, "staticMethod", String.class);
method.invoke(null, "test");
assertThat(InnerClass.overloadedCalled).as("argument method was not invoked.").isTrue();
}
@Test
void isAssignable() {
assertThat(ClassUtils.isAssignable(Object.class, Object.class)).isTrue();
@ -433,6 +409,40 @@ class ClassUtilsTests {
}
@Nested
class GetStaticMethodTests {
@BeforeEach
void clearStatics() {
NestedClass.noArgCalled = false;
NestedClass.argCalled = false;
NestedClass.overloadedCalled = false;
}
@Test
void noArgsStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(NestedClass.class, "staticMethod");
method.invoke(null, (Object[]) null);
assertThat(NestedClass.noArgCalled).as("no argument method was not invoked.").isTrue();
}
@Test
void argsStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(NestedClass.class, "argStaticMethod", String.class);
method.invoke(null, "test");
assertThat(NestedClass.argCalled).as("argument method was not invoked.").isTrue();
}
@Test
void overloadedStaticMethod() throws IllegalAccessException, InvocationTargetException {
Method method = ClassUtils.getStaticMethod(NestedClass.class, "staticMethod", String.class);
method.invoke(null, "test");
assertThat(NestedClass.overloadedCalled).as("argument method was not invoked.").isTrue();
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ValueSource(classes = { Boolean.class, Character.class, Byte.class, Short.class,
@ -447,7 +457,7 @@ class ClassUtilsTests {
@interface PrimitiveTypes {
}
public static class InnerClass {
public static class NestedClass {
static boolean noArgCalled;
static boolean argCalled;

View File

@ -639,15 +639,16 @@ You can use the `Class` property in one of two ways:
from the invocation of the `static` factory method may be the same class or another
class entirely.
.Nested class names
****
.Inner class names
If you want to configure a bean definition for a `static` nested class, you may use
either binary name of the nested class or separate it with dot.
If you want to configure a bean definition for a nested class, you may use either the
binary name or the source name of the nested class.
For example, if you have a class called `SomeThing` in the `com.example` package, and this
`SomeThing` class has a `static` nested class called `OtherThing`, they can be separated
by a dollar (`$`) or dot (`.`). So the value of the `class` attribute on a bean definition
would be `com.example.SomeThing$OtherThing` or `com.example.SomeThing.OtherThing`.
For example, if you have a class called `SomeThing` in the `com.example` package, and
this `SomeThing` class has a `static` nested class called `OtherThing`, they can be
separated by a dollar sign (`$`) or a dot (`.`). So the value of the `class` attribute in
a bean definition would be `com.example.SomeThing$OtherThing` or
`com.example.SomeThing.OtherThing`.
****