blob: 34e502d0e812dc6f3db0ada50a1e371c377082e2 [file] [log] [blame]
package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.ReferenceType;
import com.fasterxml.jackson.databind.util.AccessPattern;
/**
* Base deserializer implementation for properties {@link ReferenceType} values.
* Implements most of functionality, only leaving couple of abstract
* methods for sub-classes to implement
*
* @since 2.8
*/
public abstract class ReferenceTypeDeserializer<T>
extends StdDeserializer<T>
implements ContextualDeserializer
{
private static final long serialVersionUID = 2L; // 2.9
/**
* Full type of property (or root value) for which this deserializer
* has been constructed and contextualized.
*/
protected final JavaType _fullType;
protected final ValueInstantiator _valueInstantiator;
protected final TypeDeserializer _valueTypeDeserializer;
protected final JsonDeserializer<Object> _valueDeserializer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
@SuppressWarnings("unchecked")
public ReferenceTypeDeserializer(JavaType fullType, ValueInstantiator vi,
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
{
super(fullType);
_valueInstantiator = vi;
_fullType = fullType;
_valueDeserializer = (JsonDeserializer<Object>) deser;
_valueTypeDeserializer = typeDeser;
}
@Deprecated // since 2.9
public ReferenceTypeDeserializer(JavaType fullType,
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
{
this(fullType, null, typeDeser, deser);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException
{
JsonDeserializer<?> deser = _valueDeserializer;
if (deser == null) {
deser = ctxt.findContextualValueDeserializer(_fullType.getReferencedType(), property);
} else { // otherwise directly assigned, probably not contextual yet:
deser = ctxt.handleSecondaryContextualization(deser, property, _fullType.getReferencedType());
}
TypeDeserializer typeDeser = _valueTypeDeserializer;
if (typeDeser != null) {
typeDeser = typeDeser.forProperty(property);
}
// !!! 23-Oct-2016, tatu: TODO: full support for configurable ValueInstantiators?
if ((deser == _valueDeserializer) && (typeDeser == _valueTypeDeserializer)) {
return this;
}
return withResolved(typeDeser, deser);
}
/*
/**********************************************************
/* Partial NullValueProvider impl
/**********************************************************
*/
/**
* Null value varies dynamically (unlike with scalar types),
* so let's indicate this.
*/
@Override
public AccessPattern getNullAccessPattern() {
return AccessPattern.DYNAMIC;
}
@Override
public AccessPattern getEmptyAccessPattern() {
return AccessPattern.DYNAMIC;
}
/*
/**********************************************************
/* Abstract methods for sub-classes to implement
/**********************************************************
*/
/**
* Mutant factory method called when changes are needed; should construct
* newly configured instance with new values as indicated.
*<p>
* NOTE: caller has verified that there are changes, so implementations
* need NOT check if a new instance is needed.
*/
protected abstract ReferenceTypeDeserializer<T> withResolved(TypeDeserializer typeDeser,
JsonDeserializer<?> valueDeser);
@Override
public abstract T getNullValue(DeserializationContext ctxt) throws JsonMappingException;
@Override
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
return getNullValue(ctxt);
}
public abstract T referenceValue(Object contents);
/**
* Method called in case of "merging update", in which we should try
* update reference instead of creating a new one. If this does not
* succeed, should just create a new instance.
*
* @since 2.9
*/
public abstract T updateReference(T reference, Object contents);
/**
* Method that may be called to find contents of specified reference,
* if any; or `null` if none. Note that method should never fail, so
* for types that use concept of "absence" vs "presence", `null` is
* to be returned for both "absent" and "reference to `null`" cases.
*
* @since 2.9
*/
public abstract Object getReferenced(T reference);
/*
/**********************************************************
/* Overridden accessors
/**********************************************************
*/
@Override
public JavaType getValueType() { return _fullType; }
/**
* By default we assume that updateability mostly relies on value
* deserializer; if it supports updates, typically that's what
* matters. So let's just delegate.
*/
@Override // since 2.9
public Boolean supportsUpdate(DeserializationConfig config) {
return (_valueDeserializer == null) ? null
: _valueDeserializer.supportsUpdate(config);
}
/*
/**********************************************************
/* Deserialization
/**********************************************************
*/
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// 23-Oct-2016, tatu: ValueInstantiator only defined for non-vanilla instances,
// but do check... might work
if (_valueInstantiator != null) {
@SuppressWarnings("unchecked")
T value = (T) _valueInstantiator.createUsingDefault(ctxt);
return deserialize(p, ctxt, value);
}
Object contents = (_valueTypeDeserializer == null)
? _valueDeserializer.deserialize(p, ctxt)
: _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
return referenceValue(contents);
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt, T reference) throws IOException
{
Object contents;
// 26-Oct-2016, tatu: first things first; see if we should be able to merge:
Boolean B = _valueDeserializer.supportsUpdate(ctxt.getConfig());
// if explicitly stated that merge won't work...
if (B.equals(Boolean.FALSE) || (_valueTypeDeserializer != null)) {
contents = (_valueTypeDeserializer == null)
? _valueDeserializer.deserialize(p, ctxt)
: _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
} else {
// Otherwise, see if we can merge the value
contents = getReferenced(reference);
// Whether to error or not... for now, just go back to default then
if (contents == null) {
contents = (_valueTypeDeserializer == null)
? _valueDeserializer.deserialize(p, ctxt)
: _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
return referenceValue(contents);
} else {
contents = _valueDeserializer.deserialize(p, ctxt, contents);
}
}
return updateReference(reference, contents);
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer) throws IOException
{
final JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NULL) { // can this actually happen?
return getNullValue(ctxt);
}
// 22-Oct-2015, tatu: This handling is probably not needed (or is wrong), but
// could be result of older (pre-2.7) Jackson trying to serialize natural types.
// Because of this, let's allow for now, unless proven problematic
/*
if ((t != null) && t.isScalarValue()) {
return deserialize(p, ctxt);
}
*/
// 19-Apr-2016, tatu: Alas, due to there typically really being anything for Reference wrapper
// itself, need to just ignore `typeDeser`, use TypeDeserializer we do have for contents
// and it might just work.
if (_valueTypeDeserializer == null) {
return deserialize(p, ctxt);
}
return referenceValue(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt));
}
}