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