Skip to content

Commit 198307a

Browse files
committed
Either ~ Optional+Throwable: return type for no-exception-wrapper (beta)
1 parent 5f9fd8e commit 198307a

File tree

2 files changed

+792
-0
lines changed

2 files changed

+792
-0
lines changed
Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
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.concurrent.ExecutionException;
10+
import java.util.concurrent.Future;
11+
import java.util.concurrent.RunnableFuture;
12+
import java.util.concurrent.TimeUnit;
13+
import java.util.function.BiConsumer;
14+
import java.util.function.BiFunction;
15+
import java.util.function.Consumer;
16+
import java.util.function.Supplier;
17+
import java.util.stream.Stream;
18+
19+
/**
20+
* Mix of {@link Optional}, {@link Callable}, {@link Tuple2} and {@link Future}
21+
* to represent (potentially failed to acquire) "optional" result.
22+
*
23+
* @param <T> the type of value
24+
*/
25+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
26+
public final class Either<T> implements Callable<T>, Supplier<T> {
27+
28+
private final Optional<T> value;
29+
private final Throwable throwable;
30+
31+
32+
/** Return this Either as {@link Optional} */
33+
public Optional<T> optional () {
34+
return value;
35+
}
36+
37+
/*@Nullable*/ public Throwable throwable () {
38+
return throwable;
39+
}
40+
41+
public Tuple2<T,Throwable> tuple () {
42+
return new Tuple2<>(value.orElse(null), throwable);
43+
}
44+
45+
public Tuple2<Optional<T>,Throwable> optTuple () {
46+
return new Tuple2<>(value, throwable);
47+
}
48+
49+
// Callable
50+
51+
@Override @SuppressWarnings("RedundantThrows")
52+
/*@Nullable*/ public T call () throws Exception {
53+
if (throwable != null) {
54+
SeqUtils.sneakyThrow(throwable);// throws Exception or Error!
55+
}
56+
return value.orElse(null);
57+
}//Callable.call
58+
59+
@SuppressWarnings("RedundantThrows")
60+
public Optional<T> optCall () throws Exception {
61+
if (throwable != null) {
62+
SeqUtils.sneakyThrow(throwable);// throws Exception or Error!
63+
}
64+
return value;
65+
}
66+
67+
// Supplier
68+
69+
@Override
70+
public T get () throws IllegalStateException, NoSuchElementException {
71+
if (throwable != null) {
72+
throw new IllegalStateException("Throwable instead of value", throwable);
73+
74+
} else if (value.isEmpty()) {
75+
throw new NoSuchElementException("No value present");
76+
}
77+
return value.get();
78+
}//Supplier.get
79+
80+
// creation
81+
82+
private Either (Optional<T> nonNullOptionalAsValue, Throwable nullableThrowable) {
83+
value = nonNullOptionalAsValue;
84+
throwable = nullableThrowable;
85+
}//new
86+
87+
88+
public static <T> Either<T> success (T value) {
89+
return new Either<>(Optional.ofNullable(value), null);
90+
}
91+
92+
public static <T> Either<T> success (Optional<T> optValue) {
93+
return new Either<>(optValue, null);
94+
}
95+
96+
public static <T> Either<T> failure (Throwable t) {
97+
return new Either<>(Optional.empty(), Objects.requireNonNull(t));
98+
}
99+
100+
//
101+
102+
public boolean isSuccess () {
103+
return throwable == null;
104+
}
105+
106+
public boolean isFailure () {
107+
return throwable != null;
108+
}
109+
110+
/**
111+
* If a value is present (success and not empty), returns {@code true}, otherwise {@code false}.
112+
*
113+
* @return {@code true} if a value is present, otherwise {@code false}
114+
*/
115+
public boolean isPresent() {
116+
return isSuccess() && value.isPresent();
117+
}
118+
119+
/**
120+
* If a value is not present (failure or null), returns {@code true}, otherwise {@code false}.
121+
*
122+
* @return {@code true} if a value is not present, otherwise {@code false}
123+
*/
124+
public boolean isEmpty() {
125+
return isFailure() || value.isEmpty();
126+
}
127+
128+
/** Success, but null */
129+
public boolean isNull () {
130+
return isSuccess() && value.isEmpty();
131+
}
132+
133+
134+
public Either<T> throwIfFailure () throws IllegalStateException {
135+
if (isFailure()) {
136+
throw new IllegalStateException("Throwable instead of value", throwable);
137+
}
138+
return this;
139+
}
140+
141+
public Either<T> throwIfEmpty () throws IllegalStateException, NoSuchElementException {
142+
if (isFailure()) {
143+
throw new IllegalStateException("Throwable instead of value", throwable);
144+
} else if (value.isEmpty()) {
145+
throw new NoSuchElementException("No value present");
146+
}
147+
return this;
148+
}
149+
150+
public Either<T> throwIfNull () throws NoSuchElementException {
151+
if (isNull()) {
152+
throw new NoSuchElementException("No value present");
153+
}
154+
return this;
155+
}
156+
157+
public Either<T> optIfPresent (Consumer<Optional<? super T>> action) {
158+
if (isPresent()) {
159+
action.accept(value);
160+
}
161+
return this;
162+
}
163+
164+
public Optional<T> optIfPresent (Optional<T> elseValue) {
165+
if (isPresent()) {
166+
return value;
167+
}
168+
return elseValue;
169+
}
170+
171+
public Either<T> ifPresent (Consumer<? super T> action) {
172+
if (isPresent()) {
173+
//noinspection OptionalGetWithoutIsPresent
174+
action.accept(value.get());
175+
}
176+
return this;
177+
}
178+
179+
public T ifPresent (T elseValue) {
180+
if (isPresent()) {
181+
//noinspection OptionalGetWithoutIsPresent
182+
value.get();
183+
}
184+
return elseValue;
185+
}
186+
187+
188+
public Either<T> ifNull (Runnable nullAction) {
189+
if (isNull()) {
190+
nullAction.run();
191+
}
192+
return this;
193+
}
194+
195+
public Either<T> ifEmpty (Runnable nullAction) {
196+
if (isEmpty()) {
197+
nullAction.run();
198+
}
199+
return this;
200+
}
201+
202+
public Either<T> ifEmpty (Consumer<Throwable> failureOrNullAction) {
203+
if (isEmpty()) {
204+
failureOrNullAction.accept(throwable);
205+
}
206+
return this;
207+
}
208+
209+
public Either<T> ifSuccess (Consumer<? super T> nullSafeAction) {
210+
if (isSuccess()) {
211+
nullSafeAction.accept(value.orElse(null));
212+
}
213+
return this;
214+
}
215+
216+
public T ifSuccess (T elseValue) {
217+
if (isSuccess()) {
218+
return value.orElse(null);
219+
}
220+
return elseValue;
221+
}
222+
223+
public Either<T> optIfSuccess (Consumer<Optional<? super T>> action) {
224+
if (isSuccess()) {
225+
action.accept(value);
226+
}
227+
return this;
228+
}
229+
230+
public Optional<T> optIfSuccess (Optional<T> elseValue) {
231+
if (isSuccess()) {
232+
return value;
233+
}
234+
return elseValue;
235+
}
236+
237+
public Either<T> ifFailure (Consumer<Throwable> failureAction) {
238+
if (isFailure()) {
239+
failureAction.accept(throwable);
240+
}
241+
return this;
242+
}
243+
244+
public Optional<Throwable> ifFailure () {
245+
return Optional.ofNullable(throwable);
246+
}
247+
248+
public void consume (BiConsumer<? super T,Throwable> consumer) {
249+
consumer.accept(value.orElse(null), throwable);
250+
}
251+
252+
public void optConsume (BiConsumer<Optional<? super T>,Throwable> consumer) {
253+
consumer.accept(value, throwable);
254+
}
255+
256+
public <R> R map (BiFunction<? super T,Throwable,? extends R> fun) {
257+
return fun.apply(value.orElse(null), throwable);
258+
}
259+
260+
public <R> R optMap (BiFunction<Optional<? super T>,Throwable,? extends R> fun) {
261+
return fun.apply(value, throwable);
262+
}
263+
264+
/**
265+
* If a value is present, returns a sequential {@link Stream} containing
266+
* only that value, otherwise returns an empty {@code Stream}.
267+
* <p>
268+
* This method can be used to transform a {@code Stream} of optional
269+
* elements to a {@code Stream} of present value elements:
270+
* <pre>{@code
271+
* Stream<Optional<T>> os = ..
272+
* Stream<T> s = os.flatMap(Optional::stream)
273+
* }</pre>
274+
*
275+
* @return the optional value as a {@code Stream}
276+
*/
277+
public Stream<T> stream () {
278+
if (isEmpty()) {
279+
return Stream.empty();
280+
} else {
281+
//noinspection OptionalGetWithoutIsPresent
282+
return Stream.of(value.get());
283+
}
284+
}
285+
286+
public Stream<Optional<T>> optStream () {
287+
if (isFailure()) {
288+
return Stream.empty();
289+
} else {
290+
return Stream.of(value);
291+
}
292+
}
293+
294+
295+
/**
296+
* Indicates whether some other object is "equal to" this {@code Optional}.
297+
* The other object is considered equal if:
298+
* <ul>
299+
* <li>it is also an {@code Optional} and;
300+
* <li>both instances have no value present or;
301+
* <li>the present values are "equal to" each other via {@code equals()}.
302+
* </ul>
303+
*
304+
* @param obj an object to be tested for equality
305+
* @return {@code true} if the other object is "equal to" this object
306+
* otherwise {@code false}
307+
*/
308+
@Override
309+
public boolean equals(Object obj) {
310+
if (this == obj) {
311+
return true;
312+
}
313+
314+
if (obj instanceof Either<?>) {
315+
Either<?> o = (Either<?>) obj;
316+
return Objects.equals(value, o.value) && Objects.equals(throwable, o.throwable);
317+
318+
} else if (obj instanceof Optional<?>) {
319+
Optional<?> o = (Optional<?>) obj;
320+
return Objects.equals(value, o.orElse(null));
321+
322+
} else if (obj instanceof Throwable) {
323+
Throwable o = (Throwable) obj;
324+
return Objects.equals(throwable, o);
325+
}
326+
327+
return false;
328+
}
329+
330+
/**
331+
* Returns the hash code of the value, if present, otherwise {@code 0}
332+
* (zero) if no value is present.
333+
*
334+
* @return hash code value of the present value or {@code 0} if no value is
335+
* present
336+
*/
337+
@Override public int hashCode() {
338+
return value.hashCode() ^ Objects.hashCode(throwable);
339+
}
340+
341+
/**
342+
* Returns a non-empty string representation of this {@code Either}
343+
* suitable for debugging.
344+
*
345+
* @return the string representation of this instance
346+
*/
347+
@Override public String toString () {
348+
if (isFailure()) {
349+
return "Either.Failure("+ throwable+')';
350+
351+
} else if (value.isPresent()) {
352+
return "Either("+ value.get() +')';
353+
}
354+
return "Either.Empty";
355+
}
356+
357+
//java.util.concurrent.Future
358+
359+
public Future<T> future () {
360+
return new RunnableFuture<>() {
361+
@Override public boolean isCancelled () {
362+
return isFailure();
363+
}
364+
365+
@Override public boolean isDone () {
366+
return isSuccess();
367+
}
368+
369+
@Override
370+
/*@Nullable*/ public T get () throws InterruptedException, ExecutionException {
371+
if (throwable instanceof InterruptedException) {
372+
throw (InterruptedException) throwable;
373+
} else if (throwable != null) {
374+
throw new ExecutionException(throwable);
375+
}
376+
return value.orElse(null);
377+
}
378+
379+
@Override
380+
/*@Nullable*/ public T get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException {
381+
return get();
382+
}
383+
384+
@Override public boolean cancel (boolean mayInterruptIfRunning) {
385+
return false; // NOOP
386+
}
387+
388+
@Override public void run () {}
389+
390+
@Override public String toString () {
391+
return Either.this + ".Future";
392+
}
393+
};
394+
}
395+
396+
}

0 commit comments

Comments
 (0)