Skip to content

Instantly share code, notes, and snippets.

@MuXiu1997
Last active November 24, 2022 08:06
Show Gist options
  • Select an option

  • Save MuXiu1997/813877963896a3e6aee00d962f564871 to your computer and use it in GitHub Desktop.

Select an option

Save MuXiu1997/813877963896a3e6aee00d962f564871 to your computer and use it in GitHub Desktop.
[springboot]
package com.github.MuXiu1997.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.NonNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {
private final Map<Class<?>, Converter<String, ? extends Enum<?>>> CONVERTER_MAP = new ConcurrentHashMap<>();
private final Class<? extends Annotation> annotationClass;
public StringToEnumConverterFactory(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
@Override
@NonNull
@SuppressWarnings({"unchecked cast"})
public <T extends Enum<?>> Converter<String, T> getConverter(@NonNull Class<T> targetType) {
return (Converter<String, T>) CONVERTER_MAP.computeIfAbsent(
targetType,
ignored -> new ToEnumConverter<>(targetType)
);
}
class ToEnumConverter<T extends Enum<?>> implements Converter<String, T> {
private final Map<String, T> enumMap = new ConcurrentHashMap<>();
private final Class<T> enumType;
ToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
final Function<T, Object> toKeyFunction = getToKeyFunction(enumType);
final T[] enums = enumType.getEnumConstants();
for (T e : enums) {
final String k = toKeyFunction.apply(e).toString();
if (enumMap.containsKey(k)) {
throw new IllegalArgumentException(String.format("Duplicate key [%s] for enum %s", k, enumType.getName()));
}
enumMap.put(k, e);
}
}
@Override
@NonNull
public T convert(@NonNull String source) {
try {
return Objects.requireNonNull(enumMap.get(source));
} catch (Exception e) {
throw new IllegalArgumentException(String.format("No element matches [%s] in enum %s", source, enumType.getName()));
}
}
@NonNull
private Function<T, Object> getToKeyFunction(Class<T> enumType) {
for (Field field : enumType.getDeclaredFields()) {
if (field.isAnnotationPresent(annotationClass)) {
return e -> {
try {
field.setAccessible(true);
return field.get(e);
} catch (IllegalAccessException ignored) {
throw new IllegalArgumentException(String.format("Cannot access field [%s] in enum %s", field.getName(), enumType.getName()));
}
};
}
}
for (Method method : enumType.getDeclaredMethods()) {
if (method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE && method.isAnnotationPresent(annotationClass)) {
return e -> {
try {
method.setAccessible(true);
return method.invoke(e);
} catch (Exception ignored) {
throw new IllegalArgumentException(String.format("Cannot invoke method [%s] in enum %s", method.getName(), enumType.getName()));
}
};
}
}
return Enum::name;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment