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

io.tarantool.driver.mappers.DefaultMessagePackMapper Maven / Gradle / Ivy

Go to download

Tarantool Cartridge driver for Tarantool versions 1.10+ based on Netty framework

There is a newer version: 0.14.0
Show newest version
package io.tarantool.driver.mappers;

import org.msgpack.value.Value;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import static io.tarantool.driver.mappers.MapperReflectionUtils.getInterfaceParameterClass;

/**
 * Default implementation of {@link MessagePackObjectMapper} and {@link MessagePackValueMapper}.
 * Deals with standard Java objects
 *
 * @author Alexey Kuzin
 */
public class DefaultMessagePackMapper implements MessagePackMapper {

    private Map>> valueConverters;
    private Map>> objectConverters;
    private Map> valueConvertersByTarget;
    private Map> objectConvertersByTarget;

    /**
     * Basic constructor
     */
    public DefaultMessagePackMapper() {
        valueConverters = new HashMap<>();
        valueConvertersByTarget = new HashMap<>();
        objectConverters = new HashMap<>();
        objectConvertersByTarget = new HashMap<>();
    }

    /**
     * Copying constructor
     * @param mapper another mapper instance
     */
    public DefaultMessagePackMapper(DefaultMessagePackMapper mapper) {
        this();
        this.valueConverters.putAll(mapper.valueConverters);
        this.objectConverters.putAll(mapper.objectConverters);
        this.valueConvertersByTarget.putAll(mapper.valueConvertersByTarget);
        this.objectConvertersByTarget.putAll(mapper.objectConvertersByTarget);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  V toValue(O o) {
        Function>> getter =
                typeName -> objectConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                        .map(c -> (ObjectConverter) c)
                        .filter(c -> c.canConvertObject(o))
                        .findFirst();
        Optional> converter = findConverter(o.getClass(), getter);
        if (!converter.isPresent()) {
            throw new MessagePackObjectMapperException("ObjectConverter for type %s is not found", o.getClass());
        }
        return converter.get().toValue(o);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  O fromValue(V v) {
        Function>> getter =
                typeName -> valueConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                        .map(c -> (ValueConverter) c)
                        .filter(c -> c.canConvertValue(v))
                        .findFirst();
        return fromValue(v, getter);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  O fromValue(V v, Class targetClass) {
        Function>> getter =
                typeName -> valueConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                        .map(c -> (ValueConverter) c)
                        .filter(c -> c.canConvertValue(v))
                        .filter(c -> checkConverterByTargetType(c, targetClass))
                        .findFirst();
        return fromValue(v, getter);
    }

    private  O fromValue(V v, Function>> getter) {
        Optional> converter = findConverter(v.getClass(), getter);
        if (!converter.isPresent()) {
            throw new MessagePackValueMapperException("ValueConverter for type %s is not found", v.getClass());
        }
        return converter.get().fromValue(v);
    }

    private  Optional findConverter(Class objectClass, Function> getter) {
        Optional converter = getter.apply(objectClass.getTypeName());
        if (!converter.isPresent() && objectClass.getSuperclass() != null) {
            converter = findConverter(objectClass.getSuperclass(), getter);
        }
        if (!converter.isPresent()) {
            for (Class iface : objectClass.getInterfaces()) {
                converter = findConverter(iface, getter);
                if (converter.isPresent()) {
                    break;
                }
            }
        }
        return converter;
    }

    /**
     * Perform {@link ValueConverter} converter registration. The source entity class and target object class for
     * registration are determined automatically
     * @param converter entity-to-object converter
     * @param  MessagePack entity type
     * @param  object type
     * @see ValueConverter
     */
    public  void registerValueConverter(ValueConverter converter) {
        try {
            registerValueConverter(
                    getInterfaceParameterClass(converter, ValueConverter.class, 0), converter);
        } catch (ConverterParameterTypeNotFoundException e) {
            throw new RuntimeException("Failed to determine the source parameter type of the generic interface, " +
                    "try to use the method registerValueConverter(valueClass, objectClass, converter) " +
                    "for registering the converter");
        }
    }

    /**
     * Perform {@link ValueConverter} converter registration. The target object class for registration is determined
     * automatically
     * @param valueClass source entity class
     * @param converter entity-to-object converter
     * @param  MessagePack entity type
     * @param  object type
     * @see ValueConverter
     */
    public  void registerValueConverter(Class valueClass, ValueConverter converter) {
        try {
            Class objectClass = getInterfaceParameterClass(converter, ValueConverter.class, 1);
            registerValueConverter(valueClass, objectClass, converter);
        } catch (ConverterParameterTypeNotFoundException e) {
            throw new RuntimeException("Failed to determine the target parameter type of the generic interface, " +
                    "try to use the method registerValueConverter(valueClass, objectClass, converter) " +
                    "for registering the converter");
        }
    }

    @Override
    public  void registerValueConverter(Class valueClass, Class objectClass,
                                                            ValueConverter converter) {
        List> converters =
                valueConverters.computeIfAbsent(valueClass.getTypeName(), k -> new LinkedList<>());
        converters.add(converter);
        valueConvertersByTarget.put(objectClass.getTypeName(), converter);
    }

    /**
     * Check if the specified converter can convert to the specified object type
     */
    private boolean checkConverterByTargetType(ValueConverter converter, Class targetClass) {
        try {
            return valueConvertersByTarget.get(targetClass.getTypeName()) == converter ||
                    getInterfaceParameterClass(converter, converter.getClass(), 1)
                            .isAssignableFrom(targetClass);
        } catch (ConverterParameterTypeNotFoundException e) {
            return false;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public  Optional> getValueConverter(Class entityClass,
                                                                                 Class targetClass) {
        Function>> getter =
                typeName -> valueConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                        .filter(c -> checkConverterByTargetType(c, targetClass))
                        .map(c -> (ValueConverter) c)
                        .findFirst();
        return findConverter(entityClass, getter);
    }

    /**
     * Perform {@link ObjectConverter} converter registration. The source object class and target entity class for
     * registration are determined automatically
     * @param converter object-to-entity converter
     * @param  MessagePack entity type
     * @param  object type
     * @see ObjectConverter
     */
    public  void registerObjectConverter(ObjectConverter converter) {
        try {
            registerObjectConverter(
                    getInterfaceParameterClass(converter, ObjectConverter.class, 0), converter);
        } catch (ConverterParameterTypeNotFoundException e) {
            throw new RuntimeException("Failed to determine the target parameter type of the generic interface, " +
                    "try to use the method registerObjectConverter(objectClass, valueClass, converter) " +
                    "for registering the converter");
        }
    }

    /**
     * Adds a Java object converter to this mappers instance. The target value class for registration is determined
     * automatically
     * @param objectClass object class to register the converter for
     * @param converter entity-to-object converter
     * @param  the target MessagePack entity type
     * @param  the source object type
     * @see ObjectConverter
     */
    public  void registerObjectConverter(Class objectClass, ObjectConverter converter) {
        try {
            Class valueClass = getInterfaceParameterClass(converter, ObjectConverter.class, 1);
            registerObjectConverter(objectClass, valueClass, converter);
        } catch (ConverterParameterTypeNotFoundException e) {
            throw new RuntimeException("Failed to determine the target parameter type of the generic interface, " +
                    "try to use the method registerObjectConverter(objectClass, valueClass, converter) " +
                    "for registering the converter");
        }
    }

    @Override
    public  void registerObjectConverter(Class objectClass, Class valueClass,
                                                             ObjectConverter converter) {
        List> converters =
                objectConverters.computeIfAbsent(objectClass.getTypeName(), k -> new LinkedList<>());
        converters.add(converter);
        objectConvertersByTarget.put(valueClass.getTypeName(), converter);
    }

    /**
     * Convenience method for registering classes implementing both types of converters.
     * @param converter object-to-entity and entity-to-object converter
     * @param valueClass entity class
     * @param objectClass object class
     * @param  MessagePack entity type
     * @param  object type
     * @param  converter type
     */
    public  & ObjectConverter> void registerConverter(
            Class valueClass, Class objectClass, T converter) {
        registerValueConverter(valueClass, objectClass, converter);
        registerObjectConverter(objectClass, valueClass, converter);
    }

    /**
     * Builder for {@link DefaultMessagePackMapper}
     */
    public static class Builder {
        private final DefaultMessagePackMapper mapper;

        /**
         * Basic constructor, initialized with an empty mapper
         */
        public Builder() {
            mapper = new DefaultMessagePackMapper();
        }

        /**
         * Basic constructor, initialized with the specified mapper
         * @param mapper a mapper instance
         */
        public Builder(DefaultMessagePackMapper mapper) {
            this.mapper = new DefaultMessagePackMapper(mapper);
        }

        /**
         * Configure the mapper with default {@code MP_MAP} entity to {@link Map} converter
         * @return builder
         */
        public Builder withDefaultMapValueConverter() {
            mapper.registerValueConverter(new DefaultMapValueConverter(mapper));
            return this;
        }

        /**
         * Configure the mapper with default {@link Map} to {@code MP_MAP} entity converter
         * @return builder
         */
        public Builder withDefaultMapObjectConverter() {
            mapper.registerObjectConverter(new DefaultMapObjectConverter(mapper));
            return this;
        }

        /**
         * Configure the mapper with default {@code MP_ARRAY} entity to {@link List} converter
         * @return builder
         */
        public Builder withDefaultArrayValueConverter() {
            mapper.registerValueConverter(new DefaultListValueConverter(mapper));
            return this;
        }

        /**
         * Configure the mapper with default {@link List} to {@code MP_ARRAY} entity converter
         * @return builder
         */
        public Builder withDefaultListObjectConverter() {
            mapper.registerObjectConverter(new DefaultListObjectConverter(mapper));
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack entity-to-object and object-to-entity converter
         * @param valueClass MessagePack entity class
         * @param objectClass object class
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @param  converter type
         * @return builder
         */
        public  & ObjectConverter> Builder withConverter(
                Class valueClass, Class objectClass, T converter) {
            mapper.registerConverter(valueClass, objectClass, converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack entity-to-object converter
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         * @see #registerValueConverter(ValueConverter)
         */
        public  Builder withValueConverter(ValueConverter converter) {
            mapper.registerValueConverter(converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack entity-to-object converter
         * @param valueClass source entity class
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         * @see #registerValueConverter(Class, ValueConverter)
         */
        public  Builder withValueConverter(Class valueClass, ValueConverter converter) {
            mapper.registerValueConverter(valueClass, converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack entity-to-object converter
         * @param valueClass source entity class
         * @param objectClass target object class
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         * @see #registerValueConverter(Class, Class, ValueConverter)
         */
        public  Builder withValueConverter(Class valueClass, Class objectClass,
                                                               ValueConverter converter) {
            mapper.registerValueConverter(valueClass, objectClass, converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack object-to-entity converter
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         */
        public  Builder withObjectConverter(ObjectConverter converter) {
            mapper.registerObjectConverter(converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack object-to-entity converter
         * @param objectClass source object class
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         */
        public  Builder withObjectConverter(Class objectClass, ObjectConverter converter) {
            mapper.registerObjectConverter(objectClass, converter);
            return this;
        }

        /**
         * Configure the mapper with a specified MessagePack object-to-entity converter
         * @param objectClass source object class
         * @param valueClass target object class
         * @param converter MessagePack entity-to-object and object-to-entity converter
         * @param  MessagePack entity type
         * @param  object type
         * @return builder
         */
        public  Builder withObjectConverter(Class objectClass, Class valueClass,
                                                                ObjectConverter converter) {
            mapper.registerObjectConverter(objectClass, valueClass, converter);
            return this;
        }

        /**
         * Build the mapper instance
         * @return a new mapper instance
         */
        public DefaultMessagePackMapper build() {
            return mapper;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy