ContextFunctions.java

package com.github.dakusui.actionunit.core.context;

import com.github.dakusui.actionunit.actions.ContextVariable;
import com.github.dakusui.actionunit.core.Context;
import com.github.dakusui.actionunit.core.context.multiparams.MultiParamsContextConsumerBuilder;
import com.github.dakusui.actionunit.core.context.multiparams.MultiParamsContextFunctionBuilder;
import com.github.dakusui.actionunit.core.context.multiparams.MultiParamsContextPredicateBuilder;
import com.github.dakusui.actionunit.utils.Checks;
import com.github.dakusui.actionunit.utils.InternalUtils;
import com.github.dakusui.actionunit.utils.StableTemplatingUtils;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.IntStream;

import static com.github.dakusui.actionunit.utils.InternalUtils.objectToStringIfOverridden;
import static com.github.dakusui.actionunit.utils.InternalUtils.toStringIfOverriddenOrNoname;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;

public enum ContextFunctions {
  ;

  public static MultiParamsContextPredicateBuilder multiParamsPredicateFor(ContextVariable... variables) {
    return new MultiParamsContextPredicateBuilder(variables);
  }

  public static MultiParamsContextConsumerBuilder multiParamsConsumerFor(ContextVariable... variables) {
    return new MultiParamsContextConsumerBuilder(variables);
  }

  public static <R> MultiParamsContextFunctionBuilder<R> multiParamsFunctionFor(ContextVariable... variables) {
    return new MultiParamsContextFunctionBuilder<>(variables);
  }

  public static String describeFunctionalObject(Object f, final IntFunction<String> placeHolderFormatter, ContextVariable... variables) {
    return String.format("(%s)->%s",
        String.join(",", Arrays.stream(variables).map(ContextVariable::variableName).toArray(String[]::new)),
        summarize(StableTemplatingUtils.template(
            objectToStringIfOverridden(f, obj -> formatPlaceHolders(obj, placeHolderFormatter, variables)),
            new TreeMap<String, Object>() {{
              IntStream.range(0, variables.length).forEach(
                  i -> put(placeHolderFormatter.apply(i), String.format("${%s}", variables[i]))
              );
            }}), 60));
  }

  private static String formatPlaceHolders(Object obj, IntFunction<String> placeHolderFormatter, ContextVariable[] v) {
    return InternalUtils.fallbackFormatter().apply(obj) +
        IntStream.range(0, v.length)
            .mapToObj(placeHolderFormatter)
            .collect(joining(", ", "(", ")"));
  }

  public static <R> ContextConsumer assignTo(ContextVariable contextVariable, Function<Context, R> value) {
    requireNonNull(contextVariable);
    requireNonNull(value);
    return ContextConsumer.of(
        () -> format("assignTo[%s](%s)", contextVariable.variableName(), toStringIfOverriddenOrNoname(value)),
        (c) -> c.assignTo(contextVariable.internalVariableName(), value.apply(c)));
  }

  public static <R> ContextFunction<R> immediateOf(R value) {
    return ContextFunction.of(() -> format("%s", value), c -> value);
  }

  public static <R> ContextFunction<R> contextValueOf(ContextVariable contextVariable) {
    requireNonNull(contextVariable);
    return ContextFunction.of(
        () -> format("valueOf[%s]", contextVariable.variableName()),
        contextVariable::resolve
    );
  }

  public static ContextConsumer throwIllegalArgument() {
    return ContextConsumer.of(
        () -> "throw IllegalArgumentException",
        (c) -> {
          throw new IllegalArgumentException();
        }
    );
  }

  public static <R> ContextConsumer printTo(PrintStream ps, Function<Context, R> value) {
    requireNonNull(ps);
    return ContextConsumer.of(
        () -> format("printTo[%s](%s)", prettyClassName(ps.getClass()), value),
        c -> ps.println(value.apply(c))
    );
  }

  public static <R> ContextConsumer writeTo(Consumer<R> sink, Function<Context, R> value) {
    requireNonNull(sink);
    return ContextConsumer.of(
        () -> format("writeTo[%s](%s)", prettyClassName(sink.getClass()), value),
        c -> sink.accept(value.apply(c))
    );
  }

  public static String prettyClassName(Class<?> c) {
    String ret = c.getSimpleName();
    if (ret.equals("")) {
      Class<?> mostRecentNamedSuper = mostRecentNamedSuperOf(c);
      if (!mostRecentNamedSuper.equals(Object.class))
        ret = format("(anon:%s)", mostRecentNamedSuperOf(c).getSimpleName());
      else
        ret = Arrays.stream(c.getInterfaces()).map(Class::getSimpleName).collect(joining
            (",", "(anon:", ")"));
    }
    return ret.replaceFirst("\\$\\$Lambda\\$\\d+/(0x)?[\\da-f]+$", ".lambda");
  }

  private static Class<?> mostRecentNamedSuperOf(Class<?> c) {
    if ("".equals(c.getSimpleName()))
      return mostRecentNamedSuperOf(c.getSuperclass());
    return c;
  }

  public static String summarize(String commandLine, int length) {
    Checks.requireArgument(l -> l > 3, length);
    return requireNonNull(commandLine).length() < length ?
        replaceNewLines(commandLine) :
        replaceNewLines(commandLine).substring(0, length - 3) + "...";
  }

  private static String replaceNewLines(String s) {
    return s.replaceAll("\n", " ");
  }
}