Polish lenient enum converter
See gh-17798
This commit is contained in:
@@ -116,7 +116,7 @@ public class ApplicationConversionService extends FormattingConversionService {
|
||||
registry.addConverter(new StringToDataSizeConverter());
|
||||
registry.addConverter(new NumberToDataSizeConverter());
|
||||
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
|
||||
registry.addConverterFactory(new BooleanToEnumConverterFactory());
|
||||
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,35 +16,12 @@
|
||||
|
||||
package org.springframework.boot.convert;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* Converter to support mapping of YAML style {@code "false"} and {@code "true"} to enums
|
||||
* {@code ON} and {@code OFF}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
final class BooleanToEnumConverterFactory extends AbstractTypeToEnumConverterFactory<Boolean> {
|
||||
|
||||
@Override
|
||||
<E extends Enum> Converter<Boolean, E> getTypeToEnumConverter(Class<E> targetType) {
|
||||
return new BooleanToEnum<>(targetType);
|
||||
}
|
||||
|
||||
private class BooleanToEnum<T extends Enum> implements Converter<Boolean, T> {
|
||||
|
||||
private final Class<T> enumType;
|
||||
|
||||
BooleanToEnum(Class<T> enumType) {
|
||||
this.enumType = enumType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convert(Boolean source) {
|
||||
return findEnum(Boolean.toString(source), this.enumType);
|
||||
}
|
||||
|
||||
}
|
||||
final class LenientBooleanToEnumConverterFactory extends LenientToEnumConverterFactory<Boolean> {
|
||||
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.boot.convert;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* Converts from a String to a {@link java.lang.Enum} with lenient conversion rules.
|
||||
* Specifically:
|
||||
@@ -30,36 +28,6 @@ import org.springframework.core.convert.converter.Converter;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final class LenientStringToEnumConverterFactory extends AbstractTypeToEnumConverterFactory<String> {
|
||||
|
||||
@Override
|
||||
<E extends Enum> Converter<String, E> getTypeToEnumConverter(Class<E> targetType) {
|
||||
return new StringToEnum<>(targetType);
|
||||
}
|
||||
|
||||
private class StringToEnum<T extends Enum> implements Converter<String, T> {
|
||||
|
||||
private final Class<T> enumType;
|
||||
|
||||
StringToEnum(Class<T> enumType) {
|
||||
this.enumType = enumType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convert(String source) {
|
||||
if (source.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
source = source.trim();
|
||||
try {
|
||||
return (T) Enum.valueOf(this.enumType, source);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return findEnum(source, this.enumType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
final class LenientStringToEnumConverterFactory extends LenientToEnumConverterFactory<String> {
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.boot.convert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -37,7 +36,7 @@ import org.springframework.util.MultiValueMap;
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
abstract class AbstractTypeToEnumConverterFactory<T> implements ConverterFactory<T, Enum<?>> {
|
||||
abstract class LenientToEnumConverterFactory<T> implements ConverterFactory<T, Enum<?>> {
|
||||
|
||||
private static Map<String, List<String>> ALIASES;
|
||||
|
||||
@@ -49,43 +48,58 @@ abstract class AbstractTypeToEnumConverterFactory<T> implements ConverterFactory
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends Enum<?>> Converter<T, E> getConverter(Class<E> 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 getTypeToEnumConverter(targetType);
|
||||
return new LenientToEnumConverter<E>((Class<E>) enumType);
|
||||
}
|
||||
|
||||
abstract <E extends Enum> Converter<T, E> getTypeToEnumConverter(Class<E> targetType);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<E extends Enum> E findEnum(String source, Class<E> enumType) {
|
||||
Map<String, E> candidates = new LinkedHashMap<>();
|
||||
for (E candidate : (Set<E>) EnumSet.allOf(enumType)) {
|
||||
candidates.put(getCanonicalName(candidate.name()), candidate);
|
||||
private class LenientToEnumConverter<E extends Enum> implements Converter<T, E> {
|
||||
|
||||
private final Class<E> enumType;
|
||||
|
||||
LenientToEnumConverter(Class<E> enumType) {
|
||||
this.enumType = enumType;
|
||||
}
|
||||
String name = getCanonicalName(source);
|
||||
E result = candidates.get(name);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
for (String alias : ALIASES.getOrDefault(name, Collections.emptyList())) {
|
||||
result = candidates.get(alias);
|
||||
if (result != null) {
|
||||
return result;
|
||||
|
||||
@Override
|
||||
public E convert(T source) {
|
||||
String value = source.toString().trim();
|
||||
if (value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (E) Enum.valueOf(this.enumType, value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return findEnum(value);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + source);
|
||||
|
||||
}
|
||||
private E findEnum(String value) {
|
||||
String name = getCanonicalName(value);
|
||||
List<String> aliases = ALIASES.getOrDefault(name, Collections.emptyList());
|
||||
for (E candidate : (Set<E>) EnumSet.allOf(this.enumType)) {
|
||||
String candidateName = getCanonicalName(candidate.name());
|
||||
if (name.equals(candidateName) || aliases.contains(candidateName)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant " + this.enumType.getCanonicalName() + "." + value);
|
||||
}
|
||||
|
||||
private String getCanonicalName(String name) {
|
||||
StringBuilder canonicalName = new StringBuilder(name.length());
|
||||
name.chars().filter(Character::isLetterOrDigit).map(Character::toLowerCase)
|
||||
.forEach((c) -> canonicalName.append((char) c));
|
||||
return canonicalName.toString();
|
||||
}
|
||||
|
||||
private String getCanonicalName(String name) {
|
||||
StringBuilder canonicalName = new StringBuilder(name.length());
|
||||
name.chars().filter(Character::isLetterOrDigit).map(Character::toLowerCase)
|
||||
.forEach((c) -> canonicalName.append((char) c));
|
||||
return canonicalName.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,11 +25,11 @@ import org.springframework.core.convert.ConversionService;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BooleanToEnumConverterFactory}.
|
||||
* Tests for {@link LenientBooleanToEnumConverterFactory}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class BooleanToEnumConverterFactoryTests {
|
||||
class LenientBooleanToEnumConverterFactoryTests {
|
||||
|
||||
@ConversionServiceTest
|
||||
void convertFromBooleanToEnumWhenShouldConvertValue(ConversionService conversionService) {
|
||||
@@ -41,7 +41,7 @@ class BooleanToEnumConverterFactoryTests {
|
||||
|
||||
static Stream<? extends Arguments> conversionServices() {
|
||||
return ConversionServiceArguments
|
||||
.with((service) -> service.addConverterFactory(new BooleanToEnumConverterFactory()));
|
||||
.with((service) -> service.addConverterFactory(new LenientBooleanToEnumConverterFactory()));
|
||||
}
|
||||
|
||||
enum TestOnOffEnum {
|
||||
Reference in New Issue
Block a user