SimpleSerializers.java

package tools.jackson.databind.module;

import java.util.*;

import com.fasterxml.jackson.annotation.JsonFormat;

import tools.jackson.databind.*;
import tools.jackson.databind.jsontype.TypeSerializer;
import tools.jackson.databind.ser.Serializers;
import tools.jackson.databind.type.ArrayType;
import tools.jackson.databind.type.ClassKey;
import tools.jackson.databind.type.CollectionLikeType;
import tools.jackson.databind.type.CollectionType;
import tools.jackson.databind.type.MapLikeType;
import tools.jackson.databind.type.MapType;
import tools.jackson.databind.type.ReferenceType;

/**
 * Simple implementation {@link Serializers} which allows registration of
 * serializers based on raw (type erased class).
 * It can work well for basic bean and scalar type serializers, but is not
 * a good fit for handling generic types (like {@link Map}s and {@link Collection}s).
 *<p>
 * Type registrations are assumed to be general; meaning that registration of serializer
 * for a super type will also be used for handling subtypes, unless an exact match
 * is found first. As an example, handler for {@link CharSequence} would also be used
 * serializing {@link StringBuilder} instances, unless a direct mapping was found.
 */
public class SimpleSerializers
    extends Serializers.Base
    implements java.io.Serializable // since included by SimpleModule
{
    private static final long serialVersionUID = 3L;

    /**
     * Class-based mappings that are used both for exact and
     * sub-class matches.
     */
    protected HashMap<ClassKey,ValueSerializer<?>> _classMappings = null;

    /**
     * Interface-based matches.
     */
    protected HashMap<ClassKey,ValueSerializer<?>> _interfaceMappings = null;

    /**
     * Flag to help find "generic" enum serializer, if one has been registered.
     */
    protected boolean _hasEnumSerializer = false;

    /*
    /**********************************************************
    /* Life-cycle, construction and configuring
    /**********************************************************
     */

    public SimpleSerializers() { }

    public SimpleSerializers(List<ValueSerializer<?>> sers) {
        addSerializers(sers);
    }

    /**
     * Method for adding given serializer for type that {@link ValueSerializer#handledType}
     * specifies (which MUST return a non-null class; and CANNOT be {@link Object}, as a
     * sanity check).
     * For serializers that do not declare handled type, use the variant that takes
     * two arguments.
     *
     * @param ser
     */
    public SimpleSerializers addSerializer(ValueSerializer<?> ser)
    {
        // Interface to match?
        Class<?> cls = ser.handledType();
        if (cls == null || cls == Object.class) {
            throw new IllegalArgumentException("`ValueSerializer` of type `"+ser.getClass().getName()
                    +"` does not define valid handledType() -- must either register with method that takes type argument "
                    +" or make serializer extend 'tools.jackson.databind.ser.std.StdSerializer'");
        }
        _addSerializer(cls, ser);
        return this;
    }

    public <T> SimpleSerializers addSerializer(Class<? extends T> type, ValueSerializer<T> ser)
    {
        _addSerializer(type, ser);
        return this;
    }

    public SimpleSerializers addSerializers(List<ValueSerializer<?>> sers) {
        for (ValueSerializer<?> ser : sers) {
            addSerializer(ser);
        }
        return this;
    }

    /*
    /**********************************************************************
    /* Serializers implementation
    /**********************************************************************
     */

    @Override
    public ValueSerializer<?> findSerializer(SerializationConfig config,
            JavaType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides)
    {
        return _findSerializer(config, type);
    }


    @Override
    public ValueSerializer<?> findEnumSerializer(SerializationConfig config,
            JavaType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides)
    {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findTreeNodeSerializer(SerializationConfig config,
            JavaType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides)
    {
        return _findSerializer(config, type);
    }
    
    @Override
    public ValueSerializer<?> findArraySerializer(SerializationConfig config,
            ArrayType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findCollectionSerializer(SerializationConfig config,
            CollectionType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
            CollectionLikeType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findMapSerializer(SerializationConfig config,
            MapType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            ValueSerializer<Object> keySerializer,
            TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findMapLikeSerializer(SerializationConfig config,
            MapLikeType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            ValueSerializer<Object> keySerializer,
            TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
        return _findSerializer(config, type);
    }

    @Override
    public ValueSerializer<?> findReferenceSerializer(SerializationConfig config,
            ReferenceType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
            TypeSerializer contentTypeSerializer, ValueSerializer<Object> contentValueSerializer) {
        return _findSerializer(config, type);
    }

    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */

    protected ValueSerializer<?> _findSerializer(SerializationConfig config, JavaType type)
    {
        Class<?> cls = type.getRawClass();
        ClassKey key = new ClassKey(cls);
        ValueSerializer<?> ser = null;

        // First: direct match?
        if (cls.isInterface()) {
            if (_interfaceMappings != null) {
                ser = _interfaceMappings.get(key);
                if (ser != null) {
                    return ser;
                }
            }
        } else {
            if (_classMappings != null) {
                ser = _classMappings.get(key);
                if (ser != null) {
                    return ser;
                }
                // Handle registration of plain `Enum` serializer
                if (_hasEnumSerializer && type.isEnumType()) {
                    key.reset(Enum.class);
                    ser = _classMappings.get(key);
                    if (ser != null) {
                        return ser;
                    }
                }

                // If not direct match, maybe super-class match?
                for (Class<?> curr = cls; (curr != null); curr = curr.getSuperclass()) {
                    key.reset(curr);
                    ser = _classMappings.get(key);
                    if (ser != null) {
                        return ser;
                    }
                }
            }
        }
        // No direct match? How about super-interfaces?
        if (_interfaceMappings != null) {
            ser = _findInterfaceMapping(cls, key);
            if (ser != null) {
                return ser;
            }
            // still no matches? Maybe interfaces of super classes
            if (!cls.isInterface()) {
                while ((cls = cls.getSuperclass()) != null) {
                    ser = _findInterfaceMapping(cls, key);
                    if (ser != null) {
                        return ser;
                    }
                }
            }
        }
        return null;
    }
    
    protected ValueSerializer<?> _findInterfaceMapping(Class<?> cls, ClassKey key)
    {
        for (Class<?> iface : cls.getInterfaces()) {
            key.reset(iface);
            ValueSerializer<?> ser = _interfaceMappings.get(key);
            if (ser != null) {
                return ser;
            }
            ser = _findInterfaceMapping(iface, key);
            if (ser != null) {
                return ser;
            }
        }
        return null;
    }

    protected void _addSerializer(Class<?> cls, ValueSerializer<?> ser)
    {
        ClassKey key = new ClassKey(cls);
        // Interface or class type?
        if (cls.isInterface()) {
            if (_interfaceMappings == null) {
                _interfaceMappings = new HashMap<>();
            }
            _interfaceMappings.put(key, ser);
        } else { // nope, class:
            if (_classMappings == null) {
                _classMappings = new HashMap<>();
            }
            _classMappings.put(key, ser);
            if (cls == Enum.class) {
                _hasEnumSerializer = true;
            }
        }
    }
}