TupleImplementations.java
package tools.jackson.datatype.eclipsecollections.deser.pair;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.BeanDescription;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.deser.SettableBeanProperty;
import tools.jackson.databind.deser.ValueInstantiator;
import tools.jackson.databind.deser.bean.PropertyValueBuffer;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.datatype.eclipsecollections.util.ImplementationTypes;
/**
* Handling of concrete Eclipse Collections tuple implementation classes (like
* {@code PairImpl} or {@code IntLongPairImpl}, as named by polymorphic type ids):
* use instantiator of the most specific supported tuple interface, verifying that
* it produces instances of the requested implementation class.
*/
final class TupleImplementations
{
private TupleImplementations() { }
/**
* @param implType Type of the (possible) implementation class
* @param tupleTypes Supported tuple interface types
* @param finder Function for finding instantiator for given supported tuple
* interface (and matching parameterized type)
*
* @return Instantiator to use, if any; {@code null} if type is not handled
*/
static ValueInstantiator findInstantiator(JavaType implType,
Collection<Class<?>> tupleTypes,
BiFunction<Class<?>, JavaType, ValueInstantiator> finder)
{
final Class<?> implClass = implType.getRawClass();
if (!ImplementationTypes.isEclipseImplementationClass(implClass)) {
return null;
}
List<Class<?>> supportedTypes = ImplementationTypes.mostSpecificTypes(implClass, tupleTypes);
// none, or multiple unrelated ones (ambiguous; not the case for any current
// Eclipse Collections class): leave to standard handling
if (supportedTypes.size() != 1) {
return null;
}
JavaType supportedType = implType.findSuperType(supportedTypes.get(0));
if (supportedType == null) {
return null;
}
ValueInstantiator inst = finder.apply(supportedTypes.get(0), supportedType);
if (inst == null) {
return null;
}
return new Verifying(implClass, inst);
}
/**
* Instantiator that verifies that the delegate produces instances of
* the requested implementation class.
*/
static class Verifying extends ValueInstantiator.Delegating
{
private static final long serialVersionUID = 1L;
protected final Class<?> _implClass;
Verifying(Class<?> implClass, ValueInstantiator delegate) {
super(delegate);
_implClass = implClass;
}
@Override
public ValueInstantiator createContextual(DeserializationContext ctxt,
BeanDescription.Supplier beanDescRef)
{
ValueInstantiator d = _delegate.createContextual(ctxt, beanDescRef);
return (d == _delegate) ? this : new Verifying(_implClass, d);
}
@Override
public Class<?> getValueClass() {
return _implClass;
}
@Override
public String getValueTypeDesc() {
return _implClass.getName();
}
@Override
public Object createFromObjectWith(DeserializationContext ctxt,
SettableBeanProperty[] props, PropertyValueBuffer buffer)
throws JacksonException
{
// NOTE: `Delegating` would call delegate directly, bypassing verification
return createFromObjectWith(ctxt, buffer.getParameters(ctxt, props));
}
@Override
public Object createFromObjectWith(DeserializationContext ctxt, Object[] args)
throws JacksonException
{
Object value = super.createFromObjectWith(ctxt, args);
if (value == null || _implClass.isInstance(value)) {
return value;
}
return ctxt.reportInputMismatch(_implClass,
"Cannot deserialize value of type %s: closest supported type produces values of type %s",
ClassUtil.nameOf(_implClass), ClassUtil.classNameOf(value));
}
}
}