com.fasterxml.jackson.databind.deser.BasicDeserializerFactory Maven / Gradle / Ivy
Show all versions of jersey-all Show documentation
package com.fasterxml.jackson.databind.deser;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
import com.fasterxml.jackson.databind.deser.std.*;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.*;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.EnumResolver;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;
/**
* Abstract factory base class that can provide deserializers for standard
* JDK classes, including collection classes and simple heuristics for
* "upcasting" common collection interface types
* (such as {@link java.util.Collection}).
*
* Since all simple deserializers are eagerly instantiated, and there is
* no additional introspection or customizability of these types,
* this factory is stateless.
*/
@SuppressWarnings("serial")
public abstract class BasicDeserializerFactory
extends DeserializerFactory
implements java.io.Serializable
{
private final static Class> CLASS_OBJECT = Object.class;
private final static Class> CLASS_STRING = String.class;
private final static Class> CLASS_CHAR_BUFFER = CharSequence.class;
private final static Class> CLASS_ITERABLE = Iterable.class;
/**
* We need a placeholder for creator properties that don't have name
* but are marked with `@JsonWrapped` annotation.
*/
protected final static PropertyName UNWRAPPED_CREATOR_PARAM_NAME = new PropertyName("@JsonUnwrapped");
/* We do some defaulting for abstract Map classes and
* interfaces, to avoid having to use exact types or annotations in
* cases where the most common concrete Maps will do.
*/
@SuppressWarnings("rawtypes")
final static HashMap> _mapFallbacks =
new HashMap>();
static {
_mapFallbacks.put(Map.class.getName(), LinkedHashMap.class);
_mapFallbacks.put(ConcurrentMap.class.getName(), ConcurrentHashMap.class);
_mapFallbacks.put(SortedMap.class.getName(), TreeMap.class);
/* 11-Jan-2009, tatu: Let's see if we can still add support for
* JDK 1.6 interfaces, even if we run on 1.5. Just need to be
* more careful with typos, since compiler won't notice any
* problems...
*/
_mapFallbacks.put("java.util.NavigableMap", TreeMap.class);
try {
Class> key = java.util.concurrent.ConcurrentNavigableMap.class;
Class> value = java.util.concurrent.ConcurrentSkipListMap.class;
@SuppressWarnings("unchecked")
Class extends Map,?>> mapValue = (Class extends Map,?>>) value;
_mapFallbacks.put(key.getName(), mapValue);
} catch (Throwable e) { // some class loading problems are Errors, others Exceptions
System.err.println("Problems with (optional) types: "+e);
}
}
/* We do some defaulting for abstract Collection classes and
* interfaces, to avoid having to use exact types or annotations in
* cases where the most common concrete Collection will do.
*/
@SuppressWarnings("rawtypes")
final static HashMap> _collectionFallbacks =
new HashMap>();
static {
_collectionFallbacks.put(Collection.class.getName(), ArrayList.class);
_collectionFallbacks.put(List.class.getName(), ArrayList.class);
_collectionFallbacks.put(Set.class.getName(), HashSet.class);
_collectionFallbacks.put(SortedSet.class.getName(), TreeSet.class);
_collectionFallbacks.put(Queue.class.getName(), LinkedList.class);
// then 1.6 types:
/* 17-May-2013, tatu: [Issue#216] Should be fine to use straight Class references EXCEPT
* that some godforsaken platforms (... looking at you, Android) do not
* include these. So, use "soft" references...
*/
_collectionFallbacks.put("java.util.Deque", LinkedList.class);
_collectionFallbacks.put("java.util.NavigableSet", TreeSet.class);
}
/*
/**********************************************************
/* Config
/**********************************************************
*/
/**
* Configuration settings for this factory; immutable instance (just like this
* factory), new version created via copy-constructor (fluent-style)
*/
protected final DeserializerFactoryConfig _factoryConfig;
/*
/**********************************************************
/* Life cycle
/**********************************************************
*/
protected BasicDeserializerFactory(DeserializerFactoryConfig config) {
_factoryConfig = config;
}
/**
* Method for getting current {@link DeserializerFactoryConfig}.
*
* Note that since instances are immutable, you can NOT change settings
* by accessing an instance and calling methods: this will simply create
* new instance of config object.
*/
public DeserializerFactoryConfig getFactoryConfig() {
return _factoryConfig;
}
protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config);
/*
/********************************************************
/* Configuration handling: fluent factories
/********************************************************
*/
/**
* Convenience method for creating a new factory instance with additional deserializer
* provider.
*/
@Override
public final DeserializerFactory withAdditionalDeserializers(Deserializers additional) {
return withConfig(_factoryConfig.withAdditionalDeserializers(additional));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link KeyDeserializers}.
*/
@Override
public final DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional) {
return withConfig(_factoryConfig.withAdditionalKeyDeserializers(additional));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link BeanDeserializerModifier}.
*/
@Override
public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier) {
return withConfig(_factoryConfig.withDeserializerModifier(modifier));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link AbstractTypeResolver}.
*/
@Override
public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) {
return withConfig(_factoryConfig.withAbstractTypeResolver(resolver));
}
/**
* Convenience method for creating a new factory instance with additional
* {@link ValueInstantiators}.
*/
@Override
public final DeserializerFactory withValueInstantiators(ValueInstantiators instantiators) {
return withConfig(_factoryConfig.withValueInstantiators(instantiators));
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): type mappings
/**********************************************************
*/
@Override
public JavaType mapAbstractType(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
// first, general mappings
while (true) {
JavaType next = _mapAbstractType2(config, type);
if (next == null) {
return type;
}
/* Should not have to worry about cycles; but better verify since they will invariably
* occur... :-)
* (also: guard against invalid resolution to a non-related type)
*/
Class> prevCls = type.getRawClass();
Class> nextCls = next.getRawClass();
if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) {
throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former");
}
type = next;
}
}
/**
* Method that will find abstract type mapping for specified type, doing a single
* lookup through registered abstract type resolvers; will not do recursive lookups.
*/
private JavaType _mapAbstractType2(DeserializationConfig config, JavaType type)
throws JsonMappingException
{
Class> currClass = type.getRawClass();
if (_factoryConfig.hasAbstractTypeResolvers()) {
for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) {
JavaType concrete = resolver.findTypeMapping(config, type);
if (concrete != null && concrete.getRawClass() != currClass) {
return concrete;
}
}
}
return null;
}
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): ValueInstantiators
/**********************************************************
*/
/**
* Value instantiator is created both based on creator annotations,
* and on optional externally provided instantiators (registered through
* module interface).
*/
@Override
public ValueInstantiator findValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
ValueInstantiator instantiator = null;
// [JACKSON-633] Check @JsonValueInstantiator before anything else
AnnotatedClass ac = beanDesc.getClassInfo();
Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ac);
if (instDef != null) {
instantiator = _valueInstantiatorInstance(config, ac, instDef);
}
if (instantiator == null) {
/* Second: see if some of standard Jackson/JDK types might provide value
* instantiators.
*/
instantiator = _findStdValueInstantiator(config, beanDesc);
if (instantiator == null) {
instantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
}
}
// finally: anyone want to modify ValueInstantiator?
if (_factoryConfig.hasValueInstantiators()) {
for (ValueInstantiators insts : _factoryConfig.valueInstantiators()) {
instantiator = insts.findValueInstantiator(config, beanDesc, instantiator);
// let's do sanity check; easier to spot buggy handlers
if (instantiator == null) {
throw new JsonMappingException("Broken registered ValueInstantiators (of type "
+insts.getClass().getName()+"): returned null ValueInstantiator");
}
}
}
// Sanity check: does the chosen instantatior have incomplete creators?
if (instantiator.getIncompleteParameter() != null) {
final AnnotatedParameter nonAnnotatedParam = instantiator.getIncompleteParameter();
final AnnotatedWithParams ctor = nonAnnotatedParam.getOwner();
throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()+" of constructor "+ctor+" has no property name annotation; must have name when multiple-paramater constructor annotated as Creator");
}
return instantiator;
}
private ValueInstantiator _findStdValueInstantiator(DeserializationConfig config,
BeanDescription beanDesc)
throws JsonMappingException
{
if (beanDesc.getBeanClass() == JsonLocation.class) {
return JsonLocationInstantiator.instance;
}
return null;
}
/**
* Method that will construct standard default {@link ValueInstantiator}
* using annotations (like @JsonCreator) and visibility rules
*/
protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
boolean fixAccess = ctxt.canOverrideAccessModifiers();
CreatorCollector creators = new CreatorCollector(beanDesc, fixAccess);
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
// need to construct suitable visibility checker:
final DeserializationConfig config = ctxt.getConfig();
VisibilityChecker> vchecker = config.getDefaultVisibilityChecker();
vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker);
/* Important: first add factory methods; then constructors, so
* latter can override former!
*/
_addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators);
// constructors only usable on concrete types:
if (beanDesc.getType().isConcrete()) {
_addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators);
}
return creators.constructValueInstantiator(config);
}
public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,
Annotated annotated, Object instDef)
throws JsonMappingException
{
if (instDef == null) {
return null;
}
ValueInstantiator inst;
if (instDef instanceof ValueInstantiator) {
return (ValueInstantiator) instDef;
}
if (!(instDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+instDef.getClass().getName()
+"; expected type KeyDeserializer or Class instead");
}
Class> instClass = (Class>)instDef;
if (instClass == NoClass.class) {
return null;
}
if (!ValueInstantiator.class.isAssignableFrom(instClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "+instClass.getName()
+"; expected Class");
}
HandlerInstantiator hi = config.getHandlerInstantiator();
if (hi != null) {
inst = hi.valueInstantiatorInstance(config, annotated, instClass);
if (inst != null) {
return inst;
}
}
return (ValueInstantiator) ClassUtil.createInstance(instClass,
config.canOverrideAccessModifiers());
}
protected void _addDeserializerConstructors
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker> vchecker,
AnnotationIntrospector intr, CreatorCollector creators)
throws JsonMappingException
{
/* First things first: the "default constructor" (zero-arg
* constructor; whether implicit or explicit) is NOT included
* in list of constructors, so needs to be handled separately.
*/
AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
if (defaultCtor != null) {
if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) {
creators.setDefaultCreator(defaultCtor);
}
}
PropertyName[] ctorPropNames = null;
AnnotatedConstructor propertyCtor = null;
for (BeanPropertyDefinition propDef : beanDesc.findProperties()) {
if (propDef.getConstructorParameter() != null) {
AnnotatedParameter param = propDef.getConstructorParameter();
AnnotatedWithParams owner = param.getOwner();
if (owner instanceof AnnotatedConstructor) {
if (propertyCtor == null) {
propertyCtor = (AnnotatedConstructor) owner;
ctorPropNames = new PropertyName[propertyCtor.getParameterCount()];
}
ctorPropNames[param.getIndex()] = propDef.getFullName();
}
}
}
for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
int argCount = ctor.getParameterCount();
boolean isCreator = intr.hasCreatorAnnotation(ctor) || ctor == propertyCtor;
boolean isVisible = vchecker.isCreatorVisible(ctor);
// some single-arg constructors (String, number) are auto-detected
if (argCount == 1) {
PropertyName name = (ctor == propertyCtor) ? ctorPropNames[0] : null;
_handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
ctor, isCreator, isVisible, name);
continue;
}
if (!isCreator && !isVisible) {
continue;
}
// [JACKSON-541] improved handling a bit so:
// 2 or more args; all params must have name annotations
// ... or @JacksonInject (or equivalent)
/* [JACKSON-711] One more possibility; can have 1 or more injectables, and
* exactly one non-annotated parameter: if so, it's still delegating.
*/
AnnotatedParameter nonAnnotatedParam = null;
int namedCount = 0;
int injectCount = 0;
CreatorProperty[] properties = new CreatorProperty[argCount];
for (int i = 0; i < argCount; ++i) {
AnnotatedParameter param = ctor.getParameter(i);
PropertyName name = null;
if (ctor == propertyCtor) {
name = ctorPropNames[i];
}
if (name == null) {
name = (param == null) ? null : intr.findNameForDeserialization(param);
}
Object injectId = intr.findInjectableValueId(param);
if (name != null && name.hasSimpleName()) {
++namedCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (injectId != null) { // injectable
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else {
NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
if (unwrapper != null) { // [Issue#265]: allow unwrapped properties
properties[i] = constructCreatorProperty(ctxt, beanDesc,
UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
++namedCount;
} else {
if (nonAnnotatedParam == null) {
nonAnnotatedParam = param;
}
}
}
}
// Ok: if named or injectable, we have more work to do
if (isCreator || namedCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(ctor, properties);
} else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(ctor, properties);
} else { // otherwise, record the incomplete parameter for later error messaging.
creators.addIncompeteParameter(nonAnnotatedParam);
}
}
}
}
protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
BeanDescription beanDesc, VisibilityChecker> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
AnnotatedConstructor ctor, boolean isCreator, boolean isVisible,
PropertyName name)
throws JsonMappingException
{
// note: if we do have parameter name, it'll be "property constructor":
AnnotatedParameter param = ctor.getParameter(0);
if (name == null) {
name = (param == null) ? null : intr.findNameForDeserialization(param);
}
Object injectId = intr.findInjectableValueId(param);
if ((injectId != null) || (name != null && name.hasSimpleName())) { // property-based
// We know there's a name and it's only 1 parameter.
CreatorProperty[] properties = new CreatorProperty[1];
properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, param, injectId);
creators.addPropertyCreator(ctor, properties);
return true;
}
// otherwise either 'simple' number, String, or general delegate:
Class> type = ctor.getRawParameterType(0);
if (type == String.class) {
if (isCreator || isVisible) {
creators.addStringCreator(ctor);
}
return true;
}
if (type == int.class || type == Integer.class) {
if (isCreator || isVisible) {
creators.addIntCreator(ctor);
}
return true;
}
if (type == long.class || type == Long.class) {
if (isCreator || isVisible) {
creators.addLongCreator(ctor);
}
return true;
}
if (type == double.class || type == Double.class) {
if (isCreator || isVisible) {
creators.addDoubleCreator(ctor);
}
return true;
}
// Delegating Creator ok iff it has @JsonCreator (etc)
if (isCreator) {
creators.addDelegatingCreator(ctor, null);
return true;
}
return false;
}
protected void _addDeserializerFactoryMethods
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker> vchecker,
AnnotationIntrospector intr, CreatorCollector creators)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
boolean isCreator = intr.hasCreatorAnnotation(factory);
int argCount = factory.getParameterCount();
// zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850]
if (argCount == 0) {
if (isCreator) {
creators.setDefaultCreator(factory);
}
continue;
}
// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
AnnotatedParameter param = factory.getParameter(0);
PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
String name = (pn == null) ? null : pn.getSimpleName();
Object injectId = intr.findInjectableValueId(param);
if ((injectId == null) && (name == null || name.length() == 0)) { // not property based
_handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
factory, isCreator);
// otherwise just ignored
continue;
}
// fall through if there's name
} else {
// more than 2 args, must be @JsonCreator
if (!intr.hasCreatorAnnotation(factory)) {
continue;
}
}
// 1 or more args; all params must have name annotations
AnnotatedParameter nonAnnotatedParam = null;
CreatorProperty[] properties = new CreatorProperty[argCount];
int namedCount = 0;
int injectCount = 0;
for (int i = 0; i < argCount; ++i) {
AnnotatedParameter param = factory.getParameter(i);
PropertyName name = (param == null) ? null : intr.findNameForDeserialization(param);
Object injectId = intr.findInjectableValueId(param);
if (name != null && name.hasSimpleName()) {
++namedCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else if (injectId != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
} else {
NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
if (unwrapper != null) {
properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
++namedCount;
} else {
if (nonAnnotatedParam == null) {
nonAnnotatedParam = param;
}
}
}
}
// Ok: if named or injectable, we have more work to do
if (isCreator || namedCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(factory, properties);
} else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(factory, properties);
} else { // otherwise, epic fail
throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()
+" of factory method "+factory+" has no property name annotation; must have name when multiple-paramater constructor annotated as Creator");
}
}
}
}
protected boolean _handleSingleArgumentFactory(DeserializationConfig config,
BeanDescription beanDesc, VisibilityChecker> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
AnnotatedMethod factory, boolean isCreator)
throws JsonMappingException
{
Class> type = factory.getRawParameterType(0);
if (type == String.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addStringCreator(factory);
}
return true;
}
if (type == int.class || type == Integer.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addIntCreator(factory);
}
return true;
}
if (type == long.class || type == Long.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addLongCreator(factory);
}
return true;
}
if (type == double.class || type == Double.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addDoubleCreator(factory);
}
return true;
}
if (type == boolean.class || type == Boolean.class) {
if (isCreator || vchecker.isCreatorVisible(factory)) {
creators.addBooleanCreator(factory);
}
return true;
}
if (intr.hasCreatorAnnotation(factory)) {
creators.addDelegatingCreator(factory, null);
return true;
}
return false;
}
/**
* Method that will construct a property object that represents
* a logical property passed via Creator (constructor or static
* factory method)
*/
protected CreatorProperty constructCreatorProperty(DeserializationContext ctxt,
BeanDescription beanDesc, PropertyName name, int index,
AnnotatedParameter param,
Object injectableValueId)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
PropertyMetadata metadata;
{
Boolean b = (intr == null) ? null : intr.hasRequiredMarker(param);
boolean req = (b != null && b.booleanValue());
String desc = (intr == null) ? null : intr.findPropertyDescription(param);
metadata = PropertyMetadata.construct(req, desc);
}
JavaType t0 = config.getTypeFactory().constructType(param.getParameterType(), beanDesc.bindingsForBeanType());
BeanProperty.Std property = new BeanProperty.Std(name, t0,
intr.findWrapperName(param),
beanDesc.getClassAnnotations(), param, metadata);
JavaType type = resolveType(ctxt, beanDesc, t0, param);
if (type != t0) {
property = property.withType(type);
}
// Is there an annotation that specifies exact deserializer?
JsonDeserializer