Skip to content

Commit 75a0953

Browse files
committed
Fix #1215
1 parent 4929735 commit 75a0953

File tree

4 files changed

+110
-38
lines changed

4 files changed

+110
-38
lines changed

release-notes/VERSION

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Project: jackson-databind
66

77
2.7.6 (not yet released)
88

9+
#1215: Problem with type specialization for Maps with `@JsonDeserialize(as=subtype)`
10+
(reported by brentryan@github)
11+
#1277: Inefficient resolution of parametrized types
12+
(reported by Andriy P, plokhotnyuk@github)
913
#1279: Ensure DOM parsing defaults to not expanding external entities
1014

1115
2.7.5 (11-Jun-2016)

src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public TypeBindings withUnboundVariable(String name)
209209
/* Accessors
210210
/**********************************************************************
211211
*/
212-
212+
213213
/**
214214
* Find type bound to specified name, if there is one; returns bound type if so, null if not.
215215
*/
@@ -293,6 +293,18 @@ public boolean hasUnbound(String name) {
293293
return false;
294294
}
295295

296+
/**
297+
* Factory method that will create an object that can be used as a key for
298+
* caching purposes by {@link TypeFactory}
299+
*
300+
* @since 2.8
301+
*/
302+
public Object asKey(Class<?> rawBase) {
303+
// safe to pass _types array without copy since it is not exposed via
304+
// any access, nor modified by this class
305+
return new AsKey(rawBase, _types, _hashCode);
306+
}
307+
296308
/*
297309
/**********************************************************************
298310
/* Standard methods
@@ -302,7 +314,7 @@ public boolean hasUnbound(String name) {
302314
@Override public String toString()
303315
{
304316
if (_types.length == 0) {
305-
return "";
317+
return "<>";
306318
}
307319
StringBuilder sb = new StringBuilder();
308320
sb.append('<');
@@ -409,5 +421,53 @@ public static TypeVariable<?>[] paramsFor2(Class<?> erasedType)
409421
}
410422
return erasedType.getTypeParameters();
411423
}
412-
}
424+
}
425+
426+
/**
427+
* Helper type used to allow caching of generic types
428+
*
429+
* @since 2.8
430+
*/
431+
final static class AsKey {
432+
private final Class<?> _raw;
433+
private final JavaType[] _params;
434+
private final int _hash;
435+
436+
public AsKey(Class<?> raw, JavaType[] params, int hash) {
437+
_raw = raw ;
438+
_params = params;
439+
_hash = hash;
440+
}
441+
442+
@Override
443+
public int hashCode() { return _hash; }
444+
445+
@Override
446+
public boolean equals(Object o) {
447+
if (o == this) return true;
448+
if (o == null) return false;
449+
if (o.getClass() != getClass()) return false;
450+
AsKey other = (AsKey) o;
451+
452+
if ((_hash == other._hash) && (_raw == other._raw)) {
453+
final JavaType[] otherParams = other._params;
454+
final int len = _params.length;
455+
456+
if (len == otherParams.length) {
457+
for (int i = 0; i < len; ++i) {
458+
if (!_params[i].equals(otherParams[i])) {
459+
return false;
460+
}
461+
}
462+
return true;
463+
}
464+
}
465+
return false;
466+
}
467+
468+
@Override
469+
public String toString() {
470+
return _raw.getName()+"<>";
471+
}
472+
}
413473
}

src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public final class TypeFactory
114114
* actual generic types), we will use small cache to avoid repetitive
115115
* resolution of core types
116116
*/
117-
protected final LRUMap<Class<?>, JavaType> _typeCache = new LRUMap<Class<?>, JavaType>(16, 100);
117+
protected final LRUMap<Object,JavaType> _typeCache = new LRUMap<Object,JavaType>(16, 100);
118118

