package nl.ipo.cds.validation.execute; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ExecutionPlan<C> { private final List<ExecutorTuple<C>> executors = new ArrayList<ExecutorTuple<C>> (); private final List<ExecutionStep<C>> steps = new ArrayList<ExecutionStep<C>> (); public ExecutionPlan () { } public List<ExecutionStep<C>> getExecutionSteps () { return Collections.unmodifiableList (steps); } public ExecutionStep<C> getExecutionStep (final ExpressionExecutor<C> executor) { for (final ExecutionStep<C> step: steps) { if (step.executor == executor) { return step; } } return null; } private int getExecutionStepIndex (final ExecutableExpression<C, ?> expression) { final ExpressionExecutor<C> executor = getExecutor (expression); if (executor == null) { return -1; } return getExecutionStepIndex (executor); /* for (int i = 0; i < steps.size (); ++ i) { if (steps.get (i).executor.expression == expression || executorsAreFoldable (steps.get (i).executor, expression)) { return i; } } return -1; */ } private int getExecutionStepIndex (final ExpressionExecutor<C> executor) { for (int i = 0; i < steps.size (); ++ i) { if (steps.get (i).executor == executor) { return i; } } return -1; } public ExecutionStep<C> addExecutionStep (final ExpressionExecutor<C> executor) { final ExecutionStep<C> old = getExecutionStep (executor); if (old != null) { return old; } // Locate inputs: final int[] inputs = new int[executor.inputs.size ()]; for (int i = 0; i < inputs.length; ++ i) { inputs[i] = getExecutionStepIndex (executor.inputs.get (i)); if (inputs[i] < 0) { throw new IllegalStateException ("Unable to locate execution step for expression: " + executor.inputs.get (i)); } } final ExecutionStep<C> newStep = new ExecutionStep<C> (executor, inputs); steps.add (newStep); return newStep; } public ExpressionExecutor<C> getExecutor (final ExecutableExpression<C, ?> expression) { for (final ExecutorTuple<C> tuple: executors) { if (tuple.expression == expression || executorsAreFoldable (tuple.executor, expression)) { return tuple.executor; } } return null; } public void addExecutor (final ExecutableExpression<C, ?> expression, final ExpressionExecutor<C> executor) { if (getExecutor (executor.expression) != null) { return; } executors.add (new ExecutorTuple<C> (expression, executor)); } @Override public String toString () { final StringBuilder builder = new StringBuilder (); builder.append ("Execution plan:\n"); int n = 0; for (final ExecutionStep<C> step: steps) { builder.append (String.format (" - %d: ", n)); builder.append (step.toString ()); builder.append ('\n'); ++ n; } return builder.toString (); } private final static class ExecutorTuple<C> { public final ExecutableExpression<C, ?> expression; public final ExpressionExecutor<C> executor; public ExecutorTuple (final ExecutableExpression<C, ?> expression, final ExpressionExecutor<C> executor) { this.expression = expression; this.executor = executor; } } /** * Returns true if the given executors can be folded: executor B can be replaced by the result of executor A. An expression can be folded when: * - Executor A must be part of the execution plan. * - Both expressions are equal. * - The executors of child expressions must be in the execution plan. * - The executors of child expressions of A and B are foldable. * * @return */ private boolean executorsAreFoldable (final ExpressionExecutor<C> a, final ExecutableExpression<C, ?> b) { if (a == null || b == null) { return false; } // A must be in the execution plan: if (getExecutionStepIndex (a) < 0) { return false; } // Expressions A and B must be equal: if (!a.expression.equals (b)) { return false; } // A and B must be constant and/or deterministic. Assume B is constant/deterministic if A is constant/determinstic // since both expressions are equal: if (!a.isDeterministic) { return false; } // Each child of A must be constant and/or deterministic: for (final ExecutableExpression<C, ?> input: a.inputs) { if (!isConstantOrDeterministic (input)) { return false; } } return true; } private boolean isConstantOrDeterministic (final ExecutableExpression<C, ?> exp) { // Locate the execution step: final int index = getExecutionStepIndex (exp); if (index < 0) { return false; } final ExpressionExecutor<C> executor = steps.get (index).executor; if (!executor.isConstant && !executor.isDeterministic) { return false; } for (final ExecutableExpression<C, ?> input: executor.inputs) { if (!isConstantOrDeterministic (input)) { return false; } } return true; } }