RangeSetDeserializer.java
package com.fasterxml.jackson.datatype.guava.deser;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.type.LogicalType;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
/**
* Backported all implementations from Jackson 3.0, except following parts.
*
* <ul>
* <li>class declaration</li>
* <li>JsonDeserializer in 2.x -> ValueDeserializer in 3.0</li>
* </ul>
*
* @since 2.16
*/
public class RangeSetDeserializer
extends StdDeserializer<RangeSet<?>>
implements ContextualDeserializer
{
private static final long serialVersionUID = 1L;
private final JsonDeserializer<Object> _deserializer;
public RangeSetDeserializer() {
super(RangeSet.class);
_deserializer = null;
}
protected RangeSetDeserializer(RangeSetDeserializer base,
JsonDeserializer<Object> deser)
{
super(base);
_deserializer = deser;
}
@Override // since 2.12
public LogicalType logicalType() {
return LogicalType.Collection;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
JavaType genericType = _findType(ctxt, ctxt.getContextualType());
if (genericType == null) {
if (property != null) {
genericType = _findType(ctxt, property.getType());
}
// Cannot locate generic type to use? Leave as-is, fail on attempt to deserialize
if (genericType == null) {
return this;
}
}
JsonDeserializer<Object> deser = ctxt.findContextualValueDeserializer(genericType, property);
return new RangeSetDeserializer(this, deser);
}
private JavaType _findType(DeserializationContext ctxt, JavaType base)
{
Class<?> raw = base.getRawClass();
final TypeFactory tf = ctxt.getTypeFactory();
if (RangeSet.class.isAssignableFrom(raw)) {
JavaType valueType = tf.findFirstTypeParameter(base, RangeSet.class);
if (valueType != null) {
JavaType rangeType = tf.constructParametricType(Range.class, valueType);
return tf.constructCollectionType(List.class, rangeType);
}
}
return null;
}
@Override
public RangeSet<Comparable<?>> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (_deserializer == null) {
ctxt.reportBadDefinition(handledType(),
"Not contextualized to have value deserializer or value type of `RangeSet` was not available via type parameters");
}
final Collection<?> ranges = (Collection<?>) _deserializer.deserialize(p, ctxt);
ImmutableRangeSet.Builder<Comparable<?>> builder = ImmutableRangeSet.builder();
for (Object ob : ranges) {
if (ob == null) {
_tryToAddNull(p, ctxt, builder);
continue;
}
@SuppressWarnings("unchecked")
Range<Comparable<?>> range = (Range<Comparable<?>>) ob;
builder.add(range);
}
return builder.build();
}
/**
* Some/many Guava containers do not allow addition of {@code null} values,
* so isolate handling here.
*
* @since 2.17
*/
protected void _tryToAddNull(JsonParser p, DeserializationContext ctxt,
ImmutableRangeSet.Builder<Comparable<?>> builder)
throws IOException
{
// Ideally we'd have better idea of where nulls are accepted, but first
// let's just produce something better than NPE:
try {
builder.add(null);
} catch (NullPointerException e) {
ctxt.handleUnexpectedToken(_valueType, JsonToken.VALUE_NULL, p,
"Guava `RangeSet` does not accept `null` values");
}
}
}