Skip to content

Commit b8a92a7

Browse files
committed
[#4] Wrapped value (Safe) implementation (addition to Sneaky and Unchecked)
Sneaky and Unchecked throw Exceptions/Errors or could ignore them completely. It would be great to have something similar to Java's Optionally: a smart return. → Either Safe variants for some of the most important interfaces (like Runnable and Callable) are also desired. → SafeRunnable, SafeCallable
1 parent 7f14232 commit b8a92a7

File tree

12 files changed

+3444
-600
lines changed

12 files changed

+3444
-600
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package org.jooq.lambda;
2+
3+
import org.jooq.lambda.tuple.Tuple2;
4+
5+
import java.util.NoSuchElementException;
6+
import java.util.Objects;
7+
import java.util.Optional;
8+
import java.util.concurrent.Callable;
9+
import java.util.function.BiConsumer;
10+
import java.util.function.Consumer;
11+
import java.util.stream.Stream;
12+
13+
/**
14+
* Mix of {@link Tuple2}, {@link Optional} and {@link Callable}
15+
* to represent (potentially failed to acquire) "optional" result in functional (Stream API) chain.
16+
* <br>
17+
* {@link #v1} - nullable returned value<br>
18+
* {@link #v2} - {@link Throwable} in case of failure (null otherwise)
19+
*
20+
* @param <T> the type of value
21+
*
22+
* @author Andrej Fink
23+
*/
24+
public class Either<T> extends Tuple2<T,Throwable> implements Callable<T> {
25+
26+
private static final Either<?> EMPTY = new Either<>(null,null);
27+
28+
public static <T> Either<T> empty () {
29+
return Wrap.castUnsafe(EMPTY);
30+
}
31+
32+
33+
/** Return this Either as {@link Optional} */
34+
public Optional<T> optValue () {
35+
return Optional.ofNullable(v1);
36+
}
37+
38+
public Optional<Throwable> optThrowable () {
39+
return Optional.ofNullable(v2);
40+
}
41+
42+
/*@Nullable*/ public Object either () {
43+
return v2 != null ? v2 : v1;
44+
}
45+
46+
// Callable
47+
48+
@Override @SuppressWarnings("RedundantThrows")
49+
/*@Nullable*/ public T call () throws Exception {
50+
if (v2 != null) {
51+
SeqUtils.sneakyThrow(v2);// throws Exception or Error!
52+
}
53+
return v1;
54+
}//Callable.call
55+
56+
57+
// creation
58+
59+
private Either (T nullableValue, Throwable nullableThrowable) {
60+
super(nullableValue, nullableThrowable);
61+
}//new
62+
63+
64+
public static <T> Either<T> success (/*@Nullable*/ T value) {
65+
return new Either<>(value, null);
66+
}
67+
68+
public static <T> Either<T> failure (Throwable t) {
69+
return new Either<>(null, Objects.requireNonNull(t));
70+
}
71+
72+
73+
public boolean isSuccess () {
74+
return v2 == null;
75+
}
76+
77+
public boolean isFailure () {
78+
return v2 != null;
79+
}
80+
81+
/**
82+
* If a value is present (success and not empty), returns {@code true}, otherwise {@code false}.
83+
*
84+
* @return {@code true} if a value is present, otherwise {@code false}
85+
*/
86+
public boolean isPresent() {
87+
return isSuccess() && v1 != null;
88+
}
89+
90+
/**
91+
* If a value is not present (failure or null), returns {@code true}, otherwise {@code false}.
92+
*
93+
* @return {@code true} if a value is not present, otherwise {@code false}
94+
*/
95+
public boolean isEmpty() {
96+
return isFailure() || v1 == null;
97+
}
98+
99+
/** Success, but null */
100+
public boolean isNull () {
101+
return isSuccess() && v1 == null;
102+
}
103+
104+
105+
public Either<T> throwIfFailure () throws IllegalStateException {
106+
if (isFailure()) {
107+
throw new IllegalStateException("Throwable instead of value", v2);
108+
}
109+
return this;
110+
}
111+
112+
public Either<T> throwIfEmpty () throws IllegalStateException, NoSuchElementException {
113+
if (isFailure()) {
114+
throw new IllegalStateException("Throwable instead of value", v2);
115+
} else if (v1 == null) {
116+
throw new NoSuchElementException("No value present");
117+
}
118+
return this;
119+
}
120+
121+
public Either<T> throwIfNull () throws NoSuchElementException {
122+
if (isNull()) {
123+
throw new NoSuchElementException("No value present");
124+
}
125+
return this;
126+
}
127+
128+
129+
public Either<T> ifPresent (Consumer</*@Nullable*/ ? super T> action) {
130+
if (isPresent()) {
131+
action.accept(v1);
132+
}
133+
return this;
134+
}
135+
136+
137+
public Either<T> ifNull (Runnable nullAction) {
138+
if (isNull()) {
139+
nullAction.run();
140+
}
141+
return this;
142+
}
143+
144+
public Either<T> ifEmpty (Consumer</*@Nullable*/ Throwable> failureOrNullAction) {
145+
if (isEmpty()) {
146+
failureOrNullAction.accept(v2);
147+
}
148+
return this;
149+
}
150+
151+
public Either<T> ifSuccess (Consumer</*@Nullable*/ ? super T> nullSafeAction) {
152+
if (isSuccess()) {
153+
nullSafeAction.accept(v1);
154+
}
155+
return this;
156+
}
157+
158+
159+
public Either<T> ifFailure (Consumer<Throwable> failureAction) {
160+
if (isFailure()) {
161+
failureAction.accept(v2);
162+
}
163+
return this;
164+
}
165+
166+
167+
public void consume (BiConsumer</*@Nullable*/ ? super T,Throwable> consumer) {
168+
consumer.accept(v1, v2);
169+
}
170+
171+
172+
/**
173+
* If a value is present, returns a sequential {@link Stream} containing
174+
* only that value, otherwise returns an empty {@code Stream}.
175+
* <p>
176+
* This method can be used to transform a {@code Stream} of optional
177+
* elements to a {@code Stream} of present value elements:
178+
* <pre>{@code
179+
* Stream<Optional<T>> os = ..
180+
* Stream<T> s = os.flatMap(Optional::stream)
181+
* }</pre>
182+
*
183+
* @return the optional value as a {@code Stream}
184+
*/
185+
public Stream<T> stream () {
186+
if (isEmpty()) {
187+
return Stream.empty();
188+
} else {
189+
return Stream.of(v1);
190+
}
191+
}
192+
193+
194+
@Override
195+
public boolean equals (Object obj) {
196+
if (this == obj) {
197+
return true;
198+
}
199+
200+
if (obj instanceof Tuple2<?,?>) {
201+
Tuple2<?,?> o = (Tuple2<?,?>) obj;
202+
return Objects.equals(v1, o.v1) && Objects.equals(v2, o.v2);
203+
204+
} else if (isSuccess() && obj instanceof Optional<?>) {
205+
Optional<?> o = (Optional<?>) obj;
206+
return Objects.equals(v1, o) || Objects.equals(v1, o.orElse(null));
207+
208+
} else if (isSuccess()) {
209+
return Objects.equals(v1, obj);
210+
211+
} else if (isFailure() && obj instanceof Throwable) {
212+
return Objects.equals(v2, obj);
213+
}
214+
return false;
215+
}
216+
217+
218+
/**
219+
* Returns a non-empty string representation of this {@code Either}
220+
* suitable for debugging.
221+
*
222+
* @return the string representation of this instance
223+
*/
224+
@Override public String toString () {
225+
if (isFailure()) {
226+
return "Either.Failure("+ v2+')';
227+
228+
} else if (isPresent()) {
229+
return "Either("+ v1 +')';
230+
}
231+
return "Either.Empty";
232+
}
233+
}

0 commit comments

Comments
 (0)