Contextful.java

package com.github.dakusui.actionunit.actions;

import com.github.dakusui.actionunit.core.Action;
import com.github.dakusui.actionunit.core.Context;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.github.dakusui.actionunit.core.ActionSupport.simple;
import static com.github.dakusui.actionunit.utils.InternalUtils.toStringIfOverriddenOrNoname;
import static com.github.dakusui.printables.PrintableFunctionals.*;
import static java.util.Objects.requireNonNull;

public interface Contextful<S> extends Action, ContextVariable {
  /**
   * A function to provide a value referenced from inside an action returned by the
   * {@link Contextful#action()} method.
   *
   * @return A function to provide a value for an action.
   */
  Function<Context, S> valueSource();

  /**
   * Returns a main action.
   *
   * @return A main action.
   */
  Action action();

  @Override
  String variableName();

  @Override
  String internalVariableName();

  abstract class Base<V, S> implements Contextful<S> {
    private final String variableName;

    private final String internalVariableName;

    private final Function<Context, S> valueSource;

    private final Action action;


    public Base(String variableName, final String internalVariableName, Function<Context, S> valueSource, Action action) {
      this.variableName = variableName;
      this.internalVariableName = internalVariableName;
      this.valueSource = valueSource;
      this.action = action;
    }

    @Override
    public Function<Context, S> valueSource() {
      return valueSource;
    }

    @Override
    public Action action() {
      return action;
    }

    @Override
    public String variableName() {
      return variableName;
    }

    @Override
    public String internalVariableName() {
      return internalVariableName;
    }

  }

  abstract class Builder<
      B extends Builder<B, A, V, S>,
      A extends Contextful<S>,
      V,
      S>
      extends Action.Builder<A>
      implements ContextVariable {
    private final Function<Context, S> valueSource;
    private final String               internalVariableName;
    private final String               variableName;

    private Action action;

    protected Builder(String variableName, Function<Context, S> function) {
      this.variableName = requireNonNull(variableName);
      this.internalVariableName = composeInternalVariableName(variableName);
      this.valueSource = requireNonNull(function);
    }

    @SuppressWarnings("unchecked")
    public B action(Action action) {
      this.action = requireNonNull(action);
      return (B) this;
    }

    @SuppressWarnings("unchecked")
    public B action(Function<B, Action> action) {
      return this.action(action.apply((B) this));
    }

    public A perform(Action action) {
      return action(action).build();
    }

    public A perform(Function<B, Action> action) {
      return this.action(action).build();
    }


    public Action action() {
      return this.action;
    }

    public String variableName() {
      return this.variableName;
    }

    public String internalVariableName() {
      return this.internalVariableName;
    }

    public Function<Context, S> valueSource() {
      return this.valueSource;
    }

    /**
     * Creates an action that consumes the context variable.
     *
     * @param consumer A consumer that processes the context variable.
     * @return A created action.
     */
    public Action toAction(Consumer<V> consumer) {
      return simple("action:" + variableName(),
          printableConsumer((Context c) -> variableReferenceConsumer(consumer).accept(c)).describe(toStringIfOverriddenOrNoname(consumer)));
    }

    public <W> Function<Context, W> function(Function<V, W> function) {
      return toContextFunction(this, function);

    }

    public Consumer<Context> consumer(Consumer<V> consumer) {
      return toContextConsumer(this, consumer);
    }

    public Predicate<Context> predicate(Predicate<V> predicate) {
      return toContextPredicate(this, predicate);
    }

    public V resolveValue(Context context) {
      return this.resolve(context);
    }

    public String toString() {
      return variableName();
    }

    protected String composeInternalVariableName(String variableName) {
      return this.getClass().getEnclosingClass().getCanonicalName() + ":" + variableName + ":" + System.identityHashCode(this);
    }

    private Consumer<Context> variableReferenceConsumer(Consumer<V> consumer) {
      return (Context context) -> consumer.accept(this.resolveValue(context));
    }

    private static <V, W> Function<Context, W> toContextFunction(Builder<?, ?, V, ?> builder, Function<V, W> function) {
      return printableFunction((Context context) -> function.apply(builder.resolveValue(context)))
          .describe(toStringIfOverriddenOrNoname(function));
    }

    private static <V> Consumer<Context> toContextConsumer(Builder<?, ?, V, ?> builder, Consumer<V> consumer) {
      return printableConsumer((Context context) -> consumer.accept(builder.resolveValue(context)))
          .describe(toStringIfOverriddenOrNoname(consumer));
    }

    private static <V> Predicate<Context> toContextPredicate(Builder<?, ?, V, ?> builder, Predicate<V> predicate) {
      return printablePredicate(
          (Context context) -> predicate.test(builder.resolveValue(context)))
          .describe(() -> builder.variableName() + ":" + toStringIfOverriddenOrNoname(predicate));
    }

  }
}