DeserializationContextExt.java
package tools.jackson.databind.deser;
import java.util.*;
import java.util.Map.Entry;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import tools.jackson.core.*;
import tools.jackson.databind.*;
import tools.jackson.databind.cfg.HandlerInstantiator;
import tools.jackson.databind.deser.ReadableObjectId.Referring;
import tools.jackson.databind.introspect.Annotated;
import tools.jackson.databind.util.ClassUtil;
/**
* Complete {@link DeserializationContext} implementation that adds
* extended API for {@link ObjectMapper} (and {@link ObjectReader})
* to call, as well as implements certain parts that base class
* has left abstract.
*/
public abstract class DeserializationContextExt
extends DeserializationContext
{
protected transient LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId> _objectIds;
private List<ObjectIdResolver> _objectIdResolvers;
/**
* Constructor that will pass specified deserializer factory and
* cache: cache may be null (in which case default implementation
* will be used), factory cannot be null
*/
protected DeserializationContextExt(TokenStreamFactory tsf,
DeserializerFactory deserializerFactory, DeserializerCache cache,
DeserializationConfig config, FormatSchema schema,
InjectableValues values) {
super(tsf, deserializerFactory, cache,
config, schema, values);
}
public DeserializationContextExt assignParser(JsonParser p) {
_parser = p;
_readCapabilities = p.streamReadCapabilities();
return this;
}
public JsonParser assignAndReturnParser(JsonParser p) {
_parser = p;
_readCapabilities = p.streamReadCapabilities();
return p;
}
/*
/**********************************************************************
/* Abstract methods impls, Object Id
/**********************************************************************
*/
@Override
public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> gen, ObjectIdResolver resolverType)
{
// 02-Apr-2015, tatu: As per [databind#742] should allow 'null', similar to how
// missing id already works.
if (id == null) {
return null;
}
final ObjectIdGenerator.IdKey key = gen.key(id);
if (_objectIds == null) {
_objectIds = new LinkedHashMap<ObjectIdGenerator.IdKey,ReadableObjectId>();
} else {
ReadableObjectId entry = _objectIds.get(key);
if (entry != null) {
return entry;
}
}
// Not seen yet, must create entry and configure resolver.
ObjectIdResolver resolver = null;
if (_objectIdResolvers == null) {
_objectIdResolvers = new ArrayList<ObjectIdResolver>(8);
} else {
for (ObjectIdResolver res : _objectIdResolvers) {
if (res.canUseFor(resolverType)) {
resolver = res;
break;
}
}
}
if (resolver == null) {
resolver = resolverType.newForDeserialization(this);
_objectIdResolvers.add(resolver);
}
ReadableObjectId entry = createReadableObjectId(key);
entry.setResolver(resolver);
_objectIds.put(key, entry);
return entry;
}
/**
* Overridable factory method to create a new instance of ReadableObjectId or its
* subclass. It is meant to be overridden when custom ReadableObjectId is
* needed for {@link #tryToResolveUnresolvedObjectId}.
* Default implementation simply constructs default {@link ReadableObjectId} with
* given <code>key</code>.
*
* @param key The key to associate with the new ReadableObjectId
* @return New ReadableObjectId instance
*/
protected ReadableObjectId createReadableObjectId(IdKey key) {
return new ReadableObjectId(key);
}
@Override
public void checkUnresolvedObjectId() throws UnresolvedForwardReference
{
if (_objectIds == null) {
return;
}
// 29-Dec-2014, tatu: As per [databind#299], may also just let unresolved refs be...
if (!isEnabled(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS)) {
return;
}
UnresolvedForwardReference exception = null;
for (Entry<IdKey,ReadableObjectId> entry : _objectIds.entrySet()) {
ReadableObjectId roid = entry.getValue();
if (!roid.hasReferringProperties()) {
continue;
}
// as per [databind#675], allow resolution at this point
if (tryToResolveUnresolvedObjectId(roid)) {
continue;
}
if (exception == null) {
exception = new UnresolvedForwardReference(getParser(), "Unresolved forward references for: ")
.withStackTrace();
}
Object key = roid.getKey().key;
for (Iterator<Referring> iterator = roid.referringProperties(); iterator.hasNext(); ) {
Referring referring = iterator.next();
exception.addUnresolvedId(key, referring.getBeanType(), referring.getLocation());
}
}
if (exception != null) {
throw exception;
}
}
/**
* Overridable helper method called to try to resolve otherwise unresolvable {@link ReadableObjectId};
* and if this succeeds, return <code>true</code> to indicate problem has been resolved in
* some way, so that caller can avoid reporting it as an error.
*<p>
* Default implementation simply calls {@link ReadableObjectId#tryToResolveUnresolved} and
* returns whatever it returns.
*/
protected boolean tryToResolveUnresolvedObjectId(ReadableObjectId roid)
{
return roid.tryToResolveUnresolved(this);
}
/*
/**********************************************************************
/* Abstract methods impls, other factory methods
/**********************************************************************
*/
@SuppressWarnings("unchecked")
@Override
public ValueDeserializer<Object> deserializerInstance(Annotated ann, Object deserDef)
{
if (deserDef == null) {
return null;
}
ValueDeserializer<?> deser;
if (deserDef instanceof ValueDeserializer valueDeserializer) {
deser = valueDeserializer;
} else {
// Alas, there's no way to force return type of "either class
// X or Y" -- need to throw an exception after the fact
if (!(deserDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned deserializer definition of type "
+deserDef.getClass().getName()
+"; expected type `ValueDeserializer` or `Class<ValueDeserializer>` instead");
}
Class<?> deserClass = (Class<?>)deserDef;
// there are some known "no class" markers to consider too:
if (deserClass == ValueDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {
return null;
}
if (!ValueDeserializer.class.isAssignableFrom(deserClass)) {
throw new IllegalStateException("AnnotationIntrospector returned `Class<"+deserClass.getName()+">`; expected `Class<ValueDeserializer>`");
}
HandlerInstantiator hi = _config.getHandlerInstantiator();
deser = (hi == null) ? null : hi.deserializerInstance(_config, ann, deserClass);
if (deser == null) {
deser = (ValueDeserializer<?>) ClassUtil.createInstance(deserClass,
_config.canOverrideAccessModifiers());
}
}
// First: need to resolve
deser.resolve(this);
return (ValueDeserializer<Object>) deser;
}
@Override
public final KeyDeserializer keyDeserializerInstance(Annotated ann, Object deserDef)
{
if (deserDef == null) {
return null;
}
KeyDeserializer deser;
if (deserDef instanceof KeyDeserializer keyDeserializer) {
deser = keyDeserializer;
} else {
if (!(deserDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+deserDef.getClass().getName()
+"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
}
Class<?> deserClass = (Class<?>)deserDef;
// there are some known "no class" markers to consider too:
if (deserClass == KeyDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {
return null;
}
if (!KeyDeserializer.class.isAssignableFrom(deserClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()
+"; expected Class<KeyDeserializer>");
}
HandlerInstantiator hi = _config.getHandlerInstantiator();
deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, ann, deserClass);
if (deser == null) {
deser = (KeyDeserializer) ClassUtil.createInstance(deserClass,
_config.canOverrideAccessModifiers());
}
}
// First: need to resolve
deser.resolve(this);
return deser;
}
/*
/**********************************************************************
/* Extended API, read methods
/**********************************************************************
*/
public Object readRootValue(JsonParser p, JavaType valueType,
ValueDeserializer<Object> deser, Object valueToUpdate)
throws JacksonException
{
if (_config.useRootWrapping()) {
return _unwrapAndDeserialize(p, valueType, deser, valueToUpdate);
}
if (valueToUpdate == null) {
return deser.deserialize(p, this);
}
return deser.deserialize(p, this, valueToUpdate);
}
protected Object _unwrapAndDeserialize(JsonParser p,
JavaType rootType, ValueDeserializer<Object> deser,
Object valueToUpdate)
throws JacksonException
{
PropertyName expRootName = findRootName(rootType);
// 12-Jun-2015, tatu: Should try to support namespaces etc but...
String expSimpleName = expRootName.getSimpleName();
if (p.currentToken() != JsonToken.START_OBJECT) {
reportWrongTokenException(rootType, JsonToken.START_OBJECT,
"Current token not `JsonToken.START_OBJECT` (needed to unwrap root name %s), but %s",
ClassUtil.name(expSimpleName), p.currentToken());
}
if (p.nextToken() != JsonToken.PROPERTY_NAME) {
reportWrongTokenException(rootType, JsonToken.PROPERTY_NAME,
"Current token not `JsonToken.PROPERTY_NAME` (to contain expected root name %s), but %s",
ClassUtil.name(expSimpleName), p.currentToken());
}
String actualName = p.currentName();
if (!expSimpleName.equals(actualName)) {
reportPropertyInputMismatch(rootType, actualName,
"Root name (%s) does not match expected (%s) for type %s",
ClassUtil.name(actualName), ClassUtil.name(expSimpleName), ClassUtil.getTypeDescription(rootType));
}
// ok, then move to value itself....
p.nextToken();
final Object result;
if (valueToUpdate == null) {
result = deser.deserialize(p, this);
} else {
result = deser.deserialize(p, this, valueToUpdate);
}
// and last, verify that we now get matching END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
reportWrongTokenException(rootType, JsonToken.END_OBJECT,
"Current token not `JsonToken.END_OBJECT` (to match wrapper object with root name %s), but %s",
ClassUtil.name(expSimpleName), p.currentToken());
}
return result;
}
/*
/**********************************************************************
/* And then the concrete implementation class
/**********************************************************************
*/
/**
* Actual full concrete implementation
*/
public final static class Impl extends DeserializationContextExt
{
public Impl(TokenStreamFactory tsf,
DeserializerFactory deserializerFactory, DeserializerCache cache,
DeserializationConfig config, FormatSchema schema,
InjectableValues values) {
super(tsf, deserializerFactory, cache,
config, schema, values);
}
}
}