Skip to content

Commit 6d56faa

Browse files
marko-bekhtagsmet
authored andcommitted
HV-1526 Make a distinction between cross-parameter and other ConstraintValidatorContext implementations
Thus we don't have the method parameter names in the ConstraintValidatorContext if it does not make sense, e.g. for class-level or field validation.
1 parent c5fdd19 commit 6d56faa

File tree

8 files changed

+151
-44
lines changed

8 files changed

+151
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.constraintvalidation;
8+
9+
import java.util.List;
10+
11+
import javax.validation.ConstraintValidatorContext;
12+
13+
import org.hibernate.validator.Incubating;
14+
15+
/**
16+
* A custom {@link ConstraintValidatorContext} which provides additional functionality for cross parameter validation contexts.
17+
*
18+
* @author Marko Bekhta
19+
* @since 6.1.0
20+
*/
21+
@Incubating
22+
public interface HibernateCrossParameterConstraintValidatorContext extends HibernateConstraintValidatorContext {
23+
24+
/**
25+
* @return the list of the parameter names of the validated method.
26+
*/
27+
List<String> getMethodParameterNames();
28+
}

engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ParameterScriptAssertValidator.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
1010
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
1111

12+
import java.util.Collections;
1213
import java.util.List;
1314
import java.util.Map;
1415

@@ -18,9 +19,8 @@
1819
import javax.validation.metadata.ConstraintDescriptor;
1920

2021
import org.hibernate.validator.constraints.ParameterScriptAssert;
21-
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
2222
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
23-
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
23+
import org.hibernate.validator.constraintvalidation.HibernateCrossParameterConstraintValidatorContext;
2424
import org.hibernate.validator.internal.util.Contracts;
2525

