package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.util.*;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.LogicalType;
import com.fasterxml.jackson.databind.util.ClassUtil;
@JacksonStdImpl
public class CollectionDeserializer
extends ContainerDeserializerBase<Collection<Object>>
implements ContextualDeserializer
{
private static final long serialVersionUID = -1L;
protected final JsonDeserializer<Object> _valueDeserializer;
protected final TypeDeserializer _valueTypeDeserializer;
protected final ValueInstantiator _valueInstantiator;
protected final JsonDeserializer<Object> _delegateDeserializer;
public CollectionDeserializer(JavaType collectionType,
JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
{
this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null, null);
}
protected CollectionDeserializer(JavaType collectionType,
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
NullValueProvider nuller, Boolean unwrapSingle)
{
super(collectionType, nuller, unwrapSingle);
_valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser;
_valueInstantiator = valueInstantiator;
_delegateDeserializer = delegateDeser;
}
protected CollectionDeserializer(CollectionDeserializer src)
{
super(src);
_valueDeserializer = src._valueDeserializer;
_valueTypeDeserializer = src._valueTypeDeserializer;
_valueInstantiator = src._valueInstantiator;
_delegateDeserializer = src._delegateDeserializer;
}
@SuppressWarnings("unchecked")
protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
JsonDeserializer<?> vd, TypeDeserializer vtd,
NullValueProvider nuller, Boolean unwrapSingle)
{
return new CollectionDeserializer(_containerType,
(JsonDeserializer<Object>) vd, vtd,
_valueInstantiator, (JsonDeserializer<Object>) dd,
nuller, unwrapSingle);
}
@Override
public boolean isCachable() {
return (_valueDeserializer == null)
&& (_valueTypeDeserializer == null)
&& (_delegateDeserializer == null)
;
}
@Override
public LogicalType logicalType() {
return LogicalType.Collection;
}
@Override
public CollectionDeserializer createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
JsonDeserializer<Object> delegateDeser = null;
if (_valueInstantiator != null) {
if (_valueInstantiator.canCreateUsingDelegate()) {
JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
if (delegateType == null) {
ctxt.reportBadDefinition(_containerType, String.format(
"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
_containerType,
_valueInstantiator.getClass().getName()));
}
delegateDeser = findDeserializer(ctxt, delegateType, property);
} else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
if (delegateType == null) {
ctxt.reportBadDefinition(_containerType, String.format(
"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
_containerType,
_valueInstantiator.getClass().getName()));
}
delegateDeser = findDeserializer(ctxt, delegateType, property);
}
}
Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class,
JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
JsonDeserializer<?> valueDeser = _valueDeserializer;
valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
final JavaType vt = _containerType.getContentType();
if (valueDeser == null) {
valueDeser = ctxt.findContextualValueDeserializer(vt, property);
} else {
valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
}
TypeDeserializer valueTypeDeser = _valueTypeDeserializer;
if (valueTypeDeser != null) {
valueTypeDeser = valueTypeDeser.forProperty(property);
}
NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
if ((!Objects.equals(unwrapSingle, _unwrapSingle))
|| (nuller != _nullProvider)
|| (delegateDeser != _delegateDeserializer)
|| (valueDeser != _valueDeserializer)
|| (valueTypeDeser != _valueTypeDeserializer)
) {
return withResolved(delegateDeser, valueDeser, valueTypeDeser,
nuller, unwrapSingle);
}
return this;
}
@Override
public JsonDeserializer<Object> getContentDeserializer() {
return _valueDeserializer;
}
@Override
public ValueInstantiator getValueInstantiator() {
return _valueInstantiator;
}
@SuppressWarnings("unchecked")
@Override
public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (_delegateDeserializer != null) {
return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
_delegateDeserializer.deserialize(p, ctxt));
}
if (p.isExpectedStartArrayToken()) {
return _deserializeFromArray(p, ctxt, createDefaultInstance(ctxt));
}
if (p.hasToken(JsonToken.VALUE_STRING)) {
return _deserializeFromString(p, ctxt, p.getText());
}
return handleNonArray(p, ctxt, createDefaultInstance(ctxt));
}
@SuppressWarnings("unchecked")
protected Collection<Object> createDefaultInstance(DeserializationContext ctxt)
throws IOException
{
return (Collection<Object>) _valueInstantiator.createUsingDefault(ctxt);
}
@Override
public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
{
if (p.isExpectedStartArrayToken()) {
return _deserializeFromArray(p, ctxt, result);
}
return handleNonArray(p, ctxt, result);
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException
{
return typeDeserializer.deserializeTypedFromArray(p, ctxt);
}
@SuppressWarnings("unchecked")
protected Collection<Object> _deserializeFromString(JsonParser p, DeserializationContext ctxt,
String value)
throws IOException
{
final Class<?> rawTargetType = handledType();
if (value.isEmpty()) {
CoercionAction act = ctxt.findCoercionAction(logicalType(), rawTargetType,
CoercionInputShape.EmptyString);
act = _checkCoercionFail(ctxt, act, rawTargetType, value,
"empty String (\"\")");
if (act != null) {
return (Collection<Object>) _deserializeFromEmptyString(
p, ctxt, act, rawTargetType, "empty String (\"\")");
}
}
else if (_isBlank(value)) {
final CoercionAction act = ctxt.findCoercionFromBlankString(logicalType(), rawTargetType,
CoercionAction.Fail);
return (Collection<Object>) _deserializeFromEmptyString(
p, ctxt, act, rawTargetType, "blank String (all whitespace)");
}
return handleNonArray(p, ctxt, createDefaultInstance(ctxt));
}
protected Collection<Object> _deserializeFromArray(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
{
p.setCurrentValue(result);
JsonDeserializer<Object> valueDes = _valueDeserializer;
if (valueDes.getObjectIdReader() != null) {
return _deserializeWithObjectId(p, ctxt, result);
}
final TypeDeserializer typeDeser = _valueTypeDeserializer;
JsonToken t;
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
try {
Object value;
if (t == JsonToken.VALUE_NULL) {
if (_skipNullValues) {
continue;
}
value = _nullProvider.getNullValue(ctxt);
} else if (typeDeser == null) {
value = valueDes.deserialize(p, ctxt);
} else {
value = valueDes.deserializeWithType(p, ctxt, typeDeser);
}
result.add(value);
} catch (Exception e) {
boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
if (!wrap) {
ClassUtil.throwIfRTE(e);
}
throw JsonMappingException.wrapWithPath(e, result, result.size());
}
}
return result;
}
@SuppressWarnings("unchecked")
protected final Collection<Object> handleNonArray(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
{
boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
((_unwrapSingle == null) &&
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
if (!canWrap) {
return (Collection<Object>) ctxt.handleUnexpectedToken(_containerType, p);
}
JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
Object value;
try {
if (p.hasToken(JsonToken.VALUE_NULL)) {
if (_skipNullValues) {
return result;
}
value = _nullProvider.getNullValue(ctxt);
} else if (typeDeser == null) {
value = valueDes.deserialize(p, ctxt);
} else {
value = valueDes.deserializeWithType(p, ctxt, typeDeser);
}
} catch (Exception e) {
boolean wrap = ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
if (!wrap) {
ClassUtil.throwIfRTE(e);
}
throw JsonMappingException.wrapWithPath(e, Object.class, result.size());
}
result.add(value);
return result;
}
protected Collection<Object> _deserializeWithObjectId(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
{
if (!p.isExpectedStartArrayToken()) {
return handleNonArray(p, ctxt, result);
}
p.setCurrentValue(result);
final JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
CollectionReferringAccumulator referringAccumulator =
new CollectionReferringAccumulator(_containerType.getContentType().getRawClass(), result);
JsonToken t;
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
try {
Object value;
if (t == JsonToken.VALUE_NULL) {
if (_skipNullValues) {
continue;
}
value = _nullProvider.getNullValue(ctxt);
} else if (typeDeser == null) {
value = valueDes.deserialize(p, ctxt);
} else {
value = valueDes.deserializeWithType(p, ctxt, typeDeser);
}
referringAccumulator.add(value);
} catch (UnresolvedForwardReference reference) {
Referring ref = referringAccumulator.handleUnresolvedReference(reference);
reference.getRoid().appendReferring(ref);
} catch (Exception e) {
boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
if (!wrap) {
ClassUtil.throwIfRTE(e);
}
throw JsonMappingException.wrapWithPath(e, result, result.size());
}
}
return result;
}
public static class CollectionReferringAccumulator {
private final Class<?> _elementType;
private final Collection<Object> _result;
private List<CollectionReferring> _accumulator = new ArrayList<CollectionReferring>();
public CollectionReferringAccumulator(Class<?> elementType, Collection<Object> result) {
_elementType = elementType;
_result = result;
}
public void add(Object value)
{
if (_accumulator.isEmpty()) {
_result.add(value);
} else {
CollectionReferring ref = _accumulator.get(_accumulator.size() - 1);
ref.next.add(value);
}
}
public Referring handleUnresolvedReference(UnresolvedForwardReference reference)
{
CollectionReferring id = new CollectionReferring(this, reference, _elementType);
_accumulator.add(id);
return id;
}
public void resolveForwardReference(Object id, Object value) throws IOException
{
Iterator<CollectionReferring> iterator = _accumulator.iterator();
Collection<Object> previous = _result;
while (iterator.hasNext()) {
CollectionReferring ref = iterator.next();
if (ref.hasId(id)) {
iterator.remove();
previous.add(value);
previous.addAll(ref.next);
return;
}
previous = ref.next;
}
throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
+ "] that wasn't previously seen as unresolved.");
}
}
private final static class CollectionReferring extends Referring {
private final CollectionReferringAccumulator _parent;
public final List<Object> next = new ArrayList<Object>();
CollectionReferring(CollectionReferringAccumulator parent,
UnresolvedForwardReference reference, Class<?> contentType)
{
super(reference, contentType);
_parent = parent;
}
@Override
public void handleResolvedForwardReference(Object id, Object value) throws IOException {
_parent.resolveForwardReference(id, value);
}
}
}