Skip to content

Commit 2eda68f

Browse files
dreab8sebersole
authored andcommitted
Fix annotation default values extraction
1 parent 090bd5b commit 2eda68f

File tree

3 files changed

+159
-17
lines changed

3 files changed

+159
-17
lines changed

src/main/java/org/hibernate/models/internal/dynamic/DynamicAnnotationUsage.java

Lines changed: 93 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77
package org.hibernate.models.internal.dynamic;
88

99
import java.lang.annotation.Annotation;
10+
import java.lang.reflect.InvocationTargetException;
11+
import java.util.ArrayList;
1012
import java.util.HashMap;
13+
import java.util.List;
1114
import java.util.Locale;
1215
import java.util.Map;
1316

17+
import org.hibernate.models.AnnotationAccessException;
1418
import org.hibernate.models.UnknownAnnotationAttributeException;
1519
import org.hibernate.models.internal.AnnotationProxy;
1620
import org.hibernate.models.spi.AnnotationDescriptor;
1721
import org.hibernate.models.spi.AnnotationTarget;
1822
import org.hibernate.models.spi.AttributeDescriptor;
1923
import org.hibernate.models.spi.MutableAnnotationUsage;
24+
import org.hibernate.models.spi.SourceModelBuildingContext;
2025

2126
/**
2227
* AnnotationUsage built dynamically (for dynamic models, XML mappings, etc.)
@@ -29,15 +34,20 @@ public class DynamicAnnotationUsage<A extends Annotation> implements MutableAnno
2934

3035
private Map<String,Object> values;
3136

32-
public DynamicAnnotationUsage(AnnotationDescriptor<A> annotationDescriptor) {
33-
this( annotationDescriptor, null );
37+
public DynamicAnnotationUsage(
38+
AnnotationDescriptor<A> annotationDescriptor,
39+
SourceModelBuildingContext context) {
40+
this( annotationDescriptor, null, context );
3441
}
3542

36-
public DynamicAnnotationUsage(AnnotationDescriptor<A> annotationDescriptor, AnnotationTarget target) {
43+
public DynamicAnnotationUsage(
44+
AnnotationDescriptor<A> annotationDescriptor,
45+
AnnotationTarget target,
46+
SourceModelBuildingContext context) {
3747
this.annotationDescriptor = annotationDescriptor;
3848
this.target = target;
3949

40-
this.values = extractBaselineValues( annotationDescriptor );
50+
this.values = extractBaselineValues( annotationDescriptor, target, context );
4151
}
4252

4353
@Override
@@ -70,19 +80,32 @@ public <V> V findAttributeValue(String name) {
7080
*/
7181
@Override
7282
public <V> V getAttributeValue(String name) {
73-
final Object value = findAttributeValue( name );
74-
if ( value == null ) {
75-
// null values are not supported as annotation attribute values; we honor that
76-
// in hibernate-models. return the default.
77-
//noinspection unchecked
78-
return (V) getAnnotationDescriptor().getAttribute( name ).getAttributeMethod().getDefaultValue();
83+
if ( annotationDescriptor.getAttribute( name ) == null ) {
84+
throw new UnknownAnnotationAttributeException(
85+
String.format(
86+
Locale.ROOT,
87+
"Unknown attribute `%s` for annotation `%s`",
88+
name,
89+
getAnnotationType().getName()
90+
)
91+
);
7992
}
80-
//noinspection unchecked
81-
return (V) value;
93+
return findAttributeValue( name );
8294
}
8395

8496
@Override
8597
public <V> V setAttributeValue(String name, V value) {
98+
if (value == null){
99+
throw new IllegalArgumentException(
100+
String.format(
101+
Locale.ROOT,
102+
"Null value not allowed for attribute `%s` of annotation `%s`",
103+
name,
104+
getAnnotationType().getName()
105+
)
106+
);
107+
}
108+
86109
if ( annotationDescriptor.getAttribute( name ) == null ) {
87110
throw new UnknownAnnotationAttributeException(
88111
String.format(
@@ -102,11 +125,67 @@ public <V> V setAttributeValue(String name, V value) {
102125
return (V) values.put( name, value );
103126
}
104127

105-
private static <A extends Annotation> Map<String, Object> extractBaselineValues(AnnotationDescriptor<A> annotationDescriptor) {
128+
private static <A extends Annotation> Map<String, Object> extractBaselineValues(
129+
AnnotationDescriptor<A> annotationDescriptor,
130+
AnnotationTarget target,
131+
SourceModelBuildingContext context) {
106132
final HashMap<String, Object> values = new HashMap<>();
107133
for ( AttributeDescriptor<?> attribute : annotationDescriptor.getAttributes() ) {
108-
values.put( attribute.getName(), attribute.getAttributeMethod().getDefaultValue() );
134+
values.put( attribute.getName(), getDefaultValue( attribute, target, context ) );
109135
}
110136
return values;
111137
}
138+
139+
private static Object getDefaultValue(
140+
AttributeDescriptor<?> attribute,
141+
AnnotationTarget target,
142+
SourceModelBuildingContext context) {
143+
final Object defaultValue = attribute.getAttributeMethod().getDefaultValue();
144+
Object annotation = wrapValue( defaultValue, target, context );
145+
if ( annotation != null ) {
146+
return annotation;
147+
}
148+
return defaultValue;
149+
}
150+
151+
private static Object wrapValue(Object value, AnnotationTarget target, SourceModelBuildingContext context) {
152+
if ( value instanceof Annotation annotation ) {
153+
try {
154+
return extractDynamicAnnotationUsage( annotation, target, context );
155+
}
156+
catch (InvocationTargetException | IllegalAccessException e) {
157+
throw new AnnotationAccessException( "Error accessing default annotation attribute value", e );
158+
}
159+
}
160+
else if ( value != null && value.getClass().isArray() ) {
161+
return getList( value, target, context );
162+
}
163+
return value;
164+
}
165+
166+
private static <E> List getList(Object defaultValue, AnnotationTarget target, SourceModelBuildingContext context) {
167+
List result = new ArrayList<>();
168+
E[] d = (E[]) defaultValue;
169+
for ( E e : d ) {
170+
result.add( wrapValue( e, target, context ) );
171+
}
172+
return result;
173+
}
174+
175+
private static DynamicAnnotationUsage<?> extractDynamicAnnotationUsage(
176+
Annotation annotation,
177+
AnnotationTarget target,
178+
SourceModelBuildingContext context) throws InvocationTargetException, IllegalAccessException {
179+
final Class<? extends Annotation> annotationType = annotation.annotationType();
180+
final AnnotationDescriptor<?> descriptor = context.getAnnotationDescriptorRegistry()
181+
.getDescriptor( annotationType );
182+
final DynamicAnnotationUsage<?> annotationUsage = new DynamicAnnotationUsage<>( descriptor, target, context );
183+
for ( AttributeDescriptor<?> attribute : descriptor.getAttributes() ) {
184+
annotationUsage.setAttributeValue(
185+
attribute.getName(),
186+
attribute.getAttributeMethod().invoke( annotation )
187+
);
188+
}
189+
return annotationUsage;
190+
}
112191
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,12 @@ default <V> AttributeDescriptor<V> getAttribute(String name) {
8888
* @param context Access to needed services
8989
*/
9090
default MutableAnnotationUsage<A> createUsage(AnnotationTarget target, SourceModelBuildingContext context) {
91-
final DynamicAnnotationUsage<A> usage = new DynamicAnnotationUsage<>( this, target );
91+
final DynamicAnnotationUsage<A> usage = new DynamicAnnotationUsage<>( this, target, context );
9292
getAttributes().forEach( (attr) -> {
9393
final Object value = attr.getTypeDescriptor().createValue( attr, target, context );
94-
usage.setAttributeValue( attr.getName(), value );
94+
if ( value != null ) {
95+
usage.setAttributeValue( attr.getName(), value );
96+
}
9597
} );
9698
return usage;
9799
}

src/test/java/org/hibernate/models/annotations/DynamicAnnotationTests.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
package org.hibernate.models.annotations;
99

10+
import java.util.List;
11+
1012
import org.hibernate.models.UnknownAnnotationAttributeException;
1113
import org.hibernate.models.internal.SourceModelBuildingContextImpl;
1214
import org.hibernate.models.internal.dynamic.DynamicAnnotationUsage;
@@ -15,6 +17,10 @@
1517

1618
import org.junit.jupiter.api.Test;
1719

20+
import jakarta.persistence.ConstraintMode;
21+
import jakarta.persistence.GeneratedValue;
22+
import jakarta.persistence.GenerationType;
23+
import jakarta.persistence.JoinTable;
1824
import jakarta.persistence.SequenceGenerator;
1925

2026
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +37,8 @@ void testBasicUsage() {
3137
final DynamicClassDetails dynamicEntity = new DynamicClassDetails( "DynamicEntity", buildingContext );
3238
final DynamicAnnotationUsage<SequenceGenerator> generatorAnn = new DynamicAnnotationUsage<>(
3339
JpaAnnotations.SEQUENCE_GENERATOR,
34-
dynamicEntity
40+
dynamicEntity,
41+
buildingContext
3542
);
3643
assertThat( generatorAnn.getString( "name" ) ).isEqualTo( "" );
3744
assertThat( generatorAnn.getString( "sequenceName" ) ).isEqualTo( "" );
@@ -49,4 +56,58 @@ void testBasicUsage() {
4956
}
5057

5158
}
59+
60+
@Test
61+
void testJoinTableForeignKeyDefaultValue() {
62+
final SourceModelBuildingContextImpl buildingContext = createBuildingContext();
63+
final DynamicClassDetails dynamicEntity = new DynamicClassDetails( "DynamicEntity", buildingContext );
64+
final DynamicAnnotationUsage<JoinTable> generatorAnn = new DynamicAnnotationUsage<>(
65+
JpaAnnotations.JOIN_TABLE,
66+
dynamicEntity,
67+
buildingContext
68+
);
69+
70+
final Object foreignKey = generatorAnn.getAttributeValue( "foreignKey" );
71+
72+
assertThat( foreignKey ).isInstanceOf( DynamicAnnotationUsage.class );
73+
74+
final DynamicAnnotationUsage foreignKeyAnnotationUsage = (DynamicAnnotationUsage) foreignKey;
75+
76+
assertThat( foreignKeyAnnotationUsage.getAttributeValue( "value" ) ).isEqualTo( ConstraintMode.PROVIDER_DEFAULT );
77+
78+
assertThat( foreignKeyAnnotationUsage.getAttributeValue( "name" )).isEqualTo( "" );
79+
assertThat( foreignKeyAnnotationUsage.getAttributeValue( "options" )).isEqualTo( "" );
80+
assertThat( foreignKeyAnnotationUsage.getAttributeValue( "foreignKeyDefinition" )).isEqualTo( "" );
81+
}
82+
83+
@Test
84+
void testDefaultArrayValue() {
85+
final SourceModelBuildingContextImpl buildingContext = createBuildingContext();
86+
final DynamicClassDetails dynamicEntity = new DynamicClassDetails( "DynamicEntity", buildingContext );
87+
final DynamicAnnotationUsage<JoinTable> generatorAnn = new DynamicAnnotationUsage<>(
88+
JpaAnnotations.JOIN_TABLE,
89+
dynamicEntity,
90+
buildingContext
91+
);
92+
93+
final Object joinColumns = generatorAnn.getAttributeValue( "joinColumns" );
94+
assertThat( joinColumns ).isInstanceOf( List.class );
95+
96+
}
97+
98+
@Test
99+
void testDefaultValues() {
100+
final SourceModelBuildingContextImpl buildingContext = createBuildingContext();
101+
final DynamicClassDetails dynamicEntity = new DynamicClassDetails( "DynamicEntity", buildingContext );
102+
final DynamicAnnotationUsage<GeneratedValue> generatorAnn = new DynamicAnnotationUsage<>(
103+
JpaAnnotations.GENERATED_VALUE,
104+
dynamicEntity,
105+
buildingContext
106+
);
107+
108+
GenerationType strategy = generatorAnn.getAttributeValue( "strategy" );
109+
assertThat( strategy ).isEqualTo( GenerationType.AUTO );
110+
String generator = generatorAnn.findAttributeValue( "generator" );
111+
assertThat( generator ).isEqualTo( "" );
112+
}
52113
}

0 commit comments

Comments
 (0)