Skip to content

Enhancing Try with commonly used methods #971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Formatting Spotless Groovy import order [#960](https://github.com/ie3-institute/PowerSystemDataModel/issues/960)
- Implementing missing typical methods in `Try` [#970](https://github.com/ie3-institute/PowerSystemDataModel/issues/970)

### Fixed
- Fixed Couchbase integration tests that randomly failed [#755](https://github.com/ie3-institute/PowerSystemDataModel/issues/755)
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/edu/ie3/datamodel/utils/Try.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import edu.ie3.datamodel.exceptions.TryException;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -245,6 +246,30 @@ public abstract <R extends Exception> Try<T, R> transformF(
public abstract <U, R extends Exception> Try<U, R> transform(
Function<? super T, ? extends U> successFunc, Function<E, R> failureFunc);

/**
* If this is a Success, the value is returned, otherwise given default is returned.
*
* @param defaultData the value to be returned, if this is a failure.
* @return the value of a success, otherwise {@code defaultData}
*/
public abstract T getOrElse(Supplier<T> defaultData);

/**
* If this is a Success, it is returned, otherwise given default Try is returned.
*
* @param defaultTry the Try to be returned, if this is a failure.
* @return this try object if it is a Success, otherwise {@code defaultTry}
*/
public abstract Try<T, E> orElse(Supplier<Try<T, E>> defaultTry);

/**
* Turns this Try into an {@link Optional} by returning the wrapped value if this is a success,
* and an empty optional if this is a failure.
*
* @return an optional of the value
*/
public abstract Optional<T> toOptional();

/** Implementation of {@link Try} class. This class is used to present a successful try. */
public static final class Success<T, E extends Exception> extends Try<T, E> {
private final T data;
Expand Down Expand Up @@ -309,6 +334,21 @@ public <U, R extends Exception> Try<U, R> transform(
return new Success<>(successFunc.apply(data));
}

@Override
public T getOrElse(Supplier<T> defaultData) {
return data;
}

@Override
public Try<T, E> orElse(Supplier<Try<T, E>> defaultTry) {
return this;
}

@Override
public Optional<T> toOptional() {
return Optional.of(data);
}

/** Returns the stored data. */
public T get() {
return data;
Expand All @@ -335,6 +375,37 @@ public static <D, E extends Exception> Success<D, E> of(D data) {
public static <E extends Exception> Success<Void, E> empty() {
return (Success<Void, E>) emptySuccess;
}

/**
* Indicates whether some other object is "equal to" this {@code Success}. The other object is
* considered equal if:
*
* <ul>
* <li>it is also a {@code Success} and;
* <li>the values are "equal to" each other via {@code equals()}.
* </ul>
*
* @param obj an object to be tested for equality
* @return {@code true} if the other object is "equal to" this object otherwise {@code false}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}

return obj instanceof Success<?, ?> other && Objects.equals(data, other.data);
}

/**
* Returns the hash code of the value.
*
* @return hash code value of the value
*/
@Override
public int hashCode() {
return Objects.hashCode(data);
}
}

/** Implementation of {@link Try} class. This class is used to present a failed try. */
Expand Down Expand Up @@ -393,6 +464,21 @@ public <U, R extends Exception> Try<U, R> transform(
return Failure.of(failureFunc.apply(exception));
}

@Override
public T getOrElse(Supplier<T> defaultData) {
return defaultData.get();
}

@Override
public Try<T, E> orElse(Supplier<Try<T, E>> defaultTry) {
return defaultTry.get();
}

@Override
public Optional<T> toOptional() {
return Optional.empty();
}

/** Returns the thrown exception. */
public E get() {
return exception;
Expand Down Expand Up @@ -420,6 +506,37 @@ public static <T, E extends Exception> Failure<T, E> of(E exception) {
public static <E extends Exception> Failure<Void, E> ofVoid(E exception) {
return new Failure<>(exception);
}

/**
* Indicates whether some other object is "equal to" this {@code Failure}. The other object is
* considered equal if:
*
* <ul>
* <li>it is also a {@code Failure} and;
* <li>the exceptions are "equal to" each other via {@code equals()}.
* </ul>
*
* @param obj an object to be tested for equality
* @return {@code true} if the other object is "equal to" this object otherwise {@code false}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}

return obj instanceof Failure<?, ?> other && Objects.equals(exception, other.exception);
}

/**
* Returns the hash code of the exception.
*
* @return hash code value of the exception
*/
@Override
public int hashCode() {
return Objects.hashCode(exception);
}
}

/**
Expand Down
146 changes: 127 additions & 19 deletions src/test/groovy/edu/ie3/datamodel/utils/TryTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ class TryTest extends Specification {
}, FailureException)

then:
Exception ex = thrown()
ex.class == TryException
TryException ex = thrown(TryException)
ex.message == "Wrongly caught exception: "
Throwable cause = ex.cause
cause.class == SourceException
cause.message == "source exception"
ex.cause.class == SourceException
ex.cause.message == "source exception"
}

def "A failure is returned when using Failure#ofVoid() with an exception"() {
Expand Down Expand Up @@ -102,12 +100,10 @@ class TryTest extends Specification {
}, FailureException)

then:
Exception ex = thrown()
ex.class == TryException
TryException ex = thrown(TryException)
ex.message == "Wrongly caught exception: "
Throwable cause = ex.cause
cause.class == SourceException
cause.message == "source exception"
ex.cause.class == SourceException
ex.cause.message == "source exception"
}

def "A Try object can be creates by a boolean and an exception"() {
Expand Down Expand Up @@ -160,11 +156,9 @@ class TryTest extends Specification {
Try.ofVoid(FailureException, one, two)

then:
Exception ex = thrown()
ex.class == TryException
Throwable cause = ex.cause
cause.class == SourceException
cause.message == "source exception"
TryException ex = thrown(TryException)
ex.cause.class == SourceException
ex.cause.message == "source exception"
}

def "A void method can be applied to a try object"() {
Expand Down Expand Up @@ -241,16 +235,28 @@ class TryTest extends Specification {
scan.data.get().size() == 3
}

def "The getOrThrow method should work as expected"() {
def "The getOrThrow method should work as expected on a success"() {
given:
def value = "some value"
Try<String, SourceException> success = new Try.Success<>(value)

when:
def result = success.getOrThrow()

then:
noExceptionThrown()
result == value
}

def "The getOrThrow method should work as expected on a failure"() {
given:
Try<String, SourceException> failure = new Try.Failure<>(new SourceException("source exception"))

when:
failure.orThrow
failure.getOrThrow()

then:
Exception ex = thrown()
ex.class == SourceException
SourceException ex = thrown(SourceException)
ex.message == "source exception"
}

Expand Down Expand Up @@ -311,6 +317,108 @@ class TryTest extends Specification {
flatMapF.exception.get() == failure.get()
}

def "The getOrElse method should work as expected on a success"() {
given:
def value = "some value"
Try<String, SourceException> success = new Try.Success<>(value)

when:
def result = success.getOrElse(() -> "other")

then:
result == value
}

def "The getOrElse method should work as expected on a failure"() {
given:
Try<String, SourceException> failure = new Try.Failure<>(new SourceException("source exception"))
def defaultString = "other"

when:
def result = failure.getOrElse(() -> defaultString)

then:
result == defaultString
}

def "The orElse method should work as expected on a success"() {
given:
def otherSuccess = new Try.Success<>("other success")
def otherFailure = new Try.Failure<>(new SourceException("other failure"))
Try<String, SourceException> success = new Try.Success<>("some value")

when:
def result1 = success.orElse(() -> otherSuccess)
def result2 = success.orElse(() -> otherFailure)

then:
result1 == success
result2 == success
}

def "The orElse method should work as expected on a failure"() {
given:
def otherSuccess = new Try.Success<>("other success")
def otherFailure = new Try.Failure<>(new SourceException("other failure"))
Try<String, SourceException> failure = new Try.Failure<>(new SourceException("source exception"))

when:
def result1 = failure.orElse(() -> otherSuccess)
def result2 = failure.orElse(() -> otherFailure)

then:
result1 == otherSuccess
result2 == otherFailure
}

def "The toOptional method should work as expected on a success"() {
given:
def value = "some value"
Try<String, SourceException> success = new Try.Success<>(value)

when:
def result = success.toOptional()

then:
result == Optional.of(value)
}

def "The toOptional method should work as expected on a failure"() {
given:
Try<String, SourceException> failure = new Try.Failure<>(new SourceException("some failure"))

when:
def result = failure.toOptional()

then:
result == Optional.empty()
}

def "The equals and hashCode method should work as expected on a success"() {
given:
def value = "some value"
Try<String, SourceException> success1 = new Try.Success<>(value)
Try<String, SourceException> success2 = new Try.Success<>(value)
Try<String, SourceException> success3 = new Try.Success<>("other value")

expect:
success1 == success2
success1.hashCode() == success2.hashCode()
success1 != success3
success1.hashCode() != success3.hashCode()
}

def "The equals and hashCode method should work as expected on a failure"() {
given:
// exceptions usually do not implement #equals, this is difficult to test
Try<String, SourceException> failure1 = new Try.Failure<>(new SourceException("some failure"))
Try<String, SourceException> failure2 = new Try.Failure<>(new SourceException("other failure"))

expect:
failure1 != failure2
failure1.hashCode() != failure2.hashCode()
}

def "All exceptions of a collection of try objects should be returned"() {
given:
List<Try<String, Exception>> tries = List.of(
Expand Down