package com.googlecode.totallylazy;
import com.googlecode.totallylazy.functions.Callables;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.predicates.Predicate;
import com.googlecode.totallylazy.predicates.Predicates;
import java.util.ArrayDeque;
import java.util.Deque;
import static com.googlecode.totallylazy.Rule.rule;
import static com.googlecode.totallylazy.Sequences.sequence;
public class Rules<A, B> implements Function1<A, B>, Predicate<A>, Value<Sequence<Rule<A,B>>> {
private final Deque<Rule<A, B>> rules = new ArrayDeque<Rule<A, B>>();
private Rules(Sequence<Rule<A, B>> rules) {
this.rules.addAll(rules.toList());
}
@Deprecated
public Rules<A, B> add(final Predicate<? super A> predicate, final Function1<? super A, ? extends B> callable) {
return addFirst(predicate, callable);
}
@Deprecated
public Rules<A, B> add(Rule<? super A, ? extends B> rule) {
addFirst(Unchecked.<Rule<A, B>>cast(rule));
return this;
}
public Rules<A, B> addFirst(final Predicate<? super A> predicate, final Function1<? super A, ? extends B> callable) {
return addFirst(rule(predicate, callable));
}
public Rules<A, B> addFirst(Rule<? super A, ? extends B> rule) {
rules.addFirst(Unchecked.<Rule<A, B>>cast(rule));
return this;
}
public Rules<A, B> addLast(final Predicate<? super A> predicate, final Function1<? super A, ? extends B> callable) {
return addLast(rule(predicate, callable));
}
public Rules<A, B> addLast(Rule<? super A, ? extends B> rule) {
rules.addLast(Unchecked.<Rule<A, B>>cast(rule));
return this;
}
public Sequence<Rule<A,B>> value() {
return sequence(rules).realise();
}
@Override
public boolean matches(final A other) {
return sequence(rules).exists(Predicates.matches(other));
}
@Override
public B call(final A instance) throws Exception {
return find(instance).get().call(instance);
}
public Option<Rule<A, B>> find(A instance) {
return filter(instance).headOption();
}
public Sequence<Rule<A, B>> filter(A instance) {
return sequence(rules).
filter(Predicates.matches(instance));
}
public Sequence<B> applyMatching(A instance) {
return filter(instance).map(Callables.<A,B>callWith(instance));
}
public static <A, B> Rules<A, B> rules() {
return new Rules<A, B>(Sequences.<Rule<A, B>>empty());
}
public static <A, B> Rules<A, B> rules(Sequence<Rule<A, B>> rules) {
return new Rules<A, B>(rules);
}
}