com.fasterxml.jackson.databind.deser.CreatorProperty Maven / Gradle / Ivy
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
import java.lang.annotation.Annotation;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.Annotations;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* This concrete sub-class implements property that is passed
* via Creator (constructor or static factory method).
* It is not a full-featured implementation in that its set method
* should usually not be called for primary mutation -- instead, value must separately passed --
* but some aspects are still needed (specifically, injection).
*
* Note on injectable values: unlike with other mutators, where
* deserializer and injecting are separate, here we treat the two as related
* things. This is necessary to add proper priority, as well as to simplify
* coordination.
*/
public class CreatorProperty
extends SettableBeanProperty
{
private static final long serialVersionUID = 1L;
/**
* Placeholder that represents constructor parameter, when it is created
* from actual constructor.
* May be null when a synthetic instance is created.
*/
protected final AnnotatedParameter _annotated;
/**
* Id of value to inject, if value injection should be used for this parameter
* (in addition to, or instead of, regular deserialization).
*
* @since 2.11
*/
protected final JacksonInject.Value _injectableValue;
/**
* In special cases, when implementing "updateValue", we cannot use
* constructors or factory methods, but have to fall back on using a
* setter (or mutable field property). If so, this refers to that fallback
* accessor.
*
* Mutable only to allow setting after construction, but must be strictly
* set before any use.
*
* @since 2.3
*/
protected SettableBeanProperty _fallbackSetter;
/**
* @since 2.1
*/
protected final int _creatorIndex;
/**
* Marker flag that may have to be set during construction, to indicate that
* although property may have been constructed and added as a placeholder,
* it represents something that should be ignored during deserialization.
* This mostly concerns Creator properties which may not be easily deleted
* during processing.
*
* @since 2.9.4
*/
protected boolean _ignorable;
/**
* @since 2.11
*/
protected CreatorProperty(PropertyName name, JavaType type, PropertyName wrapperName,
TypeDeserializer typeDeser,
Annotations contextAnnotations, AnnotatedParameter param,
int index, JacksonInject.Value injectable,
PropertyMetadata metadata)
{
super(name, type, wrapperName, typeDeser, contextAnnotations, metadata);
_annotated = param;
_creatorIndex = index;
_injectableValue = injectable;
_fallbackSetter = null;
}
/**
* @deprecated Since 2.11 use factory method instead
*/
@Deprecated // since 2.11
public CreatorProperty(PropertyName name, JavaType type, PropertyName wrapperName,
TypeDeserializer typeDeser,
Annotations contextAnnotations, AnnotatedParameter param,
int index, Object injectableValueId,
PropertyMetadata metadata)
{
this(name, type, wrapperName, typeDeser, contextAnnotations, param, index,
(injectableValueId == null) ? null
: JacksonInject.Value.construct(injectableValueId, null),
metadata);
}
/**
* Factory method for creating {@link CreatorProperty} instances
*
* @param name Name of the logical property
* @param type Type of the property, used to find deserializer
* @param wrapperName Possible wrapper to use for logical property, if any
* @param typeDeser Type deserializer to use for handling polymorphic type
* information, if one is needed
* @param contextAnnotations Contextual annotations (usually by class that
* declares creator [constructor, factory method] that includes
* this property)
* @param param Representation of property, constructor or factory
* method parameter; used for accessing annotations of the property
* @param injectable Information about injectable value, if any
* @param index Index of this property within creator invocation
*
* @since 2.11
*/
public static CreatorProperty construct(PropertyName name, JavaType type, PropertyName wrapperName,
TypeDeserializer typeDeser,
Annotations contextAnnotations, AnnotatedParameter param,
int index, JacksonInject.Value injectable,
PropertyMetadata metadata)
{
return new CreatorProperty(name, type, wrapperName, typeDeser, contextAnnotations,
param, index, injectable, metadata);
}
/**
* @since 2.3
*/
protected CreatorProperty(CreatorProperty src, PropertyName newName) {
super(src, newName);
_annotated = src._annotated;
_injectableValue = src._injectableValue;
_fallbackSetter = src._fallbackSetter;
_creatorIndex = src._creatorIndex;
_ignorable = src._ignorable;
}
protected CreatorProperty(CreatorProperty src, JsonDeserializer> deser,
NullValueProvider nva) {
super(src, deser, nva);
_annotated = src._annotated;
_injectableValue = src._injectableValue;
_fallbackSetter = src._fallbackSetter;
_creatorIndex = src._creatorIndex;
_ignorable = src._ignorable;
}
@Override
public SettableBeanProperty withName(PropertyName newName) {
return new CreatorProperty(this, newName);
}
@Override
public SettableBeanProperty withValueDeserializer(JsonDeserializer> deser) {
if (_valueDeserializer == deser) {
return this;
}
// 07-May-2019, tatu: As per [databind#2303], must keep VD/NVP in-sync if they were
NullValueProvider nvp = (_valueDeserializer == _nullProvider) ? deser : _nullProvider;
return new CreatorProperty(this, deser, nvp);
}
@Override
public SettableBeanProperty withNullProvider(NullValueProvider nva) {
return new CreatorProperty(this, _valueDeserializer, nva);
}
@Override
public void fixAccess(DeserializationConfig config) {
if (_fallbackSetter != null) {
_fallbackSetter.fixAccess(config);
}
}
/**
* NOTE: one exception to immutability, due to problems with CreatorProperty instances
* being shared between Bean, separate PropertyBasedCreator
*
* @since 2.6
*/
public void setFallbackSetter(SettableBeanProperty fallbackSetter) {
_fallbackSetter = fallbackSetter;
}
@Override
public void markAsIgnorable() {
_ignorable = true;
}
@Override
public boolean isIgnorable() {
return _ignorable;
}
/*
/**********************************************************
/* Injection support
/**********************************************************
*/
// 14-Apr-2020, tatu: Does not appear to be used so deprecated in 2.11.0,
// to be removed from 2.12.0
// Method that can be called to locate value to be injected for this
// property, if it is configured for this.
@Deprecated // remove from 2.12
public Object findInjectableValue(DeserializationContext context, Object beanInstance)
throws JsonMappingException
{
if (_injectableValue == null) {
context.reportBadDefinition(ClassUtil.classOf(beanInstance),
String.format("Property %s (type %s) has no injectable value id configured",
ClassUtil.name(getName()), ClassUtil.classNameOf(this)));
}
return context.findInjectableValue(_injectableValue.getId(), this, beanInstance); // lgtm [java/dereferenced-value-may-be-null]
}
// 14-Apr-2020, tatu: Does not appear to be used so deprecated in 2.11.0,
// to be removed from 2.12.0
// Method to find value to inject, and inject it to this property.
@Deprecated // remove from 2.12
public void inject(DeserializationContext context, Object beanInstance) throws IOException
{
set(beanInstance, findInjectableValue(context, beanInstance));
}
/*
/**********************************************************
/* BeanProperty impl
/**********************************************************
*/
@Override
public A getAnnotation(Class acls) {
if (_annotated == null) {
return null;
}
return _annotated.getAnnotation(acls);
}
@Override public AnnotatedMember getMember() { return _annotated; }
@Override public int getCreatorIndex() {
return _creatorIndex;
}
/*
/**********************************************************
/* Overridden methods, SettableBeanProperty
/**********************************************************
*/
@Override
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
Object instance) throws IOException
{
_verifySetter();
_fallbackSetter.set(instance, deserialize(p, ctxt));
}
@Override
public Object deserializeSetAndReturn(JsonParser p,
DeserializationContext ctxt, Object instance) throws IOException
{
_verifySetter();
return _fallbackSetter.setAndReturn(instance, deserialize(p, ctxt));
}
@Override
public void set(Object instance, Object value) throws IOException
{
_verifySetter();
_fallbackSetter.set(instance, value);
}
@Override
public Object setAndReturn(Object instance, Object value) throws IOException
{
_verifySetter();
return _fallbackSetter.setAndReturn(instance, value);
}
@Override
public PropertyMetadata getMetadata() {
// 03-Jun-2019, tatu: Added as per [databind#2280] to support merge.
// Not 100% sure why it would be needed (or fixes things) but... appears to.
// Need to understand better in future as it seems like it should probably be
// linked earlier during construction or something.
// 22-Sep-2019, tatu: Was hoping [databind#2458] fixed this, too, but no such luck
PropertyMetadata md = super.getMetadata();
if (_fallbackSetter != null) {
return md.withMergeInfo(_fallbackSetter.getMetadata().getMergeInfo());
}
return md;
}
// Perhaps counter-intuitively, ONLY creator properties return non-null id
@Override
public Object getInjectableValueId() {
return (_injectableValue == null) ? null : _injectableValue.getId();
}
@Override
public boolean isInjectionOnly() {
return (_injectableValue != null) && !_injectableValue.willUseInput(true);
}
// public boolean isInjectionOnly() { return false; }
/*
/**********************************************************
/* Overridden methods, other
/**********************************************************
*/
@Override
public String toString() { return "[creator property, name "+ClassUtil.name(getName())+"; inject id '"+getInjectableValueId()+"']"; }
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
// since 2.9
private final void _verifySetter() throws IOException {
if (_fallbackSetter == null) {
_reportMissingSetter(null, null);
}
}
// since 2.9
private void _reportMissingSetter(JsonParser p, DeserializationContext ctxt) throws IOException
{
final String msg = "No fallback setter/field defined for creator property "+ClassUtil.name(getName());
// Hmmmh. Should we return quietly (NOP), or error?
// Perhaps better to throw an exception, since it's generally an error.
if (ctxt != null ) {
ctxt.reportBadDefinition(getType(), msg);
} else {
throw InvalidDefinitionException.from(p, msg, getType());
}
}
}