org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bean-validator Show documentation
Show all versions of bean-validator Show documentation
JSR 380's RI, Hibernate Validator version ${hibernate-validator.version} and its dependencies repackaged as OSGi bundle
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or .
*/
package org.hibernate.validator.internal.engine.constraintvalidation;
import static org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.DUMMY_CONSTRAINT_VALIDATOR;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Set;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import org.hibernate.validator.internal.engine.ValidationContext;
import org.hibernate.validator.internal.engine.ValueContext;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
/**
* Due to constraint composition a single constraint annotation can lead to a whole constraint tree being validated.
* This class encapsulates such a tree.
*
* @author Hardy Ferentschik
* @author Federico Mancini
* @author Dag Hovland
* @author Kevin Pollet <[email protected]> (C) 2012 SERLI
* @author Guillaume Smet
* @author Marko Bekhta
*/
public abstract class ConstraintTree {
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
/**
* The constraint descriptor for the constraint represented by this constraint tree.
*/
protected final ConstraintDescriptorImpl descriptor;
private final Type validatedValueType;
/**
* Either the initialized constraint validator for the default constraint validator factory or
* {@link ConstraintValidatorManager#DUMMY_CONSTRAINT_VALIDATOR}.
*/
private volatile ConstraintValidator constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext;
protected ConstraintTree(ConstraintDescriptorImpl descriptor, Type validatedValueType) {
this.descriptor = descriptor;
this.validatedValueType = validatedValueType;
}
public static ConstraintTree of(ConstraintDescriptorImpl composingDescriptor, Type validatedValueType) {
if ( composingDescriptor.getComposingConstraintImpls().isEmpty() ) {
return new SimpleConstraintTree<>( composingDescriptor, validatedValueType );
}
else {
return new ComposingConstraintTree<>( composingDescriptor, validatedValueType );
}
}
public final boolean validateConstraints(ValidationContext executionContext, ValueContext valueContext) {
Set> constraintViolations = newHashSet( 5 );
validateConstraints( executionContext, valueContext, constraintViolations );
if ( !constraintViolations.isEmpty() ) {
executionContext.addConstraintFailures( constraintViolations );
return false;
}
return true;
}
protected abstract void validateConstraints(ValidationContext executionContext, ValueContext valueContext, Set> constraintViolations);
public final ConstraintDescriptorImpl getDescriptor() {
return descriptor;
}
public final Type getValidatedValueType() {
return this.validatedValueType;
}
private ValidationException getExceptionForNullValidator(Type validatedValueType, String path) {
if ( descriptor.getConstraintType() == ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER ) {
return LOG.getValidatorForCrossParameterConstraintMustEitherValidateObjectOrObjectArrayException(
descriptor.getAnnotationType()
);
}
else {
String className = validatedValueType.toString();
if ( validatedValueType instanceof Class ) {
Class clazz = (Class) validatedValueType;
if ( clazz.isArray() ) {
className = clazz.getComponentType().toString() + "[]";
}
else {
className = clazz.getName();
}
}
return LOG.getNoValidatorFoundForTypeException( descriptor.getAnnotationType(), className, path );
}
}
protected final ConstraintValidator getInitializedConstraintValidator(ValidationContext validationContext, ValueContext valueContext) {
ConstraintValidator validator;
if ( validationContext.getConstraintValidatorFactory() == validationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory()
&& validationContext.getConstraintValidatorInitializationContext() == validationContext.getConstraintValidatorManager()
.getDefaultConstraintValidatorInitializationContext() ) {
validator = constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext;
if ( validator == null ) {
synchronized ( this ) {
validator = constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext;
if ( validator == null ) {
validator = getInitializedConstraintValidator( validationContext );
constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext = validator;
}
}
}
}
else {
// For now, we don't cache the result in the ConstraintTree if we don't use the default constraint validator
// factory. Creating a lot of CHM for that cache might not be a good idea and we prefer being conservative
// for now. Note that we have the ConstraintValidatorManager cache that mitigates the situation.
// If you come up with a use case where it makes sense, please reach out to us.
validator = getInitializedConstraintValidator( validationContext );
}
if ( validator == DUMMY_CONSTRAINT_VALIDATOR ) {
throw getExceptionForNullValidator( validatedValueType, valueContext.getPropertyPath().asString() );
}
return validator;
}
@SuppressWarnings("unchecked")
private ConstraintValidator getInitializedConstraintValidator(ValidationContext validationContext) {
ConstraintValidator validator = validationContext.getConstraintValidatorManager().getInitializedValidator(
validatedValueType,
descriptor,
validationContext.getConstraintValidatorFactory(),
validationContext.getConstraintValidatorInitializationContext()
);
if ( validator != null ) {
return validator;
}
else {
return (ConstraintValidator) DUMMY_CONSTRAINT_VALIDATOR;
}
}
protected final Set> validateSingleConstraint(ValidationContext executionContext,
ValueContext valueContext,
ConstraintValidatorContextImpl constraintValidatorContext,
ConstraintValidator validator) {
boolean isValid;
try {
@SuppressWarnings("unchecked")
V validatedValue = (V) valueContext.getCurrentValidatedValue();
isValid = validator.isValid( validatedValue, constraintValidatorContext );
}
catch (RuntimeException e) {
if ( e instanceof ConstraintDeclarationException ) {
throw e;
}
throw LOG.getExceptionDuringIsValidCallException( e );
}
if ( !isValid ) {
//We do not add these violations yet, since we don't know how they are
//going to influence the final boolean evaluation
return executionContext.createConstraintViolations(
valueContext, constraintValidatorContext
);
}
return Collections.emptySet();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "ConstraintTree" );
sb.append( "{ descriptor=" ).append( descriptor );
sb.append( '}' );
return sb.toString();
}
}