Skip to content

Commit 6aa985b

Browse files
committed
Merge branch '2.8' into 2.9
2 parents b505162 + 4675896 commit 6aa985b

File tree

7 files changed

+221
-103
lines changed

7 files changed

+221
-103
lines changed

release-notes/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Project: jackson-databind
142142

143143
2.8.11 (not yet released)
144144

145+
145146
2.8.10 (24-Aug-2017)
146147

147148
#1657: `StdDateFormat` deserializes dates with no tz/offset as UTC instead of
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.fasterxml.jackson.databind.type;
2+
3+
import com.fasterxml.jackson.databind.JavaType;
4+
5+
/**
6+
* Helper type used when introspecting bindings for already resolved types,
7+
* needed for specialization.
8+
*
9+
* @since 2.8.11
10+
*/
11+
public class PlaceholderForType extends TypeBase
12+
{
13+
private static final long serialVersionUID = 1L;
14+
15+
protected final int _ordinal;
16+
17+
/**
18+
* Type assigned during wildcard resolution (which follows type
19+
* structure resolution)
20+
*/
21+
protected JavaType _actualType;
22+
23+
public PlaceholderForType(int ordinal)
24+
{
25+
super(Object.class, TypeBindings.emptyBindings(),
26+
TypeFactory.unknownType(), null, 1, // super-class, super-interfaces, hashCode
27+
null, null, false); // value/type handler, as-static
28+
_ordinal = ordinal;
29+
}
30+
31+
public JavaType actualType() { return _actualType; }
32+
public void actualType(JavaType t) { _actualType = t; }
33+
34+
// Override to get better diagnostics
35+
@Override
36+
protected String buildCanonicalName() {
37+
return toString();
38+
}
39+
40+
@Override
41+
public StringBuilder getGenericSignature(StringBuilder sb) {
42+
return getErasedSignature(sb);
43+
}
44+
45+
@Override
46+
public StringBuilder getErasedSignature(StringBuilder sb) {
47+
sb.append('$').append(_ordinal+1);
48+
return sb;
49+
}
50+
51+
@Override
52+
public JavaType withTypeHandler(Object h) {
53+
return _unsupported();
54+
}
55+
56+
@Override
57+
public JavaType withContentTypeHandler(Object h) {
58+
return _unsupported();
59+
}
60+
61+
@Override
62+
public JavaType withValueHandler(Object h) {
63+
return _unsupported();
64+
}
65+
66+
@Override
67+
public JavaType withContentValueHandler(Object h) {
68+
return _unsupported();
69+
}
70+
71+
@Override
72+
public JavaType withContentType(JavaType contentType) {
73+
return _unsupported();
74+
}
75+
76+
@Override
77+
public JavaType withStaticTyping() {
78+
return _unsupported();
79+
}
80+
81+
@Override
82+
public JavaType refine(Class<?> rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces) {
83+
return _unsupported();
84+
}
85+
86+
@Override
87+
protected JavaType _narrow(Class<?> subclass) {
88+
return _unsupported();
89+
}
90+
91+
@Override
92+
public boolean isContainerType() {
93+
return false;
94+
}
95+
96+
@Override
97+
public String toString() {
98+
return getErasedSignature(new StringBuilder()).toString();
99+
}
100+
101+
@Override
102+
public boolean equals(Object o) {
103+
return (o == this);
104+
}
105+
106+
private <T> T _unsupported() {
107+
throw new UnsupportedOperationException("Operation should not be attempted on "+getClass().getName());
108+
}
109+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class ReferenceType extends SimpleType
2525
* @since 2.8
2626
*/
2727
protected final JavaType _anchorType;
28-
28+
2929
protected ReferenceType(Class<?> cls, TypeBindings bindings,
3030
JavaType superClass, JavaType[] superInts, JavaType refType,
3131
JavaType anchorType,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public JavaType refine(Class<?> rawType, TypeBindings bindings,
211211
// SimpleType means something not-specialized, so:
212212
return null;
213213
}
214-
214+
215215
@Override
216216
protected String buildCanonicalName()
217217
{

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

Lines changed: 71 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -397,102 +397,94 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
397397
newType = _fromClass(null, subclass, EMPTY_BINDINGS);
398398
break;
399399
}
400-
401-
// If not, we'll need to do more thorough forward+backwards resolution. Sigh.
402-
403-
// 20-Oct-2015, tatu: Container, Map-types somewhat special. There is
404-
// a way to fully resolve and merge hierarchies; but that gets expensive
405-
// so let's, for now, try to create close-enough approximation that
406-
// is not 100% same, structurally, but has equivalent information for
407-
// our specific neeeds.
408-
// 29-Mar-2016, tatu: See [databind#1173] (and test `TypeResolverTest`)
409-
// for a case where this code does get invoked: not ideal
410-
// 29-Jun-2016, tatu: As to bindings, this works for [databind#1215], but
411-
// not certain it would reliably work... but let's hope for best for now
400+
// (4) If all else fails, do the full traversal using placeholders
412401
TypeBindings tb = _bindingsForSubtype(baseType, typeParamCount, subclass);
413-
if (baseType.isInterface()) {
414-
newType = baseType.refine(subclass, tb, null, new JavaType[] { baseType });
415-
} else {
416-
newType = baseType.refine(subclass, tb, baseType, NO_TYPES);
417-
}
418-
// Only SimpleType returns null, but if so just resolve regularly
419-
if (newType == null) {
420-
newType = _fromClass(null, subclass, tb);
421-
}
402+
newType = _fromClass(null, subclass, tb);
403+
422404
} while (false);
423405

424406
// 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get
425407
// copied as well
426408
newType = newType.withHandlersFrom(baseType);
427409
return newType;
410+
}
428411

