io.tarantool.driver.mappers.DefaultMessagePackMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cartridge-driver Show documentation
Show all versions of cartridge-driver Show documentation
Tarantool Cartridge driver for Tarantool versions 1.10+ based on Netty framework
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 extends Value, ?> 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;
}
}
}