| package com.fasterxml.jackson.databind.util; |
| |
| import com.fasterxml.jackson.databind.AnnotationIntrospector; |
| |
| import java.lang.reflect.Method; |
| import java.util.*; |
| |
| /** |
| * Helper class used to resolve String values (either JSON Object field |
| * names or regular String values) into Java Enum instances. |
| */ |
| public class EnumResolver<T extends Enum<T>> implements java.io.Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| protected final Class<T> _enumClass; |
| |
| protected final T[] _enums; |
| |
| protected final HashMap<String, T> _enumsById; |
| |
| protected EnumResolver(Class<T> enumClass, T[] enums, HashMap<String, T> map) |
| { |
| _enumClass = enumClass; |
| _enums = enums; |
| _enumsById = map; |
| } |
| |
| /** |
| * Factory method for constructing resolver that maps from Enum.name() into |
| * Enum value |
| */ |
| public static <ET extends Enum<ET>> EnumResolver<ET> constructFor(Class<ET> enumCls, AnnotationIntrospector ai) |
| { |
| ET[] enumValues = enumCls.getEnumConstants(); |
| if (enumValues == null) { |
| throw new IllegalArgumentException("No enum constants for class "+enumCls.getName()); |
| } |
| HashMap<String, ET> map = new HashMap<String, ET>(); |
| for (ET e : enumValues) { |
| map.put(ai.findEnumValue(e), e); |
| } |
| return new EnumResolver<ET>(enumCls, enumValues, map); |
| } |
| |
| /** |
| * Factory method for constructing resolver that maps from Enum.toString() into |
| * Enum value |
| */ |
| public static <ET extends Enum<ET>> EnumResolver<ET> constructUsingToString(Class<ET> enumCls) |
| { |
| ET[] enumValues = enumCls.getEnumConstants(); |
| HashMap<String, ET> map = new HashMap<String, ET>(); |
| // from last to first, so that in case of duplicate values, first wins |
| for (int i = enumValues.length; --i >= 0; ) { |
| ET e = enumValues[i]; |
| map.put(e.toString(), e); |
| } |
| return new EnumResolver<ET>(enumCls, enumValues, map); |
| } |
| |
| public static <ET extends Enum<ET>> EnumResolver<ET> constructUsingMethod(Class<ET> enumCls, |
| Method accessor) |
| { |
| ET[] enumValues = enumCls.getEnumConstants(); |
| HashMap<String, ET> map = new HashMap<String, ET>(); |
| // from last to first, so that in case of duplicate values, first wins |
| for (int i = enumValues.length; --i >= 0; ) { |
| ET en = enumValues[i]; |
| try { |
| Object o = accessor.invoke(en); |
| if (o != null) { |
| map.put(o.toString(), en); |
| } |
| } catch (Exception e) { |
| throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage()); |
| } |
| } |
| return new EnumResolver<ET>(enumCls, enumValues, map); |
| } |
| |
| /** |
| * This method is needed because of the dynamic nature of constructing Enum |
| * resolvers. |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public static EnumResolver<?> constructUnsafe(Class<?> rawEnumCls, AnnotationIntrospector ai) |
| { |
| /* This is oh so wrong... but at least ugliness is mostly hidden in just |
| * this one place. |
| */ |
| Class<Enum> enumCls = (Class<Enum>) rawEnumCls; |
| return constructFor(enumCls, ai); |
| } |
| |
| /** |
| * Method that needs to be used instead of {@link #constructUsingToString} |
| * if static type of enum is not known. |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public static EnumResolver<?> constructUnsafeUsingToString(Class<?> rawEnumCls) |
| { |
| // oh so wrong... not much that can be done tho |
| Class<Enum> enumCls = (Class<Enum>) rawEnumCls; |
| return constructUsingToString(enumCls); |
| } |
| |
| /** |
| * Method used when actual String serialization is indicated using @JsonValue |
| * on a method. |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public static EnumResolver<?> constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor) |
| { |
| // wrong as ever but: |
| Class<Enum> enumCls = (Class<Enum>) rawEnumCls; |
| return constructUsingMethod(enumCls, accessor); |
| } |
| |
| public T findEnum(String key) { return _enumsById.get(key); } |
| |
| public T getEnum(int index) { |
| if (index < 0 || index >= _enums.length) { |
| return null; |
| } |
| return _enums[index]; |
| } |
| |
| public List<T> getEnums() { |
| ArrayList<T> enums = new ArrayList<T>(_enums.length); |
| for (T e : _enums) { |
| enums.add(e); |
| } |
| return enums; |
| } |
| |
| public Class<T> getEnumClass() { return _enumClass; } |
| |
| public int lastValidIndex() { return _enums.length-1; } |
| } |
| |