![JAR search and dependency download from the Maven repository](/logo.png)
com.indoqa.beanvalidation.PropertyValidator Maven / Gradle / Ivy
Show all versions of indoqa-beanvalidation Show documentation
/*
* Licensed to the Indoqa Software Design und Beratung GmbH (Indoqa) under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Indoqa licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.indoqa.beanvalidation;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.Predicate;
import com.indoqa.beanvalidation.property.PropertyExtractor;
import com.indoqa.beanvalidation.property.PropertyFunction;
/**
* The purpose of the {@link PropertyValidator} is the creation of the validation functions and their constraints for a given
* method or lambda expression on a bean.
*
* @param the type of the property (method) - usually handled by the compiler for a given method reference.
* @param the return type of the method - usually handled by the compiler for a given method reference.
*/
public class PropertyValidator {
private PropertyFunction
function;
private String property;
private String propertySeparator = ".";
private Map validationsMustBeTrue = new HashMap<>();
private Map validationsMustBeFalse = new HashMap<>();
private List beanValidators = new ArrayList<>();
/**
* Create a validator for the given method reference (property of a bean).
*
* @param function the method reference to validate
* @return A PropertyValidator for a given method.
*/
public static PropertyValidator
forMethod(PropertyFunction
function) {
return forMethod(function, null);
}
/**
* Create a validator for the given method reference (property of a bean), locks the name of the property (for validation messages).
*
* @param function the method reference to validate
* @param property the name of the property (for validation messages)
* @return A PropertyValidator for a given method.
*/
public static
PropertyValidator
forMethod(PropertyFunction
function, String property) {
PropertyValidator validator = new PropertyValidator();
validator.setFunction(function);
validator.setProperty(property);
return validator;
}
/**
* Create a validator for the given lambda expression, locks the name of the property (for validation
* messages).
*
* @param clazz the beans class to help the compiler with type interference
* @param function the lambda expression to validate
* @param property the name of the property (for validation messages)
* @return A PropertyValidator for a given method.
*/
public static
PropertyValidator
forLambda(@SuppressWarnings("unused") Class
clazz, PropertyFunction
function,
String property) {
PropertyValidator validator = new PropertyValidator();
validator.setFunction(function);
validator.setProperty(property);
return validator;
}
private void addValidatesIfTrue(String key, Predicate predicate) {
this.validationsMustBeTrue.put(predicate, key);
}
private void addValidatesIfFalse(String key, Predicate predicate) {
this.validationsMustBeFalse.put(predicate, key);
}
private void setFunction(PropertyFunction
function) {
this.function = function;
}
private void setProperty(String property) {
this.property = property;
}
private void setPropertySeparator(String propertySeparator) {
this.propertySeparator = propertySeparator;
}
/**
* Assigns a separator for nested properties for validation messages.
*/
public PropertyValidator
propertySeparator(String propertySeparator) {
this.setPropertySeparator(propertySeparator);
return this;
}
/**
* Assigns a property name for validation messages.
*/
public PropertyValidator
property(String property) {
this.setProperty(property);
return this;
}
/**
* Validates that the property is not null.
*/
public PropertyValidator
isNotNull() {
this.addValidatesIfTrue("is_not_null", (t) -> this.function.apply((P) t) != null);
return this;
}
/**
* Validates that the property is null.
*/
public PropertyValidator
isNull() {
this.addValidatesIfTrue("is_null", (t) -> this.function.apply((P) t) == null);
return this;
}
/**
* Validates that the property is empty.
* The method will:
*
* - not validate if the property is null
* - validate if the property is something else than ({@link String}, {@link Collection}, {@link Map} or an {@link Array})
*
*/
public PropertyValidator isEmpty() {
this.addValidatesIfTrue("is_empty", (t) -> {
Object value = this.function.apply((P) t);
if (value == null) {
return false;
}
if (value instanceof String) {
return ((String) value).isEmpty();
}
Boolean collectionsIsEmpty = getCollectionsIsEmpty(value);
if (collectionsIsEmpty != null) {
return collectionsIsEmpty;
}
return true;
});
return this;
}
private Boolean getCollectionsIsEmpty(Object value) {
if (value instanceof Collection) {
return ((Collection) value).isEmpty();
}
if (value instanceof Map) {
return ((Map) value).isEmpty();
}
if (value instanceof Object[]) {
return ((Object[]) value).length == 0;
}
if (value.getClass().isArray()) {
return Array.getLength(value) == 0;
}
return null;
}
/**
* Validates that the property is not empty.
*
* The method will:
*
* - not validate if the property is null
* - validate if the property is something else than ({@link String}, {@link Collection}, {@link Map} or an {@link Array})
*
*/
public PropertyValidator isNotEmpty() {
this.addValidatesIfFalse("is_not_empty", (t) -> {
Object value = this.function.apply((P) t);
if (value == null) {
return true;
}
if (value instanceof String) {
return ((String) value).isEmpty();
}
Boolean collectionsIsEmpty = getCollectionsIsEmpty(value);
if (collectionsIsEmpty != null) {
return collectionsIsEmpty;
}
return false;
});
return this;
}
/**
* Validates that the property is true.
*
* The method will:
*
* - not validate if the property is null
* - validate if the property is not a {@link Boolean}
*
*/
public PropertyValidator isTrue() {
this.addValidatesIfTrue("is_true", (t) -> {
Object value = this.function.apply((P) t);
if (value == null) {
return false;
}
if (value instanceof Boolean) {
return ((Boolean) value);
}
return true;
});
return this;
}
/**
* Validates that the property is false.
*
* The method will:
*
* - not validate if the property is null
* - validate if the property is not a {@link Boolean}
*
*/
public PropertyValidator isFalse() {
this.addValidatesIfFalse("is_false", (t) -> {
Object value = this.function.apply((P) t);
if (value == null) {
return true;
}
if (value instanceof Boolean) {
return ((Boolean) value);
}
return false;
});
return this;
}
/**
* Validates that the result of the predicate evaluation is true.
* If the property is null it does not validate.
*
* @param key for the validation error.
* @param predicate to test against
*/
public PropertyValidator
isTrue(String key, Predicate predicate) {
this.addValidatesIfTrue(key, (t) -> {
R value = this.function.apply((P) t);
if (value == null) {
return false;
}
return predicate.test(value);
});
return this;
}
/**
* Validates that the result of the predicate evaluation is false.
* If the property is null it does not validate.
*
* @param key for the validation error.
* @param predicate to test against
*/
public PropertyValidator isFalse(String key, Predicate predicate) {
this.addValidatesIfFalse(key, (t) -> {
R value = this.function.apply((P) t);
if (value == null) {
return true;
}
return predicate.test(value);
});
return this;
}
/**
* Evaluates all given validation rules against the <P> bean.
*
* @param toValidate the <P> bean to validate
* @return ValidationResult as container for validation errors.
*/
public ValidationResult validate(P toValidate) {
ValidationResult result = new ValidationResult();
result.setPropertySeparator(this.propertySeparator);
if (this.property == null) {
this.property = PropertyExtractor.getPropertyName(this.function);
}
for (Map.Entry eachEntry : this.validationsMustBeTrue.entrySet()) {
Predicate predicate = eachEntry.getKey();
if (!predicate.test(toValidate)) {
result.addError(this.property, eachEntry.getValue());
}
}
for (Map.Entry eachEntry : this.validationsMustBeFalse.entrySet()) {
Predicate predicate = eachEntry.getKey();
if (predicate.test(toValidate)) {
result.addError(this.property, eachEntry.getValue());
}
}
for (BeanValidator beanValidator : beanValidators) {
result.addErrors(this.property, beanValidator.validateAll(this.function.apply((P) toValidate)));
}
return result;
}
/**
* Adds another {@link BeanValidator} to validate nested objects.
*
* @param beanValidator for the nested object.
*/
public PropertyValidator withBeanValidator(BeanValidator> beanValidator) {
this.beanValidators.add(beanValidator);
return this;
}
}