Polish lenient enum converter

See gh-17798
pull/17947/head
Phillip Webb 5 years ago
parent b43827d626
commit 6270f5008d

@ -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;
@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);
}
}
for (String alias : ALIASES.getOrDefault(name, Collections.emptyList())) {
result = candidates.get(alias);
if (result != null) {
return result;
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);
}
throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + source);
}
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 {
Loading…
Cancel
Save