Skip to content

Commit 1a3ee72

Browse files
committed
Fix annotation default values extraction
1 parent 090bd5b commit 1a3ee72

File tree

3 files changed

+151
-17
lines changed

3 files changed

+151
-17
lines changed

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

Lines changed: 88 additions & 15 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,15 +80,17 @@ 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
@@ -98,15 +110,76 @@ public <V> V setAttributeValue(String name, V value) {
98110
values = new HashMap<>();
99111
}
100112

113+
if ( value != null ) {
114+
115+
//noinspection unchecked
116+
return (V) values.put( name, value );
117+
}
101118
//noinspection unchecked
102-
return (V) values.put( name, value );
119+
return (V) values.get( name );
103120
}
104121

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ 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 );
9494
usage.setAttributeValue( attr.getName(), value );

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)