Bulk conditions evaluation throwing a single exception of a configurable type for all unfulfilled conditions.
It is developed around several design patterns:
(1) fluent interface to configure the exception to be thrown and to pass the conditions to evaluate and the error messages to display:
Check.by(IllegalArgumentException.class.getName())
.check()
.argument(false).message("first argument doesn't pass the check")
.argument(false).message("second argument doesn't pass the check")
.argument(false).message("third argument doesn't pass the check")
.test();
(2) static factory method to get an instance of a builder for the arguments to evaluate and an instance of an implementation of strategy design patter according with the value (true, false) of the argument to be evaluated:
public class Argument {
public static Argument.Builder builder(Decorator decorator) {
return new Argument.Builder(decorator, EMPTY_CALL);
}
. . .
}
public abstract class Condition {
public static Condition of(final boolean condition) {
return condition ? new True() : new False();
}
. . .
}
(3) builder with fluent interface that builds the chain of responsibilities fo the arguments to evaluate:
public class Argument {
. . .
public static class Builder {
private Decorator decorator;
private Call call;
private Builder(Decorator decorator, Call call) {
this.decorator = decorator;
this.call = call;
}
public Argument argument(boolean expression) {
return new Argument(expression, call, decorator);
}
}
. . .
}
(4) strategy with two implementations (True, False) to return the message just in case the argument doesn’t pass the evaluation:
private static abstract class Condition {
. . .
protected String message;
private Condition() {}
abstract List<String> message(final List<String> others);
private static class False extends Condition {
private False() {}
List<String> message(final List<String> others) {
others.add(this.message);
return others;
}
}
private static class True extends Condition {
private True() {}
List<String> message(final List<String> others) {
return others;
}
}
}
(5) empty object for graceful handling of the first argument in the chain of responsibilities of the arguments to evaluate:
public class Argument {
. . .
private static final Call EMPTY_CALL = new Call() {
List<String> test(final Argument argument) { return new ArrayList<>(); }
Decorator decorator(final Argument argument) { return argument.decorator; }
};
. . .
}
(6) decorator to source the exception of the configured type to be thrown:
public abstract class Decorator {
public Argument.Builder check() {
return Argument.builder(this);
}
public abstract void test(List<String> messages) throws RuntimeException;
}
all together implemented by Argument class:
import java.util.ArrayList;
import java.util.List;
public class Argument {
public static Argument.Builder builder(Decorator decorator) {
return new Argument.Builder(decorator, EMPTY_CALL);
}
private static final Call EMPTY_CALL = new Call() {
List<String> test(final Argument argument) { return new ArrayList<>(); }
Decorator decorator(final Argument argument) { return argument.decorator; }
};
private Condition condition;
private Argument previous;
private Decorator decorator;
private Call call;
private Argument(boolean expression, final Call call, final Decorator decorator) {
this(expression);
this.call = call;
this.decorator = decorator;
}
private Argument(boolean expression) {
this.condition = Condition.of(expression);
this.call = new Call();
}
private List<String> test() {
return this.condition.message(this.call.test(this.previous));
}
private Decorator decorator() {
return this.call.decorator(this);
}
public Or message(String message) {
this.condition.message = message;
return new Or(this);
}
public static class Builder {
private Decorator decorator;
private Call call;
private Builder(Decorator decorator, Call call) {
this.decorator = decorator;
this.call = call;
}
public Argument argument(boolean expression) {
return new Argument(expression, call, decorator);
}
}
public static abstract class Decorator {
public Argument.Builder check() {
return Argument.builder(this);
}
public abstract void test(List<String> messages) throws RuntimeException;
}
private static class Call {
List<String> test(final Argument argument) {
return argument.test();
}
Decorator decorator(Argument argument) {
return argument.previous.decorator();
}
}
public static class Or {
private final Argument argument;
private Or(final Argument argument) {
this.argument = argument;
}
public Argument argument(final boolean condition) {
final Argument argument = new Argument(condition);
argument.previous = this.argument;
return argument;
}
public void test() throws RuntimeException {
this.argument.decorator().test(this.argument.test());
}
}
private static abstract class Condition {
private static Condition of(final boolean condition) {
return condition ? new True() : new False();
}
protected String message;
private Condition() {}
abstract List<String> message(final List<String> others);
private static class False extends Condition {
private False() {}
List<String> message(final List<String> others) {
others.add(this.message);
return others;
}
}
private static class True extends Condition {
private True() {}
List<String> message(final List<String> others) {
return others;
}
}
}
}
and supported by Check class that leverages the dynamic registration of the type of exception to be thrown:
public class Check {
private static final Map<String, Argument.Decorator> DECORATORS = new HashMap<>();
public static Argument.Decorator by(String key) {
return DECORATORS.get(key);
}
public static boolean register(String name, Argument.Decorator decorator) {
return DECORATORS.put(name, decorator) != null;
}
}
showcased by CheckTest class that registers for evaluation two types of RuntimeException to be thrown:
(1) IllegalArgumentException:
Check.register(IllegalArgumentException.class.getName(), new Argument.Decorator() {
public void test(List<String> messages) throws IllegalArgumentException {
if (messages.size() > 0) {
final IllegalArgumentException throwable = new IllegalArgumentException(messages.get(0));
for (String message : messages.subList(1, messages.size())) {
throwable.addSuppressed(new IllegalArgumentException(message));
}
throw throwable;
}
}
});
(2) RuntimeException:
Check.register(RuntimeException.class.getName(), new Argument.Decorator() {
public void test(List<String> messages) throws RuntimeException {
if (messages.size() > 0) {
final RuntimeException throwable = new RuntimeException(messages.get(0));
for (String message : messages.subList(1, messages.size())) {
throwable.addSuppressed(new RuntimeException(message));
}
throw throwable;
}
}
});
and implements a test method for each registered exception:
import java.util.List;
public class CheckTest {
public static CheckTest object() {
Check.register(IllegalArgumentException.class.getName(), new Argument.Decorator() {
public void test(List<String> messages) throws IllegalArgumentException {
if (messages.size() > 0) {
final IllegalArgumentException throwable = new IllegalArgumentException(messages.get(0));
for (String message : messages.subList(1, messages.size())) {
throwable.addSuppressed(new IllegalArgumentException(message));
}
throw throwable;
}
}
});
Check.register(RuntimeException.class.getName(), new Argument.Decorator() {
public void test(List<String> messages) throws RuntimeException {
if (messages.size() > 0) {
final RuntimeException throwable = new RuntimeException(messages.get(0));
for (String message : messages.subList(1, messages.size())) {
throwable.addSuppressed(new RuntimeException(message));
}
throw throwable;
}
}
});
return new CheckTest();
}
public void testIllegalArgumentException() {
try {
Check.by(IllegalArgumentException.class.getName())
.check()
.argument(false).message("first argument doesn't pass the check")
.argument(false).message("second argument doesn't pass the check")
.argument(false).message("third argument doesn't pass the check")
.test();
} catch(IllegalArgumentException e) {
e.printStackTrace(System.err);
System.out.println("testIllegalArgumentException passed");
} catch(Exception e) {
System.err.println("testIllegalArgumentException doesn't pass");
}
}
public void testRuntimeException() {
try {
Check.by(RuntimeException.class.getName())
.check()
.argument(false).message("first argument doesn't pass the check")
.argument(false).message("second argument doesn't pass the check")
.argument(false).message("third argument doesn't pass the check")
.test();
} catch(RuntimeException e) {
e.printStackTrace(System.err);
System.out.println("testRuntimeException passed");
} catch(Exception e) {
System.err.println("testRuntimeException doesn't pass");
}
}
public static void main(String[] args) {
CheckTest checktTest = CheckTest.object();
checktTest.testIllegalArgumentException();
System.out.println();
checktTest.testRuntimeException();
}
}
that outputs to system console:
java.lang.IllegalArgumentException: first argument doesn't pass the check
at org.test.condition.CheckTest$1.test(CheckTest.java:14)
at org.test.condition.Argument$Or.test(Argument.java:103)
at org.test.condition.CheckTest.testIllegalArgumentException(CheckTest.java:54)
at org.test.condition.CheckTest.main(CheckTest.java:83)
Suppressed: java.lang.IllegalArgumentException: second argument doesn't pass the check
at org.test.condition.CheckTest$1.test(CheckTest.java:18)
... 3 more
Suppressed: java.lang.IllegalArgumentException: third argument doesn't pass the check
at org.test.condition.CheckTest$1.test(CheckTest.java:18)
... 3 more
testIllegalArgumentException passed
java.lang.RuntimeException: first argument doesn't pass the check
at org.test.condition.CheckTest$2.test(CheckTest.java:31)
at org.test.condition.Argument$Or.test(Argument.java:103)
at org.test.condition.CheckTest.testRuntimeException(CheckTest.java:71)
at org.test.condition.CheckTest.main(CheckTest.java:85)
Suppressed: java.lang.RuntimeException: second argument doesn't pass the check
at org.test.condition.CheckTest$2.test(CheckTest.java:35)
... 3 more
Suppressed: java.lang.RuntimeException: third argument doesn't pass the check
at org.test.condition.CheckTest$2.test(CheckTest.java:35)
... 3 more
testRuntimeException passed