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 990cca4..ac46d8c 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 @@ -8,15 +8,18 @@ import java.util.ArrayList; import java.util.List; +import org.hibernate.models.DynamicClassException; import org.hibernate.models.internal.ClassDetailsSupport; import org.hibernate.models.internal.jdk.SerialJdkClassDetails; import org.hibernate.models.internal.util.CollectionHelper; import org.hibernate.models.serial.spi.SerialClassDetails; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableDetails; @@ -212,16 +215,21 @@ public void addMethod(MethodDetails methodDetails) { @Override public Class toJavaClass() { if ( javaClass == null ) { - if ( getClassName() == null ) { - throw new UnsupportedOperationException( "Not supported" ); - } - MODELS_CLASS_LOGGER.debugf( "Loading `%s` on to classloader from Jandex ClassDetails", getClassName() ); - javaClass = getModelContext().getClassLoading().classForName( getClassName() ); + javaClass = toJavaClass( getModelContext().getClassLoading(), getModelContext() ); } //noinspection unchecked return (Class) javaClass; } + @Override + public Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext) { + if ( getClassName() == null ) { + throw new DynamicClassException( "ClassDetails (name=" + getName() + ") did not specify a class-name" ); + } + MODELS_CLASS_LOGGER.debugf( "Loading `%s` on to classloader from Jandex ClassDetails", getClassName() ); + return classLoading.classForName( getClassName() ); + } + @Override public String toString() { return "JandexClassDetails(" + classInfo.name().toString() + ")"; diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexFieldDetails.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexFieldDetails.java index 8e2fea0..24df82c 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexFieldDetails.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexFieldDetails.java @@ -6,20 +6,22 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.Member; import java.util.Collection; import java.util.Map; import org.hibernate.models.IllegalCastException; +import org.hibernate.models.ModelsException; import org.hibernate.models.internal.AnnotationTargetSupport; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.jboss.jandex.AnnotationTarget; @@ -86,25 +88,32 @@ public int getModifiers() { return fieldInfo.flags(); } - private Member underlyingMember; + private Field underlyingMember; @Override - public Member toJavaMember() { + public Field toJavaMember() { if ( underlyingMember == null ) { - underlyingMember = resolveJavaMember(); + final Class declaringJavaClass = declaringType.toJavaClass(); + underlyingMember = resolveJavaMember( declaringJavaClass); } return underlyingMember; } - private Field resolveJavaMember() { - final Class declaringJavaClass = declaringType.toJavaClass(); + @Override + public Field toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + // make sure the type ends up on the given class-loading + type.determineRawClass().toJavaClass( classLoading, modelContext ); + return resolveJavaMember( declaringClass ); + } + + private Field resolveJavaMember(Class declaringJavaClass) { try { return declaringJavaClass.getField( fieldInfo.name() ); } catch (NoSuchFieldException e) { - throw new RuntimeException( + throw new ModelsException( String.format( - "Jandex FieldInfo had no corresponding Field : %s.%s", + "Unable to locate field `%s` on %s", declaringType.getName(), fieldInfo.name() ), diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexMethodDetails.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexMethodDetails.java index 58db7fb..29e9a0e 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexMethodDetails.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexMethodDetails.java @@ -13,7 +13,9 @@ import java.util.Map; import org.hibernate.models.IllegalCastException; +import org.hibernate.models.internal.util.ReflectionHelper; import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; @@ -23,6 +25,7 @@ import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeDetailsHelper; import org.hibernate.models.spi.TypeVariableScope; @@ -126,16 +129,6 @@ public int getModifiers() { return methodInfo.flags(); } - private Method underlyingMethod; - - @Override - public Method toJavaMember() { - if ( underlyingMethod == null ) { - underlyingMethod = resolveJavaMember(); - } - return underlyingMethod; - } - @Override public TypeDetails resolveRelativeType(TypeVariableScope container) { if ( methodKind == GETTER || methodKind == SETTER ) { @@ -152,36 +145,31 @@ public ClassBasedTypeDetails resolveRelativeClassType(TypeVariableScope containe throw new IllegalStateException( "Method does not have a type - " + this ); } - private Method resolveJavaMember() { - final Class declaringTypeClass = declaringType.toJavaClass(); - methods: for ( Method method : declaringTypeClass.getDeclaredMethods() ) { - if ( !method.getName().equals( methodInfo.name() ) ) { - continue; - } - - if ( method.getParameterCount() != methodInfo.parametersCount() ) { - continue; - } - - for ( int i = 0; i < method.getParameterTypes().length; i++ ) { - final Class methodParameterType = method.getParameterTypes()[i]; - final Type expectedType = methodInfo.parameterType( i ); - if ( !methodParameterType.getName().equals( expectedType.name().toString() ) ) { - continue methods; - } - } + private Method underlyingMethod; - // if we get here, we've found it - return method; + @Override + public Method toJavaMember() { + if ( underlyingMethod == null ) { + final Class declaringClass = declaringType.toJavaClass(); + underlyingMethod = ReflectionHelper.resolveJavaMember( + this, + declaringClass, + getModelContext().getClassLoading(), + getModelContext() + ); } + return underlyingMethod; + } - throw new RuntimeException( - String.format( - "Jandex FieldInfo had no corresponding Field : %s.%s", - declaringType.getName(), - methodInfo.name() - ) - ); } + @Override + public Method toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + return ReflectionHelper.resolveJavaMember( + this, + declaringClass, + getModelContext().getClassLoading(), + getModelContext() + ); + } @Override public ClassDetails getReturnType() { diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexRecordComponentDetails.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexRecordComponentDetails.java index 87b2476..257ee12 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexRecordComponentDetails.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexRecordComponentDetails.java @@ -12,12 +12,14 @@ import org.hibernate.models.IllegalCastException; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.jboss.jandex.AnnotationTarget; @@ -89,6 +91,12 @@ public Member toJavaMember() { return null; } + @Override + public Member toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + // we could maybe resolve the corresponding method... + return null; + } + @Override public String toString() { return "JandexFieldDetails(" + getName() + ")"; diff --git a/hibernate-models/src/main/java/org/hibernate/models/DynamicClassException.java b/hibernate-models/src/main/java/org/hibernate/models/DynamicClassException.java new file mode 100644 index 0000000..8eafc3b --- /dev/null +++ b/hibernate-models/src/main/java/org/hibernate/models/DynamicClassException.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models; + +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; +import org.hibernate.models.spi.SourceModelContext; + +/** + * Generally indicates an attempt to resolve a {@linkplain ClassDetails} + * into the corresponding Java {@linkplain Class} when the ClassDetails does not + * specify a {@linkplain ClassDetails#getClassName() class name}. + * + * @see ClassDetails#toJavaClass() + * @see ClassDetails#toJavaClass(ClassLoading, SourceModelContext) + * + * @author Steve Ebersole + */ +public class DynamicClassException extends ModelsException { + public DynamicClassException(String message) { + super( message ); + } + + public DynamicClassException(String message, Throwable cause) { + super( message, cause ); + } +} diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java index 62465ce..ba529d5 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java @@ -13,10 +13,12 @@ import org.hibernate.models.serial.spi.SerialClassDetails; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableDetails; @@ -198,6 +200,11 @@ public Class toJavaClass() { throw new UnsupportedOperationException( "Missing package-info [" + packageInfoClassName + "] cannot be converted to a Java Class" ); } + @Override + public Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext) { + return toJavaClass(); + } + @Override public SerialClassDetails toStorableForm() { return new SerialFormImpl( packageName, packageInfoClassName ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassDetails.java index 3bf4d06..7942337 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassDetails.java @@ -15,10 +15,12 @@ import org.hibernate.models.serial.spi.SerialClassDetails; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableDetails; @@ -69,6 +71,11 @@ public Class toJavaClass() { return (Class) clazz; } + @Override + public Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext) { + return classLoading.classForName( clazz.getName() ); + } + @Override public String toString() { return "ClassDetails(" + clazz.getName() + ")"; diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java b/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java index de2f351..6797ff3 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java @@ -45,11 +45,6 @@ public Class findClassForName(String name) { } } - @Override - public Package packageForName(String name) { - return getClass().getClassLoader().getDefinedPackage( name ); - } - @Override public URL locateResource(String resourceName) { return getClass().getClassLoader().getResource( resourceName ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicClassDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicClassDetails.java index eae133b..7ace89c 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicClassDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicClassDetails.java @@ -8,14 +8,17 @@ import java.util.Collections; import java.util.List; +import org.hibernate.models.DynamicClassException; import org.hibernate.models.internal.ClassDetailsSupport; import org.hibernate.models.internal.ClassTypeDetailsImpl; import org.hibernate.models.serial.spi.SerialClassDetails; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableDetails; @@ -221,10 +224,16 @@ public DynamicFieldDetails applyAttribute( @Override public Class toJavaClass() { + return toJavaClass( getModelContext().getClassLoading(), getModelContext() ); + } + + @Override + public Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext) { if ( javaType == null ) { - if ( className != null ) { - javaType = getModelContext().getClassLoading().classForName( className ); + if ( className == null ) { + throw new DynamicClassException( "ClassDetails (name=" + name + ") did not specify a class-name" ); } + javaType = getModelContext().getClassLoading().classForName( className ); } //noinspection unchecked return (Class) javaType; diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicFieldDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicFieldDetails.java index 2fbd5ed..3175084 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicFieldDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicFieldDetails.java @@ -5,11 +5,14 @@ package org.hibernate.models.internal.dynamic; import java.lang.annotation.Annotation; -import java.lang.reflect.Member; +import java.lang.reflect.Field; +import java.util.Locale; import org.hibernate.models.IllegalCastException; +import org.hibernate.models.ModelsException; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MemberDetails; import org.hibernate.models.spi.MethodDetails; @@ -17,6 +20,7 @@ import org.hibernate.models.spi.MutableMemberDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableScope; @@ -97,9 +101,35 @@ public int getModifiers() { return modifierFlags; } + private Field field; + + @Override + public Field toJavaMember() { + if ( field == null && getDeclaringType().getClassName() != null ) { + final Class declaringClass = getDeclaringType().toJavaClass(); + field = toJavaMember( declaringClass, getModelContext().getClassLoading(), getModelContext() ); + } + return field; + } + @Override - public Member toJavaMember() { - return null; + public Field toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + try { + // make sure the type ends up on the given class-loading + type.determineRawClass().toJavaClass( classLoading, modelContext ); + return declaringClass.getDeclaredField( getName() ); + } + catch (NoSuchFieldException e) { + throw new ModelsException( + String.format( + Locale.ROOT, + "Unable to locate field `%s` on %s", + getName(), + declaringClass.getName() + ), + e + ); + } } @Override diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicMethodDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicMethodDetails.java index e6a27cd..fc4fdab 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicMethodDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/dynamic/DynamicMethodDetails.java @@ -5,13 +5,14 @@ package org.hibernate.models.internal.dynamic; import java.lang.annotation.Annotation; -import java.lang.reflect.Member; +import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; import org.hibernate.models.IllegalCastException; import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; @@ -19,9 +20,12 @@ import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableScope; +import static org.hibernate.models.internal.util.ReflectionHelper.resolveJavaMember; + /** * MethodDetails which does not necessarily map to a physical Method (dynamic models) * @@ -121,9 +125,20 @@ public int getModifiers() { return modifierFlags; } + private Method method; + + @Override + public Method toJavaMember() { + if ( method == null && declaringType.getClassName() != null ) { + final Class declaringClass = declaringType.toJavaClass(); + method = toJavaMember( declaringClass, getModelContext().getClassLoading(), getModelContext() ); + } + return method; + } + @Override - public Member toJavaMember() { - return null; + public Method toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + return resolveJavaMember( this, declaringClass, classLoading, modelContext ); } @Override diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkClassDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkClassDetails.java index 914fc31..fae911b 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkClassDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkClassDetails.java @@ -19,10 +19,12 @@ import org.hibernate.models.serial.spi.SerialClassDetails; import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.ClassDetailsRegistry; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeVariableDetails; @@ -94,6 +96,11 @@ public Class toJavaClass() { return (Class) managedClass; } + @Override + public Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext) { + return classLoading.classForName( getClassName() ); + } + @Override public boolean isAbstract() { return Modifier.isAbstract( managedClass.getModifiers() ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkFieldDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkFieldDetails.java index 2622657..2a36675 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkFieldDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkFieldDetails.java @@ -7,10 +7,13 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Collection; +import java.util.Locale; import java.util.Map; import org.hibernate.models.IllegalCastException; +import org.hibernate.models.ModelsException; import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; @@ -18,6 +21,7 @@ import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; @@ -64,6 +68,29 @@ public Field toJavaMember() { return field; } + @Override + public Field toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + if ( declaringClass == field.getDeclaringClass() ) { + return field; + } + try { + // make sure the type ends up on the given class-loading + type.determineRawClass().toJavaClass( classLoading, modelContext ); + return declaringClass.getDeclaredField( getName() ); + } + catch (NoSuchFieldException e) { + throw new ModelsException( + String.format( + Locale.ROOT, + "Unable to locate field `%s` on %s", + getName(), + declaringClass.getName() + ), + e + ); + } + } + @Override public boolean isPlural() { return isPlural; diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkMethodDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkMethodDetails.java index 3ba17a2..2df3575 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkMethodDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkMethodDetails.java @@ -14,6 +14,7 @@ import org.hibernate.models.IllegalCastException; import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; @@ -23,10 +24,12 @@ import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; import org.hibernate.models.spi.TypeDetailsHelper; import org.hibernate.models.spi.TypeVariableScope; +import static org.hibernate.models.internal.util.ReflectionHelper.resolveJavaMember; import static org.hibernate.models.spi.MethodDetails.MethodKind.GETTER; import static org.hibernate.models.spi.MethodDetails.MethodKind.SETTER; @@ -128,6 +131,14 @@ public Method toJavaMember() { return method; } + @Override + public Method toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + if ( declaringClass == method.getDeclaringClass() ) { + return method; + } + return resolveJavaMember( this, declaringClass, classLoading, modelContext ); + } + @Override public TypeDetails resolveRelativeType(TypeVariableScope container) { if ( methodKind == GETTER || methodKind == SETTER ) { diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkRecordComponentDetails.java b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkRecordComponentDetails.java index ff3ef1c..dc665aa 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkRecordComponentDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/jdk/JdkRecordComponentDetails.java @@ -12,6 +12,7 @@ import org.hibernate.models.IllegalCastException; import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassLoading; import org.hibernate.models.spi.FieldDetails; import org.hibernate.models.spi.MethodDetails; import org.hibernate.models.spi.MutableClassDetails; @@ -19,6 +20,7 @@ import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.RecordComponentDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import org.hibernate.models.spi.TypeDetails; /** @@ -79,6 +81,11 @@ public Member toJavaMember() { return null; } + @Override + public Member toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext) { + return null; + } + @Override public int getModifiers() { return recordComponent.getAccessor().getModifiers(); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/util/ReflectionHelper.java b/hibernate-models/src/main/java/org/hibernate/models/internal/util/ReflectionHelper.java new file mode 100644 index 0000000..b69ee85 --- /dev/null +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/util/ReflectionHelper.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.internal.util; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Locale; + +import org.hibernate.models.ModelsException; +import org.hibernate.models.spi.ClassLoading; +import org.hibernate.models.spi.MethodDetails; +import org.hibernate.models.spi.SourceModelContext; + +/** + * @author Steve Ebersole + */ +public class ReflectionHelper { + public static Method resolveJavaMember( + MethodDetails methodDetails, + Class declaringClass, + ClassLoading classLoading, + SourceModelContext modelContext) { + final MethodDetails.MethodKind methodKind = methodDetails.getMethodKind(); + try { + if ( methodKind == MethodDetails.MethodKind.GETTER ) { + // make sure the type ends up on the given class-loading + methodDetails.getType().determineRawClass().toJavaClass( classLoading, modelContext ); + return declaringClass.getDeclaredMethod( methodDetails.getName() ); + } + else if ( methodKind == MethodDetails.MethodKind.SETTER ) { + final Class memberTypeClass = methodDetails.getType().determineRawClass().toJavaClass( classLoading, modelContext ); + return declaringClass.getDeclaredMethod( methodDetails.getName(), memberTypeClass ); + } + else { + final List> argumentClasses = CollectionHelper.arrayList( methodDetails.getArgumentTypes().size() ); + methodDetails.getArgumentTypes().forEach( (argumentClassDetails) -> { + argumentClasses.add( argumentClassDetails.toJavaClass( classLoading, modelContext ) ); + } ); + return declaringClass.getDeclaredMethod( methodDetails.getName(), argumentClasses.toArray( new Class[0] ) ); + } + } + catch (NoSuchMethodException e) { + throw new ModelsException( + String.format( + Locale.ROOT, + "Unable to locate method `%s` on %s", + methodDetails.getName(), + declaringClass.getName() + ), + e + ); + } + + } +} diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/ClassDetails.java b/hibernate-models/src/main/java/org/hibernate/models/spi/ClassDetails.java index cb91904..4da1e0d 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/ClassDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/ClassDetails.java @@ -10,6 +10,7 @@ import java.util.function.Predicate; import org.hibernate.models.IllegalCastException; +import org.hibernate.models.DynamicClassException; import org.hibernate.models.internal.AnnotationTargetHelper; import org.hibernate.models.internal.SimpleClassDetails; import org.hibernate.models.internal.util.IndexedConsumer; @@ -290,10 +291,29 @@ default void forEachPersistableMember(Consumer consumer) { } /** - * Know what you are doing before calling this method + * Load the corresponding {@linkplain Class} using standard + * {@linkplain ClassLoading}. + * + * @see SourceModelBuildingContext#getClassLoading() + * + * @apiNote Know what you are doing before calling this method + * + * @throws DynamicClassException If this ClassDetails does not correspond to a Java class + * (generally meaning {@linkplain #getClassName()} returns {@code null}). */ Class toJavaClass(); + /** + * Load the corresponding {@linkplain Class} using the specified{@linkplain ClassLoading}. + * + * @param classLoading Where to load the class from/into + * @param modelContext The model context + * + * @throws DynamicClassException If this ClassDetails does not correspond to a Java class + * (generally meaning {@linkplain #getClassName()} returns {@code null}). + */ + Class toJavaClass(ClassLoading classLoading, SourceModelContext modelContext); + @Override default ClassDetails asClassDetails() { return this; diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/ClassLoading.java b/hibernate-models/src/main/java/org/hibernate/models/spi/ClassLoading.java index 82be43e..9ed07ce 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/ClassLoading.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/ClassLoading.java @@ -33,8 +33,6 @@ public interface ClassLoading { */ Class findClassForName(String name); - Package packageForName(String name); - /** * Locate a resource by name * diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/FieldDetails.java b/hibernate-models/src/main/java/org/hibernate/models/spi/FieldDetails.java index 48e781c..0896ae4 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/FieldDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/FieldDetails.java @@ -4,6 +4,8 @@ */ package org.hibernate.models.spi; +import java.lang.reflect.Field; + import org.hibernate.models.IllegalCastException; import org.hibernate.models.internal.ModifierUtils; @@ -28,6 +30,12 @@ default boolean isPersistable() { return ModifierUtils.hasPersistableFieldModifiers( getModifiers() ); } + @Override + Field toJavaMember(); + + @Override + Field toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext); + @Override default FieldDetails asFieldDetails() { return this; diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/MemberDetails.java b/hibernate-models/src/main/java/org/hibernate/models/spi/MemberDetails.java index 4648d2e..ca24835 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/MemberDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/MemberDetails.java @@ -220,6 +220,8 @@ default boolean isField() { */ Member toJavaMember(); + Member toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext); + /** * Determine the type of the member relative to the given {@code container} type. *

diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/MethodDetails.java b/hibernate-models/src/main/java/org/hibernate/models/spi/MethodDetails.java index 7391181..d1d66f2 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/MethodDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/MethodDetails.java @@ -5,6 +5,7 @@ package org.hibernate.models.spi; import java.beans.Introspector; +import java.lang.reflect.Method; import java.util.List; import org.hibernate.models.IllegalCastException; @@ -54,6 +55,12 @@ else if ( methodName.startsWith( "get" ) ) { return null; } + @Override + Method toJavaMember(); + + @Override + Method toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext); + @Override default FieldDetails asFieldDetails() { throw new IllegalCastException( "MethodDetails cannot be cast to FieldDetails" ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/RecordComponentDetails.java b/hibernate-models/src/main/java/org/hibernate/models/spi/RecordComponentDetails.java index af2a11d..423b2c8 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/RecordComponentDetails.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/RecordComponentDetails.java @@ -4,6 +4,8 @@ */ package org.hibernate.models.spi; +import java.lang.reflect.Member; + import org.hibernate.models.IllegalCastException; import static org.hibernate.models.spi.AnnotationTarget.Kind.RECORD_COMPONENT; @@ -29,6 +31,12 @@ default boolean isPersistable() { return true; } + @Override + Member toJavaMember(); + + @Override + Member toJavaMember(Class declaringClass, ClassLoading classLoading, SourceModelContext modelContext); + @Override default FieldDetails asFieldDetails() { throw new IllegalCastException( "RecordComponentDetails cannot be cast to FieldDetails" ); diff --git a/hibernate-models/src/test/java/org/hibernate/models/MultiDimensionalArrayTypeTests.java b/hibernate-models/src/test/java/org/hibernate/models/MultiDimensionalArrayTypeTests.java index 34e8bc5..1eb7a97 100644 --- a/hibernate-models/src/test/java/org/hibernate/models/MultiDimensionalArrayTypeTests.java +++ b/hibernate-models/src/test/java/org/hibernate/models/MultiDimensionalArrayTypeTests.java @@ -1,3 +1,7 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ package org.hibernate.models; import org.hibernate.models.internal.ArrayTypeDetailsImpl; diff --git a/hibernate-models/src/test/java/org/hibernate/models/SimpleClassLoadingMigrationTests.java b/hibernate-models/src/test/java/org/hibernate/models/SimpleClassLoadingMigrationTests.java new file mode 100644 index 0000000..00b48ff --- /dev/null +++ b/hibernate-models/src/test/java/org/hibernate/models/SimpleClassLoadingMigrationTests.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models; + +import java.lang.reflect.Field; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassLoading; +import org.hibernate.models.spi.FieldDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; + +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; + +/** + * @author Steve Ebersole + */ +public class SimpleClassLoadingMigrationTests { + protected SourceModelBuildingContext createModelContext(Class... classes) { + return SourceModelTestHelper.createBuildingContext( classes ); + } + + @Test + void testSimpleMigration() { + final SourceModelBuildingContext modelContext = createModelContext( SimpleSerializationTests.SimpleClassWithAnnotations.class ); + final ClassDetails classDetails = modelContext.getClassDetailsRegistry().findClassDetails( SimpleSerializationTests.SimpleClassWithAnnotations.class.getName() ); + assertThat( classDetails ).isNotNull(); + + final CollectingClassLoading collectingClassLoading = new CollectingClassLoading(); + classDetails.toJavaClass( collectingClassLoading, modelContext ); + + assertThat( collectingClassLoading.getClassesByName() ).isNotEmpty(); + } + + @Test + void testSimpleMembersMigration() { + final SourceModelBuildingContext modelContext = createModelContext( SimpleSerializationTests.SimpleClassWithMembers.class ); + final ClassDetails classDetails = modelContext.getClassDetailsRegistry().findClassDetails( SimpleSerializationTests.SimpleClassWithMembers.class.getName() ); + assertThat( classDetails ).isNotNull(); + + final CollectingClassLoading collectingClassLoading = new CollectingClassLoading(); + final Class javaClass = classDetails.toJavaClass( collectingClassLoading, modelContext ); + + assertThat( collectingClassLoading.getClassesByName() ).hasSize( 1 ); + + final FieldDetails fieldByName = classDetails.findFieldByName( "anInt" ); + final Field anIntField = fieldByName.toJavaMember( javaClass, collectingClassLoading, modelContext ); + assertThat( anIntField.getDeclaringClass() ).isSameAs( javaClass ); + } + + @Entity(name="Thing") + @Table(name="Thing") + public static class Thing { + @Id + private Integer id; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + private static class CollectingClassLoading implements ClassLoading { + private final Map> classesByName = new HashMap<>(); + + public Map> getClassesByName() { + return classesByName; + } + + @Override + public Class classForName(String name) { + final Class classForName = SIMPLE_CLASS_LOADING.classForName( name ); + classesByName.put( name, classForName ); + return classForName; + } + + @Override + public Class findClassForName(String name) { + final Class classForName = SIMPLE_CLASS_LOADING.findClassForName( name ); + classesByName.put( name, classForName ); + return classForName; + } + + @Override + public URL locateResource(String resourceName) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection loadJavaServices(Class serviceType) { + throw new UnsupportedOperationException(); + } + } +}