package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.type.LogicalType;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.CompactStringObjectMap;
import com.fasterxml.jackson.databind.util.EnumResolver;
@JacksonStdImpl
public class EnumDeserializer
extends StdScalarDeserializer<Object>
implements ContextualDeserializer
{
private static final long serialVersionUID = 1L;
protected Object[] _enumsByIndex;
private final Enum<?> _enumDefaultValue;
protected final CompactStringObjectMap _lookupByName;
protected CompactStringObjectMap _lookupByToString;
protected final Boolean _caseInsensitive;
public EnumDeserializer(EnumResolver byNameResolver, Boolean caseInsensitive)
{
super(byNameResolver.getEnumClass());
_lookupByName = byNameResolver.constructLookup();
_enumsByIndex = byNameResolver.getRawEnums();
_enumDefaultValue = byNameResolver.getDefaultValue();
_caseInsensitive = caseInsensitive;
}
protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive)
{
super(base);
_lookupByName = base._lookupByName;
_enumsByIndex = base._enumsByIndex;
_enumDefaultValue = base._enumDefaultValue;
_caseInsensitive = caseInsensitive;
}
@Deprecated
public EnumDeserializer(EnumResolver byNameResolver) {
this(byNameResolver, null);
}
@Deprecated
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
Class<?> enumClass, AnnotatedMethod factory) {
return deserializerForCreator(config, enumClass, factory, null, null);
}
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
Class<?> enumClass, AnnotatedMethod factory,
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
{
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(factory.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return new FactoryBasedEnumDeserializer(enumClass, factory,
factory.getParameterType(0),
valueInstantiator, creatorProps);
}
public static JsonDeserializer<?> deserializerForNoArgsCreator(DeserializationConfig config,
Class<?> enumClass, AnnotatedMethod factory)
{
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(factory.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return new FactoryBasedEnumDeserializer(enumClass, factory);
}
public EnumDeserializer withResolved(Boolean caseInsensitive) {
if (Objects.equals(_caseInsensitive, caseInsensitive)) {
return this;
}
return new EnumDeserializer(this, caseInsensitive);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
Boolean caseInsensitive = findFormatFeature(ctxt, property, handledType(),
JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
if (caseInsensitive == null) {
caseInsensitive = _caseInsensitive;
}
return withResolved(caseInsensitive);
}
@Override
public boolean isCachable() { return true; }
@Override
public LogicalType logicalType() {
return LogicalType.Enum;
}
@Override
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
return _enumDefaultValue;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
if (p.hasToken(JsonToken.VALUE_STRING)) {
return _fromString(p, ctxt, p.getText());
}
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return _fromInteger(p, ctxt, p.getIntValue());
}
if (p.isExpectedStartObjectToken()) {
return _fromString(p, ctxt,
ctxt.extractScalarFromObject(p, this, _valueClass));
}
return _deserializeOther(p, ctxt);
}
protected Object _fromString(JsonParser p, DeserializationContext ctxt,
String text)
throws IOException
{
CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
? _getToStringLookup(ctxt) : _lookupByName;
Object result = lookup.find(text);
if (result == null) {
String trimmed = text.trim();
if ((trimmed == text) || (result = lookup.find(trimmed)) == null) {
return _deserializeAltString(p, ctxt, lookup, trimmed);
}
}
return result;
}
protected Object _fromInteger(JsonParser p, DeserializationContext ctxt,
int index)
throws IOException
{
final CoercionAction act = ctxt.findCoercionAction(logicalType(), handledType(),
CoercionInputShape.Integer);
if (act == CoercionAction.Fail) {
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
return ctxt.handleWeirdNumberValue(_enumClass(), index,
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
);
}
_checkCoercionFail(ctxt, act, handledType(), index,
"Integer value ("+index+")");
}
switch (act) {
case AsNull:
return null;
case AsEmpty:
return getEmptyValue(ctxt);
case TryConvert:
default:
}
if (index >= 0 && index < _enumsByIndex.length) {
return _enumsByIndex[index];
}
if ((_enumDefaultValue != null)
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
return _enumDefaultValue;
}
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
return ctxt.handleWeirdNumberValue(_enumClass(), index,
"index value outside legal index range [0..%s]",
_enumsByIndex.length-1);
}
return null;
}
private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt,
CompactStringObjectMap lookup, String nameOrig) throws IOException
{
String name = nameOrig.trim();
if (name.isEmpty()) {
CoercionAction act;
if (nameOrig.isEmpty()) {
act = _findCoercionFromEmptyString(ctxt);
act = _checkCoercionFail(ctxt, act, handledType(), nameOrig,
"empty String (\"\")");
} else {
act = _findCoercionFromBlankString(ctxt);
act = _checkCoercionFail(ctxt, act, handledType(), nameOrig,
"blank String (all whitespace)");
}
switch (act) {
case AsEmpty:
case TryConvert:
return getEmptyValue(ctxt);
case AsNull:
default:
}
return null;
} else {
if (Boolean.TRUE.equals(_caseInsensitive)) {
Object match = lookup.findCaseInsensitive(name);
if (match != null) {
return match;
}
} else if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
char c = name.charAt(0);
if (c >= '0' && c <= '9') {
try {
int index = Integer.parseInt(name);
if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
return ctxt.handleWeirdStringValue(_enumClass(), name,
"value looks like quoted Enum index, but `MapperFeature.ALLOW_COERCION_OF_SCALARS` prevents use"
);
}
if (index >= 0 && index < _enumsByIndex.length) {
return _enumsByIndex[index];
}
} catch (NumberFormatException e) {
}
}
}
}
if ((_enumDefaultValue != null)
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
return _enumDefaultValue;
}
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
return ctxt.handleWeirdStringValue(_enumClass(), name,
"not one of the values accepted for Enum class: %s", lookup.keys());
}
return null;
}
protected Object _deserializeOther(JsonParser p, DeserializationContext ctxt) throws IOException
{
if (p.hasToken(JsonToken.START_ARRAY)) {
return _deserializeFromArray(p, ctxt);
}
return ctxt.handleUnexpectedToken(_enumClass(), p);
}
protected Class<?> _enumClass() {
return handledType();
}
protected CompactStringObjectMap _getToStringLookup(DeserializationContext ctxt)
{
CompactStringObjectMap lookup = _lookupByToString;
if (lookup == null) {
synchronized (this) {
lookup = EnumResolver.constructUsingToString(ctxt.getConfig(), _enumClass())
.constructLookup();
}
_lookupByToString = lookup;
}
return lookup;
}
}