119119
/*
120120
/**********************************************************
@@ -380,31 +380,17 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
380380
// our specific neeeds.
381381
// 29-Mar-2016, tatu: See [databind#1173] (and test `TypeResolverTest`)
382382
// for a case where this code does get invoked: not ideal
383+
// 29-Jun-2016, tatu: As to bindings, this works for [databind#1215], but
384+
// not certain it would reliably work... but let's hope for best for now
385+
TypeBindings tb = _bindingsForSubtype(baseType, typeParamCount, subclass);
383386
if (baseType.isInterface()) {
384-
newType = baseType.refine(subclass, TypeBindings.emptyBindings(), null,
385-
new JavaType[] { baseType });
387+
newType = baseType.refine(subclass, tb, null, new JavaType[] { baseType });
386388
} else {
387-
newType = baseType.refine(subclass, TypeBindings.emptyBindings(), baseType,
388-
NO_TYPES);
389+
newType = baseType.refine(subclass, tb, baseType, NO_TYPES);
389390
}
390391
// Only SimpleType returns null, but if so just resolve regularly
391392
if (newType == null) {
392-
// But otherwise gets bit tricky, as we need to partially resolve the type hierarchy
393-
// (hopefully passing null Class for root is ok)
394-
TypeBindings tb = null;
395-
396-
// 14-Apr-2016, tatu: One possible short-cut; if type parameter counts
397-
// match, chances are they ought to match. Let's take our chances...
398-
if (baseType.containedTypeCount() == typeParamCount) {
399-
if (typeParamCount == 1) {
400-
tb = TypeBindings.create(subclass, baseType.containedType(0));
401-
} else if (typeParamCount == 2) {
402-
tb = TypeBindings.create(subclass, baseType.containedType(0),
403-
baseType.containedType(1));
404-
}
405-
}
406-
newType = _fromClass(null, subclass,
407-
(tb == null) ? TypeBindings.emptyBindings() : tb);
393+
newType = _fromClass(null, subclass, tb);
408394
}
409395
} while (false);
410396

@@ -458,6 +444,29 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
458444
*/
459445
}
460446

447+
private TypeBindings _bindingsForSubtype(JavaType baseType, int typeParamCount, Class<?> subclass)
448+
{
449+
// But otherwise gets bit tricky, as we need to partially resolve the type hierarchy
450+
// (hopefully passing null Class for root is ok)
451+
int baseCount = baseType.containedTypeCount();
452+
if (baseCount == typeParamCount) {
453+
if (typeParamCount == 1) {
454+
return TypeBindings.create(subclass, baseType.containedType(0));
455+
}
456+
if (typeParamCount == 2) {
457+
return TypeBindings.create(subclass, baseType.containedType(0),
458+
baseType.containedType(1));
459+
}
460+
List<JavaType> types = new ArrayList<JavaType>(baseCount);
461+
for (int i = 0; i < baseCount; ++i) {
462+
types.add(baseType.containedType(i));
463+
}
464+
return TypeBindings.create(subclass, types);
465+
}
466+
// Otherwise, two choices: match N first, or empty. Do latter, for now
467+
return TypeBindings.emptyBindings();
468+
}
469+
461470
/**
462471
* Method similar to {@link #constructSpecializedType}, but that creates a
463472
* less-specific type of given type. Usually this is as simple as simply
@@ -1157,15 +1166,17 @@ protected JavaType _fromClass(ClassStack context, Class<?> rawType, TypeBindings
11571166
if (result != null) {
11581167
return result;
11591168
}
1160-
// Barring that, we may have recently constructed an instance:
1161-
// !!! TODO 16-Oct-2015, tatu: For now let's only cached non-parameterized; otherwise
1162-
// need better cache key
1163-
boolean cachable = (bindings == null) || bindings.isEmpty();
1164-
if (cachable) {
1165-
result = _typeCache.get(rawType); // ok, cache object is synced
1166-
if (result != null) {
1167-
return result;
1168-
}
1169+
// Barring that, we may have recently constructed an instance
1170+
final Object key;
1171+
if ((bindings == null) || bindings.isEmpty()) {
1172+
key = rawType;
1173+
result = _typeCache.get(key); // ok, cache object is synced
1174+
} else {
1175+
key = bindings.asKey(rawType);
1176+
}
1177+
result = _typeCache.get(key); // ok, cache object is synced
1178+
if (result != null) {
1179+
return result;
11691180
}
11701181

11711182
// 15-Oct-2015, tatu: recursive reference?
@@ -1225,10 +1236,7 @@ else if (superClass != null) {
12251236
}
12261237
}
12271238
context.resolveSelfReferences(result);
1228-
1229-
if (cachable) {
1230-
_typeCache.putIfAbsent(rawType, result); // cache object syncs
1231-
}
1239+
_typeCache.putIfAbsent(key, result); // cache object syncs
12321240
return result;
12331241
}
12341242

src/test/java/com/fasterxml/jackson/failing/TypeRefinementForMap1215Test.java renamed to src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMap1215Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.failing;
1+
package com.fasterxml.jackson.databind.jsontype;
22

33
import java.util.LinkedHashMap;
44
import java.util.Map;

0 commit comments

Comments
 (0)