429-
// 20-Oct-2015, tatu: Old simplistic approach
412+
private TypeBindings _bindingsForSubtype(JavaType baseType, int typeParamCount, Class<?> subclass)
413+
{
414+
PlaceholderForType[] placeholders = new PlaceholderForType[typeParamCount];
415+
for (int i = 0; i < typeParamCount; ++i) {
416+
placeholders[i] = new PlaceholderForType(i);
417+
}
418+
TypeBindings b = TypeBindings.create(subclass, placeholders);
419+
// First: pseudo-resolve to get placeholders in place:
420+
JavaType tmpSub = _fromClass(null, subclass, b);
421+
// Then find super-type
422+
JavaType baseWithPlaceholders = tmpSub.findSuperType(baseType.getRawClass());
423+
if (baseWithPlaceholders == null) { // should be found but...
424+
throw new IllegalArgumentException(String.format(
425+
"Internal error: unable to locate supertype (%s) from resolved subtype %s", baseType.getRawClass().getName(),
426+
subclass.getName()));
427+
}
428+
// and traverse type hierarchies to both verify and to resolve placeholders
429+
String error = _resolveTypePlaceholders(baseType, baseWithPlaceholders);
430+
if (error != null) {
431+
throw new IllegalArgumentException("Failed to specialize base type "+baseType.toCanonical()+" as "
432+
+subclass.getName()+", problem: "+error);
433+
}
430434

431-
/*
432-
// Currently mostly SimpleType instances can become something else
433-
if (baseType instanceof SimpleType) {
434-
// and only if subclass is an array, Collection or Map
435-
if (subclass.isArray()
436-
|| Map.class.isAssignableFrom(subclass)
437-
|| Collection.class.isAssignableFrom(subclass)) {
438-
// need to assert type compatibility...
439-
if (!baseType.getRawClass().isAssignableFrom(subclass)) {
440-
throw new IllegalArgumentException("Class "+subclass.getClass().getName()+" not subtype of "+baseType);
441-
}
442-
// this _should_ work, right?
443-
JavaType subtype = _fromClass(null, subclass, EMPTY_BINDINGS);
444-
// one more thing: handlers to copy?
445-
Object h = baseType.getValueHandler();
446-
if (h != null) {
447-
subtype = subtype.withValueHandler(h);
448-
}
449-
h = baseType.getTypeHandler();
450-
if (h != null) {
451-
subtype = subtype.withTypeHandler(h);
452-
}
453-
return subtype;
435+
final JavaType[] typeParams = new JavaType[typeParamCount];
436+
for (int i = 0; i < typeParamCount; ++i) {
437+
JavaType t = placeholders[i].actualType();
438+
// 18-Oct-2017, tatu: Looks like sometimes we have incomplete bindings (even if not
439+
// common, it is possible if subtype is type-erased class with added type
440+
// variable -- see test(s) with "bogus" type(s)).
441+
if (t == null) {
442+
t = unknownType();
454443
}
444+
typeParams[i] = t;
455445
}
456-
// But there is the need for special case for arrays too, it seems
457-
if (baseType instanceof ArrayType) {
458-
if (subclass.isArray()) {
459-
// actually see if it might be a no-op first:
460-
ArrayType at = (ArrayType) baseType;
461-
Class<?> rawComp = subclass.getComponentType();
462-
if (at.getContentType().getRawClass() == rawComp) {
463-
return baseType;
464-
}
465-
JavaType componentType = _fromAny(null, rawComp, null);
466-
return ((ArrayType) baseType).withComponentType(componentType);
446+
return TypeBindings.create(subclass, typeParams);
447+
}
448+
449+
private String _resolveTypePlaceholders(JavaType sourceType, JavaType actualType)
450+
throws IllegalArgumentException
451+
{
452+
List<JavaType> expectedTypes = sourceType.getBindings().getTypeParameters();
453+
List<JavaType> actualTypes = actualType.getBindings().getTypeParameters();
454+
for (int i = 0, len = expectedTypes.size(); i < len; ++i) {
455+
JavaType exp = expectedTypes.get(i);
456+
JavaType act = actualTypes.get(i);
457+
if (!_verifyAndResolvePlaceholders(exp, act)) {
458+
return String.format("Type parameter #%d/%d differs; can not specialize %s with %s",
459+
(i+1), len, exp.toCanonical(), act.toCanonical());
467460
}
468461
}
469-
470-
// otherwise regular narrowing should work just fine
471-
return baseType.narrowBy(subclass);
472-
*/
462+
return null;
473463
}
474464

