@@ -399,102 +399,94 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
399399 newType = _fromClass (null , subclass , TypeBindings .emptyBindings ());
400400 break ;
401401 }
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
414403 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+
424406 } while (false );
425407
426408 // 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get
427409 // copied as well
428410 newType = newType .withHandlersFrom (baseType );
429411 return newType ;
412+ }
430413
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 );
457419 }
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 );
470435 }
471436
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 );
475449 }
476450
477- private TypeBindings _bindingsForSubtype (JavaType baseType , int typeParamCount , Class <?> subclass )
451+ private String _resolveTypePlaceholders (JavaType sourceType , JavaType actualType )
452+ throws IllegalArgumentException
478453 {
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 ());
489462 }
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 ;
493487 }
494- return TypeBindings .create (subclass , types );
495488 }
496- // Otherwise, two choices: match N first, or empty. Do latter, for now
497- return TypeBindings .emptyBindings ();
489+ return true ;
498490 }
499491
500492 /**
@@ -1394,13 +1386,12 @@ protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype,
13941386 // always of type Class: if not, need to add more code to resolve it to Class.
13951387 Type [] args = ptype .getActualTypeArguments ();
13961388 int paramCount = (args == null ) ? 0 : args .length ;
1397- JavaType [] pt ;
13981389 TypeBindings newBindings ;
13991390
14001391 if (paramCount == 0 ) {
14011392 newBindings = EMPTY_BINDINGS ;
14021393 } else {
1403- pt = new JavaType [paramCount ];
1394+ JavaType [] pt = new JavaType [paramCount ];
14041395 for (int i = 0 ; i < paramCount ; ++i ) {
14051396 pt [i ] = _fromAny (context , args [i ], parentBindings );
14061397 }
0 commit comments