mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Avoid exception in ConversionService.canConvert() for Enum targets
Prior to this commit, ConversionService#canConvert(Class, Class) threw an IllegalArgumentException when invoked with Enum.class as the target type (i.e., `canConvert(String.class, Enum.class)`), because ConverterFactory#getConverter() in StringToEnumConverterFactory and IntegerToEnumConverterFactory eagerly resolved the concrete enum type. To address that, StringToEnumConverterFactory and IntegerToEnumConverterFactory now implement ConditionalConverter so that matches() can reject non-concrete-enum targets before getConverter() is ever invoked. Closes gh-34532 Signed-off-by: MoonFruit <dkmoonfruit@gmail.com> Co-authored-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
This commit is contained in:
+7
-3
@@ -22,7 +22,6 @@ import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -71,12 +70,17 @@ abstract class ConversionUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Class<?> getEnumType(Class<?> targetType) {
|
||||
/**
|
||||
* Resolve the enum type for the supplied target type.
|
||||
* @param targetType the target type for which to resolve the enum type
|
||||
* @return the resolved enum type, or {@code null} if the supplied target type
|
||||
* does not refer to an enum
|
||||
*/
|
||||
public static @Nullable Class<?> resolveEnumType(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;
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -18,8 +18,11 @@ package org.springframework.core.convert.support;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
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.ConverterFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Converts from an Integer to a {@link java.lang.Enum} by calling {@link Class#getEnumConstants()}.
|
||||
@@ -29,11 +32,18 @@ import org.springframework.core.convert.converter.ConverterFactory;
|
||||
* @since 4.3
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
final class IntegerToEnumConverterFactory implements ConverterFactory<Integer, Enum> {
|
||||
final class IntegerToEnumConverterFactory implements ConverterFactory<Integer, Enum>, ConditionalConverter {
|
||||
|
||||
@Override
|
||||
public <T extends Enum> Converter<Integer, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new IntegerToEnum(ConversionUtils.getEnumType(targetType));
|
||||
Class<?> enumType = ConversionUtils.resolveEnumType(targetType);
|
||||
Assert.notNull(enumType, () -> "The target type " + targetType.getName() + " does not refer to an enum");
|
||||
return new IntegerToEnum(enumType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return (ConversionUtils.resolveEnumType(targetType.getType()) != null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+12
-2
@@ -18,8 +18,11 @@ package org.springframework.core.convert.support;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
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.ConverterFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Converts from a String to a {@link java.lang.Enum} by calling {@link Enum#valueOf(Class, String)}.
|
||||
@@ -29,11 +32,18 @@ import org.springframework.core.convert.converter.ConverterFactory;
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
|
||||
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum>, ConditionalConverter {
|
||||
|
||||
@Override
|
||||
public <T extends Enum> Converter<String, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new StringToEnum(ConversionUtils.getEnumType(targetType));
|
||||
Class<?> enumType = ConversionUtils.resolveEnumType(targetType);
|
||||
Assert.notNull(enumType, () -> "The target type " + targetType.getName() + " does not refer to an enum");
|
||||
return new StringToEnum(enumType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return (ConversionUtils.resolveEnumType(targetType.getType()) != null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+14
@@ -503,6 +503,20 @@ class GenericConversionServiceTests {
|
||||
assertThat(conversionService.convert("base1", MyEnum.class)).isEqualTo(MyEnum.A);
|
||||
}
|
||||
|
||||
@Test // gh-34532
|
||||
void canConvertToEnumDoesNotThrowForNonEnumTargetType() {
|
||||
conversionService.addConverterFactory(new StringToEnumConverterFactory());
|
||||
conversionService.addConverterFactory(new IntegerToEnumConverterFactory());
|
||||
|
||||
assertThat(conversionService.canConvert(String.class, Enum.class)).isFalse();
|
||||
assertThat(conversionService.canConvert(Integer.class, Enum.class)).isFalse();
|
||||
|
||||
assertThat(conversionService.canConvert(String.class, MyEnum.class)).isTrue();
|
||||
assertThat(conversionService.canConvert(Integer.class, MyEnum.class)).isTrue();
|
||||
assertThat(conversionService.convert("A", MyEnum.class)).isEqualTo(MyEnum.A);
|
||||
assertThat(conversionService.convert(0, MyEnum.class)).isEqualTo(MyEnum.A);
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertNullAnnotatedStringToString() throws Exception {
|
||||
String source = null;
|
||||
|
||||
Reference in New Issue
Block a user