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.

/** * 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). *<p> * 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.
/** * 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
/** * 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
/** * 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. *<p> * Mutable only to allow setting after construction, but must be strictly * set before any use. * * @since 2.3 */
protected SettableBeanProperty _fallbackSetter;
Since:2.1
/** * @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
/** * 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
/** * @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 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 CreatorProperty instances
Params:
  • name – Name of the logical property
  • type – Type of the property, used to find deserializer
  • wrapperName – Possible wrapper to use for logical property, if any
  • typeDeser – Type deserializer to use for handling polymorphic type information, if one is needed
  • contextAnnotations – Contextual annotations (usually by class that declares creator [constructor, factory method] that includes this property)
  • param – Representation of property, constructor or factory method parameter; used for accessing annotations of the property
  • injectable – Information about injectable value, if any
  • index – Index of this property within creator invocation
Since:2.11
/** * 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
/** * @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
/** * 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", getName(), getClass().getName())); } return context.findInjectableValue(_injectableValue.getId(), this, beanInstance); } // 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 extends Annotation> A getAnnotation(Class<A> 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 '"+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 '"+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()); } } }