com.vaadin.data.BeanValidationBinder Maven / Gradle / Ivy
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.data;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;
import com.vaadin.data.BeanPropertySet.NestedBeanPropertyDefinition;
import com.vaadin.data.util.BeanUtil;
import com.vaadin.data.validator.BeanValidator;
/**
* @author Vaadin Ltd
* @see Binder
* @see HasValue
*
* @since 8.0
*/
public class BeanValidationBinder extends Binder {
private final Class beanType;
private RequiredFieldConfigurator requiredConfigurator = RequiredFieldConfigurator.DEFAULT;
/**
* Creates a new binder that uses reflection based on the provided bean type
* to resolve bean properties. It assumes that JSR-303 bean validation
* implementation is present on the classpath. If there is no such
* implementation available then {@link Binder} class should be used instead
* (this constructor will throw an exception). Otherwise
* {@link BeanValidator} is added to each binding that is defined using a
* property name.
*
* @param beanType
* the bean type to use, not null
*/
public BeanValidationBinder(Class beanType) {
this(beanType, false);
}
/**
* Creates a new binder that uses reflection based on the provided bean type
* to resolve bean properties. It assumes that JSR-303 bean validation
* implementation is present on the classpath. If there is no such
* implementation available then {@link Binder} class should be used instead
* (this constructor will throw an exception). Otherwise
* {@link BeanValidator} is added to each binding that is defined using a
* property name.
*
* @param beanType
* the bean type to use, not {@code null}
* @param scanNestedDefinitions
* if {@code true}, scan for nested property definitions as well
*
* @since 8.10
*/
public BeanValidationBinder(Class beanType,
boolean scanNestedDefinitions) {
super(beanType, scanNestedDefinitions);
if (!BeanUtil.checkBeanValidationAvailable()) {
throw new IllegalStateException(BeanValidationBinder.class
.getSimpleName()
+ " cannot be used because a JSR-303 Bean Validation "
+ "implementation not found on the classpath or could not be initialized. Use "
+ Binder.class.getSimpleName() + " instead");
}
this.beanType = beanType;
}
/**
* Sets a logic which allows to configure require indicator via
* {@link HasValue#setRequiredIndicatorVisible(boolean)} based on property
* descriptor.
*
* Required indicator configuration will not be used at all if
* {@code configurator} is null.
*
* By default the {@link RequiredFieldConfigurator#DEFAULT} configurator is
* used.
*
* @param configurator
* required indicator configurator, may be {@code null}
*/
public void setRequiredConfigurator(
RequiredFieldConfigurator configurator) {
requiredConfigurator = configurator;
}
/**
* Gets field required indicator configuration logic.
*
* @see #setRequiredConfigurator(RequiredFieldConfigurator)
*
* @return required indicator configurator, may be {@code null}
*/
public RequiredFieldConfigurator getRequiredConfigurator() {
return requiredConfigurator;
}
@Override
protected BindingBuilder configureBinding(
BindingBuilder binding,
PropertyDefinition definition) {
Class> actualBeanType = findBeanType(beanType, definition);
BeanValidator validator = new BeanValidator(actualBeanType,
definition.getTopLevelName());
if (requiredConfigurator != null) {
configureRequired(binding, definition, validator);
}
return binding.withValidator(validator);
}
/**
* Finds the bean type containing the property the given definition refers
* to.
*
* @param beanType
* the root beanType
* @param definition
* the definition for the property
* @return the bean type containing the given property
*/
@SuppressWarnings({ "rawtypes" })
private Class> findBeanType(Class beanType,
PropertyDefinition definition) {
if (definition instanceof NestedBeanPropertyDefinition) {
return ((NestedBeanPropertyDefinition) definition).getParent()
.getType();
} else {
// Non nested properties must be defined in the main type
return beanType;
}
}
private void configureRequired(BindingBuilder binding,
PropertyDefinition definition, BeanValidator validator) {
assert requiredConfigurator != null;
Class> propertyHolderType = definition.getPropertyHolderType();
BeanDescriptor descriptor = validator.getJavaxBeanValidator()
.getConstraintsForClass(propertyHolderType);
PropertyDescriptor propertyDescriptor = descriptor
.getConstraintsForProperty(definition.getTopLevelName());
if (propertyDescriptor == null) {
return;
}
if (propertyDescriptor.getConstraintDescriptors().stream()
.map(ConstraintDescriptor::getAnnotation)
.anyMatch(requiredConfigurator)) {
binding.getField().setRequiredIndicatorVisible(true);
}
}
}