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

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

Go to download

Kiwi is a utility library. We really like Google's Guava, and also use Apache Commons. But if they don't have something we need, and we think it is useful, this is where we put it.

There is a newer version: 4.5.2
Show newest version
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