blob: 566b54f0099094d94a6e3d0b682dccf9b58805e6 [file] [log] [blame]
package com.fasterxml.jackson.databind.deser.impl;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
/**
* Object that is used to collect arguments for non-default creator
* (non-default-constructor, or argument-taking factory method)
* before creator can be called.
* Since ordering of JSON properties is not guaranteed, this may
* require buffering of values other than ones being passed to
* creator.
*/
public final class PropertyBasedCreator
{
/**
* Number of properties: usually same as size of {@link #_propertyLookup},
* but not necessarily, when we have unnamed injectable properties.
*/
protected final int _propertyCount;
/**
* Helper object that knows how to actually construct the instance by
* invoking creator method with buffered arguments.
*/
protected final ValueInstantiator _valueInstantiator;
/**
* Map that contains property objects for either constructor or factory
* method (whichever one is null: one property for each
* parameter for that one), keyed by logical property name
*/
protected final HashMap<String, SettableBeanProperty> _propertyLookup;
/**
* Array that contains properties that expect value to inject, if any;
* null if no injectable values are expected.
*/
protected final SettableBeanProperty[] _allProperties;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
protected PropertyBasedCreator(DeserializationContext ctxt,
ValueInstantiator valueInstantiator,
SettableBeanProperty[] creatorProps,
boolean caseInsensitive,
boolean addAliases)
{
_valueInstantiator = valueInstantiator;
if (caseInsensitive) {
_propertyLookup = new CaseInsensitiveMap();
} else {
_propertyLookup = new HashMap<String, SettableBeanProperty>();
}
final int len = creatorProps.length;
_propertyCount = len;
_allProperties = new SettableBeanProperty[len];
// 26-Feb-2017, tatu: Let's start by aliases, so that there is no
// possibility of accidental override of primary names
if (addAliases) {
final DeserializationConfig config = ctxt.getConfig();
for (SettableBeanProperty prop : creatorProps) {
// 22-Jan-2018, tatu: ignorable entries should be ignored, even if got aliases
if (!prop.isIgnorable()) {
List<PropertyName> aliases = prop.findAliases(config);
if (!aliases.isEmpty()) {
for (PropertyName pn : aliases) {
_propertyLookup.put(pn.getSimpleName(), prop);
}
}
}
}
}
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = creatorProps[i];
_allProperties[i] = prop;
// 22-Jan-2018, tatu: ignorable entries should be skipped
if (!prop.isIgnorable()) {
_propertyLookup.put(prop.getName(), prop);
}
}
}
/**
* Factory method used for building actual instances to be used with POJOS:
* resolves deserializers, checks for "null values".
*
* @since 2.9
*/
public static PropertyBasedCreator construct(DeserializationContext ctxt,
ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
BeanPropertyMap allProperties)
throws JsonMappingException
{
final int len = srcCreatorProps.length;
SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = srcCreatorProps[i];
if (!prop.hasValueDeserializer()) {
prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
}
creatorProps[i] = prop;
}
return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
allProperties.isCaseInsensitive(),
allProperties.hasAliases());
}
/**
* Factory method used for building actual instances to be used with types
* OTHER than POJOs.
* resolves deserializers and checks for "null values".
*
* @since 2.9
*/
public static PropertyBasedCreator construct(DeserializationContext ctxt,
ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
boolean caseInsensitive)
throws JsonMappingException
{
final int len = srcCreatorProps.length;
SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = srcCreatorProps[i];
if (!prop.hasValueDeserializer()) {
prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
}
creatorProps[i] = prop;
}
return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
caseInsensitive, false);
}
@Deprecated // since 2.9
public static PropertyBasedCreator construct(DeserializationContext ctxt,
ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)
throws JsonMappingException
{
return construct(ctxt, valueInstantiator, srcCreatorProps,
ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
}
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
public Collection<SettableBeanProperty> properties() {
return _propertyLookup.values();
}
public SettableBeanProperty findCreatorProperty(String name) {
return _propertyLookup.get(name);
}
public SettableBeanProperty findCreatorProperty(int propertyIndex) {
for (SettableBeanProperty prop : _propertyLookup.values()) {
if (prop.getPropertyIndex() == propertyIndex) {
return prop;
}
}
return null;
}
/*
/**********************************************************
/* Building process
/**********************************************************
*/
/**
* Method called when starting to build a bean instance.
*
* @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without)
*/
public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ctxt,
ObjectIdReader oir) {
return new PropertyValueBuffer(p, ctxt, _propertyCount, oir);
}
public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException
{
Object bean = _valueInstantiator.createFromObjectWith(ctxt,
_allProperties, buffer);
// returning null isn't quite legal, but let's let caller deal with that
if (bean != null) {
// Object Id to handle?
bean = buffer.handleIdValue(ctxt, bean);
// Anything buffered?
for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) {
pv.assign(bean);
}
}
return bean;
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Simple override of standard {@link java.util.HashMap} to support
* case-insensitive access to creator properties
*
* @since 2.8.5
*/
static class CaseInsensitiveMap extends HashMap<String, SettableBeanProperty>
{
private static final long serialVersionUID = 1L;
@Override
public SettableBeanProperty get(Object key0) {
return super.get(((String) key0).toLowerCase());
}
@Override
public SettableBeanProperty put(String key, SettableBeanProperty value) {
key = key.toLowerCase();
return super.put(key, value);
}
}
}