/* * lemonad - Some functional sweetness for Java * Copyright (C) 2019 Pacien TRAN-GIRARD * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package org.pacien.lemonad.validation; import org.pacien.lemonad.attempt.Attempt; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import lombok.NonNull; import static java.util.function.Function.identity; import static org.pacien.lemonad.attempt.Attempt.failure; import static org.pacien.lemonad.attempt.Attempt.success; /** * Wraps the result of the validation of a subject. * * @param the subject type, * @param the error type. * @author pacien */ public interface Validation { /** * @return whether no error have been reported during the validation. */ boolean isValid(); /** * @return whether some error have been reported during the validation. */ boolean isInvalid(); /** * @return the subject of the validation. */ S getSubject(); /** * @return the potentially empty list of reported validation errors. */ List getErrors(); /** * @param consumer a subject consumer called if the validation is successful. * @return the current object. */ default Validation ifValid(@NonNull Consumer consumer) { if (isValid()) consumer.accept(getSubject()); return this; } /** * @param consumer the consumer called with the validation subject and reported errors if the validation has failed. * @return the current object. */ default Validation ifInvalid(@NonNull BiConsumer> consumer) { if (isInvalid()) consumer.accept(getSubject(), getErrors()); return this; } /** * @param predicate the validation predicate testing the validity of a subject. * @param error the error to return if the subject does not pass the test. * @return an updated {@link Validation}. */ default Validation validate(@NonNull Predicate predicate, @NonNull E error) { return validate(identity(), predicate, error); } /** * @param mapper the field getter mapping the validation subject. * @param predicate the validation predicate testing the validity of a subject. * @param error the error to return if the subject does not pass the test. * @return an updated {@link Validation}. */ default Validation validate( @NonNull Function mapper, @NonNull Predicate predicate, E error ) { return validate(mapper, field -> predicate.test(field) ? List.of() : List.of(error)); } /** * @param validator the validating function to use, returning a potentially empty list of errors. * @return an updated {@link Validation}. */ default Validation validate(@NonNull Function> validator) { var errors = validator.apply(getSubject()); return errors.isEmpty() ? this : merge(errors); } /** * @param mapper the field getter mapping the validation subject. * @param validator the validating function to use, returning a potentially empty list of errors. * @return an updated {@link Validation}. */ default Validation validate( @NonNull Function mapper, @NonNull Function> validator ) { return validate(validator.compose(mapper)); } /** * @param validator a subject validating function returning a {@link Validation}. * @return an updated {@link Validation}. */ default Validation merge(@NonNull Function> validator) { return merge(validator.apply(getSubject())); } /** * @param mapper the field getter mapping the validation subject. * @param validator a subject validating function returning a {@link Validation}. * @return an updated {@link Validation}. */ default Validation merge( @NonNull Function mapper, @NonNull Function> validator ) { return merge(validator.compose(mapper)); } /** * @param validation another validation to merge into the current one. * @return an updated {@link Validation}. */ @SuppressWarnings("unchecked") default Validation merge(@NonNull Validation validation) { if (validation.isValid()) return this; if (this.isValid()) return Validation.of(this.getSubject(), (List) validation.getErrors()); return merge(validation.getErrors()); } /** * @param errors a potentially empty list of additional errors to take into account. * @return an updated {@link Validation}. */ default Validation merge(@NonNull Collection errors) { var combinedErrors = new ArrayList(getErrors().size() + errors.size()); combinedErrors.addAll(getErrors()); combinedErrors.addAll(errors); return new ValidationContainer<>(getSubject(), combinedErrors); } /** * @param mapper a function transforming a {@link Validation}. * @return the transformed {@link Validation}. */ default Validation flatMap( @NonNull Function, ? extends Validation> mapper ) { //noinspection unchecked return (Validation) mapper.apply(this); } /** * @return an {@link Attempt} with a state corresponding to the one of the validation. */ default Attempt> toAttempt() { return isValid() ? success(getSubject()) : failure(getErrors()); } /** * @param subject the subject of the validation. * @param errors some optional validation errors. * @return a {@link Validation}. */ @SafeVarargs static Validation of(S subject, E... errors) { return Validation.of(subject, List.of(errors)); } /** * @param subject the subject of the validation. * @param errors some optional validation errors. * @return a {@link Validation}. */ static Validation of(S subject, @NonNull List errors) { return new ValidationContainer<>(subject, errors); } }