MultiFunctionUtils.java

package com.github.dakusui.pcond.experimentals.currying.multi;

import com.github.dakusui.pcond.core.printable.PrintableFunctionFactory;
import com.github.dakusui.pcond.internals.InternalUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;

/**
 * A utility class that collects helper methods for the multi-parameters function.
 */
public enum MultiFunctionUtils {
  ;
  private static final ThreadLocal<Map<List<Object>, MultiFunction<?>>> METHOD_BASED_FUNCTION_POOL = new ThreadLocal<>();

  @SuppressWarnings("unchecked")
  public static <R> MultiFunction<R> multifunction(int[] order, Class<?> aClass, String methodName, Class<?>... parameterTypes) {
    Map<List<Object>, MultiFunction<?>> methodBasedMultiParameterFunctionPool = methodBasedMultiParameterFunctionPool();
    List<Object> multiParamFuncDef = composeFuncDef(order, aClass, methodName, parameterTypes);
    methodBasedMultiParameterFunctionPool.computeIfAbsent(
        multiParamFuncDef,
        MultiFunctionUtils::createMultiParameterFunctionForStaticMethod);
    return (MultiFunction<R>) methodBasedMultiParameterFunctionPool.get(multiParamFuncDef);
  }

  private static List<Object> composeFuncDef(int[] order, Class<?> aClass, String methodName, Class<?>[] parameterTypes) {
    return asList(InternalUtils.getMethod(aClass, methodName, parameterTypes), Arrays.stream(order).boxed().collect(toList()));
  }

  private static <R> MultiFunction<R> createMultiParameterFunctionForStaticMethod(List<Object> multiParamFuncDef) {
    final Method method = (Method) multiParamFuncDef.get(0);
    @SuppressWarnings("unchecked") final List<Integer> paramOrder = (List<Integer>) multiParamFuncDef.get(1);
    return PrintableFunctionFactory.multifunction(method, paramOrder);
  }

  private static Map<List<Object>, MultiFunction<?>> methodBasedMultiParameterFunctionPool() {
    if (METHOD_BASED_FUNCTION_POOL.get() == null)
      METHOD_BASED_FUNCTION_POOL.set(new HashMap<>());
    return METHOD_BASED_FUNCTION_POOL.get();
  }
}