EvaluationContext.java
package com.github.dakusui.pcond.core;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import static com.github.dakusui.pcond.core.EvaluationEntry.Type.*;
import static java.util.Objects.requireNonNull;
/**
* The new design:
*
* Evaluator: Concentrates on "evaluate" an individual evaluable (form). No aware of how to compose evaluation entries.
*/
public class EvaluationContext<T> {
final List<EvaluationEntry> evaluationEntries = new LinkedList<>();
final List<EvaluationEntry> visitorLineage = new LinkedList<>();
boolean expectationFlipped = false;
public EvaluationContext() {
}
public EvaluationContext(EvaluationContext<?> parent) {
this.expectationFlipped = parent.isExpectationFlipped();
}
/**
* @param evaluableIo An object to hold a form and its I/O.
* @param evaluatorCallback A callback that executes a logic specific to the {@code evaluable}.
*/
public <E extends Evaluable<T>, O> void evaluate(EvaluableIo<T, E, O> evaluableIo, BiFunction<E, ValueHolder<T>, ValueHolder<O>> evaluatorCallback) {
evaluate(resolveEvaluationEntryType(evaluableIo.evaluable()), evaluableIo, evaluatorCallback);
}
public <E extends Evaluable<T>, O> void evaluate(EvaluationEntry.Type type, EvaluableIo<T, E, O> evaluableIo, BiFunction<E, ValueHolder<T>, ValueHolder<O>> evaluatorCallback) {
evaluate(type, evaluableIo, io -> evaluatorCallback.apply(io.evaluable(), io.input()));
}
public <E extends Evaluable<T>, O> void evaluate(EvaluationEntry.Type type, EvaluableIo<T, E, O> evaluableIo, Function<EvaluableIo<T, E, O>, ValueHolder<O>> function) {
evaluate(type, formNameOf(evaluableIo), evaluableIo, function);
}
public <E extends Evaluable<T>, O> void evaluate(EvaluationEntry.Type type, String formName, EvaluableIo<T, E, O> evaluableIo, Function<EvaluableIo<T, E, O>, ValueHolder<O>> function) {
requireNonNull(evaluableIo);
EvaluableIo<T, E, O> evaluableIoWork = this.enter(evaluableIo.input(), type, formName, evaluableIo.evaluable());
this.leave(evaluableIoWork, function.apply(evaluableIoWork));
DebuggingUtils.printTo(this, System.err, 1);
updateEvaluableIo(evaluableIo, evaluableIoWork);
}
public static String formNameOf(EvaluableIo<?, ?, ?> evaluableIo) {
return formNameOf(evaluableIo.evaluableType(), evaluableIo.evaluable());
}
public static String formNameOf(EvaluationEntry.Type type, Evaluable<?> e) {
return type.formName(e);
}
public boolean isExpectationFlipped() {
return this.expectationFlipped;
}
public void flipExpectation() {
this.expectationFlipped = !expectationFlipped;
}
private static <T, E extends Evaluable<T>, O> void updateEvaluableIo(EvaluableIo<T, E, O> evaluableIo, EvaluableIo<T, E, O> evaluableIoWork) {
evaluableIo.output(evaluableIoWork.output());
}
public static <T> EvaluationEntry.Type resolveEvaluationEntryType(Evaluable<T> evaluable) {
if (evaluable instanceof Evaluable.LeafPred || evaluable instanceof Evaluable.CurriedContextPred || evaluable instanceof Evaluable.StreamPred)
return LEAF;
if (evaluable instanceof Evaluable.Func)
return FUNCTION;
if (evaluable instanceof Evaluable.Conjunction)
return AND;
if (evaluable instanceof Evaluable.Disjunction)
return OR;
if (evaluable instanceof Evaluable.Negation)
return NOT;
if (evaluable instanceof Evaluable.Transformation)
return TRANSFORM_AND_CHECK;
throw new IllegalArgumentException();
}
@SuppressWarnings("unchecked")
private <E extends Evaluable<T>, O> EvaluableIo<T, E, O> enter(ValueHolder<T> input, EvaluationEntry.Type type, String formName, E evaluable) {
EvaluableIo<T, Evaluable<T>, O> ret = createEvaluableIo(input, type, formName, evaluable);
this.evaluationEntries.add(createEvaluationEntry(this, ret));
this.visitorLineage.add(evaluationEntries.get(evaluationEntries.size() - 1));
return (EvaluableIo<T, E, O>) ret;
}
private <E extends Evaluable<T>, O> void leave(EvaluableIo<T, E, O> evaluableIo, ValueHolder<O> output) {
EvaluationEntry.Impl currentEvaluationEntry = (EvaluationEntry.Impl) this.visitorLineage.remove(this.visitorLineage.size() - 1);
evaluableIo.output(output);
currentEvaluationEntry.finalizeValues();
}
private static <T, O> EvaluableIo<T, Evaluable<T>, O> createEvaluableIo(ValueHolder<T> input, EvaluationEntry.Type type, String formName, Evaluable<T> evaluable) {
return new EvaluableIo<>(input, type, formName, evaluable);
}
private static <T, E extends Evaluable<T>> EvaluationEntry createEvaluationEntry(
EvaluationContext<T> evaluationContext,
EvaluableIo<T, E, ?> evaluableIo) {
return new EvaluationEntry.Impl(evaluationContext, evaluableIo);
}
public List<EvaluationEntry> resultEntries() {
return new ArrayList<>(this.evaluationEntries);
}
public <R> void importEntries(EvaluationContext<R> childContext) {
importEntries(childContext, currentIndentLevel());
}
public <R> void importEntries(EvaluationContext<R> childContext, int indentLevelGap) {
childContext.evaluationEntries.forEach(each -> each.level += indentLevelGap);
this.evaluationEntries.addAll(childContext.resultEntries());
}
public int currentIndentLevel() {
return this.visitorLineage.size();
}
}