2626
/**
@@ -42,14 +42,18 @@ public void initialize(ConstraintDescriptor<ParameterScriptAssert> constraintDes
4242

4343
@Override
4444
public boolean isValid(Object[] arguments, ConstraintValidatorContext constraintValidatorContext) {
45-
if ( constraintValidatorContext instanceof HibernateConstraintValidatorContext ) {
46-
constraintValidatorContext.unwrap( HibernateConstraintValidatorContext.class ).addMessageParameter( "script", escapedScript );
47-
}
45+
Map<String, Object> bindings;
46+
if ( constraintValidatorContext instanceof HibernateCrossParameterConstraintValidatorContext ) {
47+
HibernateCrossParameterConstraintValidatorContext crossParameterConstraintValidatorContext = constraintValidatorContext
48+
.unwrap( HibernateCrossParameterConstraintValidatorContext.class );
4849

49-
List<String> parameterNames = ( (ConstraintValidatorContextImpl) constraintValidatorContext )
50-
.getMethodParameterNames();
50+
crossParameterConstraintValidatorContext.addMessageParameter( "script", escapedScript );
5151

52-
Map<String, Object> bindings = getBindings( arguments, parameterNames );
52+
bindings = getBindings( arguments, crossParameterConstraintValidatorContext.getMethodParameterNames() );
53+
}
54+
else {
55+
bindings = Collections.emptyMap();
56+
}
5357

5458
return scriptAssertContext.evaluateScriptAssertExpression( bindings );
5559
}

engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
3636
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
3737
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext;
38+
import org.hibernate.validator.internal.engine.constraintvalidation.CrossParameterConstraintValidatorContextImpl;
3839
import org.hibernate.validator.internal.engine.path.PathImpl;
3940
import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
4041
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
4142
import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData;
4243
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
4344
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
45+
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType;
4446
import org.hibernate.validator.internal.metadata.facets.Validatable;
4547
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
4648
import org.hibernate.validator.internal.util.logging.Log;
@@ -252,7 +254,7 @@ public ConstraintValidatorManager getConstraintValidatorManager() {
252254
* @return The current executable's parameter names,if this context was
253255
* created for parameter validation, {@code null} otherwise.
254256
*/
255-
public List<String> getParameterNames() {
257+
private List<String> getParameterNames() {
256258
if ( !ValidationOperation.PARAMETER_VALIDATION.equals( validationOperation ) ) {
257259
return null;
258260
}
@@ -397,8 +399,17 @@ public void setValidatedProperty(String validatedProperty) {
397399
}
398400

399401
public ConstraintValidatorContextImpl createConstraintValidatorContextFor(ConstraintDescriptorImpl<?> constraintDescriptor, PathImpl path) {
402+
if ( ConstraintType.CROSS_PARAMETER.equals( constraintDescriptor.getConstraintType() ) ) {
403+
return new CrossParameterConstraintValidatorContextImpl(
404+
getParameterNames(),
405+
validatorScopedContext.getClockProvider(),
406+
path,
407+
constraintDescriptor,
408+
validatorScopedContext.getConstraintValidatorPayload()
409+
);
410+
}
411+
400412
return new ConstraintValidatorContextImpl(
401-
getParameterNames(),
402413
validatorScopedContext.getClockProvider(),
403414
path,
404415
constraintDescriptor,

engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public final <T> boolean validateConstraints(ValidationContext<T> validationCont
7575
for ( ConstraintValidatorContextImpl constraintValidatorContext : violatedConstraintValidatorContexts ) {
7676
for ( ConstraintViolationCreationContext constraintViolationCreationContext : constraintValidatorContext.getConstraintViolationCreationContexts() ) {
7777
validationContext.addConstraintFailure(
78-
validationContext.createConstraintViolation( valueContext, constraintViolationCreationContext, ( (ConstraintValidatorContextImpl) constraintValidatorContext ).getConstraintDescriptor() )
78+
validationContext.createConstraintViolation( valueContext, constraintViolationCreationContext, constraintValidatorContext.getConstraintDescriptor() )
7979
);
8080
}
8181
}

engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida
4545

4646
private Map<String, Object> messageParameters;
4747
private Map<String, Object> expressionVariables;
48-
private final List<String> methodParameterNames;
4948
private final ClockProvider clockProvider;
5049
private final PathImpl basePath;
5150
private final ConstraintDescriptor<?> constraintDescriptor;
@@ -54,9 +53,11 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida
5453
private Object dynamicPayload;
5554
private final Object constraintValidatorPayload;
5655

57-
public ConstraintValidatorContextImpl(List<String> methodParameterNames, ClockProvider clockProvider,
58-
PathImpl propertyPath, ConstraintDescriptor<?> constraintDescriptor, Object constraintValidatorPayload) {
59-
this.methodParameterNames = methodParameterNames;
56+
public ConstraintValidatorContextImpl(
57+
ClockProvider clockProvider,
58+
PathImpl propertyPath,
59+
ConstraintDescriptor<?> constraintDescriptor,
60+
Object constraintValidatorPayload) {
6061
this.clockProvider = clockProvider;
6162
this.basePath = propertyPath;
6263
this.constraintDescriptor = constraintDescriptor;
@@ -74,11 +75,10 @@ public final String getDefaultConstraintMessageTemplate() {
7475
}
7576

7677
@Override
77-
public final ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) {
78+
public ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) {
7879
return new ConstraintViolationBuilderImpl(
79-
methodParameterNames,
8080
messageTemplate,
81-
PathImpl.createCopy( basePath )
81+
getCopyOfBasePath()
8282
);
8383
}
8484

@@ -161,6 +161,10 @@ public final List<ConstraintViolationCreationContext> getConstraintViolationCrea
161161
return CollectionHelper.toImmutableList( returnedConstraintViolationCreationContexts );
162162
}
163163

164+
protected final PathImpl getCopyOfBasePath() {
165+
return PathImpl.createCopy( basePath );
166+
}
167+
164168
private ConstraintViolationCreationContext getDefaultConstraintViolationCreationContext() {
165169
return new ConstraintViolationCreationContext(
166170
getDefaultConstraintMessageTemplate(),
@@ -171,10 +175,6 @@ private ConstraintViolationCreationContext getDefaultConstraintViolationCreation
171175
);
172176
}
173177

174-
public List<String> getMethodParameterNames() {
175-
return methodParameterNames;
176-
}
177-
178178
private abstract class NodeBuilderBase {
179179

180180
protected final String messageTemplate;
@@ -202,13 +202,10 @@ public ConstraintValidatorContext addConstraintViolation() {
202202
}
203203
}
204204

205-
private class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder {
206-
207-
private final List<String> methodParameterNames;
205+
protected class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder {
208206

209-
private ConstraintViolationBuilderImpl(List<String> methodParameterNames, String template, PathImpl path) {
207+
protected ConstraintViolationBuilderImpl(String template, PathImpl path) {
210208
super( template, path );
211-
this.methodParameterNames = methodParameterNames;
212209
}
213210

214211
@Override
@@ -233,14 +230,7 @@ public LeafNodeBuilderCustomizableContext addBeanNode() {
233230

234231
@Override
235232
public NodeBuilderDefinedContext addParameterNode(int index) {
236-
if ( propertyPath.getLeafNode().getKind() != ElementKind.CROSS_PARAMETER ) {
237-
throw LOG.getParameterNodeAddedForNonCrossParameterConstraintException( propertyPath );
238-
}
239-
240-
dropLeafNodeIfRequired();
241-
propertyPath.addParameterNode( methodParameterNames.get( index ), index );
242-
243-
return new NodeBuilder( messageTemplate, propertyPath );
233+
throw LOG.getParameterNodeAddedForNonCrossParameterConstraintException( propertyPath );
244234
}
245235

246236
@Override
@@ -251,22 +241,20 @@ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(St
251241
}
252242

253243
/**
254-
* In case nodes are added from within a class-level or cross-parameter
255-
* constraint, the node representing the constraint element will be
256-
* dropped. inIterable(), getKey() etc.
244+
* In case nodes are added from within a class-level constraint, the node representing
245+
* the constraint element will be dropped. inIterable(), getKey() etc.
257246
*/
258247
private void dropLeafNodeIfRequired() {
259-
if ( propertyPath.getLeafNode().getKind() == ElementKind.BEAN || propertyPath.getLeafNode()
260-
.getKind() == ElementKind.CROSS_PARAMETER ) {
248+
if ( propertyPath.getLeafNode().getKind() == ElementKind.BEAN ) {
261249
propertyPath = propertyPath.getPathWithoutLeafNode();
262250
}
263251
}
264252
}
265253

266-
private class NodeBuilder extends NodeBuilderBase
254+
protected class NodeBuilder extends NodeBuilderBase
267255
implements NodeBuilderDefinedContext, LeafNodeBuilderDefinedContext, ContainerElementNodeBuilderDefinedContext {
268256

269-
private NodeBuilder(String template, PathImpl path) {
257+
protected NodeBuilder(String template, PathImpl path) {
270258
super( template, path );
271259
}
272260

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.internal.engine.constraintvalidation;
8+
9+
import java.util.List;
10+
11+
import javax.validation.ClockProvider;
12+
import javax.validation.ElementKind;
13+
import javax.validation.metadata.ConstraintDescriptor;
14+
15+
import org.hibernate.validator.constraintvalidation.HibernateCrossParameterConstraintValidatorContext;
16+
import org.hibernate.validator.internal.engine.path.PathImpl;
17+
import org.hibernate.validator.internal.util.Contracts;
18+
19+
/**
20+
* @author Marko Bekhta
21+
*/
22+
public class CrossParameterConstraintValidatorContextImpl extends ConstraintValidatorContextImpl implements HibernateCrossParameterConstraintValidatorContext {
23+
24+
private final List<String> methodParameterNames;
25+
26+
public CrossParameterConstraintValidatorContextImpl(List<String> methodParameterNames, ClockProvider clockProvider, PathImpl propertyPath, ConstraintDescriptor<?> constraintDescriptor, Object constraintValidatorPayload) {
27+
super( clockProvider, propertyPath, constraintDescriptor, constraintValidatorPayload );
28+
Contracts.assertTrue( propertyPath.getLeafNode().getKind() == ElementKind.CROSS_PARAMETER, "Context can only be used for corss parameter validation" );
29+
this.methodParameterNames = methodParameterNames;
30+
}
31+
32+
@Override
33+
public final ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) {
34+
return new CrossParameterConstraintViolationBuilderImpl(
35+
methodParameterNames,
36+
messageTemplate,
37+
getCopyOfBasePath()
38+
);
39+
}
40+
41+
@Override
42+
public List<String> getMethodParameterNames() {
43+
return methodParameterNames;
44+
}
45+
46+
@Override
47+
public <T> T unwrap(Class<T> type) {
48+
//allow unwrapping into public super types
49+
if ( type.isAssignableFrom( HibernateCrossParameterConstraintValidatorContext.class ) ) {
50+
return type.cast( this );
51+
}
52+
return super.unwrap( type );
53+
}
54+
55+
private class CrossParameterConstraintViolationBuilderImpl extends ConstraintViolationBuilderImpl {
56+
57+
private final List<String> methodParameterNames;
58+
59+
private CrossParameterConstraintViolationBuilderImpl(List<String> methodParameterNames, String template, PathImpl path) {
60+
super( template, path );
61+
this.methodParameterNames = methodParameterNames;
62+
}
63+
64+
@Override
65+
public NodeBuilderDefinedContext addParameterNode(int index) {
66+
dropLeafNode();
67+
propertyPath.addParameterNode( methodParameterNames.get( index ), index );
68+
69+
return new NodeBuilder( messageTemplate, propertyPath );
70+
}
71+
72+
private void dropLeafNode() {
73+
propertyPath = propertyPath.getPathWithoutLeafNode();
74+
}
75+
}
76+
}

engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ private ConstraintValidatorContextImpl createEmptyConstraintValidatorContextImpl
226226
PathImpl path = PathImpl.createRootPath();
227227
path.addBeanNode();
228228

229-
ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, null, path, null, null );
229+
ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, path, null, null );
230230
context.disableDefaultConstraintViolation();
231231
return context;
232232
}

engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,6 @@ public static <T, I extends T> T getValidatingProxy(I implementor, Validator exe
235235
}
236236

237237
public static HibernateConstraintValidatorContext getConstraintValidatorContext() {
238-
return new ConstraintValidatorContextImpl( null, DefaultClockProvider.INSTANCE, null, null, null );
238+
return new ConstraintValidatorContextImpl( DefaultClockProvider.INSTANCE, null, null, null );
239239
}
240240
}

0 commit comments

Comments
 (0)