TableDeserializer.java

package tools.jackson.datatype.guava.deser.table;

import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonParser;
import tools.jackson.core.JsonToken;

import tools.jackson.databind.*;
import tools.jackson.databind.deser.NullValueProvider;
import tools.jackson.databind.deser.impl.NullsConstantProvider;
import tools.jackson.databind.deser.std.StdDeserializer;
import tools.jackson.databind.jsontype.TypeDeserializer;
import tools.jackson.databind.type.MapLikeType;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.datatype.guava.util.NullElementHelper;

import com.google.common.collect.Table;

/**
 * @author Abhishekkr3003
 */
public abstract class TableDeserializer<T extends Table<Object, Object, Object>>
    extends StdDeserializer<T> {
    
    protected final MapLikeType _type;
    protected final KeyDeserializer _rowDeserializer;
    protected final KeyDeserializer _colDeserializer;
    protected final TypeDeserializer _valueTypeDeserializer;
    protected final ValueDeserializer<?> _valueDeserializer;
    
    // since 2.9.5: in 3.x demote to `ContainerDeserializerBase`
    protected final NullValueProvider _nullProvider;
    protected final boolean _skipNullValues;
    
    protected TableDeserializer(MapLikeType _type, KeyDeserializer _rowDeserializer,
        KeyDeserializer _colDeserializer, TypeDeserializer _valueTypeDeserializer,
        ValueDeserializer<?> _valueDeserializer) {
        this(
            _type, _rowDeserializer, _colDeserializer, _valueTypeDeserializer, _valueDeserializer,
            null
        );
    }
    
    protected TableDeserializer(MapLikeType _type, KeyDeserializer _rowDeserializer,
        KeyDeserializer _colDeserializer, TypeDeserializer _valueTypeDeserializer,
        ValueDeserializer<?> _valueDeserializer, NullValueProvider nvp) {
        super(_type);
        this._type = _type;
        this._rowDeserializer = _rowDeserializer;
        this._colDeserializer = _colDeserializer;
        this._valueTypeDeserializer = _valueTypeDeserializer;
        this._valueDeserializer = _valueDeserializer;
        this._nullProvider = nvp;
        _skipNullValues = (nvp == null) ? false : NullsConstantProvider.isSkipper(nvp);
    }
    
    protected TableDeserializer(MapLikeType type) {
        super(type);
        _type = type;
        _rowDeserializer = null;
        _colDeserializer = null;
        _valueDeserializer = null;
        _valueTypeDeserializer = null;
        _nullProvider = null;
        _skipNullValues = false;
    }
    
    /**
     * We need to use this method to properly handle possible contextual variants of key and value
     * deserializers, as well as type deserializers.
     */
    @Override
    public ValueDeserializer<?> createContextual(DeserializationContext ctxt,
        BeanProperty property) throws JacksonException
    {
        KeyDeserializer rkd = _rowDeserializer;
        if (rkd == null) {
            rkd = ctxt.findKeyDeserializer(_type.getKeyType(), property);
        }
        MapLikeType columnValueType = (MapLikeType) _type.getContentType();
        KeyDeserializer ckd = _colDeserializer;
        if (ckd == null) {
            ckd = ctxt.findKeyDeserializer(columnValueType.getKeyType(), property);
        }
        ValueDeserializer<?> valueDeser = _valueDeserializer;
        final JavaType vt = columnValueType.getContentType();
        if (valueDeser == null) {
            valueDeser = ctxt.findContextualValueDeserializer(vt, property);
        } else { // if directly assigned, probably not yet contextual, so:
            valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
        }
        // Type deserializer is slightly different; must be passed, but needs to become contextual.
        // But factory does not pass one, as content type of Table type is that of
        // column-to-value Map, not value: so need to locate it here [#244]
        TypeDeserializer vtd = _valueTypeDeserializer;
        if (vtd == null) {
            vtd = ctxt.findTypeDeserializer(vt);
        }
        if (vtd != null) {
            vtd = vtd.forProperty(property);
        }
        return _createContextual(_type,
                rkd, ckd, vtd, valueDeser, findContentNullProvider(ctxt, property, valueDeser));
    }
    
    protected abstract ValueDeserializer<?> _createContextual(MapLikeType t, KeyDeserializer rkd,
        KeyDeserializer ckd, TypeDeserializer vtd, ValueDeserializer<?> vd, NullValueProvider np);
    

    /**
     * Helper method for deserializing a cell value (starting with given token).
     * Since Guava tables do not accept {@code null} values, {@code null}s are
     * reported as problems (unless a problem handler provides a replacement value,
     * or drops the cell).
     *
     * @param t Token from which the value is to be deserialized
     *
     * @return Value to add, or {@code null} if cell is to be skipped
     */
    protected Object _readCellValue(JsonParser p, DeserializationContext ctxt, JsonToken t)
        throws JacksonException
    {
        final Object value;
        if (t == JsonToken.VALUE_NULL) {
            if (_skipNullValues) {
                return null;
            }
            value = _nullProvider.getNullValue(ctxt);
        } else if (_valueTypeDeserializer != null) {
            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
        } else {
            value = _valueDeserializer.deserialize(p, ctxt);
        }
        if (value != null) {
            return value;
        }
        return NullElementHelper.handleNull(p, ctxt, p.currentToken(),
                _type.getContentType().getContentType(),
                "Guava `Table` of type "+ClassUtil.getTypeDescription(_type));
    }

    protected void expect(DeserializationContext context, JsonToken expected, JsonToken actual)
    {
        if (actual != expected) {
            context.reportInputMismatch(this, String.format("Problem deserializing %s: expecting %s, found %s",
                    handledType().getName(), expected, actual));
        }
    }
}