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 io.tarantool.driver.api.MessagePackMapperBuilder;
import io.tarantool.driver.exceptions.TarantoolClientException;
import io.tarantool.driver.mappers.converters.ConverterWrapper;
import io.tarantool.driver.mappers.converters.ObjectConverter;
import io.tarantool.driver.mappers.converters.ValueConverter;
import io.tarantool.driver.mappers.converters.object.DefaultListToArrayValueConverter;
import io.tarantool.driver.mappers.converters.object.DefaultMapToMapValueConverter;
import io.tarantool.driver.mappers.converters.value.defaults.DefaultArrayValueToListConverter;
import io.tarantool.driver.mappers.converters.value.defaults.DefaultMapValueToMapConverter;
import io.tarantool.driver.mappers.converters.value.defaults.DefaultNullToNilValueConverter;
import org.msgpack.value.NilValue;
import org.msgpack.value.Value;
import org.msgpack.value.ValueType;

import java.util.ArrayList;
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 java.util.stream.Collectors;

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

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

    private static final long serialVersionUID = 20220418L;

    private final Map>>> valueConverters;
    private final Map>>> objectConverters;
    private final ObjectConverter nilConverter = new DefaultNullToNilValueConverter();

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

    /**
     * Copying constructor
     *
     * @param mapper another mapper instance
     */
    public DefaultMessagePackMapper(DefaultMessagePackMapper mapper) {
        this.valueConverters = mapper.valueConverters.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> new ArrayList<>(e.getValue())));
        this.objectConverters = mapper.objectConverters.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> new ArrayList<>(e.getValue())));
    }

    @SuppressWarnings("unchecked")
    private  ObjectConverter
    getObjectConverter(O o, Function>> getter) {
        if (o == null) {
            return (ObjectConverter) nilConverter;
        }
        Optional> converter = findObjectConverter(o.getClass(), getter);
        if (!converter.isPresent()) {
            throw new MessagePackObjectMapperException("ObjectConverter for type %s is not found", o.getClass());
        }
        return converter.get();
    }

    @Override
    @SuppressWarnings("unchecked")
    public  V toValue(O o) {
        Function>> getter =
            typeName -> objectConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                .map(c -> (ObjectConverter) c.getConverter())
                .filter(c -> c.canConvertObject(o))
                .findFirst();

        ObjectConverter converter = getObjectConverter(o, getter);
        return converter.toValue(o);
    }

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

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

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

    private  Optional findValueConverter(ValueType valueType, Function> getter) {
        return getter.apply(valueType);
    }

    /**
     * Perform {@link ValueConverter} converter registration. The target object class for registration is determined
     * automatically
     *
     * @param valueType MessagePack source type
     * @param converter entity-to-object converter
     * @param        MessagePack's entity type that the converter accepts and/or returns
     * @param        java object's type that the converter accepts and/or returns
     * @see ValueConverter
     */
    public  void registerValueConverter(
        ValueType valueType,
        ValueConverter converter) {
        try {
            Class objectClass = getInterfaceParameterClass(converter, ValueConverter.class, 1);
            registerValueConverter(valueType, objectClass, converter);
        } catch (InterfaceParameterClassNotFoundException | InterfaceParameterTypeNotFoundException e) {
            throw new TarantoolClientException("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(
        ValueType valueType,
        Class objectClass,
        ValueConverter converter) {
        List>> converters =
            valueConverters.computeIfAbsent(valueType, k -> new LinkedList<>());
        converters.add(0, new ConverterWrapper<>(converter, objectClass));
    }

    public  void registerValueConverterWithoutTargetClass(
        ValueType valueType,
        ValueConverter converter) {
        List>> converters =
            valueConverters.computeIfAbsent(valueType, k -> new LinkedList<>());
        converters.add(0, new ConverterWrapper<>(converter, Object.class));
    }

    /**
     * Check if the specified converter can convert to the specified object type
     */
    private boolean checkConverterByTargetType(Class targetClassOfConverter, Class targetClass) {
        return targetClassOfConverter.isAssignableFrom(targetClass);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  Optional> getValueConverter(
        ValueType valueType,
        Class targetClass) {
        Function>> getter =
            typeName -> valueConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                .filter(c -> checkConverterByTargetType(c.getTargetClass(), targetClass))
                .map(c -> (ValueConverter) c.getConverter())
                .findFirst();
        return findValueConverter(valueType, 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's entity type that the converter accepts and/or returns
     * @param        java object's type that the converter accepts and/or returns
     * @see ObjectConverter
     */
    public  void registerObjectConverter(ObjectConverter converter) {
        try {
            registerObjectConverter(
                getInterfaceParameterClass(converter, ObjectConverter.class, 0), converter);
        } catch (InterfaceParameterClassNotFoundException | InterfaceParameterTypeNotFoundException e) {
            throw new TarantoolClientException("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 (InterfaceParameterClassNotFoundException | InterfaceParameterTypeNotFoundException e) {
            throw new TarantoolClientException("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(0, new ConverterWrapper<>(converter, valueClass));
    }

    @Override
    @SuppressWarnings("unchecked")
    public  Optional> getObjectConverter(
        Class objectClass,
        Class valueClass) {
        Function>> getter =
            typeName -> objectConverters.getOrDefault(typeName, Collections.emptyList()).stream()
                .filter(c -> checkConverterByTargetType(c.getTargetClass(), valueClass))
                .map(c -> (ObjectConverter) c.getConverter())
                .findFirst();
        return findObjectConverter(objectClass, getter);
    }

    @Override
    public MessagePackMapper copy() {
        return new DefaultMessagePackMapper(this);
    }

    /**
     * Builder for {@link DefaultMessagePackMapper}
     */
    public static class Builder implements MessagePackMapperBuilder {
        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);
        }

        @Override
        public Builder withDefaultMapValueConverter() {
            mapper.registerValueConverter(ValueType.MAP, new DefaultMapValueToMapConverter(mapper));
            return this;
        }

        @Override
        public Builder withDefaultMapObjectConverter() {
            mapper.registerObjectConverter(new DefaultMapToMapValueConverter(mapper));
            Class> cls = (Class>) (Object) HashMap.class;
            mapper.registerObjectConverter(cls, new DefaultMapToMapValueConverter(mapper));
            return this;
        }

        @Override
        public Builder withDefaultArrayValueConverter() {
            mapper.registerValueConverter(ValueType.ARRAY, new DefaultArrayValueToListConverter(mapper));
            return this;
        }

        @Override
        public Builder withDefaultListObjectConverter() {
            mapper.registerObjectConverter(new DefaultListToArrayValueConverter(mapper));
            Class> cls = (Class>) (Object) ArrayList.class;
            mapper.registerObjectConverter(cls, new DefaultListToArrayValueConverter(mapper));
            return this;
        }

        @Override
        public  Builder withValueConverter(ValueType valueType, ValueConverter converter) {
            mapper.registerValueConverter(valueType, converter);
            return this;
        }

        @Override
        public  Builder withValueConverter(
            ValueType valueType, Class objectClass,
            ValueConverter converter) {
            mapper.registerValueConverter(valueType, objectClass, converter);
            return this;
        }

        @Override
        public  Builder withObjectConverter(ObjectConverter converter) {
            mapper.registerObjectConverter(converter);
            return this;
        }

        @Override
        public  Builder withObjectConverter(Class objectClass, ObjectConverter converter) {
            mapper.registerObjectConverter(objectClass, converter);
            return this;
        }

        @Override
        public  Builder withObjectConverter(
            Class objectClass, Class valueClass,
            ObjectConverter converter) {
            mapper.registerObjectConverter(objectClass, valueClass, converter);
            return this;
        }

        @Override
        public DefaultMessagePackMapper build() {
            return mapper;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy