Evaluable.java
package com.github.dakusui.pcond.core;
import com.github.dakusui.pcond.experimentals.currying.context.CurriedContext;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* An interface that models "forms".
* A form is a general idea that covers predicates, functions, and "special-forms".
*
* A simple form (such as one returned by {@link Predicate#isEqual(Object)}) is
* modeled as and held by {@link Evaluable.LeafPred}.
* The framework just delegates the evaluation to the object.
*
* However, for a form which has an internal structure, such as one returned by
* {@link Predicate#and(Predicate)}, this approach doesn't work.
* Because, in order to make the evaluation process visible and readable for human,
* we need intermediate evaluation results.
*
* That is, when we evaluate a form `v != null && v.startsWith("hello")`, we want
* information about which predicate was violated.
* Just showing the actual value of `v` is not sufficient, because the `v` and the
* predicates in the evaluation might have internal structures or logics that make
* it difficult/impossible to infer which predicate is violated.
*
* @param <T> The type of the value evaluated by this object.
*/
public interface Evaluable<T> {
/**
* Performs an evaluation of the `evaluationContext` with a given `evaluator`.
*
* @param evaluableIo An execution occurrence of an evaluable.
* @param evaluationContext An evaluation context.
* @param evaluator An evaluator with which the `evaluationContext` is evaluated.
*/
<O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator);
default boolean isSquashable() {
return false;
}
default Evaluable<T> makeTrivial() {
throw new UnsupportedOperationException();
}
/**
* A base interface to model all the predicates in the model of the evaluation
* framework.
*
* @param <T> The type of the value to be tested.
*/
interface Pred<T> extends Evaluable<T> {
}
/**
* A base interface for conjunction (and) and disjunction (or) of predicates.
*
* @param <T> The type of the value to be evaluated.
*/
interface Composite<T> extends Pred<T> {
/**
* Returns the predicates with which the target value is evaluated.
*
* @return The list of the child predicates.
*/
List<Evaluable<T>> children();
/**
* Returns `true` if the "shortcut" evaluation is enabled.
*
* Suppose you have a following predicate.
*
* ----
* a && b && c
* ----
*
* If the `a` results in `false`, the `b` and `c` doesn't need to be evaluated.
* The optimization, where the evaluations for the `b` and `c` are skipped,
* is called "shortcut".
*
* However, in the context of testing, sometimes we want to know the evaluation
* results for the `b` and `c`.
* Otherwise, we cannot avoid getting into a fail->fix->run->fail... loop,
* sometimes.
*
* @return `true` if the "shortcut" evaluation is enabled.
*/
boolean shortcut();
@Override
default boolean isSquashable() {
return children().size() <= 1;
}
}
/**
* An interface to model a conjunction (`and`, `&&`) predicate.
*
* @param <T> The type of the value to be evaluated.
*/
interface Conjunction<T> extends Composite<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateConjunction((EvaluableIo<T, Conjunction<T>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
}
/**
* An interface to model a disjunction (`or`, `||`) predicate.
*
* @param <T> The type of the value to be evaluated.
*/
interface Disjunction<T> extends Composite<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateDisjunction((EvaluableIo<T, Disjunction<T>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
}
/**
* An interface to model a negation (`not`, `negate`, `!`) of a predicate.
*
* @param <T> The type of the value to be evaluated.
*/
interface Negation<T> extends Pred<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateNegation((EvaluableIo<T, Negation<T>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
/**
* The predicate the negation is applied.
*
* @return A target predicate.
*/
Evaluable<T> target();
@Override
default boolean isSquashable() {
return true;
}
}
/**
* An interface to model a simple predicate in the evaluation framework.
*
* @param <T> The type of the value to be evaluated.
*/
interface LeafPred<T> extends Pred<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateLeaf((EvaluableIo<T, LeafPred<T>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
/**
* Returns an actual predicate modeled by this interface.
*
* @return The predicate modeled by this interface.
*/
Predicate<? super T> predicate();
}
/**
* An interface to model a predicate for {@link CurriedContext}.
*
* @see CurriedContext
*/
interface CurriedContextPred extends Pred<CurriedContext> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<CurriedContext, Evaluable<CurriedContext>, O> evaluableIo, EvaluationContext<CurriedContext> evaluationContext, Evaluator evaluator) {
evaluator.evaluateCurriedContextPredicate((EvaluableIo<CurriedContext, CurriedContextPred, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
<T> Evaluable<T> enclosed();
int argIndex();
}
/**
* An interface to model a predicate for {@link Stream}.
*
* @param <E> The type of elements in the stream to be evaluated.
*/
interface StreamPred<E> extends Pred<Stream<E>> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<Stream<E>, Evaluable<Stream<E>>, O> evaluableIo, EvaluationContext<Stream<E>> evaluationContext, Evaluator evaluator) {
evaluator.evaluateStreamPredicate((EvaluableIo<Stream<E>, StreamPred<E>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
/**
* Returns a default value returned as the entire result of this predicate.
* The `value` is used when a "cut" happens.
*
* @return a default value to fallback of this object.
*/
boolean defaultValue();
/**
* Returns an evaluable object which makes "cut" happen.
* If the result of the evaluation of the returned object becomes equal to the
* returned value of the {@link StreamPred#valueToCut()}, a "cut" will actually happen.
*
* @return An evaluable which triggers a "cut".
*/
Evaluable<E> cut();
/**
* Returns a value to make a "cut" happen.
*
* A "cut" is a situation, where an evaluation process for the elements in the
* stream is ended without reaching the last one.
* This is necessary to model a functionalities of `Stream`, such as
* `allMatch`, `noneMatch`, and `anyMatch`.
*
* @return value ( `true` / `false` ) to make a "cut" happen.
*/
boolean valueToCut();
/**
* In order to generate an informative report, the framework needs information
* about the expected value for each predicate.
*
* The "expected" value of a predicate can be different inside the tree of the `Evaluables`,
* when a negation is used.
*
* If this `Evaluable` node requests to flip the expectation value under the node,
* this method should return `true`.
*
* @return `true`, if the expectation flip is requested.
*/
default boolean requestExpectationFlip() {
return false;
} }
/**
* An interface to model a "transforming predicate", which models the "transform and check" style of value validation.
* The idea of the style is to first transform a value into a type, which is easy to read for human and to check for machine, such as list, integer, string, etc., in order to validate a value.
*
* @param <T> The type of the value to be evaluated.
* @param <R> The type to which the value (`T`) is transformed and then tested.
*/
interface Transformation<T, R> extends Pred<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateTransformation((EvaluableIo<T, Transformation<T, R>, Boolean>) (EvaluableIo) evaluableIo, evaluationContext);
}
/**
* Returns a transformer of this object.
*
* @return A transformer function.
*/
Evaluable<T> mapper();
Evaluable<R> checker();
/**
* Returns a name of a transformer, if any.
*
* @return An optional to store a name of the transformer.
*/
Optional<String> mapperName();
/**
* Returns a name of a checker, if any.
*
* @return An optional to store a name of the checker.
*/
Optional<String> checkerName();
}
/**
* An interface to model a function ({@link Function}) in the evaluation framework
* of the `pcond`.
*
* @param <T> The type of the value to be evaluated.
*/
interface Func<T> extends Evaluable<T> {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default <O> void accept(EvaluableIo<T, Evaluable<T>, O> evaluableIo, EvaluationContext<T> evaluationContext, Evaluator evaluator) {
evaluator.evaluateFunction((EvaluableIo<T, Func<T>, O>) (EvaluableIo) evaluableIo, evaluationContext);
}
Function<? super T, Object> head();
Optional<Evaluable<Object>> tail();
}
}