Skip to content

Commit 704b88c

Browse files
committed
#88 - Ability to remove annotations from a target
#89 - Method to iterate all persistable members
1 parent b57b144 commit 704b88c

File tree

9 files changed

+206
-3
lines changed

9 files changed

+206
-3
lines changed

src/main/java/org/hibernate/models/internal/AnnotationTargetSupport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ default <X extends Annotation> void addAnnotationUsage(X annotationUsage) {
4242
( (Map) getUsageMap() ).put( annotationUsage.annotationType(), annotationUsage );
4343
}
4444

45+
@Override
46+
default <X extends Annotation> void removeAnnotationUsage(AnnotationDescriptor<X> annotationType) {
47+
getUsageMap().remove( annotationType.getAnnotationType() );
48+
}
49+
4550
@Override
4651
default Collection<? extends Annotation> getDirectAnnotationUsages() {
4752
return getUsageMap().values();

src/main/java/org/hibernate/models/internal/ClassDetailsSupport.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ default void forEachMethod(IndexedConsumer<MethodDetails> consumer) {
4848
}
4949
}
5050

51+
@Override
52+
default void forEachRecordComponent(IndexedConsumer<RecordComponentDetails> consumer) {
53+
final List<RecordComponentDetails> recordComponents = getRecordComponents();
54+
if ( recordComponents == null ) {
55+
return;
56+
}
57+
58+
for ( int i = 0; i < recordComponents.size(); i++ ) {
59+
consumer.accept( i, recordComponents.get( i ) );
60+
}
61+
}
62+
5163
@Override
5264
default <A extends Annotation> A getAnnotationUsage(
5365
AnnotationDescriptor<A> descriptor,

src/main/java/org/hibernate/models/internal/SimpleClassDetails.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ public List<RecordComponentDetails> getRecordComponents() {
144144
return Collections.emptyList();
145145
}
146146

147+
@Override
148+
public void forEachRecordComponent(IndexedConsumer<RecordComponentDetails> consumer) {
149+
}
147150

148151
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
149152
// nor do we care about its annotations

src/main/java/org/hibernate/models/spi/ClassDetails.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.lang.annotation.Annotation;
1010
import java.util.List;
11+
import java.util.function.Consumer;
1112
import java.util.function.Predicate;
1213

1314
import org.hibernate.models.IllegalCastException;
@@ -251,6 +252,36 @@ default RecordComponentDetails findRecordComponentByName(String name) {
251252
return findRecordComponent( component -> name.equals( component.getName() ) );
252253
}
253254

255+
/**
256+
* Visit each method
257+
*/
258+
void forEachRecordComponent(IndexedConsumer<RecordComponentDetails> consumer);
259+
260+
default void forEachMember(Consumer<MemberDetails> consumer) {
261+
forEachField( (i,field) -> consumer.accept( field ) );
262+
forEachMethod( (i,method) -> consumer.accept( method ) );
263+
forEachRecordComponent( (i,recordComponent) -> consumer.accept( recordComponent ) );
264+
}
265+
266+
default void forEachPersistableMember(Consumer<MemberDetails> consumer) {
267+
forEachField( (i,field) -> {
268+
if ( field.isPersistable() ) {
269+
consumer.accept( field );
270+
}
271+
} );
272+
forEachMethod( (i,method) -> {
273+
if ( method.isPersistable() ) {
274+
consumer.accept( method );
275+
}
276+
} );
277+
forEachRecordComponent( (i,recordComponent) -> {
278+
// can they ever not be?
279+
if ( recordComponent.isPersistable() ) {
280+
consumer.accept( recordComponent );
281+
}
282+
} );
283+
}
284+
254285
/**
255286
* Know what you are doing before calling this method
256287
*/

src/main/java/org/hibernate/models/spi/MutableAnnotationTarget.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public interface MutableAnnotationTarget extends AnnotationTarget {
2525
*/
2626
<X extends Annotation> void addAnnotationUsage(X annotationUsage);
2727

28+
/**
29+
* Remove an annotation, by type, from this target if there is such a usage.
30+
*/
31+
<X extends Annotation> void removeAnnotationUsage(AnnotationDescriptor<X> annotationType);
32+
2833
/**
2934
* Applies a usage of the given {@code annotationType} to this target. Will return
3035
* an existing usage, if one, or create a new usage.
@@ -45,9 +50,8 @@ default <A extends Annotation> A applyAnnotationUsage(
4550
/**
4651
* Creates and replaces (if any) an existing usage of the given annotation.
4752
* <p/>
48-
* For repeatable annotations, use
49-
* Applies a usage of the given {@code annotationType} to this target. Will return
50-
* an existing usage, if one, or create a new usage.
53+
* For repeatable annotations, use {@linkplain #replaceAnnotationUsage(AnnotationDescriptor, AnnotationDescriptor, SourceModelBuildingContext)}
54+
* instead.
5155
*
5256
* @apiNote Generally replacement is used with XML processing and, again generally,
5357
* only for repeatable annotations using

src/test/java/org/hibernate/models/orm/JpaAnnotations.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import jakarta.persistence.SequenceGenerator;
3030
import jakarta.persistence.SequenceGenerators;
3131
import jakarta.persistence.Table;
32+
import jakarta.persistence.Transient;
3233
import jakarta.persistence.UniqueConstraint;
3334

3435
/**
@@ -65,6 +66,7 @@ public interface JpaAnnotations {
6566
AnnotationDescriptor<Index> INDEX = new OrmAnnotationDescriptor<>( Index.class, IndexAnnotation.class );
6667

6768
AnnotationDescriptor<Cacheable> CACHEABLE = new OrmAnnotationDescriptor<>( Cacheable.class, CacheableAnnotation.class );
69+
AnnotationDescriptor<Transient> TRANSIENT = new OrmAnnotationDescriptor<>( Transient.class, TransientAnnotation.class );
6870

6971
// AnnotationDescriptor<Access> ACCESS = createOrmDescriptor( Access.class );
7072
// AnnotationDescriptor<AssociationOverrides> ASSOCIATION_OVERRIDES = createOrmDescriptor( AssociationOverrides.class );
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
* Copyright: Red Hat Inc. and Hibernate Authors
6+
*/
7+
8+
package org.hibernate.models.orm;
9+
10+
import java.lang.annotation.Annotation;
11+
12+
import org.hibernate.models.spi.SourceModelBuildingContext;
13+
14+
import org.jboss.jandex.AnnotationInstance;
15+
16+
import jakarta.persistence.Transient;
17+
18+
/**
19+
* @author Steve Ebersole
20+
*/
21+
@SuppressWarnings({ "ClassExplicitlyAnnotation", "unused" })
22+
public class TransientAnnotation implements Transient {
23+
public TransientAnnotation(SourceModelBuildingContext modelContext) {
24+
}
25+
public TransientAnnotation(Transient source, SourceModelBuildingContext modelContext) {
26+
}
27+
public TransientAnnotation(AnnotationInstance source, SourceModelBuildingContext modelContext) {
28+
}
29+
30+
@Override
31+
public Class<? extends Annotation> annotationType() {
32+
return Transient.class;
33+
}
34+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
* Copyright: Red Hat Inc. and Hibernate Authors
6+
*/
7+
8+
package org.hibernate.models.xml;
9+
10+
import org.hibernate.models.SourceModelTestHelper;
11+
import org.hibernate.models.internal.SourceModelBuildingContextImpl;
12+
import org.hibernate.models.orm.JpaAnnotations;
13+
import org.hibernate.models.spi.ClassDetails;
14+
import org.hibernate.models.spi.ClassDetailsRegistry;
15+
import org.hibernate.models.spi.FieldDetails;
16+
import org.hibernate.models.spi.MutableMemberDetails;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import org.jboss.jandex.Index;
21+
22+
import jakarta.persistence.Transient;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* Tests for expected patterns for handling of "metadata complete" XML mappings.
28+
*
29+
* @author Steve Ebersole
30+
*/
31+
public class MetadataCompleteTests {
32+
@Test
33+
void testIt() {
34+
final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext(
35+
(Index) null,
36+
SimpleEntity.class
37+
);
38+
39+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
40+
final ClassDetails classDetails = classDetailsRegistry.getClassDetails( SimpleEntity.class.getName() );
41+
42+
// A metadata-complete XML mapping means that all "attributes" not explicitly listed in
43+
// the XML should be ignored. To support this we will first apply `@Transient` to all
44+
// persistable members of the class. Then, as we process the XML and "see" an attribute
45+
// we will remove that annotation.
46+
47+
// mark them all transient...
48+
classDetails.forEachPersistableMember( (member) -> ( (MutableMemberDetails) member ).applyAnnotationUsage( JpaAnnotations.TRANSIENT, buildingContext ) );
49+
50+
checkFieldIsTransient( classDetails.findFieldByName( "id" ), true );
51+
checkFieldIsTransient( classDetails.findFieldByName( "name" ), true );
52+
checkFieldIsTransient( classDetails.findFieldByName( "somethingElse" ), true );
53+
54+
// the XML lists just `id` and `name`...
55+
( (MutableMemberDetails) classDetails.findFieldByName( "id" ) ).removeAnnotationUsage( JpaAnnotations.TRANSIENT );
56+
( (MutableMemberDetails) classDetails.findFieldByName( "name" ) ).removeAnnotationUsage( JpaAnnotations.TRANSIENT );
57+
58+
checkFieldIsTransient( classDetails.findFieldByName( "id" ), false );
59+
checkFieldIsTransient( classDetails.findFieldByName( "name" ), false );
60+
checkFieldIsTransient( classDetails.findFieldByName( "somethingElse" ), true );
61+
}
62+
63+
private void checkFieldIsTransient(FieldDetails fieldDetails, boolean expectation) {
64+
assertThat( fieldDetails.hasDirectAnnotationUsage( Transient.class ) ).isEqualTo( expectation );
65+
}
66+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
* Copyright: Red Hat Inc. and Hibernate Authors
6+
*/
7+
8+
package org.hibernate.models.xml;
9+
10+
import jakarta.persistence.Entity;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.Basic;
13+
14+
/**
15+
* @author Steve Ebersole
16+
*/
17+
@SuppressWarnings("unused")
18+
@Entity
19+
public class SimpleEntity {
20+
@Id
21+
private Integer id;
22+
@Basic
23+
private String name;
24+
private String somethingElse;
25+
26+
protected SimpleEntity() {
27+
// for Hibernate use
28+
}
29+
30+
public SimpleEntity(Integer id, String name) {
31+
this.id = id;
32+
this.name = name;
33+
}
34+
35+
public Integer getId() {
36+
return id;
37+
}
38+
39+
public String getName() {
40+
return name;
41+
}
42+
43+
public void setName(String name) {
44+
this.name = name;
45+
}
46+
}

0 commit comments

Comments
 (0)