org.apache.juli.logging.com.fasterxml.jackson.databind.deser.BasicDeserializerFactory Maven / Gradle / Ivy
package org.apache.juli.logging.com.fasterxml.jackson.databind.deser;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.juli.logging.com.fasterxml.jackson.annotation.*;
import org.apache.juli.logging.com.fasterxml.jackson.core.JsonParser;
import org.apache.juli.logging.com.fasterxml.jackson.databind.*;
import org.apache.juli.logging.com.fasterxml.jackson.databind.cfg.ConfigOverride;
import org.apache.juli.logging.com.fasterxml.jackson.databind.cfg.ConstructorDetector;
import org.apache.juli.logging.com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import org.apache.juli.logging.com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import org.apache.juli.logging.com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
import org.apache.juli.logging.com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
import org.apache.juli.logging.com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators;
import org.apache.juli.logging.com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
import org.apache.juli.logging.com.fasterxml.jackson.databind.deser.std.*;
import org.apache.juli.logging.com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import org.apache.juli.logging.com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import org.apache.juli.logging.com.fasterxml.jackson.databind.introspect.*;
import org.apache.juli.logging.com.fasterxml.jackson.databind.jsontype.NamedType;
import org.apache.juli.logging.com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import org.apache.juli.logging.com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import org.apache.juli.logging.com.fasterxml.jackson.databind.type.*;
import org.apache.juli.logging.com.fasterxml.jackson.databind.util.*;
/**
* 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_SEQUENCE = CharSequence.class;
private final static Class CLASS_ITERABLE = Iterable.class;
private final static Class CLASS_MAP_ENTRY = Map.Entry.class;
private final static Class CLASS_SERIALIZABLE = Serializable.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");
/*
/**********************************************************
/* 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));
}
/*
/**********************************************************
/* DeserializerFactory 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.hasRawClass(currClass)) {
return concrete;
}
}
}
return null;
}
/*
/**********************************************************
/* DeserializerFactory 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;
// 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 = JDKValueInstantiators.findStdValueInstantiator(config, beanDesc.getBeanClass());
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) {
ctxt.reportBadTypeDefinition(beanDesc,
"Broken registered ValueInstantiators (of type %s): returned null ValueInstantiator",
insts.getClass().getName());
}
}
}
if (instantiator != null) {
instantiator = instantiator.createContextual(ctxt, beanDesc);
}
return instantiator;
}
/**
* Method that will construct standard default {@link ValueInstantiator}
* using annotations (like @JsonCreator) and visibility rules
*/
protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
final CreatorCollectionState ccState;
final ConstructorDetector ctorDetector;
{
final DeserializationConfig config = ctxt.getConfig();
// need to construct suitable visibility checker:
final VisibilityChecker vchecker = config.getDefaultVisibilityChecker(beanDesc.getBeanClass(),
beanDesc.getClassInfo());
ctorDetector = config.getConstructorDetector();
// 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information
// (which has creator parameters sprinkled around) with actual creator
// declarations (which are needed to access creator annotation, amongst other things).
// Easiest to combine that info first, then pass it to remaining processing.
// 15-Mar-2015, tatu: Alas, this won't help with constructors that only have implicit
// names. Those will need to be resolved later on.
final CreatorCollector creators = new CreatorCollector(beanDesc, config);
Map creatorDefs = _findCreatorsFromProperties(ctxt,
beanDesc);
ccState = new CreatorCollectionState(ctxt, beanDesc, vchecker,
creators, creatorDefs);
}
// Start with explicitly annotated factory methods
_addExplicitFactoryCreators(ctxt, ccState, !ctorDetector.requireCtorAnnotation());
// constructors only usable on concrete types:
if (beanDesc.getType().isConcrete()) {
// 25-Jan-2017, tatu: As per [databind#1501], [databind#1502], [databind#1503], best
// for now to skip attempts at using anything but no-args constructor (see
// `InnerClassProperty` construction for that)
final boolean isNonStaticInnerClass = beanDesc.isNonStaticInnerClass();
if (isNonStaticInnerClass) {
// TODO: look for `@JsonCreator` annotated ones, throw explicit exception?
;
} else {
// 18-Sep-2020, tatu: Although by default implicit introspection is allowed, 2.12
// has settings to prevent that either generally, or at least for JDK types
final boolean findImplicit = ctorDetector.shouldIntrospectorImplicitConstructors(beanDesc.getBeanClass());
_addExplicitConstructorCreators(ctxt, ccState, findImplicit);
if (ccState.hasImplicitConstructorCandidates()
// 05-Dec-2020, tatu: [databind#2962] explicit annotation of
// a factory should not block implicit constructor, for backwards
// compatibility (minor regression in 2.12.0)
//&& !ccState.hasExplicitFactories()
// ... explicit constructor should prevent, however
&& !ccState.hasExplicitConstructors()) {
_addImplicitConstructorCreators(ctxt, ccState, ccState.implicitConstructorCandidates());
}
}
}
// and finally, implicitly found factory methods if nothing explicit found
if (ccState.hasImplicitFactoryCandidates()
&& !ccState.hasExplicitFactories() && !ccState.hasExplicitConstructors()) {
_addImplicitFactoryCreators(ctxt, ccState, ccState.implicitFactoryCandidates());
}
return ccState.creators.constructValueInstantiator(ctxt);
}
protected Map _findCreatorsFromProperties(DeserializationContext ctxt,
BeanDescription beanDesc) throws JsonMappingException
{
Map result = Collections.emptyMap();
for (BeanPropertyDefinition propDef : beanDesc.findProperties()) {
Iterator it = propDef.getConstructorParameters();
while (it.hasNext()) {
AnnotatedParameter param = it.next();
AnnotatedWithParams owner = param.getOwner();
BeanPropertyDefinition[] defs = result.get(owner);
final int index = param.getIndex();
if (defs == null) {
if (result.isEmpty()) { // since emptyMap is immutable need to create a 'real' one
result = new LinkedHashMap();
}
defs = new BeanPropertyDefinition[owner.getParameterCount()];
result.put(owner, defs);
} else {
if (defs[index] != null) {
ctxt.reportBadTypeDefinition(beanDesc,
"Conflict: parameter #%d of %s bound to more than one property; %s vs %s",
index, owner, defs[index], propDef);
}
}
defs[index] = propDef;
}
}
return result;
}
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 (ClassUtil.isBogusClass(instClass)) {
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());
}
/*
/**********************************************************************
/* Creator introspection: Record introspection (Jackson 2.12+, Java 14+)
/**********************************************************************
*/
/**
* Helper method called when a {@code java.lang.Record} definition's "canonical"
* constructor is to be used: if so, we have implicit names to consider.
*
* @since 2.12
* @deprecated since 2.15 - no longer used, but kept because this protected method might have been overridden/used
* elsewhere
*/
@Deprecated
protected void _addRecordConstructor(DeserializationContext ctxt, CreatorCollectionState ccState,
AnnotatedConstructor canonical, List implicitNames)
throws JsonMappingException
{
final int argCount = canonical.getParameterCount();
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
final SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
for (int i = 0; i < argCount; ++i) {
final AnnotatedParameter param = canonical.getParameter(i);
JacksonInject.Value injectable = intr.findInjectableValue(param);
PropertyName name = intr.findNameForDeserialization(param);
if (name == null || name.isEmpty()) {
name = PropertyName.construct(implicitNames.get(i));
}
properties[i] = constructCreatorProperty(ctxt, ccState.beanDesc, name, i, param, injectable);
}
ccState.creators.addPropertyCreator(canonical, false, properties);
}
/*
/**********************************************************************
/* Creator introspection: constructor creator introspection
/**********************************************************************
*/
protected void _addExplicitConstructorCreators(DeserializationContext ctxt,
CreatorCollectionState ccState, boolean findImplicit)
throws JsonMappingException
{
final BeanDescription beanDesc = ccState.beanDesc;
final CreatorCollector creators = ccState.creators;
final AnnotationIntrospector intr = ccState.annotationIntrospector();
final VisibilityChecker vchecker = ccState.vchecker;
final Map creatorParams = ccState.creatorParams;
// 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() || _hasCreatorAnnotation(ctxt, defaultCtor)) {
creators.setDefaultCreator(defaultCtor);
}
}
// 21-Sep-2017, tatu: First let's handle explicitly annotated ones
for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), ctor);
if (JsonCreator.Mode.DISABLED == creatorMode) {
continue;
}
if (creatorMode == null) {
// let's check Visibility here, to avoid further processing for non-visible?
if (findImplicit && vchecker.isCreatorVisible(ctor)) {
ccState.addImplicitConstructorCandidate(CreatorCandidate.construct(intr,
ctor, creatorParams.get(ctor)));
}
continue;
}
switch (creatorMode) {
case DELEGATING:
_addExplicitDelegatingCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, ctor, null));
break;
case PROPERTIES:
_addExplicitPropertyCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor)));
break;
default:
_addExplicitAnyCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor)),
ctxt.getConfig().getConstructorDetector());
break;
}
ccState.increaseExplicitConstructorCount();
}
}
protected void _addImplicitConstructorCreators(DeserializationContext ctxt,
CreatorCollectionState ccState, List ctorCandidates)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
final BeanDescription beanDesc = ccState.beanDesc;
final CreatorCollector creators = ccState.creators;
final AnnotationIntrospector intr = ccState.annotationIntrospector();
final VisibilityChecker vchecker = ccState.vchecker;
List implicitCtors = null;
final boolean preferPropsBased = config.getConstructorDetector().singleArgCreatorDefaultsToProperties()
// [databind#3968]: Only Record's canonical constructor is allowed
// to be considered for properties-based creator to avoid failure
&& !beanDesc.isRecordType();
for (CreatorCandidate candidate : ctorCandidates) {
final int argCount = candidate.paramCount();
final AnnotatedWithParams ctor = candidate.creator();
// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
final BeanPropertyDefinition propDef = candidate.propertyDef(0);
final boolean useProps = preferPropsBased
|| _checkIfCreatorPropertyBased(beanDesc, intr, ctor, propDef);
if (useProps) {
SettableBeanProperty[] properties = new SettableBeanProperty[1];
final JacksonInject.Value injection = candidate.injection(0);
// 18-Sep-2020, tatu: [databind#1498] looks like implicit name not linked
// unless annotation found, so try again from here
PropertyName name = candidate.paramName(0);
if (name == null) {
name = candidate.findImplicitParamName(0);
if ((name == null) && (injection == null)) {
continue;
}
}
properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0,
candidate.parameter(0), injection);
creators.addPropertyCreator(ctor, false, properties);
} else {
/*boolean added = */ _handleSingleArgumentCreator(creators,
ctor, false,
vchecker.isCreatorVisible(ctor));
// one more thing: sever link to creator property, to avoid possible later
// problems with "unresolved" constructor property
if (propDef != null) {
((POJOPropertyBuilder) propDef).removeConstructors();
}
}
// regardless, fully handled
continue;
}
// 2 or more args; all params must have names or be injectable
// 14-Mar-2015, tatu (2.6): Or, as per [#725], implicit names will also
// do, with some constraints. But that will require bit post processing...
int nonAnnotatedParamIndex = -1;
SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
int explicitNameCount = 0;
int implicitWithCreatorCount = 0;
int injectCount = 0;
for (int i = 0; i < argCount; ++i) {
final AnnotatedParameter param = ctor.getParameter(i);
BeanPropertyDefinition propDef = candidate.propertyDef(i);
JacksonInject.Value injectable = intr.findInjectableValue(param);
final PropertyName name = (propDef == null) ? null : propDef.getFullName();
if ((propDef != null)
// [databind#3724]: Record canonical constructor will have implicitly named propDef
&& (propDef.isExplicitlyNamed() || beanDesc.isRecordType())) {
++explicitNameCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
continue;
}
if (injectable != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
continue;
}
NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
if (unwrapper != null) {
_reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
/*
properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
++explicitNameCount;
*/
continue;
}
// One more thing: implicit names are ok iff ctor has creator annotation
/*
if (isCreator && (name != null && !name.isEmpty())) {
++implicitWithCreatorCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
continue;
}
*/
if (nonAnnotatedParamIndex < 0) {
nonAnnotatedParamIndex = i;
}
}
final int namedCount = explicitNameCount + implicitWithCreatorCount;
// Ok: if named or injectable, we have more work to do
if ((explicitNameCount > 0) || (injectCount > 0)) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(ctor, false, properties);
continue;
}
if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
// Secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(ctor, false, properties, 0);
continue;
}
// otherwise, epic fail?
// 16-Mar-2015, tatu: due to [#725], need to be more permissive. For now let's
// only report problem if there's no implicit name
PropertyName impl = candidate.findImplicitParamName(nonAnnotatedParamIndex);
if (impl == null || impl.isEmpty()) {
// Let's consider non-static inner class as a special case...
// 25-Jan-2017, tatu: Non-static inner classes skipped altogether, now
/*
if ((nonAnnotatedParamIndex == 0) && isNonStaticInnerClass) {
throw new IllegalArgumentException("Non-static inner classes like "
+ctor.getDeclaringClass().getName()+" cannot use @JsonCreator for constructors");
}
*/
ctxt.reportBadTypeDefinition(beanDesc,
"Argument #%d of constructor %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator",
nonAnnotatedParamIndex, ctor);
}
}
// [#725]: as a fallback, all-implicit names may work as well
if (!creators.hasDefaultCreator()) {
if (implicitCtors == null) {
implicitCtors = new LinkedList<>();
}
implicitCtors.add(ctor);
}
}
// last option, as per [#725]: consider implicit-names-only, visible constructor,
// if just one found
if ((implicitCtors != null) && !creators.hasDelegatingCreator()
&& !creators.hasPropertyBasedCreator()) {
_checkImplicitlyNamedConstructors(ctxt, beanDesc, vchecker, intr,
creators, implicitCtors);
}
}
/*
/**********************************************************************
/* Creator introspection: factory creator introspection
/**********************************************************************
*/
protected void _addExplicitFactoryCreators(DeserializationContext ctxt,
CreatorCollectionState ccState, boolean findImplicit)
throws JsonMappingException
{
final BeanDescription beanDesc = ccState.beanDesc;
final CreatorCollector creators = ccState.creators;
final AnnotationIntrospector intr = ccState.annotationIntrospector();
final VisibilityChecker vchecker = ccState.vchecker;
final Map creatorParams = ccState.creatorParams;
// 21-Sep-2017, tatu: First let's handle explicitly annotated ones
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), factory);
final int argCount = factory.getParameterCount();
if (creatorMode == null) {
// Only potentially accept 1-argument factory methods
if (findImplicit && (argCount == 1) && vchecker.isCreatorVisible(factory)) {
ccState.addImplicitFactoryCandidate(CreatorCandidate.construct(intr, factory, null));
}
continue;
}
if (creatorMode == JsonCreator.Mode.DISABLED) {
continue;
}
// zero-arg method factory methods fine, as long as explicit
if (argCount == 0) {
creators.setDefaultCreator(factory);
continue;
}
switch (creatorMode) {
case DELEGATING:
_addExplicitDelegatingCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, factory, null));
break;
case PROPERTIES:
_addExplicitPropertyCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, factory, creatorParams.get(factory)));
break;
case DEFAULT:
default:
_addExplicitAnyCreator(ctxt, beanDesc, creators,
CreatorCandidate.construct(intr, factory, creatorParams.get(factory)),
// 13-Sep-2020, tatu: Factory methods do not follow config settings
// (as of Jackson 2.12)
ConstructorDetector.DEFAULT);
break;
}
ccState.increaseExplicitFactoryCount();
}
}
protected void _addImplicitFactoryCreators(DeserializationContext ctxt,
CreatorCollectionState ccState, List factoryCandidates)
throws JsonMappingException
{
final BeanDescription beanDesc = ccState.beanDesc;
final CreatorCollector creators = ccState.creators;
final AnnotationIntrospector intr = ccState.annotationIntrospector();
final VisibilityChecker vchecker = ccState.vchecker;
final Map creatorParams = ccState.creatorParams;
// And then implicitly found
for (CreatorCandidate candidate : factoryCandidates) {
final int argCount = candidate.paramCount();
AnnotatedWithParams factory = candidate.creator();
final BeanPropertyDefinition[] propDefs = creatorParams.get(factory);
// some single-arg factory methods (String, number) are auto-detected
if (argCount != 1) {
continue; // 2 and more args? Must be explicit, handled earlier
}
BeanPropertyDefinition argDef = candidate.propertyDef(0);
boolean useProps = _checkIfCreatorPropertyBased(beanDesc, intr, factory, argDef);
if (!useProps) { // not property based but delegating
/*boolean added=*/ _handleSingleArgumentCreator(creators,
factory, false, vchecker.isCreatorVisible(factory));
// 23-Sep-2016, tatu: [databind#1383]: Need to also sever link to avoid possible
// later problems with "unresolved" constructor property
if (argDef != null) {
((POJOPropertyBuilder) argDef).removeConstructors();
}
continue;
}
AnnotatedParameter nonAnnotatedParam = null;
SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
int implicitNameCount = 0;
int explicitNameCount = 0;
int injectCount = 0;
for (int i = 0; i < argCount; ++i) {
final AnnotatedParameter param = factory.getParameter(i);
BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
JacksonInject.Value injectable = intr.findInjectableValue(param);
final PropertyName name = (propDef == null) ? null : propDef.getFullName();
if (propDef != null && propDef.isExplicitlyNamed()) {
++explicitNameCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
continue;
}
if (injectable != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
continue;
}
NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
if (unwrapper != null) {
_reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
/*
properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
++implicitNameCount;
*/
continue;
}
// One more thing: implicit names are ok iff ctor has creator annotation
/*
if (isCreator) {
if (name != null && !name.isEmpty()) {
++implicitNameCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
continue;
}
}
*/
/* 25-Sep-2014, tatu: Actually, we may end up "losing" naming due to higher-priority constructor
* (see TestCreators#testConstructorCreator() test). And just to avoid running into that problem,
* let's add one more work around
*/
/*
PropertyName name2 = _findExplicitParamName(param, intr);
if (name2 != null && !name2.isEmpty()) {
// Hmmh. Ok, fine. So what are we to do with it... ?
// For now... skip. May need to revisit this, should this become problematic
continue main_loop;
}
*/
if (nonAnnotatedParam == null) {
nonAnnotatedParam = param;
}
}
final int namedCount = explicitNameCount + implicitNameCount;
// Ok: if named or injectable, we have more work to do
if (explicitNameCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(factory, false, properties);
} else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
// secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(factory, false, properties, 0);
} else { // otherwise, epic fail
ctxt.reportBadTypeDefinition(beanDesc,
"Argument #%d of factory method %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator",
(nonAnnotatedParam == null) ? -1 : nonAnnotatedParam.getIndex(),
factory);
}
}
}
}
/*
/**********************************************************************
/* Creator introspection: helper methods
/**********************************************************************
*/
/**
* Helper method called when there is the explicit "is-creator" with mode of "delegating"
*
* @since 2.9.2
*/
protected void _addExplicitDelegatingCreator(DeserializationContext ctxt,
BeanDescription beanDesc, CreatorCollector creators,
CreatorCandidate candidate)
throws JsonMappingException
{
// Somewhat simple: find injectable values, if any, ensure there is one
// and just one delegated argument; report violations if any
int ix = -1;
final int argCount = candidate.paramCount();
SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
for (int i = 0; i < argCount; ++i) {
AnnotatedParameter param = candidate.parameter(i);
JacksonInject.Value injectId = candidate.injection(i);
if (injectId != null) {
properties[i] = constructCreatorProperty(ctxt, beanDesc, null, i, param, injectId);
continue;
}
if (ix < 0) {
ix = i;
continue;
}
// Illegal to have more than one value to delegate to
ctxt.reportBadTypeDefinition(beanDesc,
"More than one argument (#%d and #%d) left as delegating for Creator %s: only one allowed",
ix, i, candidate);
}
// Also, let's require that one Delegating argument does eixt
if (ix < 0) {
ctxt.reportBadTypeDefinition(beanDesc,
"No argument left as delegating for Creator %s: exactly one required", candidate);
}
// 17-Jan-2018, tatu: as per [databind#1853] need to ensure we will distinguish
// "well-known" single-arg variants (String, int/long, boolean) from "generic" delegating...
if (argCount == 1) {
_handleSingleArgumentCreator(creators, candidate.creator(), true, true);
// one more thing: sever link to creator property, to avoid possible later
// problems with "unresolved" constructor property
BeanPropertyDefinition paramDef = candidate.propertyDef(0);
if (paramDef != null) {
((POJOPropertyBuilder) paramDef).removeConstructors();
}
return;
}
creators.addDelegatingCreator(candidate.creator(), true, properties, ix);
}
/**
* Helper method called when there is the explicit "is-creator" annotation with mode
* of "properties-based"
*
* @since 2.9.2
*/
protected void _addExplicitPropertyCreator(DeserializationContext ctxt,
BeanDescription beanDesc, CreatorCollector creators,
CreatorCandidate candidate)
throws JsonMappingException
{
final int paramCount = candidate.paramCount();
SettableBeanProperty[] properties = new SettableBeanProperty[paramCount];
for (int i = 0; i < paramCount; ++i) {
JacksonInject.Value injectId = candidate.injection(i);
AnnotatedParameter param = candidate.parameter(i);
PropertyName name = candidate.paramName(i);
if (name == null) {
// 21-Sep-2017, tatu: Looks like we want to block accidental use of Unwrapped,
// as that will not work with Creators well at all
NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(param);
if (unwrapper != null) {
_reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
/*
properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
++explicitNameCount;
*/
}
name = candidate.findImplicitParamName(i);
_validateNamedPropertyParameter(ctxt, beanDesc, candidate, i,
name, injectId);
}
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
}
creators.addPropertyCreator(candidate.creator(), true, properties);
}
@Deprecated // since 2.12, remove from 2.13 or later
protected void _addExplicitAnyCreator(DeserializationContext ctxt,
BeanDescription beanDesc, CreatorCollector creators,
CreatorCandidate candidate)
throws JsonMappingException
{
_addExplicitAnyCreator(ctxt, beanDesc, creators, candidate,
ctxt.getConfig().getConstructorDetector());
}
/**
* Helper method called when there is explicit "is-creator" marker,
* but no mode declaration.
*
* @since 2.12
*/
protected void _addExplicitAnyCreator(DeserializationContext ctxt,
BeanDescription beanDesc, CreatorCollector creators,
CreatorCandidate candidate, ConstructorDetector ctorDetector)
throws JsonMappingException
{
// Looks like there's bit of magic regarding 1-parameter creators; others simpler:
if (1 != candidate.paramCount()) {
// Ok: for delegates, we want one and exactly one parameter without
// injection AND without name
// 13-Sep-2020, tatu: Can only be delegating if not forced to be properties-based
if (!ctorDetector.singleArgCreatorDefaultsToProperties()) {
int oneNotInjected = candidate.findOnlyParamWithoutInjection();
if (oneNotInjected >= 0) {
// getting close; but most not have name (or be explicitly specified
// as default-to-delegate)
if (ctorDetector.singleArgCreatorDefaultsToDelegating()
|| candidate.paramName(oneNotInjected) == null) {
_addExplicitDelegatingCreator(ctxt, beanDesc, creators, candidate);
return;
}
}
}
_addExplicitPropertyCreator(ctxt, beanDesc, creators, candidate);
return;
}
// And here be the "simple" single-argument construct
final AnnotatedParameter param = candidate.parameter(0);
final JacksonInject.Value injectId = candidate.injection(0);
PropertyName paramName = null;
boolean useProps;
switch (ctorDetector.singleArgMode()) {
case DELEGATING:
useProps = false;
break;
case PROPERTIES:
useProps = true;
// 13-Sep-2020, tatu: since we are configured to prefer Properties-style,
// any name (explicit OR implicit does):
paramName = candidate.paramName(0);
// [databind#2977]: Need better exception if name missing
if (paramName == null) {
_validateNamedPropertyParameter(ctxt, beanDesc, candidate, 0,
paramName, injectId);
}
break;
case REQUIRE_MODE:
ctxt.reportBadTypeDefinition(beanDesc,
"Single-argument constructor (%s) is annotated but no 'mode' defined; `CreatorDetector`"
+ "configured with `SingleArgConstructor.REQUIRE_MODE`",
candidate.creator());
return;
case HEURISTIC:
default:
{ // Note: behavior pre-Jackson-2.12
final BeanPropertyDefinition paramDef = candidate.propertyDef(0);
// with heuristic, need to start with just explicit name
paramName = candidate.explicitParamName(0);
// If there's injection or explicit name, should be properties-based
useProps = (paramName != null);
if (!useProps) {
// Otherwise, `@JsonValue` suggests delegation
if (beanDesc.findJsonValueAccessor() != null) {
;
} else if (injectId != null) {
// But Injection suggests property-based (for legacy reasons?)
useProps = true;
} else if (paramDef != null) {
// One more thing: if implicit name matches property with a getter
// or field, we'll consider it property-based as well
// 25-May-2018, tatu: as per [databind#2051], looks like we have to get
// not implicit name, but name with possible strategy-based-rename
// paramName = candidate.findImplicitParamName(0);
paramName = candidate.paramName(0);
useProps = (paramName != null) && paramDef.couldSerialize();
}
}
}
}
if (useProps) {
SettableBeanProperty[] properties = new SettableBeanProperty[] {
constructCreatorProperty(ctxt, beanDesc, paramName, 0, param, injectId)
};
creators.addPropertyCreator(candidate.creator(), true, properties);
return;
}
_handleSingleArgumentCreator(creators, candidate.creator(), true, true);
// one more thing: sever link to creator property, to avoid possible later
// problems with "unresolved" constructor property
final BeanPropertyDefinition paramDef = candidate.propertyDef(0);
if (paramDef != null) {
((POJOPropertyBuilder) paramDef).removeConstructors();
}
}
private boolean _checkIfCreatorPropertyBased(BeanDescription beanDesc,
AnnotationIntrospector intr,
AnnotatedWithParams creator, BeanPropertyDefinition propDef)
{
// If explicit name, property-based
if ((propDef != null) && propDef.isExplicitlyNamed()) {
return true;
}
// 01-Dec-2022, tatu: [databind#3654] Consider `@JsonValue` to strongly
// hint at delegation-based
if (beanDesc.findJsonValueAccessor() != null) {
return false;
}
// Inject id considered property-based
// 01-Dec-2022, tatu: ... but why?
if (intr.findInjectableValue(creator.getParameter(0)) != null) {
return true;
}
if (propDef != null) {
// One more thing: if implicit name matches property with a getter
// or field, we'll consider it property-based as well
String implName = propDef.getName();
if (implName != null && !implName.isEmpty()) {
if (propDef.couldSerialize()) {
return true;
}
}
// [databind#3897]: Record canonical constructor will have implicitly named propDef
if (!propDef.isExplicitlyNamed() && beanDesc.isRecordType()) {
return true;
}
}
// in absence of everything else, default to delegating
return false;
}
private void _checkImplicitlyNamedConstructors(DeserializationContext ctxt,
BeanDescription beanDesc, VisibilityChecker vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
List implicitCtors) throws JsonMappingException
{
AnnotatedWithParams found = null;
SettableBeanProperty[] foundProps = null;
// Further checks: (a) must have names for all parameters, (b) only one visible
// Also, since earlier matching of properties and creators relied on existence of
// `@JsonCreator` (or equivalent) annotation, we need to do bit more re-inspection...
main_loop:
for (AnnotatedWithParams ctor : implicitCtors) {
if (!vchecker.isCreatorVisible(ctor)) {
continue;
}
// as per earlier notes, only end up here if no properties associated with creator
final int argCount = ctor.getParameterCount();
SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
for (int i = 0; i < argCount; ++i) {
final AnnotatedParameter param = ctor.getParameter(i);
final PropertyName name = _findParamName(param, intr);
// must have name (implicit fine)
if (name == null || name.isEmpty()) {
continue main_loop;
}
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, param.getIndex(),
param, /*injectId*/ null);
}
if (found != null) { // only one allowed; but multiple not an error
found = null;
break;
}
found = ctor;
foundProps = properties;
}
// found one and only one visible? Ship it!
if (found != null) {
creators.addPropertyCreator(found, /*isCreator*/ false, foundProps);
BasicBeanDescription bbd = (BasicBeanDescription) beanDesc;
// Also: add properties, to keep error messages complete wrt known properties...
for (SettableBeanProperty prop : foundProps) {
PropertyName pn = prop.getFullName();
if (!bbd.hasProperty(pn)) {
BeanPropertyDefinition newDef = SimpleBeanPropertyDefinition.construct(
ctxt.getConfig(), prop.getMember(), pn);
bbd.addProperty(newDef);
}
}
}
}
protected boolean _handleSingleArgumentCreator(CreatorCollector creators,
AnnotatedWithParams ctor, boolean isCreator, boolean isVisible)
{
// otherwise either 'simple' number, String, or general delegate:
Class type = ctor.getRawParameterType(0);
if (type == String.class || type == CLASS_CHAR_SEQUENCE) {
if (isCreator || isVisible) {
creators.addStringCreator(ctor, isCreator);
}
return true;
}
if (type == int.class || type == Integer.class) {
if (isCreator || isVisible) {
creators.addIntCreator(ctor, isCreator);
}
return true;
}
if (type == long.class || type == Long.class) {
if (isCreator || isVisible) {
creators.addLongCreator(ctor, isCreator);
}
return true;
}
if (type == double.class || type == Double.class) {
if (isCreator || isVisible) {
creators.addDoubleCreator(ctor, isCreator);
}
return true;
}
if (type == boolean.class || type == Boolean.class) {
if (isCreator || isVisible) {
creators.addBooleanCreator(ctor, isCreator);
}
return true;
}
if (type == BigInteger.class) {
if (isCreator || isVisible) {
creators.addBigIntegerCreator(ctor, isCreator);
}
}
if (type == BigDecimal.class) {
if (isCreator || isVisible) {
creators.addBigDecimalCreator(ctor, isCreator);
}
}
// Delegating Creator ok iff it has @JsonCreator (etc)
if (isCreator) {
creators.addDelegatingCreator(ctor, isCreator, null, 0);
return true;
}
return false;
}
// Helper method to check that parameter of Property-based creator either
// has name or is marked as Injectable
//
// @since 2.12.1
protected void _validateNamedPropertyParameter(DeserializationContext ctxt,
BeanDescription beanDesc,
CreatorCandidate candidate, int paramIndex,
PropertyName name, JacksonInject.Value injectId)
throws JsonMappingException
{
// Must be injectable or have name; without either won't work
if ((name == null) && (injectId == null)) {
ctxt.reportBadTypeDefinition(beanDesc,
"Argument #%d of constructor %s has no property name (and is not Injectable): can not use as property-based Creator",
paramIndex, candidate);
}
}
// 01-Dec-2016, tatu: As per [databind#265] we cannot yet support passing
// of unwrapped values through creator properties, so fail fast
protected void _reportUnwrappedCreatorProperty(DeserializationContext ctxt,
BeanDescription beanDesc, AnnotatedParameter param)
throws JsonMappingException
{
ctxt.reportBadTypeDefinition(beanDesc,
"Cannot define Creator parameter %d as `@JsonUnwrapped`: combination not yet supported",
param.getIndex());
}
/**
* Method that will construct a property object that represents
* a logical property passed via Creator (constructor or static
* factory method)
*/
protected SettableBeanProperty constructCreatorProperty(DeserializationContext ctxt,
BeanDescription beanDesc, PropertyName name, int index,
AnnotatedParameter param,
JacksonInject.Value injectable)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
PropertyMetadata metadata;
final PropertyName wrapperName;
{
if (intr == null) {
metadata = PropertyMetadata.STD_REQUIRED_OR_OPTIONAL;
wrapperName = null;
} else {
Boolean b = intr.hasRequiredMarker(param);
String desc = intr.findPropertyDescription(param);
Integer idx = intr.findPropertyIndex(param);
String def = intr.findPropertyDefaultValue(param);
metadata = PropertyMetadata.construct(b, desc, idx, def);
wrapperName = intr.findWrapperName(param);
}
}
JavaType type = resolveMemberAndTypeAnnotations(ctxt, param, param.getType());
BeanProperty.Std property = new BeanProperty.Std(name, type,
wrapperName, param, metadata);
// Type deserializer: either comes from property (and already resolved)
TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler();
// or if not, based on type being referenced:
if (typeDeser == null) {
typeDeser = findTypeDeserializer(config, type);
}
// 22-Sep-2019, tatu: for [databind#2458] need more work on getting metadata
// about SetterInfo, mergeability
metadata = _getSetterInfo(ctxt, property, metadata);
// Note: contextualization of typeDeser _should_ occur in constructor of CreatorProperty
// so it is not called directly here
SettableBeanProperty prop = CreatorProperty.construct(name, type, property.getWrapperName(),
typeDeser, beanDesc.getClassAnnotations(), param, index, injectable,
metadata);
JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, param);
if (deser == null) {
deser = type.getValueHandler();
}
if (deser != null) {
// As per [databind#462] need to ensure we contextualize deserializer before passing it on
deser = ctxt.handlePrimaryContextualization(deser, prop, type);
prop = prop.withValueDeserializer(deser);
}
return prop;
}
private PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr)
{
if (intr != null) {
PropertyName name = intr.findNameForDeserialization(param);
if (name != null) {
// 16-Nov-2020, tatu: One quirk, wrt [databind#2932]; may get "use implicit"
// marker; should not return that
if (!name.isEmpty()) {
return name;
}
}
// 14-Apr-2014, tatu: Need to also consider possible implicit name
// (for JDK8, or via paranamer)
String str = intr.findImplicitPropertyName(param);
if (str != null && !str.isEmpty()) {
return PropertyName.construct(str);
}
}
return null;
}
/**
* Helper method copied from {@code POJOPropertyBuilder} since that won't be
* applied to creator parameters
*
* @since 2.10
*/
protected PropertyMetadata _getSetterInfo(DeserializationContext ctxt,
BeanProperty prop, PropertyMetadata metadata)
{
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
final DeserializationConfig config = ctxt.getConfig();
boolean needMerge = true;
Nulls valueNulls = null;
Nulls contentNulls = null;
// NOTE: compared to `POJOPropertyBuilder`, we only have access to creator
// parameter, not other accessors, so code bit simpler
AnnotatedMember prim = prop.getMember();
if (prim != null) {
// Ok, first: does property itself have something to say?
if (intr != null) {
JsonSetter.Value setterInfo = intr.findSetterInfo(prim);
if (setterInfo != null) {
valueNulls = setterInfo.nonDefaultValueNulls();
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
// If not, config override?
// 25-Oct-2016, tatu: Either this, or type of accessor...
if (needMerge || (valueNulls == null) || (contentNulls == null)) {
ConfigOverride co = config.getConfigOverride(prop.getType().getRawClass());
JsonSetter.Value setterInfo = co.getSetterInfo();
if (setterInfo != null) {
if (valueNulls == null) {
valueNulls = setterInfo.nonDefaultValueNulls();
}
if (contentNulls == null) {
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
}
}
if (needMerge || (valueNulls == null) || (contentNulls == null)) {
JsonSetter.Value setterInfo = config.getDefaultSetterInfo();
if (valueNulls == null) {
valueNulls = setterInfo.nonDefaultValueNulls();
}
if (contentNulls == null) {
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
if ((valueNulls != null) || (contentNulls != null)) {
metadata = metadata.withNulls(valueNulls, contentNulls);
}
return metadata;
}
/*
/**********************************************************
/* DeserializerFactory impl: array deserializers
/**********************************************************
*/
@Override
public JsonDeserializer createArrayDeserializer(DeserializationContext ctxt,
ArrayType type, final BeanDescription beanDesc)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
JavaType elemType = type.getContentType();
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer