From 8bed92fbb3aaaa4facd12356416c65923d507bfe Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 23 Sep 2024 16:24:13 -0500 Subject: [PATCH] #100 - AnnotationTarget.getContainer() --- .../annotations/AnnotationCycleTests.java | 2 +- .../target/AnnotationTargetTests.java | 166 +++++++++++++++ .../target/ClassGeneratorEntity.java | 14 ++ .../target/GeneratorAnnotation.java | 23 ++ .../target/MemberGeneratorEntity.java | 14 ++ .../annotations/target/NoGeneratorEntity.java | 15 ++ .../annotations/target/package-info.java | 10 + .../target/sub/SubNoGeneratorEntity.java | 15 ++ .../internal/AnnotationTargetHelper.java | 79 +++++++ .../internal/MissingPackageInfoDetails.java | 200 ++++++++++++++++++ .../models/internal/SimpleClassLoading.java | 11 + .../StandardAnnotationDescriptor.java | 1 + .../models/spi/AnnotationDescriptor.java | 7 + .../models/spi/AnnotationTarget.java | 12 ++ .../hibernate/models/spi/ClassDetails.java | 6 + .../hibernate/models/spi/ClassLoading.java | 6 + .../hibernate/models/spi/MemberDetails.java | 5 + 17 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/AnnotationTargetTests.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/ClassGeneratorEntity.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/GeneratorAnnotation.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/MemberGeneratorEntity.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/NoGeneratorEntity.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/package-info.java create mode 100644 hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/sub/SubNoGeneratorEntity.java create mode 100644 hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationTargetHelper.java create mode 100644 hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/AnnotationCycleTests.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/AnnotationCycleTests.java index fb5fb0c..b8e937c 100644 --- a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/AnnotationCycleTests.java +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/AnnotationCycleTests.java @@ -29,7 +29,7 @@ public class AnnotationCycleTests { @Test void testWithJandex() { - testAnnotationCycle( buildJandexIndex( SelfReferenceTests.SimpleClass.class ) ); + testAnnotationCycle( buildJandexIndex( SimpleClass.class ) ); } @Test diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/AnnotationTargetTests.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/AnnotationTargetTests.java new file mode 100644 index 0000000..08cfdbb --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/AnnotationTargetTests.java @@ -0,0 +1,166 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target; + +import org.hibernate.models.annotations.target.sub.SubNoGeneratorEntity; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassDetailsRegistry; +import org.hibernate.models.spi.FieldDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; + +import org.junit.jupiter.api.Test; + +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexView; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.hibernate.models.SourceModelTestHelper.buildJandexIndex; +import static org.hibernate.models.SourceModelTestHelper.createBuildingContext; + +/** + * @author Steve Ebersole + */ +public class AnnotationTargetTests { + @Test + void testPackageDefinedWithJandex() { + testPackageDefined( buildJandexIndex( NoGeneratorEntity.class ) ); + } + + @Test + void testPackageDefinedWithoutJandex() { + testPackageDefined( null ); + } + + /** + * We should find the annotation on the package + */ + void testPackageDefined(IndexView jandexIndex) { + final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, NoGeneratorEntity.class ); + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + + final ClassDetails entityClass = classDetailsRegistry.getClassDetails( NoGeneratorEntity.class.getName() ); + final FieldDetails idMember = entityClass.findFieldByName( "id" ); + assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass ); + + assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails idMemberContainer = idMember.getContainer( buildingContext ); + assertThat( idMemberContainer ).isSameAs( entityClass ); + assertThat( idMemberContainer.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( idMemberContainer.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext ); + assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" ); + assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + } + + @Test + void testClassDefinedWithJandex() { + testClassDefined( buildJandexIndex( ClassGeneratorEntity.class ) ); + } + + @Test + void testClassDefinedWithoutJandex() { + testClassDefined( null ); + } + + private void testClassDefined(Index jandexIndex) { + final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, ClassGeneratorEntity.class ); + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + + final ClassDetails entityClass = classDetailsRegistry.getClassDetails( ClassGeneratorEntity.class.getName() ); + final FieldDetails idMember = entityClass.findFieldByName( "id" ); + assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass ); + + assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + assertThat( entityClass.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( entityClass.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + + final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext ); + assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" ); + assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + } + + @Test + void testMemberDefinedWithJandex() { + testMemberDefined( buildJandexIndex( MemberGeneratorEntity.class ) ); + } + + @Test + void testMemberDefinedWithoutJandex() { + testMemberDefined( null ); + } + + private void testMemberDefined(Index jandexIndex) { + final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, MemberGeneratorEntity.class ); + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + + final ClassDetails entityClass = classDetailsRegistry.getClassDetails( MemberGeneratorEntity.class.getName() ); + final FieldDetails idMember = entityClass.findFieldByName( "id" ); + assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass ); + + assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + + assertThat( entityClass.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( entityClass.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext ); + assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" ); + assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + } + + @Test + void testUpPackageDefinedWithJandex() { + testUpPackageDefined( buildJandexIndex( SubNoGeneratorEntity.class ) ); + } + + @Test + void testUpPackageDefinedWithoutJandex() { + testUpPackageDefined( null ); + } + + /** + */ + void testUpPackageDefined(IndexView jandexIndex) { + final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, SubNoGeneratorEntity.class ); + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + + final ClassDetails entityClass = classDetailsRegistry.getClassDetails( SubNoGeneratorEntity.class.getName() ); + final FieldDetails idMember = entityClass.findFieldByName( "id" ); + assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass ); + + assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails idMemberContainer = idMember.getContainer( buildingContext ); + assertThat( idMemberContainer ).isSameAs( entityClass ); + assertThat( idMemberContainer.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( idMemberContainer.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext ); + assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.sub.package-info" ); + assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse(); + assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse(); + + final ClassDetails entityClassPackagePackage = entityClassPackage.getContainer( buildingContext ); + assertThat( entityClassPackagePackage.getName() ).endsWith( "annotations.target.package-info" ); + assertThat( entityClassPackagePackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue(); + assertThat( entityClassPackagePackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue(); + } + +} diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/ClassGeneratorEntity.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/ClassGeneratorEntity.java new file mode 100644 index 0000000..8bf82d6 --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/ClassGeneratorEntity.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target; + +/** + * @author Steve Ebersole + */ +@GeneratorAnnotation(GeneratorAnnotation.Source.TYPE) +public class ClassGeneratorEntity { + private Integer id; +} diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/GeneratorAnnotation.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/GeneratorAnnotation.java new file mode 100644 index 0000000..84144f5 --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/GeneratorAnnotation.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + * Let's mimic a Hibernate/JPA id generator annotation + * + * @author Steve Ebersole + */ +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface GeneratorAnnotation { + enum Source { PACKAGE, TYPE, MEMBER } + Source value(); +} diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/MemberGeneratorEntity.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/MemberGeneratorEntity.java new file mode 100644 index 0000000..15b43d9 --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/MemberGeneratorEntity.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target; + +/** + * @author Steve Ebersole + */ +public class MemberGeneratorEntity { + @GeneratorAnnotation(GeneratorAnnotation.Source.MEMBER) + private Integer id; +} diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/NoGeneratorEntity.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/NoGeneratorEntity.java new file mode 100644 index 0000000..c6136a4 --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/NoGeneratorEntity.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target; + +/** + * Models an entity where we should get the generator from the package + * + * @author Steve Ebersole + */ +public class NoGeneratorEntity { + private Integer id; +} diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/package-info.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/package-info.java new file mode 100644 index 0000000..5f82810 --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +/** + * @author Steve Ebersole + */ +@GeneratorAnnotation(GeneratorAnnotation.Source.PACKAGE) +package org.hibernate.models.annotations.target; diff --git a/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/sub/SubNoGeneratorEntity.java b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/sub/SubNoGeneratorEntity.java new file mode 100644 index 0000000..9c1b28f --- /dev/null +++ b/hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/target/sub/SubNoGeneratorEntity.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.annotations.target.sub; + +/** + * This ideally should not have a generator. But + * + * @author Steve Ebersole + */ +public class SubNoGeneratorEntity { + private Integer id; +} diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationTargetHelper.java b/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationTargetHelper.java new file mode 100644 index 0000000..758489a --- /dev/null +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationTargetHelper.java @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.internal; + +import org.hibernate.models.internal.jdk.JdkClassDetails; +import org.hibernate.models.internal.util.StringHelper; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; + +/** + * Utilities related to {@linkplain org.hibernate.models.spi.AnnotationTarget} + * + * @author Steve Ebersole + */ +public class AnnotationTargetHelper { + /** + * Resolve the ClassDetails descriptor for the package which contains the + * given {@code classDetails}. + * + * @apiNote {@code classDetails} may be the ClassDetails for a `package-info` itself, + * in which case this returns the `package-info` ClassDetails for the containing package. + */ + public static ClassDetails resolvePackageInfo( + ClassDetails classDetails, + SourceModelBuildingContext modelBuildingContext) { + if ( classDetails.getClassName() == null ) { + return null; + } + final String containingPackageName = determineContainingPackageName( classDetails ); + final String packageInfoClassName = containingPackageName + ".package-info"; + + return modelBuildingContext.getClassDetailsRegistry() + .as( MutableClassDetailsRegistry.class ) + .resolveClassDetails( packageInfoClassName, name -> { + // see if there is a physical package-info Class + final Class packageInfoClass = modelBuildingContext.getClassLoading().findClassForName( packageInfoClassName ); + if ( packageInfoClass == null ) { + return new MissingPackageInfoDetails( containingPackageName, packageInfoClassName ); + } + else { + return new JdkClassDetails( packageInfoClass, modelBuildingContext ); + } + } ); + } + + public static String determineContainingPackageName(ClassDetails classDetails) { + final String className = classDetails.getClassName(); + assert className != null; + + // 2 broad cases here - + // + // 1. we have a "normal" class. + // e.g. given `org.hibernate.FlushMode`, the containing package is `org.hibernate` + // 2. we have a package-info class + // e.g. given `org.hibernate.package-info`, the containing package is really `org` + + // in both cases, this is `org.hibernate` + final String classNameNamespace = StringHelper.qualifier( className ); + if ( className.endsWith( "package-info" ) ) { + return classNameNamespace.indexOf( '.' ) > 1 + ? StringHelper.qualifier( classNameNamespace ) + : null; + } + else { + return classNameNamespace; + } + } + + private AnnotationTargetHelper() { + } +} 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 new file mode 100644 index 0000000..0427ce9 --- /dev/null +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/MissingPackageInfoDetails.java @@ -0,0 +1,200 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.internal; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.models.internal.util.IndexedConsumer; +import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.ClassDetails; +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.TypeDetails; +import org.hibernate.models.spi.TypeVariableDetails; + +/** + * ClassDetails implementation used to represent a {@code package-info} details when + * there is not a physical {@code package-info} class. + * + * @author Steve Ebersole + */ +public record MissingPackageInfoDetails(String packageName, String packageInfoClassName) implements ClassDetails { + + @Override + public String getName() { + return packageInfoClassName; + } + + @Override + public Collection getDirectAnnotationUsages() { + return List.of(); + } + + @Override + public boolean hasDirectAnnotationUsage(Class type) { + return false; + } + + @Override + public A getDirectAnnotationUsage(AnnotationDescriptor descriptor) { + return null; + } + + @Override + public A getDirectAnnotationUsage(Class type) { + return null; + } + + @Override + public boolean hasAnnotationUsage(Class type, SourceModelBuildingContext modelContext) { + return false; + } + + @Override + public A getAnnotationUsage( + AnnotationDescriptor descriptor, + SourceModelBuildingContext modelContext) { + return null; + } + + @Override + public A locateAnnotationUsage(Class type, SourceModelBuildingContext modelContext) { + return null; + } + + @Override + public A[] getRepeatedAnnotationUsages( + AnnotationDescriptor type, + SourceModelBuildingContext modelContext) { + return null; + } + + @Override + public void forEachRepeatedAnnotationUsages( + Class repeatable, + Class container, + SourceModelBuildingContext modelContext, + Consumer consumer) { + + } + + @Override + public List getMetaAnnotated( + Class metaAnnotationType, + SourceModelBuildingContext modelContext) { + return List.of(); + } + + @Override + public X getNamedAnnotationUsage( + AnnotationDescriptor type, + String matchName, + String attributeToMatch, + SourceModelBuildingContext modelContext) { + return null; + } + + @Override + public X getNamedAnnotationUsage( + Class type, + String matchName, + String attributeToMatch, + SourceModelBuildingContext modelContext) { + return null; + } + + @Override + public String getClassName() { + return packageInfoClassName; + } + + @Override + public boolean isResolved() { + return true; + } + + @Override + public boolean isAbstract() { + return false; + } + + @Override + public boolean isInterface() { + return false; + } + + @Override + public boolean isEnum() { + return false; + } + + @Override + public boolean isRecord() { + return false; + } + + @Override + public ClassDetails getSuperClass() { + return null; + } + + @Override + public TypeDetails getGenericSuperType() { + return null; + } + + @Override + public List getImplementedInterfaces() { + return List.of(); + } + + @Override + public List getTypeParameters() { + return List.of(); + } + + @Override + public boolean isImplementor(Class checkType) { + return false; + } + + @Override + public List getFields() { + return List.of(); + } + + @Override + public void forEachField(IndexedConsumer consumer) { + } + + @Override + public List getMethods() { + return List.of(); + } + + @Override + public void forEachMethod(IndexedConsumer consumer) { + } + + @Override + public List getRecordComponents() { + return List.of(); + } + + @Override + public void forEachRecordComponent(IndexedConsumer consumer) { + } + + @Override + public Class toJavaClass() { + throw new UnsupportedOperationException( "Missing package-info [" + packageInfoClassName + "] cannot be converted to a Java Class" ); + } +} 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 ed40815..e85dc2b 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 @@ -33,6 +33,17 @@ public Class classForName(String name) { } } + @Override + public Class findClassForName(String name) { + try { + //noinspection unchecked + return (Class) getClass().getClassLoader().loadClass( name ); + } + catch (ClassNotFoundException e) { + return null; + } + } + @Override public Package packageForName(String name) { return getClass().getClassLoader().getDefinedPackage( name ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/StandardAnnotationDescriptor.java b/hibernate-models/src/main/java/org/hibernate/models/internal/StandardAnnotationDescriptor.java index 2b2d617..5a75080 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/StandardAnnotationDescriptor.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/StandardAnnotationDescriptor.java @@ -16,6 +16,7 @@ import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.AnnotationDescriptorRegistry; import org.hibernate.models.spi.AttributeDescriptor; +import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.SourceModelBuildingContext; /** diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationDescriptor.java b/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationDescriptor.java index 9bf1c70..0ca66a1 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationDescriptor.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationDescriptor.java @@ -35,6 +35,13 @@ default S as(Class type) { return (S) type; } + @Override + default ClassDetails getContainer(SourceModelBuildingContext modelBuildingContext) { + final ClassDetails annotationClassDetails = modelBuildingContext + .getClassDetailsRegistry() + .resolveClassDetails( getAnnotationType().getName() ); + return annotationClassDetails.getContainer( modelBuildingContext ); + } /** * Create an empty usage. Used when there is no source form, such as XML processing. */ diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationTarget.java b/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationTarget.java index 4aa1e1b..6501797 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationTarget.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/AnnotationTarget.java @@ -34,6 +34,18 @@ public interface AnnotationTarget { */ String getName(); + /** + * Get the ClassDetails for the class which is the "container" for this target.
    + *
  • For a member ({@code org.hib.Thing#id}), this will be the declaring type ({@code org.hib.Thing}). + *
  • For a class ({@code org.hib.Thing}), this will be the ClassDetails for its package ({@code org.hib.package-info}) + *
  • For a package ({@code org.hib.package-info}), this will be the ClassDetails for the containing package ({@code org.package-info}) + *
+ * + * @apiNote If not already, this resolution should be registered into the context's + * {@linkplain SourceModelBuildingContext#getClassDetailsRegistry() class registry} + */ + ClassDetails getContainer(SourceModelBuildingContext modelBuildingContext); + /** * Access to all the annotations used on this target. * 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 b0dffcd..9a62f70 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.internal.AnnotationTargetHelper; import org.hibernate.models.internal.SimpleClassDetails; import org.hibernate.models.internal.util.IndexedConsumer; @@ -90,6 +91,11 @@ default Kind getKind() { */ boolean isRecord(); + @Override + default ClassDetails getContainer(SourceModelBuildingContext modelBuildingContext) { + return AnnotationTargetHelper.resolvePackageInfo( this, modelBuildingContext ); + } + /** * Details for the class that is the super type for this class. */ 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 3e63906..82be43e 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 @@ -27,6 +27,12 @@ public interface ClassLoading { */ Class classForName(String name); + /** + * Like {@linkplain #classForName(String)}, except here we simply return {@code null} if + * there is not a matching class. This is often useful, e.g., when looking for a package-info. + */ + Class findClassForName(String name); + Package packageForName(String name); /** 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 08d857d..4648d2e 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 @@ -41,6 +41,11 @@ public interface MemberDetails extends AnnotationTarget { */ TypeDetails getType(); + @Override + default ClassDetails getContainer(SourceModelBuildingContext modelBuildingContext) { + return getDeclaringType(); + } + /** * For plural members, the {@linkplain #getElementType "element type"}; * otherwise, the member's {@linkplain #getType() type}