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