Skip to content

Commit

Permalink
Fix #2480
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Nov 14, 2019
1 parent 25bb1b6 commit 25bf4ff
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 27 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Project: jackson-databind

#2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
(reported by Antonio P)
#2480: Fix `JavaType.isEnumType()` to support sub-classes
#2487: BeanDeserializerBuilder Protected Factory Method for Extension
(contributed by Ville K)
#2503: Support `@JsonSerialize(keyUsing)` and `@JsonDeserialize(keyUsing)` on Key class
Expand Down
18 changes: 13 additions & 5 deletions src/main/java/com/fasterxml/jackson/databind/JavaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.core.type.ResolvedType;
import com.fasterxml.jackson.databind.type.TypeBindings;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
* Base class for type token classes used both to contain information
Expand Down Expand Up @@ -289,11 +290,18 @@ public boolean isConcrete() {

@Override
public final boolean isEnumType() {
// 29-Sep-2019, tatu: `Class.isEnum()` not enough to detect custom subtypes,
// but for some reason this fix will break couple of unit tests:
// See [databind#2480]:
// return ClassUtil.isEnumType(_class);
return _class.isEnum();
// 29-Sep-2019, tatu: `Class.isEnum()` not enough to detect custom subtypes.
return ClassUtil.isEnumType(_class);
}

/**
* Similar to {@link #isEnumType} except does NOT return {@code true}
* for {@link java.lang.Enum} (since that is not Enum implementation type).
*
* @since 2.11
*/
public final boolean isEnumImplType() {
return ClassUtil.isEnumType(_class) && (_class != Enum.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1326,8 +1326,7 @@ public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
} else {
inst = findValueInstantiator(ctxt, beanDesc);
}
Class<?> kt = keyType.getRawClass();
if (kt == null || !ClassUtil.isEnumType(kt)) {
if (!keyType.isEnumImplType()) {
throw new IllegalArgumentException("Cannot construct EnumMap; generic (key) type not available");
}
deser = new EnumMapDeserializer(type, inst, null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.AccessPattern;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
* Standard deserializer for {@link EnumSet}s.
Expand All @@ -28,8 +27,6 @@ public class EnumSetDeserializer

protected final JavaType _enumType;

protected final Class<Enum> _enumClass;

protected JsonDeserializer<Enum<?>> _enumDeserializer;

/**
Expand Down Expand Up @@ -67,9 +64,8 @@ public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
{
super(EnumSet.class);
_enumType = enumType;
_enumClass = (Class<Enum>) enumType.getRawClass();
// sanity check
if (!ClassUtil.isEnumType(_enumClass)) {
if (!enumType.isEnumType()) {
throw new IllegalArgumentException("Type "+enumType+" not Java Enum type");
}
_enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
Expand All @@ -96,7 +92,6 @@ protected EnumSetDeserializer(EnumSetDeserializer base,
JsonDeserializer<?> deser, NullValueProvider nuller, Boolean unwrapSingle) {
super(base);
_enumType = base._enumType;
_enumClass = base._enumClass;
_enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
_nullProvider = nuller;
_skipNullValues = NullsConstantProvider.isSkipper(nuller);
Expand Down Expand Up @@ -250,7 +245,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
@SuppressWarnings("unchecked")
private EnumSet constructSet()
{
return EnumSet.noneOf(_enumClass);
return EnumSet.noneOf((Class<Enum>) _enumType.getRawClass());
}

@SuppressWarnings("unchecked")
Expand All @@ -267,7 +262,7 @@ protected EnumSet<?> handleNonArray(JsonParser p, DeserializationContext ctxt,
}
// First: since `null`s not allowed, slightly simpler...
if (p.hasToken(JsonToken.VALUE_NULL)) {
return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p);
return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumType, p);
}
try {
Enum<?> value = _enumDeserializer.deserialize(p, ctxt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,11 @@ protected final JsonSerializer<?> findSerializerByPrimaryType(SerializerProvider
boolean staticTyping)
throws JsonMappingException
{
Class<?> raw = type.getRawClass();

if (type.isEnumType()) {
return buildEnumSerializer(prov.getConfig(), type, beanDesc);
}

final Class<?> raw = type.getRawClass();
// Then check for optional/external serializers
JsonSerializer<?> ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping);
if (ser != null) {
Expand Down Expand Up @@ -471,9 +474,6 @@ protected final JsonSerializer<?> findSerializerByPrimaryType(SerializerProvider
}
return NumberSerializer.instance;
}
if (ClassUtil.isEnumType(raw) && raw != Enum.class) {
return buildEnumSerializer(prov.getConfig(), type, beanDesc);
}
return null;
}

Expand Down Expand Up @@ -714,7 +714,7 @@ protected JsonSerializer<?> buildCollectionSerializer(SerializerProvider prov,
// this may or may not be available (Class doesn't; type of field/method does)
JavaType enumType = type.getContentType();
// and even if nominally there is something, only use if it really is enum
if (!enumType.isEnumType()) {
if (!enumType.isEnumImplType()) { // usually since it's `Enum.class`
enumType = null;
}
ser = buildEnumSetSerializer(enumType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import java.util.*;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.WritableTypeId;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
Expand All @@ -23,7 +25,6 @@
import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.Converter;
import com.fasterxml.jackson.databind.util.NameTransformer;

Expand Down Expand Up @@ -420,13 +421,13 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,

// Let's start with one big transmutation: Enums that are annotated
// to serialize as Objects may want to revert
JsonFormat.Value format = findFormatOverrides(provider, property, handledType());
JsonFormat.Value format = findFormatOverrides(provider, property, _handledType);
JsonFormat.Shape shape = null;
if ((format != null) && format.hasShape()) {
shape = format.getShape();
// or, alternatively, asked to revert "back to" other representations...
if ((shape != JsonFormat.Shape.ANY) && (shape != _serializationShape)) {
if (ClassUtil.isEnumType(_handledType)) {
if (_beanType.isEnumType()) {
switch (shape) {
case STRING:
case NUMBER:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ static enum MyEnum2 {
private MyEnum2(int value) { }
}

static enum MyEnumSub {
A(1) {
@Override public String toString() {
return "a";
}
},
B(2) {
@Override public String toString() {
return "b";
}
}
;

private MyEnumSub(int value) { }
}

// [databind#728]
static class Issue728 {
public <C extends CharSequence> C method(C input) { return null; }
Expand Down Expand Up @@ -122,7 +138,8 @@ public void testArrayType()

assertTrue(arrayT.equals(arrayT));
assertFalse(arrayT.equals(null));
assertFalse(arrayT.equals("xyz"));
final Object bogus = "xyz";
assertFalse(arrayT.equals(bogus));

assertTrue(arrayT.equals(ArrayType.construct(tf.constructType(String.class), null)));
assertFalse(arrayT.equals(ArrayType.construct(tf.constructType(Integer.class), null)));
Expand All @@ -145,14 +162,19 @@ public void testMapType()

assertTrue(mapT.equals(mapT));
assertFalse(mapT.equals(null));
assertFalse(mapT.equals("xyz"));
Object bogus = "xyz";
assertFalse(mapT.equals(bogus));
}

public void testEnumType()
{
TypeFactory tf = TypeFactory.defaultInstance();
JavaType enumT = tf.constructType(MyEnum.class);
// JDK actually works fine with "basic" Enum types...
assertTrue(enumT.getRawClass().isEnum());
assertTrue(enumT.isEnumType());
assertTrue(enumT.isEnumImplType());

assertFalse(enumT.hasHandlers());
assertTrue(enumT.isTypeOrSubTypeOf(MyEnum.class));
assertTrue(enumT.isTypeOrSubTypeOf(Object.class));
Expand All @@ -165,6 +187,17 @@ public void testEnumType()
assertTrue(tf.constructType(MyEnum2.class).isEnumType());
assertTrue(tf.constructType(MyEnum.A.getClass()).isEnumType());
assertTrue(tf.constructType(MyEnum2.A.getClass()).isEnumType());

// [databind#2480]
assertFalse(tf.constructType(Enum.class).isEnumImplType());
JavaType enumSubT = tf.constructType(MyEnumSub.B.getClass());
assertTrue(enumSubT.isEnumType());
assertTrue(enumSubT.isEnumImplType());

// and this is kind of odd twist by JDK: one might except this to return true,
// but no, sub-classes (when Enum values have overrides, and require sub-class)
// are NOT considered enums for whatever reason
assertFalse(enumSubT.getRawClass().isEnum());
}

public void testClassKey()
Expand Down

0 comments on commit 25bf4ff

Please sign in to comment.