All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kiwiproject.validation.KiwiConstraintViolations Maven / Gradle / Ivy

package org.kiwiproject.validation;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.kiwiproject.base.KiwiPreconditions.checkArgumentNotNull;
import static org.kiwiproject.collect.KiwiSets.isNotNullOrEmpty;
import static org.kiwiproject.collect.KiwiSets.isNullOrEmpty;

import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.WordUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

/**
 * Static utilities for working with {@link javax.validation.ConstraintViolation} objects, generally
 * {@link java.util.Set}s of them.
 * 

* Dependency requirements: *

* The {@code jakarta.validation:jakarta.validation-api} dependency and some implementation such as Hibernate Validator * ({@code org.hibernate.validator:hibernate-validator} must be available at runtime. *

* In addition, currently the "pretty" methods use the {@code #humanize} methods, which rely on {@link WordUtils} from * commons-text. So if you use any of these, you will need to ensure {@code org.apache.commons:commons-text} is * available at runtime. */ @UtilityClass public class KiwiConstraintViolations { /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. If the given set is empty (or null), then throw IllegalArgumentException. * * @param violations set of non-empty violations * @param type of object being validated * @return the combined error message * @throws IllegalArgumentException if violations is null or empty */ public static String simpleCombinedErrorMessage(Set> violations) { return combinedErrorMessage(violations, Objects::toString); } /** * Given a set of non-empty violations, produce a single string containing all violation messages separated * by commas. If the given set is empty (or null), then return null. * * @param violations set of violations * @param type of object being validated * @return the combined error message, or null */ public static String simpleCombinedErrorMessageOrNull(Set> violations) { return combinedErrorMessageOrNull(violations, Objects::toString); } /** * Given a set of non-empty violations, produce a single string containing all violation messages separated * by commas. If the given set is empty (or null), then return an empty Optional. * * @param violations set of violations * @param type of object being validated * @return the combined error message, or en empty Optional */ public static Optional simpleCombinedErrorMessageOrEmpty(Set> violations) { return combinedErrorMessageOrEmpty(violations, Objects::toString); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. Each property name is "prettified" by converting {@code camelCase} to sentence case, * for example {@code firstName} becomes "First Name" in the resulting error message. * If the given set is empty (or null), then throw IllegalArgumentException. * * @param violations set of non-empty violations * @param type of object being validated * @return the combined error message * @throws IllegalArgumentException if violations is null or empty */ public static String prettyCombinedErrorMessage(Set> violations) { return combinedErrorMessage(violations, KiwiConstraintViolations::humanize); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. If the given set is empty (or null), then return null. *

* Each property name is "prettified" by converting {@code camelCase} to sentence case, * for example {@code firstName} becomes "First Name" in the resulting error message. * * @param violations set of violations * @param type of object being validated * @return the combined error message, or null */ public static String prettyCombinedErrorMessageOrNull(Set> violations) { return combinedErrorMessageOrNull(violations, KiwiConstraintViolations::humanize); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. If the given set is empty (or null), then return an empty Optional. * * @param violations set of violations * @param type of object being validated * @return the combined error message, or an empty Optional */ public static Optional prettyCombinedErrorMessageOrEmpty(Set> violations) { return combinedErrorMessageOrEmpty(violations, KiwiConstraintViolations::humanize); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. Each property name is transformed using the specified {@code pathTransformer} function. * If the given set is empty (or null), then throw IllegalArgumentException. * * @param violations set of non-empty violations * @param pathTransformer function to convert a Path into a String * @param type of object being validated * @return the combined error message * @throws IllegalArgumentException if violations is null or empty */ public static String combinedErrorMessage(Set> violations, Function pathTransformer) { checkNotNullOrEmpty(violations); checkArgumentNotNull(pathTransformer); return combinedErrorMessageOrEmpty(violations, pathTransformer).orElseThrow(); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. If the given set is empty (or null), then return null. *

* Each property name is transformed using the specified {@code pathTransformer} function. * * @param violations set of violations * @param pathTransformer function to convert a Path into a String * @param type of object being validated * @return the combined error message, or null */ public static String combinedErrorMessageOrNull(Set> violations, Function pathTransformer) { return combinedErrorMessageOrEmpty(violations, pathTransformer).orElse(null); } /** * Given a non-empty set of violations, produce a single string containing all violation messages * separated by commas. If the given set is empty (or null), then return an empty Optional. *

* Each property name is transformed using the specified {@code pathTransformer} function. * * @param violations set of violations * @param pathTransformer function to convert a Path into a String * @param type of object being validated * @return the combined error message, or an empty Optional */ public static Optional combinedErrorMessageOrEmpty(Set> violations, Function pathTransformer) { checkArgumentNotNull(pathTransformer); if (isNullOrEmpty(violations)) { return Optional.empty(); } var result = violations.stream() .map(violation -> KiwiConstraintViolations.propertyAndErrorMessage(violation, pathTransformer)) .sorted() .collect(joining(", ")); return Optional.of(result); } /** * Given a non-empty set of violations, produce a list of strings containing all violation messages. * Each message will contain the property followed by the error message, e.g. "firstName must not be blank". * If the given set is empty (or null), then return an empty list. * * @param violations set of non-empty violations * @param type of object being validated * @return a list of the error messages */ public static List simpleCombinedErrorMessages(Set> violations) { return combinedErrorMessages(violations, Objects::toString); } /** * Given a non-empty set of violations, produce a list of strings containing all violation messages. * Each message will contain the "prettified" property name followed by the error message, e.g. for * a violation on the {@code firstName} property, the message would look like "First Name must not be blank". * If the given set is empty (or null), then return an empty list. * * @param violations set of non-empty violations * @param type of object being validated * @return a list of the error messages */ public static List prettyCombinedErrorMessages(Set> violations) { return combinedErrorMessages(violations, KiwiConstraintViolations::humanize); } /** * Given a non-empty set of violations, produce a list of strings containing all violation messages. * Each message will contain the transformed property name followed by the error message, e.g. * "firstName must not be blank". Each property name is transformed using the specified {@code pathTransformer} * function. If the given set is empty (or null), then return an empty list. * * @param violations set of non-empty violations * @param pathTransformer function to convert a Path into a String * @param type of object being validated * @return a list of the error messages */ public static List combinedErrorMessages(Set> violations, Function pathTransformer) { checkArgumentNotNull(pathTransformer); if (isNullOrEmpty(violations)) { return List.of(); } return violations.stream() .map(violation -> KiwiConstraintViolations.propertyAndErrorMessage(violation, pathTransformer)) .sorted() .collect(toList()); } /** * Given a non-empty set of violations, produce map whose keys are the properties and the corresponding * values are strings containing all violation messages. If the given set is empty (or null), then return an empty * map. * * @param violations set of non-empty violations * @param type of object being validated * @return a map of error messages */ public static Map simpleCombineErrorMessagesIntoMap(Set> violations) { return combineErrorMessagesIntoMap(violations, Objects::toString); } /** * Given a non-empty set of violations, produce map whose keys are the "prettified" properties and the * corresponding values are strings containing all violation messages. If the given set is empty (or null), then * return an empty map. * * @param violations set of non-empty violations * @param type of object being validated * @return a map of error messages */ public static Map prettyCombineErrorMessagesIntoMap(Set> violations) { return combineErrorMessagesIntoMap(violations, KiwiConstraintViolations::humanize); } /** * Given a non-empty set of violations, produce map whose keys are the transformed properties and the * corresponding values are strings containing all violation messages. Each property name is transformed using * the specified {@code pathTransformer} function. If the given set is empty (or null), then return an empty map. * * @param violations set of non-empty violations * @param pathTransformer function to convert a Path into a String * @param type of object being validated * @return a map of error messages */ public static Map combineErrorMessagesIntoMap(Set> violations, Function pathTransformer) { checkArgumentNotNull(pathTransformer); if (isNullOrEmpty(violations)) { return Map.of(); } return violations.stream() .collect(toMap( violation -> pathTransformer.apply(violation.getPropertyPath()), ConstraintViolation::getMessage, (accumulatedMessage, newErrorMessage) -> accumulatedMessage + ", " + newErrorMessage)); } /** * Transforms the given property path into a human-readable version. Nested paths are separated by * a slash character. Examples: *

    *
  • age becomes Age
  • *
  • firstName becomes First Name
  • *
  • contactInfo.email.address becomes Contact Info / Email / Address
  • *
* * @param propertyPath the property path from a {@link ConstraintViolation} * @return a human-readable path * @throws IllegalArgumentException if either argument is null */ public static String humanize(Path propertyPath) { return humanize(propertyPath, "/"); } /** * Transforms the give property path into a human-readable version. Nested paths are separated by * the given {@code pathSeparator}. *

* For example contactInfo.email.address using ":" as the path separator would result in Contact Info:Email:Address. * * @param propertyPath the property path from a {@link ConstraintViolation} * @param pathSeparator the separator to use between path elements * @return a human-readable path * @throws IllegalArgumentException if either argument is null */ public static String humanize(Path propertyPath, String pathSeparator) { checkArgumentNotNull(propertyPath, "propertyPath must not be null"); checkArgumentNotNull(pathSeparator, "pathSeparator must not be null"); var splat = StringUtils.splitByCharacterTypeCamelCase(propertyPath.toString()); var joined = Arrays.stream(splat) .map(str -> ".".equals(str) ? pathSeparator : str) .collect(joining(" ")); return WordUtils.capitalize(joined); } private static void checkNotNullOrEmpty(Set> violations) { checkArgument(isNotNullOrEmpty(violations), "There are no violations to combine"); } private static String propertyAndErrorMessage(ConstraintViolation violation, Function pathTransformer) { return pathTransformer.apply(violation.getPropertyPath()) + " " + violation.getMessage(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy