From 9034e73bd221d14f37e817f3aaf06b299ec822fb Mon Sep 17 00:00:00 2001 From: MoonFruit Date: Tue, 4 Mar 2025 17:36:49 +0800 Subject: [PATCH] Ensure ConversionService.canConvert(Enum) no longer throws an exception --- .../core/convert/support/ConversionUtils.java | 24 ++++++++++++++++--- .../IntegerToEnumConverterFactory.java | 6 ++++- .../support/StringToEnumConverterFactory.java | 7 ++++-- .../GenericConversionServiceTests.java | 10 ++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java index aad0e4d13bc9..9166493e3100 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java @@ -21,8 +21,9 @@ import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalConverter; +import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -71,13 +72,30 @@ public static boolean canConvertElements(@Nullable TypeDescriptor sourceElementT return false; } - public static Class getEnumType(Class targetType) { + public static @Nullable Class getEnumType(Class targetType) { Class enumType = targetType; while (enumType != null && !enumType.isEnum()) { enumType = enumType.getSuperclass(); } - Assert.notNull(enumType, () -> "The target type " + targetType.getName() + " does not refer to an enum"); return enumType; } + static class NonConvertableToEnum implements Converter, ConditionalConverter { + + private final Class targetType; + + public NonConvertableToEnum(Class targetType) { + this.targetType = targetType; + } + + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + return false; + } + + @Override + public @Nullable T convert(String source) { + throw new IllegalArgumentException("The target type " + targetType.getName() + " does not refer to an enum"); + } + } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/IntegerToEnumConverterFactory.java b/spring-core/src/main/java/org/springframework/core/convert/support/IntegerToEnumConverterFactory.java index a322065988eb..17cd46473f4a 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/IntegerToEnumConverterFactory.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/IntegerToEnumConverterFactory.java @@ -31,7 +31,11 @@ final class IntegerToEnumConverterFactory implements ConverterFactory Converter getConverter(Class targetType) { - return new IntegerToEnum(ConversionUtils.getEnumType(targetType)); + Class enumType = ConversionUtils.getEnumType(targetType); + if (enumType == null) { + return new ConversionUtils.NonConvertableToEnum(targetType); + } + return new IntegerToEnum(enumType); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java b/spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java index 78427e4ca879..cfc1137a2686 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java @@ -33,10 +33,13 @@ final class StringToEnumConverterFactory implements ConverterFactory Converter getConverter(Class targetType) { - return new StringToEnum(ConversionUtils.getEnumType(targetType)); + Class enumType = ConversionUtils.getEnumType(targetType); + if (enumType == null) { + return new ConversionUtils.NonConvertableToEnum(targetType); + } + return new StringToEnum(enumType); } - private static class StringToEnum implements Converter { private final Class enumType; diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index 87fac1f6bcfc..baa443a72cc5 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -503,6 +503,16 @@ void stringToEnumWithBaseInterfaceConversion() { assertThat(conversionService.convert("base1", MyEnum.class)).isEqualTo(MyEnum.A); } + @Test + void toEnumCanConvertShouldNotThrowException() { + conversionService.addConverterFactory(new IntegerToEnumConverterFactory()); + conversionService.addConverterFactory(new StringToEnumConverterFactory()); + assertThat(conversionService.canConvert(Integer.class, Enum.class)).isFalse(); + assertThat(conversionService.canConvert(Integer.class, MyEnum.class)).isTrue(); + assertThat(conversionService.canConvert(String.class, Enum.class)).isFalse(); + assertThat(conversionService.canConvert(String.class, MyEnum.class)).isTrue(); + } + @Test void convertNullAnnotatedStringToString() throws Exception { String source = null;