All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fizzed.crux.jackson.EnumMapper Maven / Gradle / Ivy

There is a newer version: 1.0.48
Show newest version
package com.fizzed.crux.jackson;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class EnumMapper {

    static private class EnumInfo {
        
        private final boolean ignoreUnknown;
        private final Method unknownEnumMethod;
        private final Method createEnumMethod;

        public EnumInfo(boolean ignoreUnknown, Method unknownEnumMethod, Method createEnumMethod) {
            this.ignoreUnknown = ignoreUnknown;
            this.unknownEnumMethod = unknownEnumMethod;
            this.createEnumMethod = createEnumMethod;
        }

        public boolean isIgnoreUnknown() {
            return ignoreUnknown;
        }

        public Method getUnknownEnumMethod() {
            return unknownEnumMethod;
        }

        public Method getCreateEnumMethod() {
            return createEnumMethod;
        }

    }
    
    static private final ConcurrentHashMap,EnumInfo> ENUM_INFOS = new ConcurrentHashMap<>();
    
    static private EnumInfo computeEnumInfo(
            Class rawClass) {
        
        return ENUM_INFOS.computeIfAbsent(rawClass, t -> {
            boolean ignoreUnknown = false;
            
            JsonIgnoreProperties ignoreProps = rawClass.getAnnotation(JsonIgnoreProperties.class);
            if (ignoreProps != null) {
                ignoreUnknown = ignoreProps.ignoreUnknown();
            }

            Method unknownEnumMethod = null;
            
            for (Method m : rawClass.getMethods()) {
                JsonUnknownEnum v = (JsonUnknownEnum)m.getAnnotation(JsonUnknownEnum.class);
                if (v != null) {
                    System.out.println(m.toGenericString());
                    if (!Modifier.isStatic(m.getModifiers())) {
                        throw new IllegalArgumentException("OnUnknownEnum method must be static");
                    }
                    if (m.getParameterCount() != 1) {
                        throw new IllegalArgumentException("OnUnknownEnum method must have exactly 1 parameter");
                    }
                    if (!m.getParameterTypes()[0].equals(String.class)) {
                        throw new IllegalArgumentException("OnUnknownEnum method first parameter must be a java.lang.String");
                    }
                    if (!m.getReturnType().equals(rawClass)) {
                        throw new IllegalArgumentException("OnUnknownEnum method return type must be " + rawClass.getCanonicalName());
                    }
                    unknownEnumMethod = m;
                    break;
                }
            }

            Method createEnumMethod = null;
            
            for (Method m : rawClass.getMethods()) {
                JsonCreator v = (JsonCreator)m.getAnnotation(JsonCreator.class);
                if (v != null) {
                    if (!Modifier.isStatic(m.getModifiers())) {
                        throw new IllegalArgumentException("JsonCreator method must be static");
                    }
                    if (m.getParameterCount() != 1) {
                        throw new IllegalArgumentException("JsonCreator method must have exactly 1 parameter");
                    }
                    if (!m.getParameterTypes()[0].equals(String.class)) {
                        throw new IllegalArgumentException("JsonCreator method first parameter must be a java.lang.String");
                    }
                    if (!m.getReturnType().equals(rawClass)) {
                        throw new IllegalArgumentException("JsonCreator method return type must be " + rawClass.getCanonicalName());
                    }
                    createEnumMethod = m;
                    break;
                }
            }
            
            return new EnumInfo(ignoreUnknown, unknownEnumMethod, createEnumMethod);
        });
    }
    
    
    private final EnumSerializeStrategy serializeStrategy;
    private final EnumDeserializeStrategy deserializeStrategy;
    private EnumGlobalUnknownHandler globalUnknownEnumHandler;
    private boolean nullOnUnknown;
    
    public EnumMapper() {
        this(EnumSerializeStrategy.LOWER_CASE, EnumDeserializeStrategy.IGNORE_CASE);
    }
    
    public EnumMapper(
            EnumSerializeStrategy serializeStrategy,
            EnumDeserializeStrategy deserializeStrategy) {
        
        Objects.requireNonNull(serializeStrategy, "serialize strategy was null");
        Objects.requireNonNull(deserializeStrategy, "deserialize strategy was null");
        
        this.serializeStrategy = serializeStrategy;
        this.deserializeStrategy = deserializeStrategy;
        this.nullOnUnknown = false;
    }

    public boolean isNullOnUnknown() {
        return nullOnUnknown;
    }

    public EnumMapper setNullOnUnknown(boolean nullOnUnknown) {
        this.nullOnUnknown = nullOnUnknown;
        return this;
    }
    
    public EnumGlobalUnknownHandler getGlobalUnknownEnumHandler() {
        return globalUnknownEnumHandler;
    }

    public EnumMapper setGlobalUnknownEnumHandler(
            EnumGlobalUnknownHandler globalUnknownEnumHandler) {
        
        this.globalUnknownEnumHandler = globalUnknownEnumHandler;
        return this;
    }

    public String serialize(Enum value) throws IOException {
        switch (EnumMapper.this.serializeStrategy) {
            case LOWER_CASE:
                return value.name().toLowerCase();
            case UPPER_CASE:
                return value.name().toUpperCase();
            case RETAIN_CASE:
                return value.name();
            default:
                throw new IllegalArgumentException("Unsupported serialize strategy " + this.serializeStrategy);
        }
    }
    
    public  T deserialize(String value, Class rawClass) throws IOException {

        if (value == null || value.isEmpty()) {
            return null;
        }

        // does it have an unknown enum handler?
        final EnumInfo enumInfo = computeEnumInfo(rawClass);

        // is there a creator method?
        if (enumInfo.getCreateEnumMethod() != null) {
            try {
                return (T)enumInfo.getCreateEnumMethod().invoke(null, value);
            } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
                throw new IllegalArgumentException(e.getMessage(), e);
            }   
        }

        switch (EnumMapper.this.deserializeStrategy) {
            case IGNORE_CASE:
                for (T en : rawClass.getEnumConstants()) {
                    if (en.name().equalsIgnoreCase(value)) {
                        return en;
                    }
                }
                break;
            case UPPER_CASE:
                for (T en : rawClass.getEnumConstants()) {
                    if (en.name().toUpperCase().equals(value)) {
                        return en;
                    }
                }
                break;
            case LOWER_CASE:
                for (T en : rawClass.getEnumConstants()) {
                    if (en.name().toLowerCase().equals(value)) {
                        return en;
                    }
                }
                break;
        }

        // if there is an unknown enum handler on the enum itself, call that first
        if (enumInfo.getUnknownEnumMethod() != null) {
            try {
                return (T)enumInfo.getUnknownEnumMethod().invoke(null, value);
            } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
                throw new IllegalArgumentException(e.getMessage(), e);
            }
        }

        // global handler?
        if (EnumMapper.this.globalUnknownEnumHandler != null) {
            EnumMapper.this.globalUnknownEnumHandler.onUnknownEnum(rawClass, value);
        }

        // if the enum is flagged as ignoring unknown, then do nothing
        if (enumInfo.isIgnoreUnknown()) {
            return null;
        }

        if (this.nullOnUnknown) {
            return null;
        }
        
        // default is to mimic jackson's behavior and throw an unknown property exception
        throw new IOException("No enum constant " + rawClass.getCanonicalName() + "." + value);
//        throw new UnrecognizedPropertyException(
//            jp, "No enum constant " + rawClass.getCanonicalName() + "." + value,
//            JsonLocation.NA, rawClass, "value", null);
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy