tech.ydb.shaded.jackson.databind.deser.std.EnumSetDeserializer Maven / Gradle / Ivy
package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
/**
 * Standard deserializer for {@link EnumSet}s.
 * 
 * Note: casting within this class is all messed up -- just could not figure out a way
 * to properly deal with recursive definition of "EnumSet<K extends Enum<K>, V>
 */
@SuppressWarnings("rawtypes")
public class EnumSetDeserializer
    extends StdDeserializer>
    implements ContextualDeserializer
{
    private static final long serialVersionUID = 1L; // since 2.5
    protected final JavaType _enumType;
    protected final Class _enumClass;
    protected JsonDeserializer> _enumDeserializer;
    /**
     * Specific override for this instance (from proper, or global per-type overrides)
     * to indicate whether single value may be taken to mean an unwrapped one-element array
     * or not. If null, left to global defaults.
     *
     * @since 2.7
     */
    protected final Boolean _unwrapSingle;
    /*
    /**********************************************************
    /* Life-cycle
    /**********************************************************
     */
    @SuppressWarnings("unchecked" )
    public EnumSetDeserializer(JavaType enumType, JsonDeserializer> deser)
    {
        super(EnumSet.class);
        _enumType = enumType;
        _enumClass = (Class) enumType.getRawClass();
        // sanity check
        if (!_enumClass.isEnum()) {
            throw new IllegalArgumentException("Type "+enumType+" not Java Enum type");
        }
        _enumDeserializer = (JsonDeserializer>) deser;
        _unwrapSingle = null;
    }
    /**
     * @since 2.7
     */
    @SuppressWarnings("unchecked" )
    protected EnumSetDeserializer(EnumSetDeserializer base,
            JsonDeserializer> deser, Boolean unwrapSingle) {
        super(base);
        _enumType = base._enumType;
        _enumClass = base._enumClass;
        _enumDeserializer = (JsonDeserializer>) deser;
        _unwrapSingle = unwrapSingle;
    }
    public EnumSetDeserializer withDeserializer(JsonDeserializer> deser) {
        if (_enumDeserializer == deser) {
            return this;
        }
        return new EnumSetDeserializer(this, deser, _unwrapSingle);
    }
    public EnumSetDeserializer withResolved(JsonDeserializer> deser, Boolean unwrapSingle) {
        if ((_unwrapSingle == unwrapSingle) && (_enumDeserializer == deser)) {
            return this;
        }
        return new EnumSetDeserializer(this, deser, unwrapSingle);
    }
    /**
     * Because of costs associated with constructing Enum resolvers,
     * let's cache instances by default.
     */
    @Override
    public boolean isCachable() {
        // One caveat: content deserializer should prevent caching
        if (_enumType.getValueHandler() != null) {
            return false;
        }
        return true;
    }
    @Override // since 2.9
    public Boolean supportsUpdate(DeserializationConfig config) {
        return Boolean.TRUE;
    }
    @Override
    public JsonDeserializer> createContextual(DeserializationContext ctxt,
            BeanProperty property) throws JsonMappingException
    {
        Boolean unwrapSingle = findFormatFeature(ctxt, property, EnumSet.class,
                JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        JsonDeserializer> deser = _enumDeserializer;
        if (deser == null) {
            deser = ctxt.findContextualValueDeserializer(_enumType, property);
        } else { // if directly assigned, probably not yet contextual, so:
            deser = ctxt.handleSecondaryContextualization(deser, property, _enumType);
        }
        return withResolved(deser, unwrapSingle);
    }
    /*
    /**********************************************************
    /* JsonDeserializer API
    /**********************************************************
     */
    @Override
    public EnumSet> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        EnumSet result = constructSet();
        // Ok: must point to START_ARRAY (or equivalent)
        if (!p.isExpectedStartArrayToken()) {
            return handleNonArray(p, ctxt, result);
        }
        return _deserialize(p, ctxt, result);
    }
    @Override
    public EnumSet> deserialize(JsonParser p, DeserializationContext ctxt,
            EnumSet> result) throws IOException
    {
        // Ok: must point to START_ARRAY (or equivalent)
        if (!p.isExpectedStartArrayToken()) {
            return handleNonArray(p, ctxt, result);
        }
        return _deserialize(p, ctxt, result);
    }
    
    @SuppressWarnings("unchecked") 
    protected final EnumSet> _deserialize(JsonParser p, DeserializationContext ctxt,
            EnumSet result) throws IOException
    {
        JsonToken t;
        try {
            while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
                /* What to do with nulls? Fail or ignore? Fail, for now
                 * (note: would fail if we passed it to EnumDeserializer, too,
                 * but in general nulls should never be passed to non-container
                 * deserializers)
                 */
                if (t == JsonToken.VALUE_NULL) {
                    return (EnumSet>) ctxt.handleUnexpectedToken(_enumClass, p);
                }
                Enum> value = _enumDeserializer.deserialize(p, ctxt);
                /* 24-Mar-2012, tatu: As per [JACKSON-810], may actually get nulls;
                 *    but EnumSets don't allow nulls so need to skip.
                 */
                if (value != null) { 
                    result.add(value);
                }
            }
        } catch (Exception e) {
            throw JsonMappingException.wrapWithPath(e, result, result.size());
        }
        return result;
    }
    @Override
    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
            TypeDeserializer typeDeserializer)
        throws IOException, JsonProcessingException
    {
        return typeDeserializer.deserializeTypedFromArray(p, ctxt);
    }
    
    @SuppressWarnings("unchecked") 
    private EnumSet constructSet()
    {
        return EnumSet.noneOf(_enumClass);
    }
    @SuppressWarnings("unchecked") 
    protected EnumSet> handleNonArray(JsonParser p, DeserializationContext ctxt,
            EnumSet result)
        throws IOException
    {
        boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
                ((_unwrapSingle == null) &&
                        ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
        if (!canWrap) {
            return (EnumSet>) ctxt.handleUnexpectedToken(EnumSet.class, p);
        }
        // First: since `null`s not allowed, slightly simpler...
        if (p.hasToken(JsonToken.VALUE_NULL)) {
            return (EnumSet>) ctxt.handleUnexpectedToken(_enumClass, p);
        }
        try {
            Enum> value = _enumDeserializer.deserialize(p, ctxt);
            if (value != null) { 
                result.add(value);
            }
        } catch (Exception e) {
            throw JsonMappingException.wrapWithPath(e, result, result.size());
        }
        return result;
    }
}