package nl.ipo.cds.validation.execute;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Method can have one of the following signatures:
* - (inputType[0], (inputType[1] ...))
* - C, (inputType[0], (inputType[1] ...))
* C is the context object for the executor.
* - Object[]
* The beans that provide input (attributes) for use within the expression are added in the order they
* have been declared when compiling the expression.
* - Object[], C, (inputType[0], (inputType[1] ...))
*
* @author erik
*
* @param <C>
*/
public class ExpressionExecutor<C> {
public final ExecutableExpression<C, ?> expression;
public final List<ExecutableExpression<C, ?>> inputs;
public final boolean isConstant;
public final boolean isDeterministic;
public final MethodHandle methodHandle;
final boolean addContextObjects;
final boolean addContext;
final boolean useVarargs;
final Class<?> varargsType;
final int argumentsArrayLength;
public ExpressionExecutor (
final ExecutableExpression<C, ?> expression,
final List<ExecutableExpression<C, ?>> inputs,
final boolean isConstant,
final boolean isDeterministic,
final MethodHandle methodHandle,
final boolean isVarArgs) throws CompilerException {
this.expression = expression;
this.inputs = Collections.unmodifiableList (new ArrayList<ExecutableExpression<C, ?>> (inputs));
this.isConstant = isConstant;
this.isDeterministic = isDeterministic;
this.methodHandle = methodHandle;
// Precalculate several properties of the evaluation method:
final MethodType type = methodHandle.type ();
final Class<?>[] types = type.parameterList().toArray (new Class<?>[0]);
int n = 0;
if (types.length > 0 && types[n].equals (Object[].class)) {
addContextObjects = true;
++ n;
} else {
addContextObjects = false;
}
final Class<?> contextClass = expression.getContextType ();
if (n < types.length && contextClass.isAssignableFrom (types[n])) {
addContext = true;
++ n;
} else {
addContext = false;
}
if (n == types.length - 1 && isVarArgs) {
// Using varargs, inputs are passed as an array:
useVarargs = true;
varargsType = types[types.length - 1].getComponentType ();
} else {
useVarargs = false;
varargsType = null;
// Not using varargs, inputs should be passed as arguments:
if (n + inputs.size () != types.length) {
throw new IllegalArgumentException (String.format ("Input count %d does not match argument count of %s", inputs.size (), methodHandle));
}
for (int i = 0; i < inputs.size (); ++ i) {
final ExecutableExpression<C, ?> input = inputs.get (i);
final Class<?> inputType = input.getResultType ();
if (!types[n + i].isAssignableFrom (inputType)) {
throw new IllegalArgumentException (String.format ("Argument %d of %s is of type %s while the corresponding input is of type %s", n + i, expression.toString (), types[n + i], inputType));
}
}
}
// Precalculate and store the length of the arguments array:
argumentsArrayLength =
(addContextObjects ? 1 : 0)
+ (addContext ? 1 : 0)
+ (useVarargs ? 1 : inputs.size ());
}
public static <C> ExpressionExecutor<C> create (
final ExecutableExpression<C, ?> expression,
final boolean isConstant,
final boolean isDeterministic,
final MethodHandle methodHandle,
final boolean isVarArgs) throws CompilerException {
final List<ExecutableExpression<C, ?>> inputs = new ArrayList<ExecutableExpression<C,?>> (0);
return new ExpressionExecutor<C> (expression, inputs, isConstant, isDeterministic, methodHandle, isVarArgs);
}
public static <C> ExpressionExecutor<C> create (
final ExecutableExpression<C, ?> expression,
final ExecutableExpression<C, ?> input,
final boolean isConstant,
final boolean isDeterministic,
final MethodHandle methodHandle,
final boolean isVarArgs) throws CompilerException {
final List<ExecutableExpression<C, ?>> inputs = new ArrayList<ExecutableExpression<C,?>> (1);
inputs.add (input);
return new ExpressionExecutor<C> (expression, inputs, isConstant, isDeterministic, methodHandle, isVarArgs);
}
public static <C> ExpressionExecutor<C> create (
final ExecutableExpression<C, ?> expression,
final ExecutableExpression<C, ?> a,
final ExecutableExpression<C, ?> b,
final boolean isConstant,
final boolean isDeterministic,
final MethodHandle methodHandle,
final boolean isVarArgs) throws CompilerException {
final List<ExecutableExpression<C, ?>> inputs = new ArrayList<ExecutableExpression<C,?>> (2);
inputs.add (a);
inputs.add (b);
return new ExpressionExecutor<C> (expression, inputs, isConstant, isDeterministic, methodHandle, isVarArgs);
}
@Override
public String toString () {
return expression.toString () + (isConstant ? " (constant)" : "") + (isDeterministic ? " (deterministic)" : "");
}
}