package nl.hsac.fitnesse.fixture.util;

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import static java.lang.invoke.MethodType.methodType;

/**
 * Helper to create Functions based on reflection types.
 */
public class LambdaMetaHelper {

    public <T> Supplier<T> getConstructor(Class<? extends T> clazz) {
        return getConstructorAs(Supplier.class, "get", clazz);
    }

    public <T, A> Function<A, T> getConstructor(Class<? extends T> clazz, Class<A> arg) {
        return getConstructorAs(Function.class, "apply", clazz, arg);
    }

    public <T, U, R> BiFunction<T, U, R> getConstructor(Class<? extends R> clazz, Class<?>... args) {
        return getConstructorAs(BiFunction.class, "apply", clazz, args);
    }

    protected <T, R> T getConstructorAs(Class<T> targetClass, String methodName, Class<? extends R> clazz, Class<?>... args) {
        try {
            MethodType methodType = methodType(clazz, args);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.findConstructor(clazz, methodType(void.class, args));
            MethodType targetType = methodType(targetClass);
            CallSite callSite = LambdaMetafactory.metafactory(lookup, methodName, targetType, methodType.generic(), handle, methodType);
            return (T) callSite.getTarget().invoke();
        } catch (Throwable t) {
            throw new RuntimeException("Unable to create function for constructor of " + clazz.getName(), t);
        }
    }
}

@@ -513,317 +513,1074 @@
     
-public %3CT%3E Supplier%3CT%3E getConstructor(Class%3C? extends T%3E clazz) %7B%0D%0A        return getConstructorAs(Supplier.class, %22get%22, clazz);%0D%0A    %7D%0D%0A%0D%0A    public %3CT, A%3E Function%3CA, T%3E getConstructor(Class%3C? extends T%3E clazz, Class%3CA%3E arg) %7B%0D%0A        return getConstructorAs(Function.class, %22apply%22, clazz, arg);%0D%0A    %7D%0D%0A
+/**%0D%0A     * Gets no-arg constructor as Supplier.%0D%0A     * @param clazz class to get constructor for.%0D%0A     * @param %3CT%3E clazz.%0D%0A     * @return supplier.%0D%0A     */%0D%0A    public %3CT%3E Supplier%3CT%3E getConstructor(Class%3C? extends T%3E clazz) %7B%0D%0A        return getConstructorAs(Supplier.class, %22get%22, clazz);%0D%0A    %7D%0D%0A%0D%0A    /**%0D%0A     * Gets single arg constructor as Function.%0D%0A     * @param clazz class to get constructor for.%0D%0A     * @param arg constructor argument type.%0D%0A     * @param %3CT%3E clazz.%0D%0A     * @param %3CA%3E argument class.%0D%0A     * @return function.%0D%0A     */%0D%0A    public %3CT, A%3E Function%3CA, T%3E getConstructor(Class%3C? extends T%3E clazz, Class%3CA%3E arg) %7B%0D%0A        return getConstructorAs(Function.class, %22apply%22, clazz, arg);%0D%0A    %7D%0D%0A%0D%0A    /**%0D%0A     * Gets two arg constructor as BiFunction.%0D%0A     * @param clazz class to get constructor for.%0D%0A     * @param arg1 first argument class.%0D%0A     * @param arg2 second argument class.%0D%0A     * @param %3CT%3E clazz.%0D%0A     * @param %3CA1%3E first argument class%0D%0A     * @param %3CA2%3E second argument class.%0D%0A     * @return bifunction.%0D%0A     */
 %0D%0A  
@@ -1589,23 +1589,25 @@
 public %3C
-T, U, R
+A1, A2, T
 %3E BiFunc
@@ -1615,15 +1615,17 @@
 ion%3C
-T, U, R
+A1, A2, T
 %3E ge
@@ -1645,33 +1645,33 @@
 Class%3C? extends 
-R
+T
 %3E clazz, Class%3C?
@@ -1665,34 +1665,48 @@
 lazz, Class%3C
-?%3E... args
+A1%3E arg1, Class%3CA2%3E arg2
 ) %7B%0D%0A       
@@ -1760,28 +1760,51 @@
 ly%22, clazz, 
-args
+new Class%3C?%3E%5B%5D %7Barg1, arg2%7D
 );%0D%0A    %7D%0D%0A%0D

Add JavaDoc