Skip to content

Commit 8bed92f

Browse files
committed
#100 - AnnotationTarget.getContainer()
1 parent e1ec9df commit 8bed92f

17 files changed

+585
-1
lines changed

hibernate-models-jandex/src/test/java/org/hibernate/models/annotations/AnnotationCycleTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public class AnnotationCycleTests {
3030
@Test
3131
void testWithJandex() {
32-
testAnnotationCycle( buildJandexIndex( SelfReferenceTests.SimpleClass.class ) );
32+
testAnnotationCycle( buildJandexIndex( SimpleClass.class ) );
3333
}
3434

3535
@Test
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
/*
7+
* SPDX-License-Identifier: Apache-2.0
8+
* Copyright: Red Hat Inc. and Hibernate Authors
9+
*/
10+
11+
package org.hibernate.models.annotations.target;
12+
13+
import org.hibernate.models.annotations.target.sub.SubNoGeneratorEntity;
14+
import org.hibernate.models.spi.ClassDetails;
15+
import org.hibernate.models.spi.ClassDetailsRegistry;
16+
import org.hibernate.models.spi.FieldDetails;
17+
import org.hibernate.models.spi.SourceModelBuildingContext;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.jboss.jandex.Index;
22+
import org.jboss.jandex.IndexView;
23+
24+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
25+
import static org.hibernate.models.SourceModelTestHelper.buildJandexIndex;
26+
import static org.hibernate.models.SourceModelTestHelper.createBuildingContext;
27+
28+
/**
29+
* @author Steve Ebersole
30+
*/
31+
public class AnnotationTargetTests {
32+
@Test
33+
void testPackageDefinedWithJandex() {
34+
testPackageDefined( buildJandexIndex( NoGeneratorEntity.class ) );
35+
}
36+
37+
@Test
38+
void testPackageDefinedWithoutJandex() {
39+
testPackageDefined( null );
40+
}
41+
42+
/**
43+
* We should find the annotation on the package
44+
*/
45+
void testPackageDefined(IndexView jandexIndex) {
46+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, NoGeneratorEntity.class );
47+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
48+
49+
final ClassDetails entityClass = classDetailsRegistry.getClassDetails( NoGeneratorEntity.class.getName() );
50+
final FieldDetails idMember = entityClass.findFieldByName( "id" );
51+
assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass );
52+
53+
assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
54+
assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
55+
56+
final ClassDetails idMemberContainer = idMember.getContainer( buildingContext );
57+
assertThat( idMemberContainer ).isSameAs( entityClass );
58+
assertThat( idMemberContainer.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
59+
assertThat( idMemberContainer.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
60+
61+
final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext );
62+
assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" );
63+
assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
64+
assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
65+
}
66+
67+
@Test
68+
void testClassDefinedWithJandex() {
69+
testClassDefined( buildJandexIndex( ClassGeneratorEntity.class ) );
70+
}
71+
72+
@Test
73+
void testClassDefinedWithoutJandex() {
74+
testClassDefined( null );
75+
}
76+
77+
private void testClassDefined(Index jandexIndex) {
78+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, ClassGeneratorEntity.class );
79+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
80+
81+
final ClassDetails entityClass = classDetailsRegistry.getClassDetails( ClassGeneratorEntity.class.getName() );
82+
final FieldDetails idMember = entityClass.findFieldByName( "id" );
83+
assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass );
84+
85+
assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
86+
assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
87+
88+
assertThat( entityClass.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
89+
assertThat( entityClass.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
90+
91+
final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext );
92+
assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" );
93+
assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
94+
assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
95+
}
96+
97+
@Test
98+
void testMemberDefinedWithJandex() {
99+
testMemberDefined( buildJandexIndex( MemberGeneratorEntity.class ) );
100+
}
101+
102+
@Test
103+
void testMemberDefinedWithoutJandex() {
104+
testMemberDefined( null );
105+
}
106+
107+
private void testMemberDefined(Index jandexIndex) {
108+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, MemberGeneratorEntity.class );
109+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
110+
111+
final ClassDetails entityClass = classDetailsRegistry.getClassDetails( MemberGeneratorEntity.class.getName() );
112+
final FieldDetails idMember = entityClass.findFieldByName( "id" );
113+
assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass );
114+
115+
assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
116+
assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
117+
118+
assertThat( entityClass.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
119+
assertThat( entityClass.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
120+
121+
final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext );
122+
assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.package-info" );
123+
assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
124+
assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
125+
}
126+
127+
@Test
128+
void testUpPackageDefinedWithJandex() {
129+
testUpPackageDefined( buildJandexIndex( SubNoGeneratorEntity.class ) );
130+
}
131+
132+
@Test
133+
void testUpPackageDefinedWithoutJandex() {
134+
testUpPackageDefined( null );
135+
}
136+
137+
/**
138+
*/
139+
void testUpPackageDefined(IndexView jandexIndex) {
140+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, SubNoGeneratorEntity.class );
141+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
142+
143+
final ClassDetails entityClass = classDetailsRegistry.getClassDetails( SubNoGeneratorEntity.class.getName() );
144+
final FieldDetails idMember = entityClass.findFieldByName( "id" );
145+
assertThat( idMember.getContainer( buildingContext ) ).isSameAs( entityClass );
146+
147+
assertThat( idMember.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
148+
assertThat( idMember.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
149+
150+
final ClassDetails idMemberContainer = idMember.getContainer( buildingContext );
151+
assertThat( idMemberContainer ).isSameAs( entityClass );
152+
assertThat( idMemberContainer.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
153+
assertThat( idMemberContainer.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
154+
155+
final ClassDetails entityClassPackage = entityClass.getContainer( buildingContext );
156+
assertThat( entityClassPackage.getName() ).endsWith( "annotations.target.sub.package-info" );
157+
assertThat( entityClassPackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isFalse();
158+
assertThat( entityClassPackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isFalse();
159+
160+
final ClassDetails entityClassPackagePackage = entityClassPackage.getContainer( buildingContext );
161+
assertThat( entityClassPackagePackage.getName() ).endsWith( "annotations.target.package-info" );
162+
assertThat( entityClassPackagePackage.hasDirectAnnotationUsage( GeneratorAnnotation.class ) ).isTrue();
163+
assertThat( entityClassPackagePackage.hasAnnotationUsage( GeneratorAnnotation.class, buildingContext ) ).isTrue();
164+
}
165+
166+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
package org.hibernate.models.annotations.target;
7+
8+
/**
9+
* @author Steve Ebersole
10+
*/
11+
@GeneratorAnnotation(GeneratorAnnotation.Source.TYPE)
12+
public class ClassGeneratorEntity {
13+
private Integer id;
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
package org.hibernate.models.annotations.target;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.RetentionPolicy;
10+
import java.lang.annotation.Target;
11+
import java.lang.annotation.Retention;
12+
13+
/**
14+
* Let's mimic a Hibernate/JPA id generator annotation
15+
*
16+
* @author Steve Ebersole
17+
*/
18+
@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
19+
@Retention(RetentionPolicy.RUNTIME)
20+
public @interface GeneratorAnnotation {
21+
enum Source { PACKAGE, TYPE, MEMBER }
22+
Source value();
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
package org.hibernate.models.annotations.target;
7+
8+
/**
9+
* @author Steve Ebersole
10+
*/
11+
public class MemberGeneratorEntity {
12+
@GeneratorAnnotation(GeneratorAnnotation.Source.MEMBER)
13+
private Integer id;
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
package org.hibernate.models.annotations.target;
7+
8+
/**
9+
* Models an entity where we should get the generator from the package
10+
*
11+
* @author Steve Ebersole
12+
*/
13+
public class NoGeneratorEntity {
14+
private Integer id;
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
/**
7+
* @author Steve Ebersole
8+
*/
9+
@GeneratorAnnotation(GeneratorAnnotation.Source.PACKAGE)
10+
package org.hibernate.models.annotations.target;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
package org.hibernate.models.annotations.target.sub;
7+
8+
/**
9+
* This ideally should not have a generator. But
10+
*
11+
* @author Steve Ebersole
12+
*/
13+
public class SubNoGeneratorEntity {
14+
private Integer id;
15+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
6+
/*
7+
* SPDX-License-Identifier: Apache-2.0
8+
* Copyright: Red Hat Inc. and Hibernate Authors
9+
*/
10+
11+
package org.hibernate.models.internal;
12+
13+
import org.hibernate.models.internal.jdk.JdkClassDetails;
14+
import org.hibernate.models.internal.util.StringHelper;
15+
import org.hibernate.models.spi.ClassDetails;
16+
import org.hibernate.models.spi.SourceModelBuildingContext;
17+
18+
/**
19+
* Utilities related to {@linkplain org.hibernate.models.spi.AnnotationTarget}
20+
*
21+
* @author Steve Ebersole
22+
*/
23+
public class AnnotationTargetHelper {
24+
/**
25+
* Resolve the ClassDetails descriptor for the package which contains the
26+
* given {@code classDetails}.
27+
*
28+
* @apiNote {@code classDetails} may be the ClassDetails for a `package-info` itself,
29+
* in which case this returns the `package-info` ClassDetails for the containing package.
30+
*/
31+
public static ClassDetails resolvePackageInfo(
32+
ClassDetails classDetails,
33+
SourceModelBuildingContext modelBuildingContext) {
34+
if ( classDetails.getClassName() == null ) {
35+
return null;
36+
}
37+
final String containingPackageName = determineContainingPackageName( classDetails );
38+
final String packageInfoClassName = containingPackageName + ".package-info";
39+
40+
return modelBuildingContext.getClassDetailsRegistry()
41+
.as( MutableClassDetailsRegistry.class )
42+
.resolveClassDetails( packageInfoClassName, name -> {
43+
// see if there is a physical package-info Class
44+
final Class<Object> packageInfoClass = modelBuildingContext.getClassLoading().findClassForName( packageInfoClassName );
45+
if ( packageInfoClass == null ) {
46+
return new MissingPackageInfoDetails( containingPackageName, packageInfoClassName );
47+
}
48+
else {
49+
return new JdkClassDetails( packageInfoClass, modelBuildingContext );
50+
}
51+
} );
52+
}
53+
54+
public static String determineContainingPackageName(ClassDetails classDetails) {
55+
final String className = classDetails.getClassName();
56+
assert className != null;
57+
58+
// 2 broad cases here -
59+
//
60+
// 1. we have a "normal" class.
61+
// e.g. given `org.hibernate.FlushMode`, the containing package is `org.hibernate`
62+
// 2. we have a package-info class
63+
// e.g. given `org.hibernate.package-info`, the containing package is really `org`
64+
65+
// in both cases, this is `org.hibernate`
66+
final String classNameNamespace = StringHelper.qualifier( className );
67+
if ( className.endsWith( "package-info" ) ) {
68+
return classNameNamespace.indexOf( '.' ) > 1
69+
? StringHelper.qualifier( classNameNamespace )
70+
: null;
71+
}
72+
else {
73+
return classNameNamespace;
74+
}
75+
}
76+
77+
private AnnotationTargetHelper() {
78+
}
79+
}

0 commit comments

Comments
 (0)