Return an Enum object from String

In Spring framework, the LenientToEnumConverter class in LenientObjectToEnumConverterFactory.java provides a neat way to return an enum object based on its String key. For example, spring security uses this method to retrieve the CommonOAuth2Provider enum using a String input. The Enum.valueOf(this.enumType, value); finds the enum object where the name matches exactly with the source. If the result is not found, then the findEnum(value) will compare canonical value of the source and canonical values of enum names to find the match.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private class LenientToEnumConverter<E extends Enum> implements Converter<T, E> {
private final Class<E> enumType;

LenientToEnumConverter(Class<E> enumType) {
this.enumType = enumType;
}

@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);
}
}

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();
}
}