package com.fasterxml.jackson.databind.deser.impl;

import java.lang.reflect.Member;
import java.util.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.util.ClassUtil;

Container class for storing information on creators (based on annotations, visibility), to be able to build actual ValueInstantiator later on.
/** * Container class for storing information on creators (based on annotations, * visibility), to be able to build actual {@code ValueInstantiator} later on. */
public class CreatorCollector { protected final static int C_DEFAULT = 0; protected final static int C_STRING = 1; protected final static int C_INT = 2; protected final static int C_LONG = 3; protected final static int C_BIG_INTEGER = 4; protected final static int C_DOUBLE = 5; protected final static int C_BIG_DECIMAL = 6; protected final static int C_BOOLEAN = 7; protected final static int C_DELEGATE = 8; protected final static int C_PROPS = 9; protected final static int C_ARRAY_DELEGATE = 10; protected final static String[] TYPE_DESCS = new String[] { "default", "from-String", "from-int", "from-long", "from-big-integer", "from-double", "from-big-decimal", "from-boolean", "delegate", "property-based", "array-delegate" };
Type of bean being created
/** * Type of bean being created */
protected final BeanDescription _beanDesc; protected final boolean _canFixAccess;
Since:2.7
/** * @since 2.7 */
protected final boolean _forceAccess;
Set of creators we have collected so far
Since:2.5
/** * Set of creators we have collected so far * * @since 2.5 */
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[11];
Bitmask of creators that were explicitly marked as creators; false for auto-detected (ones included base on naming and/or visibility, not annotation)
Since:2.5
/** * Bitmask of creators that were explicitly marked as creators; false for * auto-detected (ones included base on naming and/or visibility, not * annotation) * * @since 2.5 */
protected int _explicitCreators = 0; protected boolean _hasNonDefaultCreator = false; // when there are injectable values along with delegate: protected SettableBeanProperty[] _delegateArgs; protected SettableBeanProperty[] _arrayDelegateArgs; protected SettableBeanProperty[] _propertyBasedArgs; /* /********************************************************** /* Life-cycle /********************************************************** */ public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config) { _beanDesc = beanDesc; _canFixAccess = config.canOverrideAccessModifiers(); _forceAccess = config .isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS); } public ValueInstantiator constructValueInstantiator(DeserializationContext ctxt) throws JsonMappingException { final DeserializationConfig config = ctxt.getConfig(); final JavaType delegateType = _computeDelegateType(ctxt, _creators[C_DELEGATE], _delegateArgs); final JavaType arrayDelegateType = _computeDelegateType(ctxt, _creators[C_ARRAY_DELEGATE], _arrayDelegateArgs); final JavaType type = _beanDesc.getType(); StdValueInstantiator inst = new StdValueInstantiator(config, type); inst.configureFromObjectSettings(_creators[C_DEFAULT], _creators[C_DELEGATE], delegateType, _delegateArgs, _creators[C_PROPS], _propertyBasedArgs); inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE], arrayDelegateType, _arrayDelegateArgs); inst.configureFromStringCreator(_creators[C_STRING]); inst.configureFromIntCreator(_creators[C_INT]); inst.configureFromLongCreator(_creators[C_LONG]); inst.configureFromBigIntegerCreator(_creators[C_BIG_INTEGER]); inst.configureFromDoubleCreator(_creators[C_DOUBLE]); inst.configureFromBigDecimalCreator(_creators[C_BIG_DECIMAL]); inst.configureFromBooleanCreator(_creators[C_BOOLEAN]); return inst; } /* /********************************************************** /* Setters /********************************************************** */
Method called to indicate the default creator: no-arguments constructor or factory method that is called to instantiate a value before populating it with data. Default creator is only used if no other creators are indicated.
Params:
  • creator – Creator method; no-arguments constructor or static factory method.
/** * Method called to indicate the default creator: no-arguments constructor * or factory method that is called to instantiate a value before populating * it with data. Default creator is only used if no other creators are * indicated. * * @param creator * Creator method; no-arguments constructor or static factory * method. */
public void setDefaultCreator(AnnotatedWithParams creator) { _creators[C_DEFAULT] = _fixAccess(creator); } public void addStringCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_STRING, explicit); } public void addIntCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_INT, explicit); } public void addLongCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_LONG, explicit); } public void addBigIntegerCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_BIG_INTEGER, explicit); } public void addDoubleCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_DOUBLE, explicit); } public void addBigDecimalCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_BIG_DECIMAL, explicit); } public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) { verifyNonDup(creator, C_BOOLEAN, explicit); } public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit, SettableBeanProperty[] injectables, int delegateeIndex) { if (creator.getParameterType(delegateeIndex).isCollectionLikeType()) { if (verifyNonDup(creator, C_ARRAY_DELEGATE, explicit)) { _arrayDelegateArgs = injectables; } } else { if (verifyNonDup(creator, C_DELEGATE, explicit)) { _delegateArgs = injectables; } } } public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit, SettableBeanProperty[] properties) { if (verifyNonDup(creator, C_PROPS, explicit)) { // Better ensure we have no duplicate names either... if (properties.length > 1) { HashMap<String, Integer> names = new HashMap<String, Integer>(); for (int i = 0, len = properties.length; i < len; ++i) { String name = properties[i].getName(); // Need to consider Injectables, which may not have // a name at all, and need to be skipped if (name.isEmpty() && (properties[i].getInjectableValueId() != null)) { continue; } Integer old = names.put(name, Integer.valueOf(i)); if (old != null) { throw new IllegalArgumentException(String.format( "Duplicate creator property \"%s\" (index %s vs %d) for type %s ", name, old, i, ClassUtil.nameOf(_beanDesc.getBeanClass()))); } } } _propertyBasedArgs = properties; } } /* /********************************************************** /* Accessors /********************************************************** */
Since:2.1
/** * @since 2.1 */
public boolean hasDefaultCreator() { return _creators[C_DEFAULT] != null; }
Since:2.6
/** * @since 2.6 */
public boolean hasDelegatingCreator() { return _creators[C_DELEGATE] != null; }
Since:2.6
/** * @since 2.6 */
public boolean hasPropertyBasedCreator() { return _creators[C_PROPS] != null; } /* /********************************************************** /* Helper methods /********************************************************** */ private JavaType _computeDelegateType(DeserializationContext ctxt, AnnotatedWithParams creator, SettableBeanProperty[] delegateArgs) throws JsonMappingException { if (!_hasNonDefaultCreator || (creator == null)) { return null; } // need to find type... int ix = 0; if (delegateArgs != null) { for (int i = 0, len = delegateArgs.length; i < len; ++i) { if (delegateArgs[i] == null) { // marker for delegate itself ix = i; break; } } } final DeserializationConfig config = ctxt.getConfig(); // 03-May-2018, tatu: need to check possible annotation-based // custom deserializer [databind#2012], // type refinement(s) [databind#2016]. JavaType baseType = creator.getParameterType(ix); AnnotationIntrospector intr = config.getAnnotationIntrospector(); if (intr != null) { AnnotatedParameter delegate = creator.getParameter(ix); // First: custom deserializer(s): Object deserDef = intr.findDeserializer(delegate); if (deserDef != null) { JsonDeserializer<Object> deser = ctxt.deserializerInstance(delegate, deserDef); baseType = baseType.withValueHandler(deser); } else { // Second: type refinement(s), if no explicit deserializer was located baseType = intr.refineDeserializationType(config, delegate, baseType); } } return baseType; } private <T extends AnnotatedMember> T _fixAccess(T member) { if (member != null && _canFixAccess) { ClassUtil.checkAndFixAccess((Member) member.getAnnotated(), _forceAccess); } return member; }
Returns:True if specified Creator is to be used
/** * @return True if specified Creator is to be used */
protected boolean verifyNonDup(AnnotatedWithParams newOne, int typeIndex, boolean explicit) { final int mask = (1 << typeIndex); _hasNonDefaultCreator = true; AnnotatedWithParams oldOne = _creators[typeIndex]; // already had an explicitly marked one? if (oldOne != null) { boolean verify; if ((_explicitCreators & mask) != 0) { // already had explicitly annotated, leave as-is // but skip, if new one not annotated if (!explicit) { return false; } // both explicit: verify verify = true; } else { // otherwise only verify if neither explicitly annotated. verify = !explicit; } // one more thing: ok to override in sub-class // 23-Feb-2021, tatu: Second check is for case of static factory vs constructor, // which is handled by caller, presumably. // Removing it would fail one test (in case interested). if (verify && (oldOne.getClass() == newOne.getClass())) { // [databind#667]: avoid one particular class of bogus problems final Class<?> oldType = oldOne.getRawParameterType(0); final Class<?> newType = newOne.getRawParameterType(0); if (oldType == newType) { // 13-Jul-2016, tatu: One more thing to check; since Enum classes // always have implicitly created `valueOf()`, let's resolve in // favor of other implicit creator (`fromString()`) if (_isEnumValueOf(newOne)) { return false; // ignore } if (_isEnumValueOf(oldOne)) { ; } else { _reportDuplicateCreator(typeIndex, explicit, oldOne, newOne); } } // otherwise, which one to choose? else if (newType.isAssignableFrom(oldType)) { // new type less specific use old return false; } else if (oldType.isAssignableFrom(newType)) { // new type more specific, use it ; // 23-Feb-2021, tatu: due to [databind#3062], backwards-compatibility, // let's allow "primitive/Wrapper" case and tie-break in favor // of PRIMITIVE argument (null would never map to scalar creators, // and fundamentally all we need is a tie-breaker: up to caller to // annotate if wants the wrapper one) } else if (oldType.isPrimitive() != newType.isPrimitive()) { // Prefer primitive one if (oldType.isPrimitive()) { return false; } } else { // 02-May-2020, tatu: Should this only result in exception if both // explicit? Doing so could lead to arbitrary choice between // multiple implicit creators tho? _reportDuplicateCreator(typeIndex, explicit, oldOne, newOne); } } } if (explicit) { _explicitCreators |= mask; } _creators[typeIndex] = _fixAccess(newOne); return true; } // @since 2.12 protected void _reportDuplicateCreator(int typeIndex, boolean explicit, AnnotatedWithParams oldOne, AnnotatedWithParams newOne) { throw new IllegalArgumentException(String.format( "Conflicting %s creators: already had %s creator %s, encountered another: %s", TYPE_DESCS[typeIndex], explicit ? "explicitly marked" : "implicitly discovered", oldOne, newOne)); }
Helper method for recognizing `Enum.valueOf()` factory method
Since:2.8.1
/** * Helper method for recognizing `Enum.valueOf()` factory method * * @since 2.8.1 */
protected boolean _isEnumValueOf(AnnotatedWithParams creator) { return ClassUtil.isEnumType(creator.getDeclaringClass()) && "valueOf".equals(creator.getName()); } }