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());
}