diff --git a/src/main/java/io/jbock/util/Eithers.java b/src/main/java/io/jbock/util/Eithers.java index 835fc45..ee1807b 100644 --- a/src/main/java/io/jbock/util/Eithers.java +++ b/src/main/java/io/jbock/util/Eithers.java @@ -17,9 +17,103 @@ */ public final class Eithers { - static final Set CH_NOID = Set.of(); + private static final Set CH_NOID = Set.of(); - private Eithers() { + /** + * Returns a {@code Collector} that accumulates the input elements into + * a Right containing all values in the original order, + * but only if there are no Left instances in the stream. + * If the stream does contain a Left instance, it discards the Right instances and + * accumulates a Left instance, which contains the first LHS value in the stream, + * in encounter order. + * + * @param the type of the LHS values in the stream + * @param the type of the RHS values in the stream + * @return a {@code Collector} which collects all the input elements into + * a Right containing all RHS values in the stream, or, + * if an LHS value exists, a Left containing the first LHS value + */ + public static + Collector, ?, Either>> + firstFailure() { + + BiConsumer, Either> accumulator = (acc, either) -> + either.ifLeftOrElse(acc::addLeft, acc::addRight); + + BinaryOperator> combiner = (acc, other) -> + (FirstFailureAcc) acc.combine(other); + + return new CollectorImpl<>(FirstFailureAcc::new, accumulator, combiner, FirstFailureAcc::finish); + } + + /** + * Returns a {@code Collector} that accumulates the input elements into + * a Right containing all values in the original order, + * but only if there are no Left instances in the stream. + * If the stream does contain a Left instance, it discards the Right instances and + * accumulates a Left containing only the LHS values, + * in encounter order. + * + * @param the type of the LHS values in the stream + * @param the type of the RHS values in the stream + * @return a {@code Collector} which collects all the input elements into + * a Right containing all RHS values in the stream, + * or, if an LHS value exists, a Left containing a nonempty list + * of all LHS values in the stream + */ + public static + Collector, ?, Either, List>> + allFailures() { + + BiConsumer, Either> accumulator = (acc, either) -> + either.ifLeftOrElse(acc::addLeft, acc::addRight); + + BinaryOperator> combiner = (acc, other) -> + (AllFailuresAcc) acc.combine(other); + + return new CollectorImpl<>(AllFailuresAcc::new, accumulator, combiner, AllFailuresAcc::finish); + } + + /** + * Returns a {@code Collector} that accumulates the input elements into + * a new {@code List}. There are no guarantees on the type, mutability, + * serializability, or thread-safety of the {@code List} returned. + * The final list is wrapped in an {@code Optional}, + * which is empty if and only if the list is empty. + * + * @see #optionalList(List) + * @param the type of the input elements + * @return a list of the RHS values in the stream, + * or, if an LHS value exists, a nonempty list of all LHS values + */ + public static Collector>> toOptionalList() { + return Collectors.collectingAndThen( + Collectors.toList(), + Eithers::optionalList); + } + + /** + * If the provided list is empty, returns an empty {@link Optional}. + * Otherwise, returns an {@code Optional} containing the nonempty + * input list. + * + *

Note: The resulting {@code Optional} might be used in a + * {@link Either#filter(Function) filter} or + * {@link Either#filterLeft(Function) filterLeft} operation. + * + * @see #toOptionalList() + * @param values a list of objects + * @param the type of the members of {@code values} + * @return an {@code Optional} which is either empty, or + * contains a nonempty list + */ + public static Optional> optionalList(List values) { + if (values.isEmpty()) { + return Optional.empty(); + } + @SuppressWarnings("unchecked") + List result = (List) values; + return Optional.of(result); } /** @@ -28,7 +122,7 @@ private Eithers() { * @param the type of elements to be collected * @param the type of the result */ - private static class CollectorImpl implements Collector { + private static final class CollectorImpl implements Collector { final Supplier supplier; final BiConsumer accumulator; final BinaryOperator combiner; @@ -82,10 +176,12 @@ static abstract class Acc { abstract void combineLeft(C otherLeft); // nullable - abstract C left(); + abstract C leftColl(); + + abstract void addLeft(L left); final void addRight(R value) { - if (left() != null) { + if (leftColl() != null) { return; } if (right == null) { @@ -95,11 +191,11 @@ final void addRight(R value) { } final Acc combine(Acc other) { - if (left() != null) { - combineLeft(other.left()); + if (leftColl() != null) { + combineLeft(other.leftColl()); return this; } - if (other.left() != null) { + if (other.leftColl() != null) { return other; } if (other.right == null) { @@ -114,14 +210,14 @@ final Acc combine(Acc other) { } final Either> finish() { - C left = left(); + C left = leftColl(); return left != null ? Either.left(left) : Either.right(right == null ? List.of() : right); } } - private static class ShortcuttingAcc extends Acc { + private static final class FirstFailureAcc extends Acc { L left; @Override @@ -129,18 +225,19 @@ void combineLeft(L otherLeft) { addLeft(otherLeft); } + @Override void addLeft(L value) { if (left == null) { left = value; } } - L left() { + L leftColl() { return left; } } - private static class FullAcc extends Acc, R> { + private static final class AllFailuresAcc extends Acc, R> { List left; @Override @@ -152,6 +249,7 @@ void combineLeft(List otherLeft) { } } + @Override void addLeft(L value) { if (left == null) { left = new ArrayList<>(); @@ -160,101 +258,11 @@ void addLeft(L value) { } @Override - List left() { + List leftColl() { return left; } } - /** - * Returns a {@code Collector} that accumulates the input elements into - * a Right containing all values in the original order, - * but only if there are no Left instances in the stream. - * If the stream does contain a Left instance, it discards the Right instances and - * accumulates a Left instance, which contains the first LHS value in the stream, - * in encounter order. - * - * @param the type of the LHS values in the stream - * @param the type of the RHS values in the stream - * @return a {@code Collector} which collects all the input elements into - * a Right containing all RHS values in the stream, or, - * if an LHS value exists, a Left containing the first LHS value - */ - public static Collector, ?, Either>> toValidList() { - - BiConsumer, Either> accumulate = (acc, either) -> - either.ifLeftOrElse(acc::addLeft, acc::addRight); - - BinaryOperator> combine = (acc, other) -> - (ShortcuttingAcc) acc.combine(other); - - return new CollectorImpl<>(ShortcuttingAcc::new, accumulate, combine, ShortcuttingAcc::finish); - } - - /** - * Returns a {@code Collector} that accumulates the input elements into - * a Right containing all values in the original order, - * but only if there are no Left instances in the stream. - * If the stream does contain a Left instance, it discards the Right instances and - * accumulates a Left containing only the LHS values, - * in encounter order. - * - * @param the type of the LHS values in the stream - * @param the type of the RHS values in the stream - * @return a {@code Collector} which collects all the input elements into - * a Right containing all RHS values in the stream, - * or, if an LHS value exists, a Left containing a nonempty list - * of all LHS values in the stream - */ - public static Collector, ?, Either, List>> toValidListAll() { - - BiConsumer, Either> accumulate = (acc, either) -> - either.ifLeftOrElse(acc::addLeft, acc::addRight); - - BinaryOperator> combine = (acc, other) -> - (FullAcc) acc.combine(other); - - return new CollectorImpl<>(FullAcc::new, accumulate, combine, FullAcc::finish); - } - - /** - * Returns a {@code Collector} that accumulates the input elements into - * a new {@code List}. There are no guarantees on the type, mutability, - * serializability, or thread-safety of the {@code List} returned. - * The resulting list is wrapped in an {@code Optional}, - * which is empty if and only if the list is empty. - * - * @see #optionalList(List) - * @param the type of the input elements - * @return a list of the RHS values in the stream, - * or, if an LHS value exists, a nonempty list of all LHS values - */ - public static Collector>> toOptionalList() { - return Collectors.collectingAndThen( - Collectors.toList(), - Eithers::optionalList); - } - - /** - * If the provided list is empty, returns an empty {@link Optional}. - * Otherwise, returns an {@code Optional} containing the nonempty - * input list. - * - *

Note: The resulting {@code Optional} might be used in a - * {@link Either#filter(Function) filter} or - * {@link Either#filterLeft(Function) filterLeft} operation. - * - * @see #toOptionalList() - * @param values a list of objects - * @param the type of the members of {@code values} - * @return an {@code Optional} which is either empty, or - * contains a nonempty list - */ - public static Optional> optionalList(List values) { - if (values.isEmpty()) { - return Optional.empty(); - } - @SuppressWarnings("unchecked") - List result = (List) values; - return Optional.of(result); + private Eithers() { } } diff --git a/src/test/java/io/jbock/util/ToValidListAllTest.java b/src/test/java/io/jbock/util/ToValidListAllTest.java index 3bae640..e2f1e33 100644 --- a/src/test/java/io/jbock/util/ToValidListAllTest.java +++ b/src/test/java/io/jbock/util/ToValidListAllTest.java @@ -33,13 +33,13 @@ void testRight() { } private Either, List> apply(List> data) { - return data.stream().collect(Eithers.toValidListAll()); + return data.stream().collect(Eithers.allFailures()); } private void checkAssociativity(Either t1, Either t2) { @SuppressWarnings("unchecked") Collector, Eithers.Acc, Either, List>> coll = - (Collector, Eithers.Acc, Either, List>>) Eithers.toValidListAll(); + (Collector, Eithers.Acc, Either, List>>) Eithers.allFailures(); Eithers.Acc a1 = coll.supplier().get(); coll.accumulator().accept(a1, t1); diff --git a/src/test/java/io/jbock/util/ToValidListTest.java b/src/test/java/io/jbock/util/ToValidListTest.java index 514eaf4..fa1db00 100644 --- a/src/test/java/io/jbock/util/ToValidListTest.java +++ b/src/test/java/io/jbock/util/ToValidListTest.java @@ -33,13 +33,13 @@ void testRight() { } private Either> apply(List> data) { - return data.stream().collect(Eithers.toValidList()); + return data.stream().collect(Eithers.firstFailure()); } private void checkAssociativity(Either t1, Either t2) { @SuppressWarnings("unchecked") Collector, Eithers.Acc, Either>> coll = - (Collector, Eithers.Acc, Either>>) Eithers.toValidList(); + (Collector, Eithers.Acc, Either>>) Eithers.firstFailure(); Eithers.Acc a1 = coll.supplier().get(); coll.accumulator().accept(a1, t1);