package nl.ipo.cds.validation; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.*; import nl.ipo.cds.validation.execute.*; import nl.ipo.cds.validation.execute.Compiler; public class Validator<K extends Enum<K> & ValidationMessage<K, C>, C extends ValidatorContext<K, C>> extends AbstractExpression<K, C, Boolean> implements UnaryExpression<K, C, Boolean, Boolean>{ public final Expression<K, C, Boolean> expression; public final K messageKey; public final List<Expression<K, C, ?>> parameters; public final boolean isBlocking; private final List<Executor<C>> parameterExecutors = new ArrayList<> (); public Validator (final Expression<K, C, Boolean> expression, final K messageKey, final List<Expression<K, C, ?>> parameters) { this (expression, messageKey, parameters, messageKey != null ? messageKey.isBlocking () : true); } public Validator (final Expression<K, C, Boolean> expression, final K messageKey, final List<Expression<K, C, ?>> parameters, final boolean isBlocking) { if (expression == null) { throw new NullPointerException ("expression cannot be null"); } this.expression = expression; this.messageKey = messageKey; this.parameters = createParameters (messageKey, parameters); this.isBlocking = isBlocking; } private List<Expression<K, C, ?>> createParameters (final K messageKey, final List<Expression<K, C, ?>> explicitParameters) { final List<Expression<K, C, ?>> messageParameters = messageKey != null ? messageKey.getMessageParameters () : Collections.<Expression<K, C, ?>>emptyList (); final List<Expression<K, C, ?>> parameters = new ArrayList<> (); if (messageParameters != null) { parameters.addAll (messageParameters); } if (explicitParameters != null) { parameters.addAll (explicitParameters); } return parameters.size () == 0 ? null : parameters; } public Validator<K, C> message (final K messageKey) { return message (messageKey, (List<Expression<K, C, ?>>)null); } public Validator<K, C> message (final K messageKey, final Expression<K, C, ?> a) { final ArrayList<Expression<K, C, ?>> l = new ArrayList<Expression<K, C, ?>> (1); l.add (a); return message (messageKey, l); } public Validator<K, C> message (final K messageKey, final Expression<K, C, ?> a, final Expression<K, C, ?> b) { final ArrayList<Expression<K, C, ?>> l = new ArrayList<Expression<K, C, ?>> (2); l.add (a); l.add (b); return message (messageKey, l); } public Validator<K, C> message (final K messageKey, final Expression<K, C, ?> a, final Expression<K, C, ?> b, final Expression<K, C, ?> c) { final ArrayList<Expression<K, C, ?>> l = new ArrayList<Expression<K, C, ?>> (3); l.add (a); l.add (b); l.add (c); return message (messageKey, l); } public Validator<K, C> message (final K messageKey, final Expression<K, C, ?> a, final Expression<K, C, ?> b, final Expression<K, C, ?> c, final Expression<K, C, ?> d) { final ArrayList<Expression<K, C, ?>> l = new ArrayList<Expression<K, C, ?>> (4); l.add (a); l.add (b); l.add (c); l.add (d); return message (messageKey, l); } public Validator<K, C> message (final K messageKey, final List<Expression<K, C, ?>> parameters) { return new Validator<K, C> (expression, messageKey, parameters); } public Validator<K, C> nonBlocking () { final List<Expression<K, C, ?>> messageParams = (messageKey != null ? messageKey.getMessageParameters () : null); final int start = messageParams == null ? 0 : messageParams.size (); return new Validator<K, C> (expression, messageKey, parameters != null && start <= parameters.size () ? parameters.subList (start, parameters.size ()) : parameters, false); } @Override public Class<Boolean> getResultType () { return Boolean.class; } public Boolean evaluate (final Object[] objects, final C context, final Boolean input) throws ExecutorException { final Boolean result = input; if (result == null || !result) { reportValidationError (context, objects); } return isBlocking ? result : true; } @Override public Class<Boolean> getInputType() { return Boolean.class; } private void reportValidationError (final C context, final Object[] objects) throws ExecutorException { if (context.getReporter () == null || messageKey == null) { return; } // Collect parameters by evaluating parameter expressions (if any): final Object[] logParameters; if (parameters != null) { logParameters = new Object[parameters.size ()]; for (int i = 0; i < parameters.size (); ++ i) { final Object value = parameterExecutors.get (i).execute (context, objects); logParameters[i] = value; } } else { logParameters = new Object[0]; } // Invoke the reporter: context.getReporter ().reportValidationError (this, context, messageKey, logParameters); } @Override public String toString () { return String.format ("validate(%s)", expression.toString ()); } private final static MethodHandle evaluateHandle = Compiler .findMethod ( Validator.class, "evaluate", MethodType.methodType (Boolean.class, Object[].class, ValidatorContext.class, Boolean.class) ); @Override public ExpressionExecutor<C> getExecutor(final Compiler<C> compiler) throws CompilerException { parameterExecutors.clear (); if (parameters != null) { for (final Expression<K, C, ?> parameterExpression: parameters) { parameterExecutors.add (compiler.compile (parameterExpression)); } } return ExpressionExecutor.create ( this, expression, false, true, evaluateHandle.bindTo (this), false ); } }