package org.lambda.functions.implementations;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import com.spun.util.ObjectUtils;
public class Function<In1, In2, In3, In4, In5, In6, In7, In8, In9, Out>
{
public Out returnValue;
private final Object[] extraVariables;
private Object[] params;
private int startAt = 0;
private int totalLengthOfParameters;
private Constructor<Function> constructor;
public In1 a;
public In2 b;
public In3 c;
public In4 d;
public In5 e;
public In6 f;
public In7 g;
public In8 h;
public In9 i;
public Function(Object[] extraVariables)
{
this.extraVariables = extraVariables;
}
protected Out call(Object[] mainParams)
{
Object[] parameters = null;
try
{
initalizeConstructor();
parameters = getParameters(totalLengthOfParameters, extraVariables, mainParams);
Function intsance = constructor.newInstance(parameters);
return (Out) intsance.returnValue;
}
catch (IllegalArgumentException e)
{
throw new RuntimeException(e.getMessage() + "\nExpected " + Arrays.toString(constructor.getParameterTypes())
+ "\nGot " + Arrays.toString(parameters));
}
catch (Throwable e)
{
throw ObjectUtils.throwAsError(e);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void initalizeConstructor()
{
if (constructor == null)
{
Class<? extends Function> clazz = this.getClass();
constructor = (Constructor<Function>) clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
totalLengthOfParameters = constructor.getParameterTypes().length;
}
}
private Object[] getParameters(int length, Object[] extraVariables2, Object[] mainParams)
{
if (params == null)
{
params = initalizeParameters(length, extraVariables2, mainParams);
}
for (int i = 0; i < mainParams.length; i++)
{
params[i + startAt] = mainParams[i];
}
return params;
}
private Object[] initalizeParameters(int total, Object[] extraVariables, Object[] mainParams)
{
ArrayList<Object> list = new ArrayList<Object>(total);
if ((extraVariables.length + 1 + mainParams.length) != total)
{
startAt = 1;
list.add(getParentThisReference());
}
for (Object object : mainParams)
{
list.add(object);
}
list.add(extraVariables);
for (Object object : extraVariables)
{
list.add(object);
}
return list.toArray();
}
private Object getParentThisReference()
{
Object parent = tryToGetParentByName();
if (parent == null)
{
parent = tryToGetParentByType();
}
return parent;
}
private Object tryToGetParentByType()
{
try
{
Class<? extends Function> clazz = this.getClass();
Field[] fields = clazz.getDeclaredFields();
Class<?> desiredType = clazz.getDeclaredConstructors()[0].getParameterTypes()[0];
for (Field field : fields)
{
if (field.getType().equals(desiredType))
{
field.setAccessible(true);
return field.get(this);
}
}
}
catch (Exception e)
{
// Couldn't find method of parent type.
}
return null;
}
private Object tryToGetParentByName()
{
try
{
Class<? extends Function> clazz = this.getClass();
Field parentField = clazz.getDeclaredField("this$0");
parentField.setAccessible(true);
return parentField.get(this);
}
catch (Exception e)
{
// Failed try 1 -> this$0 field doesn't exist
}
return null;
}
public void returnValue(Out returnValue)
{
this.returnValue = returnValue;
}
public void ret(Out returnValue)
{
returnValue(returnValue);
}
}