package com.googlecode.totallylazy.validations; import com.googlecode.totallylazy.functions.Callables; import com.googlecode.totallylazy.functions.CurriedMonoid; import com.googlecode.totallylazy.functions.Function1; import com.googlecode.totallylazy.functions.Curried2; import com.googlecode.totallylazy.Pair; import com.googlecode.totallylazy.Sequence; import com.googlecode.totallylazy.Unchecked; import com.googlecode.totallylazy.collections.PersistentMap; import com.googlecode.totallylazy.comparators.Comparators; import com.googlecode.totallylazy.predicates.LogicalPredicate; import static com.googlecode.totallylazy.Objects.equalTo; import static com.googlecode.totallylazy.Sequences.empty; import static com.googlecode.totallylazy.Sequences.identity; import static com.googlecode.totallylazy.Sequences.sequence; import static com.googlecode.totallylazy.collections.AVLTree.constructors.avlTree; import static com.googlecode.totallylazy.collections.AVLTree.constructors.factory; import static com.googlecode.totallylazy.validations.ValidationResult.constructors.pass; import static java.lang.String.format; /** * An immutable map of String keys to sequences of failure messages */ public class ValidationResult { public static final String DEFAULT_KEY = "value"; private final PersistentMap<String, Sequence<String>> messages; public ValidationResult(PersistentMap<String, Sequence<String>> messages) { this.messages = messages; } public ValidationResult assignToKey(String key) { return new ValidationResult(avlTree(key, allMessages())); } public Sequence<Pair<String, Sequence<String>>> messages() { return sequence(messages); } public Sequence<String> messages(String key) { return messages.lookup(key).getOrElse(empty(String.class)); } /** * Calls key.toString() and returns messages for that string */ public Sequence<String> messages(Object key) { return messages(key.toString()); } public Sequence<String> allMessages() { return sequence(messages). map(Callables.<Sequence<String>>second()). flatMap(identity(String.class)); } public ValidationResult add(String key, Iterable<String> messages) { Sequence<String> newMessages = messages(key).join(messages); if(newMessages.isEmpty()) return this; return new ValidationResult(this.messages.insert(key, newMessages)); } public ValidationResult add(String key, String message) { return add(key, sequence(message)); } public ValidationResult add(Iterable<Pair<String, Iterable<String>>> messages) { return sequence(messages).fold(this, ValidationResult.functions.add()); } public ValidationResult merge(ValidationResult value) { return this.add(Unchecked.<Iterable<Pair<String, Iterable<String>>>>cast(value.messages())); } public ValidationResult remove(String key) { return new ValidationResult(messages.delete(key)); } public boolean succeeded() { return allMessages().isEmpty(); } public boolean failed() { return !succeeded(); } @Override public String toString() { return format("%s%s", succeeded()? "success" : "failure", messages); } @Override public boolean equals(Object o) { return o instanceof ValidationResult && equalTo(((ValidationResult) o).messages, messages); } @Override public int hashCode() { return messages == null ? 19 : messages.hashCode(); } public PersistentMap<String, Sequence<String>> toMap() { return messages; } public static class constructors { public static ValidationResult pass() { return new ValidationResult(emptyMap); } public static ValidationResult failure(String message) { return failure(DEFAULT_KEY, message); } public static ValidationResult failure(String key, String message) { return pass().add(key, message); } } public static class functions { public static Function1<ValidationResult, Sequence<String>> allMessages(){ return ValidationResult::allMessages; } public static Function1<ValidationResult, Sequence<String>> messages(final String key){ return result -> result.messages(key); } public static Function1<ValidationResult, Sequence<String>> messages(final Object key){ return result -> result.messages(key); } public static LogicalPredicate<ValidationResult> succeeded() { return new LogicalPredicate<ValidationResult>() { @Override public boolean matches(ValidationResult other) { return other.succeeded(); } }; } public static Function1<ValidationResult, ValidationResult> assignToKey(final String key) { return validationResult -> validationResult.assignToKey(key); } public static Curried2<ValidationResult, Iterable<String>, ValidationResult> addWithKey(final String key) { return (validationResult, messages1) -> validationResult.add(key, messages1); } public static Curried2<ValidationResult, Pair<String, String>, ValidationResult> addSingleMessage() { return (validationResult, keyAndMessage) -> validationResult.add(keyAndMessage.first(), keyAndMessage.second()); } public static Curried2<ValidationResult, String, ValidationResult> addSingleMessageWithKey(final String key) { return (validationResult, message) -> validationResult.add(key, message); } public static Curried2<ValidationResult, Pair<String, ? extends Iterable<String>>, ValidationResult> add() { return (validationResult, keyAndMessages) -> validationResult.add(keyAndMessages.first(), keyAndMessages.second()); } public static CurriedMonoid<ValidationResult> merge() { return new CurriedMonoid<ValidationResult>() { @Override public ValidationResult call(ValidationResult seed, ValidationResult value) throws Exception { return seed.merge(value); } @Override public ValidationResult identity() { return pass(); } }; } } private static final PersistentMap<String, Sequence<String>> emptyMap = factory.create(Comparators.<String>ascending()); }