java-commons/commons-serialize-databind/src/main/java/io/gitlab/jfronny/commons/serialize/databind/impl/adapter/EnumTypeAdapterFactory.java

91 lines
4.1 KiB
Java

package io.gitlab.jfronny.commons.serialize.databind.impl.adapter;
import io.gitlab.jfronny.commons.serialize.annotations.SerializedName;
import io.gitlab.jfronny.commons.serialize.databind.ObjectMapper;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeAdapter;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapterFactory;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class EnumTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(ObjectMapper mapper, TypeToken<T> type) {
Class<? super T> rawRype = type.getRawType();
if (!Enum.class.isAssignableFrom(rawRype) || rawRype == Enum.class) {
return null;
}
if (!rawRype.isEnum()) {
rawRype = rawRype.getSuperclass();
}
return new EnumTypeAdapter(rawRype);
}
private static class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
private final Map<String, T> nameToConstant = new HashMap<>();
private final Map<String, T> stringToConstant = new HashMap<>();
private final Map<T, String> constantToName = new HashMap<>();
public EnumTypeAdapter(final Class<T> classOfT) {
try {
// Uses reflection to find enum constants to work around name mismatches for obfuscated
// classes
// Reflection access might throw SecurityException, therefore run this in privileged
// context; should be acceptable because this only retrieves enum constants, but does not
// expose anything else
Field[] constantFields;
{
Field[] fields = classOfT.getDeclaredFields();
ArrayList<Field> constantFieldsList = new ArrayList<>(fields.length);
for (Field f : fields) {
if (f.isEnumConstant()) {
constantFieldsList.add(f);
}
}
constantFields = constantFieldsList.toArray(new Field[0]);
AccessibleObject.setAccessible(constantFields, true);
}
for (Field constantField : constantFields) {
@SuppressWarnings("unchecked")
T constant = (T) constantField.get(null);
String name = constant.name();
String toStringVal = constant.toString();
SerializedName annotation = constantField.getAnnotation(SerializedName.class);
if (annotation != null) {
name = annotation.value();
for (String alternate : annotation.alternate()) {
nameToConstant.put(alternate, constant);
}
}
nameToConstant.put(name, constant);
stringToConstant.put(toStringVal, constant);
constantToName.put(constant, name);
}
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(T value, Writer writer) throws TEx, MalformedDataException {
writer.value(value == null ? null : constantToName.get(value));
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> T deserialize(Reader reader) throws TEx, MalformedDataException {
String key = reader.nextString();
T constant = nameToConstant.get(key);
return (constant == null) ? stringToConstant.get(key) : constant;
}
}
}