From 9776f5ffd0e2afcfc0951db50b96651c2972dded Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 28 Apr 2015 22:41:16 -0700 Subject: [PATCH] Fix #698, add new `ReferenceType` sub-class of SimpleType --- release-notes/VERSION | 1 + .../deser/BasicDeserializerFactory.java | 45 +++++++++---------- .../databind/ser/BasicSerializerFactory.java | 15 ++++--- .../jackson/databind/type/ReferenceType.java | 2 + .../jackson/databind/type/TypeFactory.java | 43 ++++++++++++++++-- 5 files changed, 70 insertions(+), 36 deletions(-) diff --git a/release-notes/VERSION b/release-notes/VERSION index aa687d92f2..f1a25312fa 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -15,6 +15,7 @@ Project: jackson-databind (requested by Laird N) #696: Copy constructor does not preserve `_injectableValues` (reported by Charles A) +#698: Add support for referential types (ReferenceType) #700: Cannot Change Default Abstract Type Mapper from LinkedHashMap (reported by wealdtech@github) #725: Auto-detect multi-argument constructor with implicit names if it is the only visible creator diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java index 9800b72fd6..d70f9fca4e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -1518,6 +1518,17 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, if (rawType == CLASS_STRING || rawType == CLASS_CHAR_BUFFER) { return StringDeserializer.instance; } + + if (type.isReferenceType()) { + JavaType referencedType = type.getReferencedType(); + if (AtomicReference.class.isAssignableFrom(rawType)) { + TypeDeserializer vts = findTypeDeserializer(ctxt.getConfig(), referencedType); + BeanDescription refdDesc = ctxt.getConfig().introspectClassAnnotations(referencedType); + JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, refdDesc.getClassInfo()); + return new AtomicReferenceDeserializer(referencedType, vts, deser); + } + // Hmmh. Should we continue here for unknown referential types? + } if (rawType == CLASS_ITERABLE) { // [Issue#199]: Can and should 'upgrade' to a Collection type: TypeFactory tf = ctxt.getTypeFactory(); @@ -1528,19 +1539,18 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, return createCollectionDeserializer(ctxt, ct, beanDesc); } if (rawType == CLASS_MAP_ENTRY) { - final DeserializationConfig config = ctxt.getConfig(); - TypeFactory tf = ctxt.getTypeFactory(); - JavaType[] tps = tf.findTypeParameters(type, CLASS_MAP_ENTRY); - JavaType kt, vt; - if (tps == null || tps.length != 2) { - kt = vt = TypeFactory.unknownType(); - } else { - kt = tps[0]; - vt = tps[1]; + // 28-Apr-2015, tatu: TypeFactory does it all for us already so + JavaType kt = type.containedType(0); + if (kt == null) { + kt = TypeFactory.unknownType(); + } + JavaType vt = type.containedType(1); + if (vt == null) { + vt = TypeFactory.unknownType(); } TypeDeserializer vts = (TypeDeserializer) vt.getTypeHandler(); if (vts == null) { - vts = findTypeDeserializer(config, vt); + vts = findTypeDeserializer(ctxt.getConfig(), vt); } JsonDeserializer valueDeser = vt.getValueHandler(); KeyDeserializer keyDes = (KeyDeserializer) kt.getValueHandler(); @@ -1561,21 +1571,6 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, if (rawType == TokenBuffer.class) { return new TokenBufferDeserializer(); } - if (AtomicReference.class.isAssignableFrom(rawType)) { - // Must find parameterization - TypeFactory tf = ctxt.getTypeFactory(); - JavaType[] params = tf.findTypeParameters(type, AtomicReference.class); - JavaType referencedType; - if (params == null || params.length < 1) { // untyped (raw) - referencedType = TypeFactory.unknownType(); - } else { - referencedType = params[0]; - } - TypeDeserializer vts = findTypeDeserializer(ctxt.getConfig(), referencedType); - BeanDescription refdDesc = ctxt.getConfig().introspectClassAnnotations(referencedType); - JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, refdDesc.getClassInfo()); - return new AtomicReferenceDeserializer(referencedType, vts, deser); - } JsonDeserializer deser = findOptionalStdDeserializer(ctxt, type, beanDesc); if (deser != null) { return deser; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java index e8c0e7711b..7535b0deeb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java @@ -384,13 +384,14 @@ protected final JsonSerializer findSerializerByPrimaryType(SerializerProvider return DateSerializer.instance; } if (Map.Entry.class.isAssignableFrom(raw)) { - JavaType kt, vt; - JavaType[] params = prov.getTypeFactory().findTypeParameters(type, Map.Entry.class); - if (params == null || params.length != 2) { // assume that if we don't get 2, they are wrong... - kt = vt = TypeFactory.unknownType(); - } else { - kt = params[0]; - vt = params[1]; + // 28-Apr-2015, tatu: TypeFactory does it all for us already so + JavaType kt = type.containedType(0); + if (kt == null) { + kt = TypeFactory.unknownType(); + } + JavaType vt = type.containedType(1); + if (vt == null) { + vt = TypeFactory.unknownType(); } return buildMapEntrySerializer(prov.getConfig(), type, beanDesc, staticTyping, kt, vt); } diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java index 1630884080..b724510b4b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java @@ -185,6 +185,8 @@ public boolean equals(Object o) ReferenceType other = (ReferenceType) o; + if (other._class != _class) return false; + // Otherwise actually mostly worry about referenced type return _referencedType.equals(other._referencedType); } diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java index b291ff9361..cd460b5eeb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java @@ -805,10 +805,10 @@ protected JavaType _fromClass(Class clz, TypeBindings context) } else { // 28-Apr-2015, tatu: New class of types, referential... if (AtomicReference.class.isAssignableFrom(clz)) { + JavaType[] pts = findTypeParameters(clz, AtomicReference.class); JavaType rt = (pts == null || pts.length != 1) ? unknownType() : pts[0]; result = constructReferenceType(clz, rt); - // 29-Sep-2014, tatu: We may want to pre-resolve well-known generic types } else if (Map.Entry.class.isAssignableFrom(clz)) { JavaType[] pts = findTypeParameters(clz, Map.Entry.class); @@ -894,7 +894,7 @@ protected JavaType _fromParamType(ParameterizedType type, TypeBindings context) // Ok: Map or Collection? if (Map.class.isAssignableFrom(rawType)) { - // 19-Mar-2015, tatu: Looks like 2nd arg ough to be Map.class, but that causes fails + // 19-Mar-2015, tatu: Looks like 2nd arg ought to be Map.class, but that causes fails JavaType subtype = constructSimpleType(rawType, rawType, pt); JavaType[] mapParams = findTypeParameters(subtype, Map.class); if (mapParams.length != 2) { @@ -903,7 +903,7 @@ protected JavaType _fromParamType(ParameterizedType type, TypeBindings context) return MapType.construct(rawType, mapParams[0], mapParams[1]); } if (Collection.class.isAssignableFrom(rawType)) { - // 19-Mar-2015, tatu: Looks like 2nd arg ough to be Collection.class, but that causes fails + // 19-Mar-2015, tatu: Looks like 2nd arg ought to be Collection.class, but that causes fails JavaType subtype = constructSimpleType(rawType, rawType, pt); JavaType[] collectionParams = findTypeParameters(subtype, Collection.class); if (collectionParams.length != 1) { @@ -911,13 +911,48 @@ protected JavaType _fromParamType(ParameterizedType type, TypeBindings context) } return CollectionType.construct(rawType, collectionParams[0]); } + // 28-Apr-2015, tatu: New class of types, referential... + if (AtomicReference.class.isAssignableFrom(rawType)) { + JavaType rt = null; + + if (rawType == AtomicReference.class) { + if (paramCount == 1) { + rt = pt[0]; + } + } else { + JavaType[] pts = findTypeParameters(rawType, AtomicReference.class); + if (pts != null && pts.length != 1) { + rt = pts[0]; + } + } + return constructReferenceType(rawType, (rt == null) ? unknownType() : rt); + } + if (Map.Entry.class.isAssignableFrom(rawType)) { + JavaType kt = null, vt = null; + + if (rawType == Map.Entry.class) { + if (paramCount == 2) { + kt = pt[0]; + vt = pt[1]; + } + } else { + JavaType[] pts = findTypeParameters(rawType, Map.Entry.class); + if (pts != null && pts.length != 2) { + kt = pts[0]; + vt = pts[1]; + } + } + return constructSimpleType(rawType, Map.Entry.class, new JavaType[] { + (kt == null) ? unknownType() : kt, + (vt == null) ? unknownType() : vt }); + } + if (paramCount == 0) { // no generics return new SimpleType(rawType); } return constructSimpleType(rawType, pt); } - protected JavaType _fromArrayType(GenericArrayType type, TypeBindings context) { JavaType compType = _constructType(type.getGenericComponentType(), context);