blob: 6fc6c2ccbed1e848f061f13426c79f2262d8a59c [file] [log] [blame]
package org.codehaus.jackson.map.deser;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.io.SerializedString;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.deser.BeanDeserializer;
import org.codehaus.jackson.map.introspect.AnnotatedClass;
import org.codehaus.jackson.map.type.*;
import org.codehaus.jackson.map.util.ClassUtil;
import org.codehaus.jackson.map.util.RootNameLookup;
import org.codehaus.jackson.type.JavaType;
/**
* Default {@link DeserializerProvider} implementation.
* Handles low-level caching (non-root) aspects of deserializer
* handling; all construction details are delegated to configured
* {@link DeserializerFactory} instance that the provider owns.
*/
public class StdDeserializerProvider
extends DeserializerProvider
{
/*
/**********************************************************
/* Caching
/**********************************************************
*/
/**
* We will also cache some dynamically constructed deserializers;
* specifically, ones that are expensive to construct.
* This currently means bean and Enum deserializers; array, List and Map
* deserializers will not be cached.
*<p>
* Given that we don't expect much concurrency for additions
* (should very quickly converge to zero after startup), let's
* explicitly define a low concurrency setting.
*/
final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers
= new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.75f, 2);
/**
* During deserializer construction process we may need to keep track of partially
* completed deserializers, to resolve cyclic dependencies. This is the
* map used for storing deserializers before they are fully complete.
*/
final protected HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
= new HashMap<JavaType, JsonDeserializer<Object>>(8);
final protected RootNameLookup _rootNames;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Factory responsible for constructing actual deserializers, if not
* one of pre-configured types.
*/
protected DeserializerFactory _factory;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* Default constructor. Equivalent to calling
*<pre>
* new StdDeserializerProvider(BeanDeserializerFactory.instance);
*</pre>
*/
public StdDeserializerProvider() { this(BeanDeserializerFactory.instance); }
public StdDeserializerProvider(DeserializerFactory f) {
_factory = f;
_rootNames = new RootNameLookup();
}
@Override
public DeserializerProvider withAdditionalDeserializers(Deserializers d) {
return withFactory(_factory.withAdditionalDeserializers(d));
}
@Override
public DeserializerProvider withAdditionalKeyDeserializers(KeyDeserializers d) {
return withFactory(_factory.withAdditionalKeyDeserializers(d));
}
@Override
public DeserializerProvider withDeserializerModifier(BeanDeserializerModifier modifier) {
return withFactory(_factory.withDeserializerModifier(modifier));
}
@Override
public DeserializerProvider withAbstractTypeResolver(AbstractTypeResolver resolver) {
return withFactory(_factory.withAbstractTypeResolver(resolver));
}
@Override
public DeserializerProvider withValueInstantiators(ValueInstantiators instantiators) {
return withFactory(_factory.withValueInstantiators(instantiators));
}
@Override
public StdDeserializerProvider withFactory(DeserializerFactory factory) {
// sanity-check to try to prevent hard-to-debug problems; sub-classes MUST override this method
if (this.getClass() != StdDeserializerProvider.class) {
throw new IllegalStateException("DeserializerProvider of type "
+this.getClass().getName()+" does not override 'withFactory()' method");
}
return new StdDeserializerProvider(factory);
}
/*
/**********************************************************
/* Abstract methods impls
/**********************************************************
*/
@Override
public JavaType mapAbstractType(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
return _factory.mapAbstractType(config, type);
}
@Override
public SerializedString findExpectedRootName(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
return _rootNames.findRootName(type, config);
}
@SuppressWarnings("unchecked")
@Override
public JsonDeserializer<Object> findValueDeserializer(DeserializationConfig config,
JavaType propertyType, BeanProperty property)
throws JsonMappingException
{
JsonDeserializer<Object> deser = _findCachedDeserializer(propertyType);
if (deser != null) {
// [JACKSON-385]: need to support contextualization:
if (deser instanceof ContextualDeserializer<?>) {
JsonDeserializer<?> d = ((ContextualDeserializer<?>) deser).createContextual(config, property);
deser = (JsonDeserializer<Object>) d;
}
return deser;
}
// If not, need to request factory to construct (or recycle)
deser = _createAndCacheValueDeserializer(config, propertyType, property);
if (deser == null) {
/* Should we let caller handle it? Let's have a helper method
* decide it; can throw an exception, or return a valid
* deserializer
*/
deser = _handleUnknownValueDeserializer(propertyType);
}
// [JACKSON-385]: need to support contextualization:
if (deser instanceof ContextualDeserializer<?>) {
JsonDeserializer<?> d = ((ContextualDeserializer<?>) deser).createContextual(config, property);
deser = (JsonDeserializer<Object>) d;
}
return deser;
}
@Override
public JsonDeserializer<Object> findTypedValueDeserializer(DeserializationConfig config,
JavaType type, BeanProperty property)
throws JsonMappingException
{
JsonDeserializer<Object> deser = findValueDeserializer(config, type, property);
TypeDeserializer typeDeser = _factory.findTypeDeserializer(config, type, property);
if (typeDeser != null) {
return new WrappedDeserializer(typeDeser, deser);
}
return deser;
}
@Override
public KeyDeserializer findKeyDeserializer(DeserializationConfig config,
JavaType type, BeanProperty property)
throws JsonMappingException
{
KeyDeserializer kd = _factory.createKeyDeserializer(config, type, property);
// One more thing: contextuality
if (kd instanceof ContextualKeyDeserializer) {
kd = ((ContextualKeyDeserializer) kd).createContextual(config, property);
}
if (kd == null) { // if none found, need to use a placeholder that'll fail
return _handleUnknownKeyDeserializer(type);
}
return kd;
}
/**
* Method that can be called to find out whether a deserializer can
* be found for given type
*/
@Override
public boolean hasValueDeserializerFor(DeserializationConfig config, JavaType type)
{
/* Note: mostly copied from findValueDeserializer, except for
* handling of unknown types
*/
JsonDeserializer<Object> deser = _findCachedDeserializer(type);
if (deser == null) {
try {
deser = _createAndCacheValueDeserializer(config, type, null);
} catch (Exception e) {
return false;
}
}
return (deser != null);
}
@Override
public int cachedDeserializersCount() {
return _cachedDeserializers.size();
}
/**
* Method that will drop all dynamically constructed deserializers (ones that
* are counted as result value for {@link #cachedDeserializersCount}).
* This can be used to remove memory usage (in case some deserializers are
* only used once or so), or to force re-construction of deserializers after
* configuration changes for mapper than owns the provider.
*
* @since 1.4
*/
@Override
public void flushCachedDeserializers() {
_cachedDeserializers.clear();
}
/*
/**********************************************************
/* Overridable helper methods
/**********************************************************
*/
protected JsonDeserializer<Object> _findCachedDeserializer(JavaType type)
{
if (type == null) {
throw new IllegalArgumentException();
}
return _cachedDeserializers.get(type);
}
/**
* Method that will try to create a deserializer for given type,
* and resolve and cache it if necessary
*
* @param config Configuration
* @param type Type of property to deserializer
* @param property Property (field, setter, ctor arg) to use deserializer for
*/
protected JsonDeserializer<Object>_createAndCacheValueDeserializer(DeserializationConfig config,
JavaType type, BeanProperty property)
throws JsonMappingException
{
/* Only one thread to construct deserializers at any given point in time;
* limitations necessary to ensure that only completely initialized ones
* are visible and used.
*/
synchronized (_incompleteDeserializers) {
// Ok, then: could it be that due to a race condition, deserializer can now be found?
JsonDeserializer<Object> deser = _findCachedDeserializer(type);
if (deser != null) {
return deser;
}
int count = _incompleteDeserializers.size();
// Or perhaps being resolved right now?
if (count > 0) {
deser = _incompleteDeserializers.get(type);
if (deser != null) {
return deser;
}
}
// Nope: need to create and possibly cache
try {
return _createAndCache2(config, type, property);
} finally {
// also: any deserializers that have been created are complete by now
if (count == 0 && _incompleteDeserializers.size() > 0) {
_incompleteDeserializers.clear();
}
}
}
}
/**
* Method that handles actual construction (via factory) and caching (both
* intermediate and eventual)
*/
protected JsonDeserializer<Object> _createAndCache2(DeserializationConfig config, JavaType type,
BeanProperty property)
throws JsonMappingException
{
JsonDeserializer<Object> deser;
try {
deser = _createDeserializer(config, type, property);
} catch (IllegalArgumentException iae) {
/* We better only expose checked exceptions, since those
* are what caller is expected to handle
*/
throw new JsonMappingException(iae.getMessage(), null, iae);
}
if (deser == null) {
return null;
}
/* cache resulting deserializer? always true for "plain" BeanDeserializer
* (but can be re-defined for sub-classes by using @JsonCachable!)
*/
// 08-Jun-2010, tatu: Related to [JACKSON-296], need to avoid caching MapSerializers... so:
boolean isResolvable = (deser instanceof ResolvableDeserializer);
boolean addToCache = (deser.getClass() == BeanDeserializer.class);
if (!addToCache) {
// 14-Feb-2011, tatu: As per [JACKSON-487], try fully blocking annotation access:
if (config.isEnabled(DeserializationConfig.Feature.USE_ANNOTATIONS)) {
AnnotationIntrospector aintr = config.getAnnotationIntrospector();
// note: pass 'null' to prevent mix-ins from being used
AnnotatedClass ac = AnnotatedClass.construct(deser.getClass(), aintr, null);
Boolean cacheAnn = aintr.findCachability(ac);
if (cacheAnn != null) {
addToCache = cacheAnn.booleanValue();
}
}
}
/* we will temporarily hold on to all created deserializers (to
* handle cyclic references, and possibly reuse non-cached
* deserializers (list, map))
*/
/* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental
* resolution of a reference -- couple of ways to prevent this;
* either not add Lists or Maps, or clear references eagerly.
* Let's actually do both; since both seem reasonable.
*/
/* Need to resolve? Mostly done for bean deserializers; required for
* resolving cyclic references.
*/
if (isResolvable) {
_incompleteDeserializers.put(type, deser);
_resolveDeserializer(config, (ResolvableDeserializer)deser);
_incompleteDeserializers.remove(type);
}
if (addToCache) {
_cachedDeserializers.put(type, deser);
}
return deser;
}
/* Refactored so we can isolate the casts that require suppression
* of type-safety warnings.
*/
@SuppressWarnings("unchecked")
protected JsonDeserializer<Object> _createDeserializer(DeserializationConfig config,
JavaType type, BeanProperty property)
throws JsonMappingException
{
if (type.isEnumType()) {
return (JsonDeserializer<Object>) _factory.createEnumDeserializer(config, this, type, property);
}
if (type.isContainerType()) {
if (type.isArrayType()) {
return (JsonDeserializer<Object>)_factory.createArrayDeserializer(config, this,
(ArrayType) type, property);
}
if (type.isMapLikeType()) {
MapLikeType mlt = (MapLikeType) type;
if (mlt.isTrueMapType()) {
return (JsonDeserializer<Object>)_factory.createMapDeserializer(config, this,
(MapType) mlt, property);
}
return (JsonDeserializer<Object>)_factory.createMapLikeDeserializer(config, this,
mlt, property);
}
if (type.isCollectionLikeType()) {
CollectionLikeType clt = (CollectionLikeType) type;
if (clt.isTrueCollectionType()) {
return (JsonDeserializer<Object>)_factory.createCollectionDeserializer(config, this,
(CollectionType) clt, property);
}
return (JsonDeserializer<Object>)_factory.createCollectionLikeDeserializer(config, this,
clt, property);
}
}
// 02-Mar-2009, tatu: Let's consider JsonNode to be a type of its own
if (JsonNode.class.isAssignableFrom(type.getRawClass())) {
return (JsonDeserializer<Object>)_factory.createTreeDeserializer(config, this, type, property);
}
return (JsonDeserializer<Object>)_factory.createBeanDeserializer(config, this, type, property);
}
protected void _resolveDeserializer(DeserializationConfig config, ResolvableDeserializer ser)
throws JsonMappingException
{
ser.resolve(config, this);
}
/*
/**********************************************************
/* Overridable error reporting methods
/**********************************************************
*/
protected JsonDeserializer<Object> _handleUnknownValueDeserializer(JavaType type)
throws JsonMappingException
{
// Let's try to figure out the reason, to give better error
Class<?> rawClass = type.getRawClass();
if (!ClassUtil.isConcrete(rawClass)) {
throw new JsonMappingException("Can not find a Value deserializer for abstract type "+type);
}
throw new JsonMappingException("Can not find a Value deserializer for type "+type);
}
protected KeyDeserializer _handleUnknownKeyDeserializer(JavaType type)
throws JsonMappingException
{
throw new JsonMappingException("Can not find a (Map) Key deserializer for type "+type);
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Simple deserializer that will call configured type deserializer, passing
* in configured data deserializer, and exposing it all as a simple
* deserializer.
*/
protected final static class WrappedDeserializer
extends JsonDeserializer<Object>
{
final TypeDeserializer _typeDeserializer;
final JsonDeserializer<Object> _deserializer;
public WrappedDeserializer(TypeDeserializer typeDeser, JsonDeserializer<Object> deser)
{
super();
_typeDeserializer = typeDeser;
_deserializer = deser;
}
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _deserializer.deserializeWithType(jp, ctxt, _typeDeserializer);
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// should never happen? (if it can, could call on that object)
throw new IllegalStateException("Type-wrapped deserializer's deserializeWithType should never get called");
}
}
}