forked from jOOQ/jOOL
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#4] Wrapped value (Safe) implementation (addition to Sneaky and Unch…
…ecked) 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
- Loading branch information
1 parent
7f14232
commit b8a92a7
Showing
12 changed files
with
3,444 additions
and
600 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package org.jooq.lambda; | ||
|
||
import org.jooq.lambda.tuple.Tuple2; | ||
|
||
import java.util.NoSuchElementException; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.concurrent.Callable; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.Consumer; | ||
import java.util.stream.Stream; | ||
|
||
/** | ||
* Mix of {@link Tuple2}, {@link Optional} and {@link Callable} | ||
* to represent (potentially failed to acquire) "optional" result in functional (Stream API) chain. | ||
* <br> | ||
* {@link #v1} - nullable returned value<br> | ||
* {@link #v2} - {@link Throwable} in case of failure (null otherwise) | ||
* | ||
* @param <T> the type of value | ||
* | ||
* @author Andrej Fink | ||
*/ | ||
public class Either<T> extends Tuple2<T,Throwable> implements Callable<T> { | ||
|
||
private static final Either<?> EMPTY = new Either<>(null,null); | ||
|
||
public static <T> Either<T> empty () { | ||
return Wrap.castUnsafe(EMPTY); | ||
} | ||
|
||
|
||
/** Return this Either as {@link Optional} */ | ||
public Optional<T> optValue () { | ||
return Optional.ofNullable(v1); | ||
} | ||
|
||
public Optional<Throwable> optThrowable () { | ||
return Optional.ofNullable(v2); | ||
} | ||
|
||
/*@Nullable*/ public Object either () { | ||
return v2 != null ? v2 : v1; | ||
} | ||
|
||
// Callable | ||
|
||
@Override @SuppressWarnings("RedundantThrows") | ||
/*@Nullable*/ public T call () throws Exception { | ||
if (v2 != null) { | ||
SeqUtils.sneakyThrow(v2);// throws Exception or Error! | ||
} | ||
return v1; | ||
}//Callable.call | ||
|
||
|
||
// creation | ||
|
||
private Either (T nullableValue, Throwable nullableThrowable) { | ||
super(nullableValue, nullableThrowable); | ||
}//new | ||
|
||
|
||
public static <T> Either<T> success (/*@Nullable*/ T value) { | ||
return new Either<>(value, null); | ||
} | ||
|
||
public static <T> Either<T> failure (Throwable t) { | ||
return new Either<>(null, Objects.requireNonNull(t)); | ||
} | ||
|
||
|
||
public boolean isSuccess () { | ||
return v2 == null; | ||
} | ||
|
||
public boolean isFailure () { | ||
return v2 != null; | ||
} | ||
|
||
/** | ||
* If a value is present (success and not empty), returns {@code true}, otherwise {@code false}. | ||
* | ||
* @return {@code true} if a value is present, otherwise {@code false} | ||
*/ | ||
public boolean isPresent() { | ||
return isSuccess() && v1 != null; | ||
} | ||
|
||
/** | ||
* If a value is not present (failure or null), returns {@code true}, otherwise {@code false}. | ||
* | ||
* @return {@code true} if a value is not present, otherwise {@code false} | ||
*/ | ||
public boolean isEmpty() { | ||
return isFailure() || v1 == null; | ||
} | ||
|
||
/** Success, but null */ | ||
public boolean isNull () { | ||
return isSuccess() && v1 == null; | ||
} | ||
|
||
|
||
public Either<T> throwIfFailure () throws IllegalStateException { | ||
if (isFailure()) { | ||
throw new IllegalStateException("Throwable instead of value", v2); | ||
} | ||
return this; | ||
} | ||
|
||
public Either<T> throwIfEmpty () throws IllegalStateException, NoSuchElementException { | ||
if (isFailure()) { | ||
throw new IllegalStateException("Throwable instead of value", v2); | ||
} else if (v1 == null) { | ||
throw new NoSuchElementException("No value present"); | ||
} | ||
return this; | ||
} | ||
|
||
public Either<T> throwIfNull () throws NoSuchElementException { | ||
if (isNull()) { | ||
throw new NoSuchElementException("No value present"); | ||
} | ||
return this; | ||
} | ||
|
||
|
||
public Either<T> ifPresent (Consumer</*@Nullable*/ ? super T> action) { | ||
if (isPresent()) { | ||
action.accept(v1); | ||
} | ||
return this; | ||
} | ||
|
||
|
||
public Either<T> ifNull (Runnable nullAction) { | ||
if (isNull()) { | ||
nullAction.run(); | ||
} | ||
return this; | ||
} | ||
|
||
public Either<T> ifEmpty (Consumer</*@Nullable*/ Throwable> failureOrNullAction) { | ||
if (isEmpty()) { | ||
failureOrNullAction.accept(v2); | ||
} | ||
return this; | ||
} | ||
|
||
public Either<T> ifSuccess (Consumer</*@Nullable*/ ? super T> nullSafeAction) { | ||
if (isSuccess()) { | ||
nullSafeAction.accept(v1); | ||
} | ||
return this; | ||
} | ||
|
||
|
||
public Either<T> ifFailure (Consumer<Throwable> failureAction) { | ||
if (isFailure()) { | ||
failureAction.accept(v2); | ||
} | ||
return this; | ||
} | ||
|
||
|
||
public void consume (BiConsumer</*@Nullable*/ ? super T,Throwable> consumer) { | ||
consumer.accept(v1, v2); | ||
} | ||
|
||
|
||
/** | ||
* If a value is present, returns a sequential {@link Stream} containing | ||
* only that value, otherwise returns an empty {@code Stream}. | ||
* <p> | ||
* This method can be used to transform a {@code Stream} of optional | ||
* elements to a {@code Stream} of present value elements: | ||
* <pre>{@code | ||
* Stream<Optional<T>> os = .. | ||
* Stream<T> s = os.flatMap(Optional::stream) | ||
* }</pre> | ||
* | ||
* @return the optional value as a {@code Stream} | ||
*/ | ||
public Stream<T> stream () { | ||
if (isEmpty()) { | ||
return Stream.empty(); | ||
} else { | ||
return Stream.of(v1); | ||
} | ||
} | ||
|
||
|
||
@Override | ||
public boolean equals (Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (obj instanceof Tuple2<?,?>) { | ||
Tuple2<?,?> o = (Tuple2<?,?>) obj; | ||
return Objects.equals(v1, o.v1) && Objects.equals(v2, o.v2); | ||
|
||
} else if (isSuccess() && obj instanceof Optional<?>) { | ||
Optional<?> o = (Optional<?>) obj; | ||
return Objects.equals(v1, o) || Objects.equals(v1, o.orElse(null)); | ||
|
||
} else if (isSuccess()) { | ||
return Objects.equals(v1, obj); | ||
|
||
} else if (isFailure() && obj instanceof Throwable) { | ||
return Objects.equals(v2, obj); | ||
} | ||
return false; | ||
} | ||
|
||
|
||
/** | ||
* Returns a non-empty string representation of this {@code Either} | ||
* suitable for debugging. | ||
* | ||
* @return the string representation of this instance | ||
*/ | ||
@Override public String toString () { | ||
if (isFailure()) { | ||
return "Either.Failure("+ v2+')'; | ||
|
||
} else if (isPresent()) { | ||
return "Either("+ v1 +')'; | ||
} | ||
return "Either.Empty"; | ||
} | ||
} |
Oops, something went wrong.