@@ -397,102 +397,94 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
397
397
newType = _fromClass (null , subclass , EMPTY_BINDINGS );
398
398
break ;
399
399
}
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
412
401
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
+
422
404
} while (false );
423
405
424
406
// 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get
425
407
// copied as well
426
408
newType = newType .withHandlersFrom (baseType );
427
409
return newType ;
410
+ }
428
411
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
+ }
430
434
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 ();
454
443
}
444
+ typeParams [i ] = t ;
455
445
}
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 ());
467
460
}
468
461
}
469
-
470
- // otherwise regular narrowing should work just fine
471
- return baseType.narrowBy(subclass);
472
- */
462
+ return null ;
473
463
}
474
464
475
- private TypeBindings _bindingsForSubtype (JavaType baseType , int typeParamCount , Class <?> subclass )
465
+ private boolean _verifyAndResolvePlaceholders (JavaType exp , JavaType act )
476
466
{
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 ;
491
485
}
492
- return TypeBindings .create (subclass , types );
493
486
}
494
- // Otherwise, two choices: match N first, or empty. Do latter, for now
495
- return EMPTY_BINDINGS ;
487
+ return true ;
496
488
}
497
489
498
490
/**
@@ -1427,13 +1419,12 @@ protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype,
1427
1419
// always of type Class: if not, need to add more code to resolve it to Class.
1428
1420
Type [] args = ptype .getActualTypeArguments ();
1429
1421
int paramCount = (args == null ) ? 0 : args .length ;
1430
- JavaType [] pt ;
1431
1422
TypeBindings newBindings ;
1432
1423
1433
1424
if (paramCount == 0 ) {
1434
1425
newBindings = EMPTY_BINDINGS ;
1435
1426
} else {
1436
- pt = new JavaType [paramCount ];
1427
+ JavaType [] pt = new JavaType [paramCount ];
1437
1428
for (int i = 0 ; i < paramCount ; ++i ) {
1438
1429
pt [i ] = _fromAny (context , args [i ], parentBindings );
1439
1430
}
0 commit comments