475-
private TypeBindings _bindingsForSubtype(JavaType baseType, int typeParamCount, Class<?> subclass)
465+
private boolean _verifyAndResolvePlaceholders(JavaType exp, JavaType act)
476466
{
477-
// But otherwise gets bit tricky, as we need to partially resolve the type hierarchy
478-
// (hopefully passing null Class for root is ok)
479-
int baseCount = baseType.containedTypeCount();
480-
if (baseCount == typeParamCount) {
481-
if (typeParamCount == 1) {
482-
return TypeBindings.create(subclass, baseType.containedType(0));
483-
}
484-
if (typeParamCount == 2) {
485-
return TypeBindings.create(subclass, baseType.containedType(0),
486-
baseType.containedType(1));
487-
}
488-
List<JavaType> types = new ArrayList<JavaType>(baseCount);
489-
for (int i = 0; i < baseCount; ++i) {
490-
types.add(baseType.containedType(i));
467+
// See if we have an actual type placeholder to resolve; if yes, replace
468+
if (act instanceof PlaceholderForType) {
469+
((PlaceholderForType) act).actualType(exp);
470+
return true;
471+
}
472+
// if not, try to verify compatibility. But note that we can not
473+
// use simple equality as we need to resolve recursively
474+
if (exp.getRawClass() != act.getRawClass()) {
475+
return false;
476+
}
477+
// But we can check type parameters "blindly"
478+
List<JavaType> expectedTypes = exp.getBindings().getTypeParameters();
479+
List<JavaType> actualTypes = act.getBindings().getTypeParameters();
480+
for (int i = 0, len = expectedTypes.size(); i < len; ++i) {
481+
JavaType exp2 = expectedTypes.get(i);
482+
JavaType act2 = actualTypes.get(i);
483+
if (!_verifyAndResolvePlaceholders(exp2, act2)) {
484+
return false;
491485
}
492-
return TypeBindings.create(subclass, types);
493486
}
494-
// Otherwise, two choices: match N first, or empty. Do latter, for now
495-
return EMPTY_BINDINGS;
487+
return true;
496488
}
497489

498490
/**
@@ -1427,13 +1419,12 @@ protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype,
14271419
// always of type Class: if not, need to add more code to resolve it to Class.
14281420
Type[] args = ptype.getActualTypeArguments();
14291421
int paramCount = (args == null) ? 0 : args.length;
1430-
JavaType[] pt;
14311422
TypeBindings newBindings;
14321423

14331424
if (paramCount == 0) {
14341425
newBindings = EMPTY_BINDINGS;
14351426
} else {
1436-
pt = new JavaType[paramCount];
1427+
JavaType[] pt = new JavaType[paramCount];
14371428
for (int i = 0; i < paramCount; ++i) {
14381429
pt[i] = _fromAny(context, args[i], parentBindings);
14391430
}

src/test/java/com/fasterxml/jackson/failing/NestedTypes1604Test.java renamed to src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.failing;
1+
package com.fasterxml.jackson.databind.type;
22

33
import java.util.ArrayList;
44
import java.util.List;
@@ -38,12 +38,15 @@ public DataList(List<T> data) {
3838
}
3939
}
4040

41-
public static class RefinedDataList<T> extends Data<List<T>> {
41+
// And then add one level between types
42+
public static class RefinedDataList<T> extends DataList<T> {
4243
public RefinedDataList(List<T> data) {
4344
super(data);
4445
}
4546
}
4647

48+
// And/or add another type parameter that is not relevant (less common
49+
// but potential concern)
4750
public static class SneakyDataList<BOGUS,T> extends Data<List<T>> {
4851
public SneakyDataList(List<T> data) {
4952
super(data);

0 commit comments

Comments
 (0)