ImplementationTypeDeserializer.java
package tools.jackson.datatype.eclipsecollections.deser;
import java.util.LinkedHashSet;
import java.util.Set;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.ValueDeserializer;
import tools.jackson.databind.deser.std.DelegatingDeserializer;
import tools.jackson.databind.jsontype.TypeDeserializer;
import tools.jackson.databind.util.ClassUtil;
/**
* Deserializer used for concrete Eclipse Collections implementation types
* (like {@code IntIntHashMap}, or {@code ImmutableTripletonList} named by a
* polymorphic type id): delegates to the deserializer of the most specific supported
* interface the type implements, and verifies that the result is compatible
* with the requested implementation type.
*<p>
* Implementation classes cannot generally be constructed directly, and the class
* produced depends on content (for example, {@code ImmutableList} with 2 elements
* is always produced as {@code ImmutableDoubletonList}, even if serialized
* from an {@code ImmutableArrayList}). So result is accepted if it is either an
* instance of the requested type, or implements all Eclipse Collections API
* interfaces the requested type implements: this way it can be assigned to any
* property declared using API types.
*/
public class ImplementationTypeDeserializer extends DelegatingDeserializer
{
protected final Class<?> _implType;
/**
* Eclipse Collections API interfaces implemented by {@link #_implType}
*/
protected final Class<?>[] _apiTypes;
public ImplementationTypeDeserializer(Class<?> implType, ValueDeserializer<?> delegatee) {
this(implType, delegatee, _findApiTypes(implType));
}
protected ImplementationTypeDeserializer(Class<?> implType, ValueDeserializer<?> delegatee,
Class<?>[] apiTypes) {
super(delegatee);
_implType = implType;
_apiTypes = apiTypes;
}
@Override
protected ValueDeserializer<?> newDelegatingInstance(ValueDeserializer<?> newDelegatee) {
return new ImplementationTypeDeserializer(_implType, newDelegatee, _apiTypes);
}
@Override
public Class<?> handledType() {
return _implType;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return _verify(ctxt, super.deserialize(p, ctxt));
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue)
throws JacksonException
{
return _verify(ctxt, super.deserialize(p, ctxt, intoValue));
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws JacksonException
{
return _verify(ctxt, super.deserializeWithType(p, ctxt, typeDeserializer));
}
protected Object _verify(DeserializationContext ctxt, Object value)
throws JacksonException
{
if (value == null || _implType.isInstance(value)) {
return value;
}
boolean compatible = true;
for (Class<?> apiType : _apiTypes) {
if (!apiType.isInstance(value)) {
compatible = false;
break;
}
}
if (compatible) {
return value;
}
return ctxt.reportInputMismatch(_implType,
"Cannot deserialize value of type %s: closest supported type produces values of type %s",
ClassUtil.nameOf(_implType), ClassUtil.classNameOf(value));
}
private static Class<?>[] _findApiTypes(Class<?> implType) {
Set<Class<?>> result = new LinkedHashSet<>();
for (Class<?> cls = implType; cls != null; cls = cls.getSuperclass()) {
_addApiTypes(cls, result);
}
return result.toArray(new Class<?>[0]);
}
private static void _addApiTypes(Class<?> cls, Set<Class<?>> result) {
for (Class<?> iface : cls.getInterfaces()) {
if (iface.getName().startsWith("org.eclipse.collections.api.")) {
result.add(iface);
}
_addApiTypes(iface, result);
}
}
}