1 package io.atlassian.fugue.extensions.step;
2
3 import io.atlassian.fugue.Either;
4
5 import java.util.function.BiFunction;
6 import java.util.function.BiPredicate;
7 import java.util.function.Function;
8 import java.util.function.Supplier;
9
10 /**
11 * The second step of the {@link Either} type.
12 * <p>
13 * This class is not intended to be contructed manually, and should only be used
14 * as part of a {@link Steps} chain, started by {@link Steps#begin(Either)}
15 *
16 * @param <A> The right hand side type of the first defined right value
17 * @param <B> The right hand side type of the second defined right value
18 * @param <LEFT> The left hand side type of the Either result
19 * @see Steps for usage examples
20 * @see Either
21 * @since 4.7.0
22 */
23 public final class EitherStep2<A, B, LEFT> {
24
25 private final Either<LEFT, A> either1;
26 private final Either<LEFT, B> either2;
27
28 EitherStep2(Either<LEFT, A> either1, Either<LEFT, B> either2) {
29 this.either1 = either1;
30 this.either2 = either2;
31 }
32
33 /**
34 * Apply the provided function with the previous Step results.
35 * <p>
36 * Internally this will perform a {@link Either#flatMap(Function)} and the
37 * result will become the next step value.
38 *
39 * @param functor The functor to be applied as a flatMap with the previous
40 * steps
41 * @param <C> The right hand side type of the next step result
42 * @param <LL> The left hand side type of the result that must be related to
43 * {@link LEFT}
44 * @return The next step class
45 */
46 public <C, LL extends LEFT> EitherStep3<A, B, C, LEFT> then(BiFunction<? super A, ? super B, Either<LL, C>> functor) {
47 Either<LEFT, C> either3 = either1.flatMap(value1 -> either2.flatMap(value2 -> functor.apply(value1, value2)));
48 return new EitherStep3<>(either1, either2, either3);
49 }
50
51 /**
52 * Apply the provided supplier with the previous Step results.
53 * <p>
54 * Internally this will perform a {@link Either#flatMap(Function)} and the
55 * supplier will become the next step value.
56 * <p>
57 * This is different to {@link #then(BiFunction)} in that the previous step
58 * results are not provided for the new step evaluation.
59 *
60 * @param supplier The supplier to provide the result of the flatMap with the
61 * previous step.
62 * @param <C> The right hand side type of the next step result
63 * @param <LL> The left hand side type of the result that must be related to
64 * {@link LEFT}
65 * @return The next step class
66 */
67 public <C, LL extends LEFT> EitherStep3<A, B, C, LEFT> then(Supplier<Either<LL, C>> supplier) {
68 Either<LEFT, C> either3 = either1.flatMap(value1 -> either2.flatMap(value2 -> supplier.get()));
69 return new EitherStep3<>(either1, either2, either3);
70 }
71
72 /**
73 * Apply the provided predicate with the previous step results.
74 * <p>
75 * If the predicate is not satisfied then the unsatisfiedSupplier is used to
76 * populate the left value that will prevent any further steps evaluation.
77 *
78 * @param predicate The check that must be satisfied by contained values
79 * @param unsatisfiedSupplier Provide the value to populate the left if not
80 * satisfied
81 * @return This step class with either the same last step value, or changed to
82 * a left
83 */
84 public EitherStep2<A, B, LEFT> filter(BiPredicate<? super A, ? super B> predicate, Supplier<? extends LEFT> unsatisfiedSupplier) {
85 Either<LEFT, B> filterEither2 = either1.flatMap(value1 -> either2.filterOrElse(value2 -> predicate.test(value1, value2), unsatisfiedSupplier));
86 return new EitherStep2<>(either1, filterEither2);
87 }
88
89 /**
90 * Terminating step expression, that will provide the previous steps to this
91 * function and return the result as a <code>Right</code>
92 *
93 * @param functor The yield function to map on previous values
94 * @param <Z> The right hand side type for the returned result
95 * @return An Either with the result of this function on right, or the
96 * existing left
97 */
98 public <Z> Either<LEFT, Z> yield(BiFunction<? super A, ? super B, Z> functor) {
99 return either1.flatMap(value1 -> either2.map(value2 -> functor.apply(value1, value2)));
100 }
101
102 }