package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
/**
* Deserializer for {@link EnumMap} values.
*
* Note: casting within this class is all messed up -- just could not figure out a way
* to properly deal with recursive definition of "EnumMap, V>
*
* @author tsaloranta
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class EnumMapDeserializer
extends StdDeserializer>
implements ContextualDeserializer
{
private static final long serialVersionUID = 1518773374647478964L;
protected final JavaType _mapType;
protected final Class> _enumClass;
protected JsonDeserializer> _keyDeserializer;
protected JsonDeserializer _valueDeserializer;
/**
* If value instances have polymorphic type information, this
* is the type deserializer that can handle it
*/
protected final TypeDeserializer _valueTypeDeserializer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @deprecated Since 2.1.3 -- use variant that takes one more argument.
*/
@Deprecated
public EnumMapDeserializer(JavaType mapType,
JsonDeserializer> keyDeserializer, JsonDeserializer> valueDeser) {
this(mapType, keyDeserializer, valueDeser, null);
}
public EnumMapDeserializer(JavaType mapType,
JsonDeserializer> keyDeserializer, JsonDeserializer> valueDeser,
TypeDeserializer valueTypeDeser)
{
super(EnumMap.class);
_mapType = mapType;
_enumClass = mapType.getKeyType().getRawClass();
_keyDeserializer = (JsonDeserializer>) keyDeserializer;
_valueDeserializer = (JsonDeserializer) valueDeser;
_valueTypeDeserializer = valueTypeDeser;
}
/**
* @deprecated Since 2.1.3 -- use variant that takes one more argument.
*/
@Deprecated
public EnumMapDeserializer withResolved(JsonDeserializer> keyDeserializer,
JsonDeserializer> valueDeserializer)
{
return withResolved(keyDeserializer, valueDeserializer, null);
}
public EnumMapDeserializer withResolved(JsonDeserializer> keyDeserializer,
JsonDeserializer> valueDeserializer, TypeDeserializer valueTypeDeser)
{
if ((keyDeserializer == _keyDeserializer)
&& (valueDeserializer == _valueDeserializer)
&& (valueTypeDeser == _valueTypeDeserializer)) {
return this;
}
return new EnumMapDeserializer(_mapType,
keyDeserializer, valueDeserializer, _valueTypeDeserializer);
}
/**
* Method called to finalize setup of this deserializer,
* when it is known for which property deserializer is needed for.
*/
@Override
public JsonDeserializer> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
// note: instead of finding key deserializer, with enums we actually
// work with regular deserializers (less code duplication; but not
// quite as clean as it ought to be)
JsonDeserializer> kd = _keyDeserializer;
if (kd == null) {
kd = ctxt.findContextualValueDeserializer(_mapType.getKeyType(), property);
}
JsonDeserializer> vd = _valueDeserializer;
if (vd == null) {
vd = ctxt.findContextualValueDeserializer(_mapType.getContentType(), property);
} else { // if directly assigned, probably not yet contextual, so:
vd = ctxt.handleSecondaryContextualization(vd, property);
}
TypeDeserializer vtd = _valueTypeDeserializer;
if (vtd != null) {
vtd = vtd.forProperty(property);
}
return withResolved(kd, vd, vtd);
}
/**
* Because of costs associated with constructing Enum resolvers,
* let's cache instances by default.
*/
@Override
public boolean isCachable() { return true; }
/*
/**********************************************************
/* Actual deserialization
/**********************************************************
*/
@Override
public EnumMap,?> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
// Ok: must point to START_OBJECT
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw ctxt.mappingException(EnumMap.class);
}
EnumMap result = constructMap();
final JsonDeserializer valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
while ((jp.nextToken()) != JsonToken.END_OBJECT) {
Enum> key = _keyDeserializer.deserialize(jp, ctxt);
if (key == null) {
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
String value = null;
try { // bit ugly, but will have to do; works with usual scalars
if (jp.hasCurrentToken()) {
value = jp.getText();
}
} catch (Exception e) { }
throw ctxt.weirdStringException(value, _enumClass, "value not one of declared Enum instance names");
}
/* 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
* just skip the entry then. But we must skip the value then.
*/
jp.nextToken();
jp.skipChildren();
continue;
}
// And then the value...
JsonToken t = jp.nextToken();
/* note: MUST check for nulls separately: deserializers will
* not handle them (and maybe fail or return bogus data)
*/
Object value;
if (t == JsonToken.VALUE_NULL) {
value = null;
} else if (typeDeser == null) {
value = valueDes.deserialize(jp, ctxt);
} else {
value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
}
result.put(key, value);
}
return result;
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
}
private EnumMap,?> constructMap() {
return new EnumMap(_enumClass);
}
}