Skip to content

Commit cd64f25

Browse files
marko-bekhtagsmet
authored andcommitted
HV-1623 Deprecate property() in programmatic API and introduce field() and getter() instead
- add two new methods field() and getter() to replace deprecated property() - update property constraint mapping implementations to use generics and get rid of casting objects
1 parent 34e9db9 commit cd64f25

File tree

5 files changed

+132
-66
lines changed

5 files changed

+132
-66
lines changed

engine/src/main/java/org/hibernate/validator/cfg/context/PropertyTarget.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,46 @@ public interface PropertyTarget {
2121
* <p>
2222
* Until this method is called constraints apply on class level. After calling this method constraints
2323
* apply on the specified property with the given access type.
24-
* </p>
2524
* <p>
2625
* A given property may only be configured once.
27-
* </p>
2826
*
2927
* @param property The property on which to apply the following constraints (Java Bean notation).
3028
* @param type The access type (field/property).
3129
*
3230
* @return A creational context representing the selected property.
31+
*
32+
* @deprecated Since 6.1. Planned for removal. Use either {@link PropertyTarget#field(String)} or
33+
* {@link PropertyTarget#getter(String)} instead.
3334
*/
35+
@Deprecated
3436
PropertyConstraintMappingContext property(String property, ElementType type);
37+
38+
/**
39+
* Selects a field to which the next operations shall apply.
40+
* <p>
41+
* Until this method is called constraints apply on class level. After calling this method constraints
42+
* apply on the specified field property.
43+
* <p>
44+
* A given field may only be configured once.
45+
*
46+
* @param property The field name that represents a property on which to apply the following constraints.
47+
*
48+
* @return A creational context representing the selected field property.
49+
*/
50+
PropertyConstraintMappingContext field(String property);
51+
52+
/**
53+
* Selects a getter to which the next operations shall apply.
54+
* <p>
55+
* Until this method is called constraints apply on class level. After calling this method constraints
56+
* apply on the specified getter property.
57+
* <p>
58+
* A given getter may only be configured once.
59+
*
60+
* @param property The getter property name (using the Java Bean notation, e.g. {@code name} to address {@code getName()})
61+
* that represents a property on which to apply the following constraints.
62+
*
63+
* @return A creational context representing the selected getter property.
64+
*/
65+
PropertyConstraintMappingContext getter(String property);
3566
}

engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ public PropertyConstraintMappingContext property(String property, ElementType el
123123
return typeContext.property( property, elementType );
124124
}
125125

126+
@Override
127+
public PropertyConstraintMappingContext field(String property) {
128+
return typeContext.field( property );
129+
}
130+
131+
@Override
132+
public PropertyConstraintMappingContext getter(String property) {
133+
return typeContext.getter( property );
134+
}
135+
126136
@Override
127137
public ConstructorConstraintMappingContext constructor(Class<?>... parameterTypes) {
128138
return typeContext.constructor( parameterTypes );

engine/src/main/java/org/hibernate/validator/internal/cfg/context/PropertyConstraintMappingContextImpl.java

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package org.hibernate.validator.internal.cfg.context;
88

99
import java.lang.annotation.ElementType;
10-
import java.lang.invoke.MethodHandles;
1110

1211
import org.hibernate.validator.cfg.context.ConstructorConstraintMappingContext;
1312
import org.hibernate.validator.cfg.context.ContainerElementConstraintMappingContext;
@@ -19,11 +18,7 @@
1918
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
2019
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
2120
import org.hibernate.validator.internal.properties.Property;
22-
import org.hibernate.validator.internal.properties.javabean.JavaBeanField;
23-
import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter;
2421
import org.hibernate.validator.internal.util.TypeResolutionHelper;
25-
import org.hibernate.validator.internal.util.logging.Log;
26-
import org.hibernate.validator.internal.util.logging.LoggerFactory;
2722

2823
/**
2924
* Constraint mapping creational context which allows to configure the constraints for one bean property.
@@ -37,32 +32,12 @@ abstract class PropertyConstraintMappingContextImpl<T extends Property>
3732
extends CascadableConstraintMappingContextImplBase<PropertyConstraintMappingContext>
3833
implements PropertyConstraintMappingContext {
3934

40-
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
41-
4235
private final TypeConstraintMappingContextImpl<?> typeContext;
4336

4437
// either Field or Method
4538
private final T property;
4639
private final ConstraintLocation location;
4740

48-
static PropertyConstraintMappingContextImpl context(ElementType elementType, TypeConstraintMappingContextImpl<?> typeContext, Property property) {
49-
if ( elementType == ElementType.FIELD ) {
50-
return new FieldPropertyConstraintMappingContextImpl(
51-
typeContext,
52-
property.as( JavaBeanField.class )
53-
);
54-
}
55-
else if ( elementType == ElementType.METHOD ) {
56-
return new GetterPropertyConstraintMappingContextImpl(
57-
typeContext,
58-
property.as( JavaBeanGetter.class )
59-
);
60-
}
61-
else {
62-
throw LOG.getUnexpectedElementType( elementType, ElementType.FIELD, ElementType.METHOD );
63-
}
64-
}
65-
6641
protected PropertyConstraintMappingContextImpl(TypeConstraintMappingContextImpl<?> typeContext, T property, ConstraintLocation location) {
6742
super( typeContext.getConstraintMapping(), property.getType() );
6843
this.typeContext = typeContext;
@@ -91,6 +66,16 @@ public PropertyConstraintMappingContext property(String property, ElementType el
9166
return typeContext.property( property, elementType );
9267
}
9368

69+
@Override
70+
public PropertyConstraintMappingContext field(String property) {
71+
return typeContext.field( property );
72+
}
73+
74+
@Override
75+
public PropertyConstraintMappingContext getter(String property) {
76+
return typeContext.getter( property );
77+
}
78+
9479
@Override
9580
public ConstructorConstraintMappingContext constructor(Class<?>... parameterTypes) {
9681
return typeContext.constructor( parameterTypes );

engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
3535
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
3636
import org.hibernate.validator.internal.properties.Constrainable;
37-
import org.hibernate.validator.internal.properties.Property;
3837
import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor;
3938
import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable;
4039
import org.hibernate.validator.internal.properties.javabean.JavaBeanField;
@@ -119,24 +118,58 @@ public TypeConstraintMappingContext<C> defaultGroupSequenceProviderClass(Class<?
119118

120119
@Override
121120
public PropertyConstraintMappingContext property(String property, ElementType elementType) {
122-
Contracts.assertNotNull( property, "The property name must not be null." );
123121
Contracts.assertNotNull( elementType, "The element type must not be null." );
122+
123+
if ( !( ElementType.FIELD.equals( elementType ) || ElementType.METHOD.equals( elementType ) ) ) {
124+
throw LOG.getElementTypeHasToBeFieldOrMethodException();
125+
}
126+
127+
if ( ElementType.FIELD == elementType ) {
128+
return field( property );
129+
}
130+
else {
131+
return getter( property );
132+
}
133+
}
134+
135+
@Override
136+
public PropertyConstraintMappingContext field(String property) {
137+
Contracts.assertNotNull( property, "The property name must not be null." );
124138
Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() );
125139

126-
Property foundProperty = getProperty(
127-
beanClass, property, elementType
128-
);
140+
JavaBeanField javaBeanField = getFieldProperty( beanClass, property );
141+
142+
if ( javaBeanField == null || javaBeanField.getDeclaringClass() != beanClass ) {
143+
throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, ElementType.FIELD );
144+
}
145+
146+
if ( configuredMembers.contains( javaBeanField ) ) {
147+
throw LOG.getPropertyHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, property );
148+
}
129149

130-
if ( foundProperty == null || foundProperty.getDeclaringClass() != beanClass ) {
131-
throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, elementType );
150+
PropertyConstraintMappingContextImpl context = new FieldPropertyConstraintMappingContextImpl( this, javaBeanField );
151+
configuredMembers.add( javaBeanField );
152+
propertyContexts.add( context );
153+
return context;
154+
}
155+
156+
@Override
157+
public PropertyConstraintMappingContext getter(String property) {
158+
Contracts.assertNotNull( property, "The property name must not be null." );
159+
Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() );
160+
161+
JavaBeanGetter javaBeanGetter = getGetterProperty( beanClass, property );
162+
163+
if ( javaBeanGetter == null || javaBeanGetter.getDeclaringClass() != beanClass ) {
164+
throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, ElementType.METHOD );
132165
}
133166

134-
if ( configuredMembers.contains( foundProperty ) ) {
167+
if ( configuredMembers.contains( javaBeanGetter ) ) {
135168
throw LOG.getPropertyHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, property );
136169
}
137170

138-
PropertyConstraintMappingContextImpl context = PropertyConstraintMappingContextImpl.context( elementType, this, foundProperty );
139-
configuredMembers.add( foundProperty );
171+
PropertyConstraintMappingContextImpl context = new GetterPropertyConstraintMappingContextImpl( this, javaBeanGetter );
172+
configuredMembers.add( javaBeanGetter );
140173
propertyContexts.add( context );
141174
return context;
142175
}
@@ -252,41 +285,25 @@ protected ConstraintType getConstraintType() {
252285
return ConstraintType.GENERIC;
253286
}
254287

255-
/**
256-
* Returns the property with the given name and type.
257-
*
258-
* @param clazz The class from which to retrieve the property. Cannot be {@code null}.
259-
* @param property The property name without "is", "get" or "has". Cannot be {@code null} or empty.
260-
* @param elementType The element type. Either {@code ElementType.FIELD} or {@code ElementType METHOD}.
261-
*
262-
* @return the property which matches the name and type or {@code null} if no such property exists.
263-
*/
264-
private Property getProperty(Class<?> clazz, String property, ElementType elementType) {
288+
private JavaBeanField getFieldProperty(Class<?> clazz, String property) {
265289
Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() );
266290

267-
if ( property == null || property.length() == 0 ) {
268-
throw LOG.getPropertyNameCannotBeNullOrEmptyException();
269-
}
291+
Field field = run( GetDeclaredField.action( clazz, property ) );
292+
return field == null ? null : new JavaBeanField( field );
293+
}
270294

271-
if ( !( ElementType.FIELD.equals( elementType ) || ElementType.METHOD.equals( elementType ) ) ) {
272-
throw LOG.getElementTypeHasToBeFieldOrMethodException();
273-
}
295+
private JavaBeanGetter getGetterProperty(Class<?> clazz, String property) {
296+
Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() );
274297

275-
if ( ElementType.FIELD.equals( elementType ) ) {
276-
Field field = run( GetDeclaredField.action( clazz, property ) );
277-
return field == null ? null : new JavaBeanField( field );
278-
}
279-
else {
280-
Method method = null;
281-
String methodName = property.substring( 0, 1 ).toUpperCase( Locale.ROOT ) + property.substring( 1 );
282-
for ( String prefix : ReflectionHelper.PROPERTY_ACCESSOR_PREFIXES ) {
283-
method = run( GetMethod.action( clazz, prefix + methodName ) );
284-
if ( method != null ) {
285-
break;
286-
}
298+
Method method = null;
299+
String methodName = property.substring( 0, 1 ).toUpperCase( Locale.ROOT ) + property.substring( 1 );
300+
for ( String prefix : ReflectionHelper.PROPERTY_ACCESSOR_PREFIXES ) {
301+
method = run( GetMethod.action( clazz, prefix + methodName ) );
302+
if ( method != null ) {
303+
break;
287304
}
288-
return method == null ? null : new JavaBeanGetter( method );
289305
}
306+
return method == null ? null : new JavaBeanGetter( method );
290307
}
291308

292309
/**

engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import org.hibernate.validator.internal.util.TypeResolutionHelper;
6161
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
6262
import org.hibernate.validator.testutils.ValidatorUtil;
63+
6364
import org.testng.annotations.BeforeMethod;
6465
import org.testng.annotations.Test;
6566

@@ -535,6 +536,28 @@ public void testProgrammaticAndAnnotationPropertyConstraintsAddUp() {
535536
);
536537
}
537538

539+
@Test
540+
public void testFieldAndGetterMethodsForProgrammaticConstraintDefinition() {
541+
mapping.type( Marathon.class )
542+
.getter( "name" )
543+
.constraint( new SizeDef().min( 5 ) )
544+
.constraint( new SizeDef().min( 10 ) )
545+
.field( "runners" )
546+
.constraint( new SizeDef().max( 10 ).min( 1 ) );
547+
config.addMapping( mapping );
548+
Validator validator = config.buildValidatorFactory().getValidator();
549+
550+
Marathon marathon = new Marathon();
551+
marathon.setName( "Foo" );
552+
553+
Set<ConstraintViolation<Marathon>> violations = validator.validate( marathon );
554+
assertThat( violations ).containsOnlyViolations(
555+
violationOf( Size.class ).withMessage( "size must be between 10 and 2147483647" ),
556+
violationOf( Size.class ).withMessage( "size must be between 5 and 2147483647" ),
557+
violationOf( Size.class ).withMessage( "size must be between 1 and 10" )
558+
);
559+
}
560+
538561
private <T> BeanConfiguration<T> getBeanConfiguration(Class<T> type) {
539562
Set<BeanConfiguration<?>> beanConfigurations = mapping.getBeanConfigurations(
540563
new ConstraintHelper(),

0 commit comments

Comments
 (0)