@@ -399,102 +399,94 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
399
399
newType = _fromClass (null , subclass , TypeBindings .emptyBindings ());
400
400
break ;
401
401
}
402
-
403
- // If not, we'll need to do more thorough forward+backwards resolution. Sigh.
404
-
405
- // 20-Oct-2015, tatu: Container, Map-types somewhat special. There is
406
- // a way to fully resolve and merge hierarchies; but that gets expensive
407
- // so let's, for now, try to create close-enough approximation that
408
- // is not 100% same, structurally, but has equivalent information for
409
- // our specific neeeds.
410
- // 29-Mar-2016, tatu: See [databind#1173] (and test `TypeResolverTest`)
411
- // for a case where this code does get invoked: not ideal
412
- // 29-Jun-2016, tatu: As to bindings, this works for [databind#1215], but
413
- // not certain it would reliably work... but let's hope for best for now
402
+ // (4) If all else fails, do the full traversal using placeholders
414
403
TypeBindings tb = _bindingsForSubtype (baseType , typeParamCount , subclass );
415
- if (baseType .isInterface ()) {
416
- newType = baseType .refine (subclass , tb , null , new JavaType [] { baseType });
417
- } else {
418
- newType = baseType .refine (subclass , tb , baseType , NO_TYPES );
419
- }
420
- // Only SimpleType returns null, but if so just resolve regularly
421
- if (newType == null ) {
422
- newType = _fromClass (null , subclass , tb );
423
- }
404
+ newType = _fromClass (null , subclass , tb );
405
+
424
406
} while (false );
425
407
426
408
// 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get
427
409
// copied as well
428
410
newType = newType .withHandlersFrom (baseType );
429
411
return newType ;
412
+ }
430
413
431
- // 20-Oct-2015, tatu: Old simplistic approach
432
-
433
- /*
434
- // Currently mostly SimpleType instances can become something else
435
- if (baseType instanceof SimpleType) {
436
- // and only if subclass is an array, Collection or Map
437
- if (subclass.isArray()
438
- || Map.class.isAssignableFrom(subclass)
439
- || Collection.class.isAssignableFrom(subclass)) {
440
- // need to assert type compatibility...
441
- if (!baseType.getRawClass().isAssignableFrom(subclass)) {
442
- throw new IllegalArgumentException("Class "+subclass.getClass().getName()+" not subtype of "+baseType);
443
- }
444
- // this _should_ work, right?
445
- JavaType subtype = _fromClass(null, subclass, TypeBindings.emptyBindings());
446
- // one more thing: handlers to copy?
447
- Object h = baseType.getValueHandler();
448
- if (h != null) {
449
- subtype = subtype.withValueHandler(h);
450
- }
451
- h = baseType.getTypeHandler();
452
- if (h != null) {
453
- subtype = subtype.withTypeHandler(h);
454
- }
455
- return subtype;
456
- }
414
+ private TypeBindings _bindingsForSubtype (JavaType baseType , int typeParamCount , Class <?> subclass )
415
+ {
416
+ PlaceholderForType [] placeholders = new PlaceholderForType [typeParamCount ];
417
+ for (int i = 0 ; i < typeParamCount ; ++i ) {
418
+ placeholders [i ] = new PlaceholderForType (i );
457
419
}
458
- // But there is the need for special case for arrays too, it seems
459
- if (baseType instanceof ArrayType) {
460
- if (subclass.isArray()) {
461
- // actually see if it might be a no-op first:
462
- ArrayType at = (ArrayType) baseType;
463
- Class<?> rawComp = subclass.getComponentType();
464
- if (at.getContentType().getRawClass() == rawComp) {
465
- return baseType;
466
- }
467
- JavaType componentType = _fromAny(null, rawComp, null);
468
- return ((ArrayType) baseType).withComponentType(componentType);
469
- }
420
+ TypeBindings b = TypeBindings .create (subclass , placeholders );
421
+ // First: pseudo-resolve to get placeholders in place:
422
+ JavaType tmpSub = _fromClass (null , subclass , b );
423
+ // Then find super-type
424
+ JavaType baseWithPlaceholders = tmpSub .findSuperType (baseType .getRawClass ());
425
+ if (baseWithPlaceholders == null ) { // should be found but...
426
+ throw new IllegalArgumentException (String .format (
427
+ "Internal error: unable to locate supertype (%s) from resolved subtype %s" , baseType .getRawClass ().getName (),
428
+ subclass .getName ()));
429
+ }
430
+ // and traverse type hierarchies to both verify and to resolve placeholders
431
+ String error = _resolveTypePlaceholders (baseType , baseWithPlaceholders );
432
+ if (error != null ) {
433
+ throw new IllegalArgumentException ("Failed to specialize base type " +baseType .toCanonical ()+" as "
434
+ +subclass .getName ()+", problem: " +error );
470
435
}
471
436
472
- // otherwise regular narrowing should work just fine
473
- return baseType.narrowBy(subclass);
474
- */
437
+ final JavaType [] typeParams = new JavaType [typeParamCount ];
438
+ for (int i = 0 ; i < typeParamCount ; ++i ) {
439
+ JavaType t = placeholders [i ].actualType ();
440
+ // 18-Oct-2017, tatu: Looks like sometimes we have incomplete bindings (even if not
441
+ // common, it is possible if subtype is type-erased class with added type
442
+ // variable -- see test(s) with "bogus" type(s)).
443
+ if (t == null ) {
444
+ t = unknownType ();
445
+ }
446
+ typeParams [i ] = t ;
447
+ }
448
+ return TypeBindings .create (subclass , typeParams );
475
449
}
476
450
477
- private TypeBindings _bindingsForSubtype (JavaType baseType , int typeParamCount , Class <?> subclass )
451
+ private String _resolveTypePlaceholders (JavaType sourceType , JavaType actualType )
452
+ throws IllegalArgumentException
478
453
{
479
- // But otherwise gets bit tricky, as we need to partially resolve the type hierarchy
480
- // (hopefully passing null Class for root is ok)
481
- int baseCount = baseType .containedTypeCount ();
482
- if (baseCount == typeParamCount ) {
483
- if (typeParamCount == 1 ) {
484
- return TypeBindings .create (subclass , baseType .containedType (0 ));
485
- }
486
- if (typeParamCount == 2 ) {
487
- return TypeBindings .create (subclass , baseType .containedType (0 ),
488
- baseType .containedType (1 ));
454
+ List <JavaType > expectedTypes = sourceType .getBindings ().getTypeParameters ();
455
+ List <JavaType > actualTypes = actualType .getBindings ().getTypeParameters ();
456
+ for (int i = 0 , len = expectedTypes .size (); i < len ; ++i ) {
457
+ JavaType exp = expectedTypes .get (i );
458
+ JavaType act = actualTypes .get (i );
459
+ if (!_verifyAndResolvePlaceholders (exp , act )) {
460
+ return String .format ("Type parameter #%d/%d differs; can not specialize %s with %s" ,
461
+ (i +1 ), len , exp .toCanonical (), act .toCanonical ());
489
462
}
490
- List <JavaType > types = new ArrayList <JavaType >(baseCount );
491
- for (int i = 0 ; i < baseCount ; ++i ) {
492
- types .add (baseType .containedType (i ));
463
+ }
464
+ return null ;
465
+ }
466
+
467
+ private boolean _verifyAndResolvePlaceholders (JavaType exp , JavaType act )
468
+ {
469
+ // See if we have an actual type placeholder to resolve; if yes, replace
470
+ if (act instanceof PlaceholderForType ) {
471
+ ((PlaceholderForType ) act ).actualType (exp );
472
+ return true ;
473
+ }
474
+ // if not, try to verify compatibility. But note that we can not
475
+ // use simple equality as we need to resolve recursively
476
+ if (exp .getRawClass () != act .getRawClass ()) {
477
+ return false ;
478
+ }
479
+ // But we can check type parameters "blindly"
480
+ List <JavaType > expectedTypes = exp .getBindings ().getTypeParameters ();
481
+ List <JavaType > actualTypes = act .getBindings ().getTypeParameters ();
482
+ for (int i = 0 , len = expectedTypes .size (); i < len ; ++i ) {
483
+ JavaType exp2 = expectedTypes .get (i );
484
+ JavaType act2 = actualTypes .get (i );
485
+ if (!_verifyAndResolvePlaceholders (exp2 , act2 )) {
486
+ return false ;
493
487
}
494
- return TypeBindings .create (subclass , types );
495
488
}
496
- // Otherwise, two choices: match N first, or empty. Do latter, for now
497
- return TypeBindings .emptyBindings ();
489
+ return true ;
498
490
}
499
491
500
492
/**
@@ -1394,13 +1386,12 @@ protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype,
1394
1386
// always of type Class: if not, need to add more code to resolve it to Class.
1395
1387
Type [] args = ptype .getActualTypeArguments ();
1396
1388
int paramCount = (args == null ) ? 0 : args .length ;
1397
- JavaType [] pt ;
1398
1389
TypeBindings newBindings ;
1399
1390
1400
1391
if (paramCount == 0 ) {
1401
1392
newBindings = EMPTY_BINDINGS ;
1402
1393
} else {
1403
- pt = new JavaType [paramCount ];
1394
+ JavaType [] pt = new JavaType [paramCount ];
1404
1395
for (int i = 0 ; i < paramCount ; ++i ) {
1405
1396
pt [i ] = _fromAny (context , args [i ], parentBindings );
1406
1397
}
0 commit comments