package nl.ipo.cds.validation.flow;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.HashMap;
import nl.ipo.cds.validation.AbstractExpression;
import nl.ipo.cds.validation.Expression;
import nl.ipo.cds.validation.ValidationMessage;
import nl.ipo.cds.validation.Validator;
import nl.ipo.cds.validation.ValidatorContext;
import nl.ipo.cds.validation.execute.Compiler;
import nl.ipo.cds.validation.execute.CompilerException;
import nl.ipo.cds.validation.execute.Executor;
import nl.ipo.cds.validation.execute.ExpressionExecutor;
public class ForEachExpression<K extends Enum<K> & ValidationMessage<K, C>, C extends ValidatorContext<K, C>, T> extends AbstractExpression<K, C, Boolean> {
public final String variableName;
public final Expression<K, C, T[]> input;
public final Validator<K, C> validator;
public ForEachExpression (final String variableName, final Expression<K, C, T[]> input, final Validator<K, C> validator) {
this.variableName = variableName;
this.input = input;
this.validator = validator;
}
@Override
public Class<Boolean> getResultType () {
return Boolean.class;
}
@Override
public ExpressionExecutor<C> getExecutor(final Compiler<C> compiler) throws CompilerException {
// Compile the validator:
final Compiler<C> validatorCompiler = compiler
.addMap ("loop." + variableName + ".map")
.addBean ("loop." + variableName + ".bean", input.getResultType ()
);
final Executor<C> validatorExecutor = validatorCompiler.compile (validator);
// Create the executor for the for each loop:
final ForEachExecutor<K, C, T> executor = new ForEachExecutor<K, C, T> () {
@Override
public Boolean execute(Object[] objects, C context, T[] input) throws Exception {
if (input == null || input.length == 0) {
return true;
}
final Object[] contextObjects = Arrays.copyOf (objects, objects.length + 2);
final HashMap<String, Object> map = new HashMap<String, Object> ();
contextObjects[contextObjects.length - 2] = map;
final T[] list = input;
boolean allValid = true;
for (final T item: list) {
map.put (variableName, item);
contextObjects[contextObjects.length - 1] = item;
final Boolean result = (Boolean)validatorExecutor.execute (context, contextObjects);
if (result == null || !result) {
allValid = false;
}
}
return allValid;
}
};
return ExpressionExecutor.create (
this,
input,
false,
false,
executeHandle.bindTo (executor),
false
);
}
private final static MethodHandle executeHandle = Compiler
.findMethod (
ForEachExecutor.class,
"execute",
MethodType.methodType (Boolean.class, Object[].class, ValidatorContext.class, Object[].class)
);
public static interface ForEachExecutor<K extends Enum<K> & ValidationMessage<K, C>, C extends ValidatorContext<K, C>, T> {
Boolean execute (Object[] objects, C context, T[] input) throws Exception;
}
}