blob: 8e38f5decdb92eec2b1f9641b8d3031f6c933ba6 [file] [log] [blame]
package com.fasterxml.jackson.databind.deser.impl;
import java.io.IOException;
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.CreatorProperty;
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 instantiator later on.
*/
public class CreatorCollector
{
// Since 2.5
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_DOUBLE = 4;
protected final static int C_BOOLEAN = 5;
protected final static int C_DELEGATE = 6;
protected final static int C_PROPS = 7;
protected final static int C_ARRAY_DELEGATE = 8;
protected final static String[] TYPE_DESCS = new String[] {
"default",
"String", "int", "long", "double", "boolean",
"delegate", "property-based"
};
/// Type of bean being created
final protected BeanDescription _beanDesc;
final protected boolean _canFixAccess;
/**
* @since 2.7
*/
final protected boolean _forceAccess;
/**
* Set of creators we have collected so far
*
* @since 2.5
*/
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9];
/**
* 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;
protected AnnotatedParameter _incompleteParameter;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config)
{
_beanDesc = beanDesc;
_canFixAccess = config.canOverrideAccessModifiers();
_forceAccess = config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
}
public ValueInstantiator constructValueInstantiator(DeserializationConfig config)
{
final JavaType delegateType = _computeDelegateType(_creators[C_DELEGATE], _delegateArgs);
final JavaType arrayDelegateType = _computeDelegateType(_creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
final JavaType type = _beanDesc.getType();
// Any non-standard creator will prevent; with one exception: int-valued constructor
// that standard containers have can be ignored
if (!_hasNonDefaultCreator) {
/* 10-May-2014, tatu: If we have nothing special, and we are dealing with one
* of "well-known" types, can create a non-reflection-based instantiator.
*/
final Class<?> rawType = type.getRawClass();
if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class) {
return new Vanilla(Vanilla.TYPE_COLLECTION);
}
if (rawType == Map.class || rawType == LinkedHashMap.class) {
return new Vanilla(Vanilla.TYPE_MAP);
}
if (rawType == HashMap.class) {
return new Vanilla(Vanilla.TYPE_HASH_MAP);
}
}
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.configureFromDoubleCreator(_creators[C_DOUBLE]);
inst.configureFromBooleanCreator(_creators[C_BOOLEAN]);
inst.configureIncompleteParameter(_incompleteParameter);
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.
*
* @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 addDoubleCreator(AnnotatedWithParams creator, boolean explicit) {
verifyNonDup(creator, C_DOUBLE, explicit);
}
public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) {
verifyNonDup(creator, C_BOOLEAN, explicit);
}
public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit,
SettableBeanProperty[] injectables)
{
if (creator.getParameterType(0).isCollectionLikeType()) {
verifyNonDup(creator, C_ARRAY_DELEGATE, explicit);
_arrayDelegateArgs = injectables;
} else {
verifyNonDup(creator, C_DELEGATE, explicit);
_delegateArgs = injectables;
}
}
public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit,
SettableBeanProperty[] properties)
{
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();
/* [Issue-13]: Need to consider Injectables, which may not have
* a name at all, and need to be skipped
*/
if (name.length() == 0 && properties[i].getInjectableValueId() != null) {
continue;
}
Integer old = names.put(name, Integer.valueOf(i));
if (old != null) {
throw new IllegalArgumentException("Duplicate creator property \""+name+"\" (index "+old+" vs "+i+")");
}
}
}
_propertyBasedArgs = properties;
}
public void addIncompeteParameter(AnnotatedParameter parameter) {
if (_incompleteParameter == null) {
_incompleteParameter = parameter;
}
}
// Bunch of methods deprecated in 2.5, to be removed from 2.6 or later
@Deprecated // since 2.5
public void addStringCreator(AnnotatedWithParams creator) {
addStringCreator(creator, false);
}
@Deprecated // since 2.5
public void addIntCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
@Deprecated // since 2.5
public void addLongCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
@Deprecated // since 2.5
public void addDoubleCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
@Deprecated // since 2.5
public void addBooleanCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
@Deprecated // since 2.5
public void addDelegatingCreator(AnnotatedWithParams creator, CreatorProperty[] injectables) {
addDelegatingCreator(creator, false, injectables);
}
@Deprecated // since 2.5
public void addPropertyCreator(AnnotatedWithParams creator, CreatorProperty[] properties) {
addPropertyCreator(creator, false, properties);
}
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
/**
* @since 2.1
*/
public boolean hasDefaultCreator() {
return _creators[C_DEFAULT] != null;
}
/**
* @since 2.6
*/
public boolean hasDelegatingCreator() {
return _creators[C_DELEGATE] != null;
}
/**
* @since 2.6
*/
public boolean hasPropertyBasedCreator() {
return _creators[C_PROPS] != null;
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
private JavaType _computeDelegateType(AnnotatedWithParams creator,
SettableBeanProperty[] delegateArgs)
{
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;
}
}
}
return creator.getParameterType(ix);
}
private <T extends AnnotatedMember> T _fixAccess(T member)
{
if (member != null && _canFixAccess) {
ClassUtil.checkAndFixAccess((Member) member.getAnnotated(), _forceAccess);
}
return member;
}
protected void 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;
}
// both explicit: verify
verify = true;
} else {
// otherwise only verify if neither explicitly annotated.
verify = !explicit;
}
// one more thing: ok to override in sub-class
if (verify && (oldOne.getClass() == newOne.getClass())) {
// [databind#667]: avoid one particular class of bogus problems
Class<?> oldType = oldOne.getRawParameterType(0);
Class<?> newType = newOne.getRawParameterType(0);
if (oldType == newType) {
throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
+" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
}
// otherwise, which one to choose?
if (newType.isAssignableFrom(oldType)) {
// new type more generic, use old
return;
}
// new type more specific, use it
}
}
if (explicit) {
_explicitCreators |= mask;
}
_creators[typeIndex] = _fixAccess(newOne);
}
/*
/**********************************************************
/* Helper class(es)
/**********************************************************
*/
protected final static class Vanilla
extends ValueInstantiator
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
public final static int TYPE_COLLECTION = 1;
public final static int TYPE_MAP = 2;
public final static int TYPE_HASH_MAP = 3;
private final int _type;
public Vanilla(int t) {
_type = t;
}
@Override
public String getValueTypeDesc() {
switch (_type) {
case TYPE_COLLECTION: return ArrayList.class.getName();
case TYPE_MAP: return LinkedHashMap.class.getName();
case TYPE_HASH_MAP: return HashMap.class.getName();
}
return Object.class.getName();
}
@Override
public boolean canInstantiate() { return true; }
@Override
public boolean canCreateUsingDefault() { return true; }
@Override
public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
switch (_type) {
case TYPE_COLLECTION: return new ArrayList<Object>();
case TYPE_MAP: return new LinkedHashMap<String,Object>();
case TYPE_HASH_MAP: return new HashMap<String,Object>();
}
throw new IllegalStateException("Unknown type "+_type);
}
}
}