// Copyright © 2015 HSL <https://www.hsl.fi> // This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses. package fi.hsl.parkandride.util; import com.google.common.collect.ImmutableMap; import fi.hsl.parkandride.core.domain.Violation; import fi.hsl.parkandride.core.domain.validation.NotNullElement; import fi.hsl.parkandride.core.service.ValidationException; import javax.annotation.Nonnull; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import static fi.hsl.parkandride.util.Predicates.*; import static java.util.stream.Collectors.toList; public class ArgumentValidator<T> { private static final String UNKNOWN_TYPE = "Unknown"; private final T obj; private final Set<PredicateWithMessage<T>> predicates; ArgumentValidator(T obj, @Nonnull @NotNullElement Set<PredicateWithMessage<T>> predicates) { this.obj = obj; this.predicates = Objects.requireNonNull(predicates); } public static <T> Builder<T> validate(T arg) { return validate(arg, UNKNOWN_TYPE); } public static <T> Builder<T> validate(T arg, String type) { return new Builder(arg, type); } public T match() { final List<Violation> violations = predicates.stream() .map(pred -> pred.test(obj)) .filter(Objects::nonNull) .collect(toList()); if (violations.size() > 0) { throw new ValidationException(violations); } return obj; } private static class PredicateWithMessage<T> { final Predicate<T> predicate; final Supplier<String> type; final Function<T, String> message; public PredicateWithMessage(Predicate<T> predicate, Supplier<String> type, Function<T, String> message) { this.predicate = predicate; this.type = type; this.message = message; } Violation test(T obj) { if (predicate.test(obj)) { return null; } return new Violation(type.get(), ImmutableMap.of(), null, message.apply(obj)); } } public static class Builder<T> { private final T obj; private final Set<PredicateWithMessage<T>> predicates = new HashSet<>(); private final String type; public Builder(T obj, String type) { this.obj = obj; this.type = type; } public Builder<T> addPredicate(Predicate<T> pred, Function<T, String> message) { predicates.add(new PredicateWithMessage<>( pred, () -> type, message )); return this; } public Builder<T> addPredicate(Predicate<T> pred, String message) { return addPredicate(pred, t -> message); } public T gt(Comparable<? super T> other) { addPredicate( greaterThan(other), t -> String.format("<%s> must be greater than <%s>", t, other) ); return match(); } public T gte(Comparable<? super T> other) { addPredicate( greaterThanOrEqualTo(other), t -> String.format("<%s> must be greater than or equal to <%s>", t, other) ); return match(); } public T lt(Comparable<? super T> other) { addPredicate( lessThan(other), t -> String.format("<%s> must be less than <%s>", t, other) ); return match(); } public T lte(Comparable<? super T> other) { addPredicate( lessThanOrEqualTo(other), t -> String.format("<%s> must be less than or equal to <%s>", t, other) ); return match(); } public T match() { return build().match(); } public ArgumentValidator<T> build() { Objects.requireNonNull(type, "Validation type not supplied"); return new ArgumentValidator<>(obj, predicates); } public T notNull() { addPredicate( Objects::nonNull, "Must not be null" ); return match(); } } }