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.
* <p>
* Note: casting within this class is all messed up -- just could not figure out a way
* to properly deal with recursive definition of "EnumMap<K extends Enum<K>, V>
*
* @author tsaloranta
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class EnumMapDeserializer
extends StdDeserializer<EnumMap<?,?>>
implements ContextualDeserializer
{
protected final JavaType _mapType;
protected final Class<?> _enumClass;
protected JsonDeserializer<Enum<?>> _keyDeserializer;
protected JsonDeserializer<Object> _valueDeserializer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public EnumMapDeserializer(JavaType mapType,
JsonDeserializer<?> keyDeserializer, JsonDeserializer<?> valueDeser)
{
super(EnumMap.class);
_mapType = mapType;
_enumClass = mapType.getKeyType().getRawClass();
_keyDeserializer = (JsonDeserializer<Enum<?>>) keyDeserializer;
_valueDeserializer = (JsonDeserializer<Object>) valueDeser;
}
public EnumMapDeserializer withResolved(JsonDeserializer<?> keyDeserializer,
JsonDeserializer<?> valueDeserializer)
{
if ((keyDeserializer == _keyDeserializer) && valueDeserializer == _valueDeserializer) {
return this;
}
return new EnumMapDeserializer(_mapType,
keyDeserializer, valueDeserializer);
}
/**
* 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:
if (vd instanceof ContextualDeserializer) {
vd = ((ContextualDeserializer) vd).createContextual(ctxt, property);
}
}
return withResolved(kd, vd);
}
/**
* 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();
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 = (t == JsonToken.VALUE_NULL) ?
null : _valueDeserializer.deserialize(jp, ctxt);
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);
}
}