diff --git a/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/ClassDetailsImpl.java b/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/ClassDetailsImpl.java index 086e7a7..7b90070 100644 --- a/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/ClassDetailsImpl.java +++ b/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/ClassDetailsImpl.java @@ -121,7 +121,7 @@ public List getImplementedInterfaces() { @Override public List getTypeParameters() { if ( typeParameters == null ) { - this.typeParameters = determineTypeParameters( typeDescription, this, getModelContext() ); + this.typeParameters = determineTypeParameters( typeDescription, getModelContext() ); } return typeParameters; } @@ -227,15 +227,15 @@ private static ClassDetails determineSuperType( .resolveClassDetails( typeDescription.getSuperClass().asRawType().getTypeName() ); } - private static TypeDetails determineGenericSuperType(TypeDescription typeDescription, ModelsContext modelsContext) { + private TypeDetails determineGenericSuperType(TypeDescription typeDescription, ModelsContext modelsContext) { if ( typeDescription.getSuperClass() == null ) { return null; } - return TypeSwitchStandard.switchType( typeDescription.getSuperClass(), modelsContext ); + return TypeSwitchStandard.switchType( typeDescription.getSuperClass(), this, modelsContext ); } - private static List determineInterfaces( + private List determineInterfaces( TypeDescription typeDescription, ModelsContext modelsContext) { final TypeList.Generic interfaceTypes = typeDescription.getInterfaces(); @@ -247,6 +247,7 @@ private static List determineInterfaces( for ( TypeDescription.Generic interfaceType : interfaceTypes ) { final TypeDetails switchedType = TypeSwitchStandard.switchType( interfaceType, + this, modelsContext ); result.add( switchedType ); @@ -254,9 +255,8 @@ private static List determineInterfaces( return result; } - private static List determineTypeParameters( + private List determineTypeParameters( TypeDescription typeDescription, - ClassDetailsImpl current, ModelsContext modelsContext) { final TypeList.Generic typeArguments = typeDescription.getTypeVariables(); if ( CollectionHelper.isEmpty( typeArguments ) ) { @@ -265,7 +265,7 @@ private static List determineTypeParameters( final ArrayList result = arrayList( typeArguments.size() ); for ( TypeDescription.Generic typeArgument : typeArguments ) { - result.add( (TypeVariableDetails) TypeSwitchStandard.switchType( typeArgument, current, modelsContext ) ); + result.add( (TypeVariableDetails) TypeSwitchStandard.switchType( typeArgument, this, modelsContext ) ); } return result; } diff --git a/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/TypeSwitchStandard.java b/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/TypeSwitchStandard.java index 86e7dad..22fb8ce 100644 --- a/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/TypeSwitchStandard.java +++ b/hibernate-models-bytebuddy/src/main/java/org/hibernate/models/bytebuddy/internal/TypeSwitchStandard.java @@ -35,10 +35,6 @@ */ public class TypeSwitchStandard implements TypeSwitch { - public static TypeDetails switchType(TypeDefinition typeDescription, ModelsContext modelsContext) { - return switchType( typeDescription, null, modelsContext ); - } - public static TypeDetails switchType(TypeDefinition typeDescription, ClassDetails declaringType, ModelsContext modelsContext) { final TypeSwitchStandard switchImpl = new TypeSwitchStandard( declaringType ); return TypeSwitcher.switchType( typeDescription, switchImpl, modelsContext ); @@ -47,7 +43,7 @@ public static TypeDetails switchType(TypeDefinition typeDescription, ClassDetail private final ClassDetails declaringType; - public TypeSwitchStandard(ClassDetails declaringType) { + private TypeSwitchStandard(ClassDetails declaringType) { this.declaringType = declaringType; } diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexClassDetails.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexClassDetails.java index 3c03cae..55b9fe3 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexClassDetails.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexClassDetails.java @@ -121,7 +121,7 @@ public List getImplementedInterfaces() { @Override public List getTypeParameters() { if ( typeParameters == null ) { - this.typeParameters = determineTypeParameters( classInfo, this, getModelContext() ); + this.typeParameters = determineTypeParameters( classInfo, getModelContext() ); } return typeParameters; } @@ -246,15 +246,15 @@ private static ClassDetails determineSuperType( .resolveClassDetails( classInfo.superClassType().name().toString() ); } - private static TypeDetails determineGenericSuperType(ClassInfo classInfo, ModelsContext modelsContext) { + private TypeDetails determineGenericSuperType(ClassInfo classInfo, ModelsContext modelsContext) { if ( classInfo.superClassType() == null ) { return null; } - return JandexTypeSwitchStandard.switchType( classInfo.superClassType(), modelsContext ); + return JandexTypeSwitchStandard.switchType( classInfo.superClassType(), this, modelsContext ); } - private static List determineInterfaces( + private List determineInterfaces( ClassInfo classInfo, ModelsContext modelsContext) { final List interfaceTypes = classInfo.interfaceTypes(); @@ -266,6 +266,7 @@ private static List determineInterfaces( for ( Type interfaceType : interfaceTypes ) { final TypeDetails switchedType = JandexTypeSwitchStandard.switchType( interfaceType, + this, modelsContext ); result.add( switchedType ); @@ -273,7 +274,7 @@ private static List determineInterfaces( return result; } - private static List determineTypeParameters(ClassInfo classInfo, JandexClassDetails current, ModelsContext modelsContext) { + private List determineTypeParameters(ClassInfo classInfo, ModelsContext modelsContext) { final List jandexTypeVariables = classInfo.typeParameters(); if ( CollectionHelper.isEmpty( jandexTypeVariables ) ) { return emptyList(); @@ -281,7 +282,7 @@ private static List determineTypeParameters(ClassInfo class final ArrayList result = arrayList( jandexTypeVariables.size() ); for ( TypeVariable jandexTypeVariable : jandexTypeVariables ) { - result.add( (TypeVariableDetails) JandexTypeSwitchStandard.switchType( jandexTypeVariable, current, modelsContext ) ); + result.add( (TypeVariableDetails) JandexTypeSwitchStandard.switchType( jandexTypeVariable, this, modelsContext ) ); } return result; } diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexTypeSwitchStandard.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexTypeSwitchStandard.java index 4c80563..3338e09 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexTypeSwitchStandard.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexTypeSwitchStandard.java @@ -43,10 +43,6 @@ * @author Steve Ebersole */ public class JandexTypeSwitchStandard implements JandexTypeSwitch { - public static TypeDetails switchType(Type type, ModelsContext modelsContext) { - assert type.kind() != Type.Kind.TYPE_VARIABLE; - return switchType( type, null, modelsContext ); - } public static TypeDetails switchType(Type type, ClassDetails declaringType, ModelsContext modelsContext) { final JandexTypeSwitchStandard genericVariableSwitch = new JandexTypeSwitchStandard( declaringType ); @@ -55,7 +51,7 @@ public static TypeDetails switchType(Type type, ClassDetails declaringType, Mode private final ClassDetails declaringType; - public JandexTypeSwitchStandard(ClassDetails declaringType) { + private JandexTypeSwitchStandard(ClassDetails declaringType) { this.declaringType = declaringType; } diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/TypeDetailsHelper.java b/hibernate-models/src/main/java/org/hibernate/models/spi/TypeDetailsHelper.java index 31f2907..bdd3773 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/TypeDetailsHelper.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/TypeDetailsHelper.java @@ -4,14 +4,14 @@ */ package org.hibernate.models.spi; -import java.util.Collections; -import java.util.List; - import org.hibernate.models.internal.ArrayTypeDetailsImpl; import org.hibernate.models.internal.ParameterizedTypeDetailsImpl; import org.hibernate.models.internal.PrimitiveKind; import org.hibernate.models.internal.util.CollectionHelper; +import java.util.Collections; +import java.util.List; + import static org.hibernate.models.internal.util.CollectionHelper.arrayList; import static org.hibernate.models.spi.StandardTypeDetails.OBJECT_TYPE_DETAILS; @@ -185,35 +185,37 @@ public static ClassDetails resolveRawClass(TypeDetails typeDetails) { * @param parameterizedType the parameterized type used to resolve the type variable's relative type * @param typeVariable the type variable to resolve * - * @return the type variable's relative type, or {@code null} if not resolved + * @return the type variable's relative type */ public static TypeDetails resolveTypeVariableFromParameterizedType( ParameterizedTypeDetails parameterizedType, TypeVariableDetails typeVariable) { final ClassDetails classDetails = parameterizedType.getRawClassDetails(); - final TypeDetails typeArgument = findMatchingTypeArgument( - classDetails.getTypeParameters(), - parameterizedType.getArguments(), - typeVariable.getIdentifier() - ); - - // If no type argument is found, or the type variable is defined by another - // class, try resolving it in the generic super type if present - if ( typeArgument == null || classDetails != typeVariable.getDeclaringType() ) { + if ( classDetails == typeVariable.getDeclaringType() ) { + // If the type variable is defined by the parameterized class, try to find the matching type argument + return findMatchingTypeArgument( + classDetails.getTypeParameters(), + parameterizedType.getArguments(), + typeVariable.getIdentifier() + ); + } + else { + // Try resolving the type variable in the generic super type final TypeDetails genericSuper = classDetails.getGenericSuperType(); final TypeDetails resolvedType = genericSuper != null ? genericSuper.resolveTypeVariable( typeVariable ) : null; - if ( typeArgument == null || resolvedType != null - && resolvedType.getTypeKind() != TypeDetails.Kind.TYPE_VARIABLE ) { - return resolvedType; + if ( resolvedType != null ) { + return resolvedType.getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE ? + parameterizedType.resolveTypeVariable( resolvedType.asTypeVariable() ) : + resolvedType; } } - // Either we found the exact type variable's argument, or the parameterized class redefines - // a type variable with the same identifier as a supertype, and it should be ignored. - // Return the matching generic type argument - return typeArgument; + throw new IllegalArgumentException( + "Unable to resolve type variable [" + typeVariable.getIdentifier() + "] from parameterized type [" + + parameterizedType.getName() + "]" + ); } private static TypeDetails findMatchingTypeArgument( diff --git a/hibernate-models/src/test/java/org/hibernate/models/testing/tests/generics/InheritanceTypeVariableTests.java b/hibernate-models/src/test/java/org/hibernate/models/testing/tests/generics/InheritanceTypeVariableTests.java index 6065bf9..80b0196 100644 --- a/hibernate-models/src/test/java/org/hibernate/models/testing/tests/generics/InheritanceTypeVariableTests.java +++ b/hibernate-models/src/test/java/org/hibernate/models/testing/tests/generics/InheritanceTypeVariableTests.java @@ -27,12 +27,16 @@ void testParameterizedHierarchy() { final ModelsContext modelsContext = createModelContext( Root.class, Base1.class, - Base2.class + Base2.class, + Level1.class, + Level2.class, + Base3.class ); final ClassDetails rootClassDetails = modelsContext.getClassDetailsRegistry().getClassDetails( Root.class.getName() ); final ClassDetails base1ClassDetails = modelsContext.getClassDetailsRegistry().getClassDetails( Base1.class.getName() ); final ClassDetails base2ClassDetails = modelsContext.getClassDetailsRegistry().getClassDetails( Base2.class.getName() ); + final ClassDetails base3ClassDetails = modelsContext.getClassDetailsRegistry().getClassDetails( Base3.class.getName() ); final FieldDetails idField = rootClassDetails.findFieldByName( "id" ); final TypeDetails idFieldType = idField.getType(); @@ -80,6 +84,35 @@ void testParameterizedHierarchy() { assertThat( resolvedClassType.isResolved() ).isTrue(); } + { + final TypeDetails concreteType = idField.resolveRelativeType( base3ClassDetails ); + assertThat( concreteType ).isInstanceOf( ClassTypeDetails.class ); + final ClassDetails concreteClassDetails = ( (ClassTypeDetails) concreteType ).getClassDetails(); + assertThat( concreteClassDetails.toJavaClass() ).isEqualTo( Long.class ); + + final ClassBasedTypeDetails resolvedClassType = idField.resolveRelativeClassType( base3ClassDetails ); + assertThat( resolvedClassType.isResolved() ).isTrue(); + assertThat( resolvedClassType.getClassDetails().toJavaClass() ).isEqualTo( Long.class ); + assertThat( resolvedClassType.isResolved() ).isTrue(); + } + + { + final ClassDetails level1Class = modelsContext.getClassDetailsRegistry().getClassDetails( Level1.class.getName() ); + final FieldDetails middleField = level1Class.findFieldByName( "middle" ); + final TypeDetails middleFieldType = middleField.getType(); + assertThat( middleFieldType.getTypeKind() ).isEqualTo( TypeDetails.Kind.TYPE_VARIABLE ); + assertThat( middleFieldType.isResolved() ).isFalse(); + + final TypeDetails concreteType = middleField.resolveRelativeType( base3ClassDetails ); + assertThat( concreteType ).isInstanceOf( ClassTypeDetails.class ); + final ClassDetails concreteClassDetails = ( (ClassTypeDetails) concreteType ).getClassDetails(); + assertThat( concreteClassDetails.toJavaClass() ).isEqualTo( Short.class ); + + final ClassBasedTypeDetails resolvedClassType = middleField.resolveRelativeClassType( base3ClassDetails ); + assertThat( resolvedClassType.isResolved() ).isTrue(); + assertThat( resolvedClassType.getClassDetails().toJavaClass() ).isEqualTo( Short.class ); + assertThat( resolvedClassType.isResolved() ).isTrue(); + } } @SuppressWarnings("unused") @@ -92,4 +125,15 @@ static class Base1 extends Root { static class Base2 extends Root { } + + // reuse same type parameter identifier as Root + static class Level1 extends Root { + I middle; + } + + static class Level2 extends Level1 { + } + + static class Base3 extends Level2 { + } }