1 package io.atlassian.fugue.extensions.step;
2
3 import io.atlassian.fugue.Try;
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 Try} 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(Try)}
15 *
16 * @param <A> The type of the first defined value
17 * @param <B> The type of the second defined value
18 * @param <C> The type of the third defined value
19 * @param <D> The type of the fourth defined value
20 * @see Steps for usage examples
21 * @see Try
22 * @since 4.7.0
23 */
24 public final class TryStep4<A, B, C, D> {
25
26 private final Try<A> try1;
27 private final Try<B> try2;
28 private final Try<C> try3;
29 private final Try<D> try4;
30
31 TryStep4(Try<A> try1, Try<B> try2, Try<C> try3, Try<D> try4) {
32 this.try1 = try1;
33 this.try2 = try2;
34 this.try3 = try3;
35 this.try4 = try4;
36 }
37
38 /**
39 * Apply the provided function with the previous Step results.
40 * <p>
41 * Internally this will perform a {@link Try#flatMap(Function)} and the result
42 * 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 type of the next step result
47 * @return The next step class
48 */
49 public <E> TryStep5<A, B, C, D, E> then(Function4<? super A, ? super B, ? super C, ? super D, Try<E>> functor) {
50 Try<E> try5 = try1.flatMap(value1 -> try2.flatMap(value2 -> try3.flatMap(value3 -> try4.flatMap(value4 -> functor.apply(value1, value2, value3,
51 value4)))));
52 return new TryStep5<>(try1, try2, try3, try4, try5);
53 }
54
55 /**
56 * Apply the provided supplier with the previous Step results.
57 * <p>
58 * Internally this will perform a {@link Try#flatMap(Function)} and the
59 * supplier will become the next step value.
60 * <p>
61 * This is different to {@link #then(Function4)} in that the previous step
62 * results are not provided for the new step evaluation.
63 *
64 * @param supplier The supplier to provide the result of the flatMap with the
65 * previous step.
66 * @param <E> The type of the next step result
67 * @return The next step class
68 */
69 public <E> TryStep5<A, B, C, D, E> then(Supplier<Try<E>> supplier) {
70 Try<E> try5 = try1.flatMap(value1 -> try2.flatMap(value2 -> try3.flatMap(value3 -> try4.flatMap(value4 -> supplier.get()))));
71 return new TryStep5<>(try1, try2, try3, try4, try5);
72 }
73
74 /**
75 * Apply the provided predicate with the previous step results.
76 * <p>
77 * If the predicate is not satisfied then the unsatisfiedSupplier is used to
78 * populate the failure value that will prevent any further steps evaluation.
79 *
80 * @param predicate The check that must be satisfied by contained values
81 * @param unsatisfiedSupplier Provide the value to populate the failure if not
82 * satisfied
83 * @return This step class with either the same last step value, or changed to
84 * a failure
85 */
86 public TryStep4<A, B, C, D> filter(Predicate4<? super A, ? super B, ? super C, ? super D> predicate, Supplier<Exception> unsatisfiedSupplier) {
87 Try<D> filterTry4 = try1.flatMap(value1 -> try2.flatMap(value2 -> try3.flatMap(value3 -> try4.filterOrElse(
88 value4 -> predicate.test(value1, value2, value3, value4), unsatisfiedSupplier))));
89 return new TryStep4<>(try1, try2, try3, filterTry4);
90 }
91
92 /**
93 * Terminating step expression, that will provide the previous steps to this
94 * function and return the result as a <code>Success</code>
95 *
96 * @param functor The yield function to map on previous values
97 * @param <Z> The type for the returned result
98 * @return A Try containing this result as success or failure
99 */
100 public <Z> Try<Z> yield(Function4<? super A, ? super B, ? super C, ? super D, Z> functor) {
101 return try1.flatMap(value1 -> try2.flatMap(value2 -> try3.flatMap(value3 -> try4.map(value4 -> functor.apply(value1, value2, value3, value4)))));
102 }
103
104 }