All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dakusui.scriptiveunit.model.func.Func Maven / Gradle / Ivy

package com.github.dakusui.scriptiveunit.model.func;

import com.github.dakusui.scriptiveunit.core.ObjectMethod;
import com.github.dakusui.scriptiveunit.model.Stage;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Formattable;
import java.util.Formatter;

import static com.github.dakusui.scriptiveunit.core.Utils.check;
import static com.github.dakusui.scriptiveunit.exceptions.ScriptiveUnitException.fail;
import static com.github.dakusui.scriptiveunit.exceptions.ScriptiveUnitException.wrap;
import static com.github.dakusui.scriptiveunit.exceptions.TypeMismatch.valueReturnedByScriptableMethodMustBeFunc;

@FunctionalInterface
public interface Func extends
    java.util.function.Function,
    com.google.common.base.Function,
    Formattable {
  @Override
  O apply(Stage input);

  @Override
  default void formatTo(Formatter formatter, int flags, int width, int precision) {
    try {
      formatter.out().append("(unprintable)");
    } catch (IOException e) {
      throw wrap(e);
    }
  }

  interface Memoized extends Func {
  }

  /**
   * An interface that represents a constant. The {@code apply} method of an
   * implementation of this class must return the same value always regardless
   * of its arguments value, even if a {@code null} is given.
   *
   * @param  Type of output constant.
   */
  interface Const extends Func {
  }

  class Factory {
    private final FuncHandler funcHandler;

    public Factory(FuncHandler funcHandler) {
      this.funcHandler = funcHandler;
    }

    public Func create(FuncInvoker invoker, ObjectMethod objectMethod, /* TODO: Actually, this can be Func[] */ Object[] args) {
      Object returnedValue;
      /*
       * By using dynamic proxy, we are making it possible to print structured pretty log.
       */
      return createFunc(
          invoker,
          objectMethod.getName(),
          (Func) check(
              returnedValue = objectMethod.invoke(args),
              (Object o) -> o instanceof Func,
              () -> valueReturnedByScriptableMethodMustBeFunc(objectMethod.getName(), returnedValue)
          ));
    }

    public  Func createConst(FuncInvoker invoker, T value) {
      return createProxy((proxy, method, args) -> funcHandler.handleConst(invoker, value), Const.class);
    }

    private Func createFunc(FuncInvoker invoker, String name, Func target) {
      return createProxy(createInvocationHandler(invoker, name, target), Func.class);
    }

    private InvocationHandler createInvocationHandler(FuncInvoker invoker, String name, Func target) {
      return (Object proxy, Method method, Object[] args) -> {
        check("apply".equals(method.getName()),
            fail("This should only be executed on 'apply' method.")
        );
        check(args.length == 1 && args[0] instanceof Stage,
            fail("The argument should be an array of length 1 and its first element should be '%s', but: %s",
                Arrays.toString(args),
                Stage.class.getCanonicalName()
            ));
        return funcHandler.handle(invoker, target, (Stage) args[0], name);
      };
    }

    private static  Func createProxy(InvocationHandler handler, Class interfaceClass) {
      //noinspection unchecked
      return (Func) Proxy.newProxyInstance(
          Func.class.getClassLoader(),
          new Class[] { interfaceClass },
          handler
      );
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy