Skip to content

Commit d85847a

Browse files
committed
fix(core): Handle possible nulls in factory methods
1 parent 9ee0fd9 commit d85847a

File tree

8 files changed

+201
-51
lines changed

8 files changed

+201
-51
lines changed

Diff for: src/main/java/io/github/joselion/maybe/CloseableHandler.java

+21-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.github.joselion.maybe;
22

3+
import static java.util.Objects.isNull;
4+
35
import java.util.Optional;
46

57
import io.github.joselion.maybe.helpers.Commons;
@@ -22,12 +24,8 @@ public class CloseableHandler<T extends AutoCloseable, E extends Throwable> {
2224

2325
private final Either<E, T> value;
2426

25-
private CloseableHandler(final T resource) {
26-
this.value = Either.ofRight(resource);
27-
}
28-
29-
private CloseableHandler(final E error) {
30-
this.value = Either.ofLeft(error);
27+
private CloseableHandler(final Either<E, T> value) {
28+
this.value = value;
3129
}
3230

3331
/**
@@ -39,7 +37,12 @@ private CloseableHandler(final E error) {
3937
* @return a new instance of CloseableHandler with the given resource
4038
*/
4139
static <T extends AutoCloseable, E extends Throwable> CloseableHandler<T, E> from(final T resource) {
42-
return new CloseableHandler<>(resource);
40+
final var nullException = new NullPointerException("The \"Maybe<T>\" resource solved to null");
41+
final var either = isNull(resource) // NOSONAR
42+
? Either.<E, T>ofLeft(Commons.cast(nullException))
43+
: Either.<E, T>ofRight(resource);
44+
45+
return new CloseableHandler<>(either);
4346
}
4447

4548
/**
@@ -51,7 +54,12 @@ static <T extends AutoCloseable, E extends Throwable> CloseableHandler<T, E> fro
5154
* @return a new instance of the failed CloseableHandler with the error
5255
*/
5356
static <T extends AutoCloseable, E extends Throwable> CloseableHandler<T, E> failure(final E error) {
54-
return new CloseableHandler<>(error);
57+
final var nullException = new NullPointerException("The \"Maybe<T>\" error was null");
58+
final var either = isNull(error) // NOSONAR
59+
? Either.<E, T>ofLeft(Commons.cast(nullException))
60+
: Either.<E, T>ofLeft(error);
61+
62+
return new CloseableHandler<>(either);
5563
}
5664

5765
/**
@@ -95,13 +103,13 @@ public <S, X extends Throwable> SolveHandler<S, X> solve(
95103
return this.value
96104
.mapLeft(Commons::<X>cast)
97105
.unwrap(
98-
SolveHandler::ofError,
106+
SolveHandler::failure,
99107
resource -> {
100108
try (var res = resource) {
101-
return SolveHandler.ofSuccess(solver.apply(res));
109+
return SolveHandler.from(solver.apply(res));
102110
} catch (final Throwable e) { //NOSONAR
103111
final var error = Commons.<X>cast(e);
104-
return SolveHandler.ofError(error);
112+
return SolveHandler.failure(error);
105113
}
106114
}
107115
);
@@ -154,14 +162,14 @@ public <X extends Throwable> EffectHandler<X> effect(
154162
return this.value
155163
.mapLeft(Commons::<X>cast)
156164
.unwrap(
157-
EffectHandler::ofError,
165+
EffectHandler::failure,
158166
resource -> {
159167
try (var res = resource) {
160168
effect.accept(res);
161169
return EffectHandler.empty();
162170
} catch (final Throwable e) { // NOSONAR
163171
final var error = Commons.<X>cast(e);
164-
return EffectHandler.ofError(error);
172+
return EffectHandler.failure(error);
165173
}
166174
}
167175
);

Diff for: src/main/java/io/github/joselion/maybe/EffectHandler.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package io.github.joselion.maybe;
22

3+
import static java.util.Objects.isNull;
4+
35
import java.util.Optional;
46
import java.util.function.Consumer;
57
import java.util.function.Function;
68

79
import org.eclipse.jdt.annotation.Nullable;
810

11+
import io.github.joselion.maybe.helpers.Commons;
912
import io.github.joselion.maybe.util.function.ThrowingConsumer;
1013
import io.github.joselion.maybe.util.function.ThrowingRunnable;
1114

@@ -45,8 +48,12 @@ static <E extends Throwable> EffectHandler<E> empty() {
4548
* @param error the error to instanciate the EffectHandler
4649
* @return a EffectHandler instance with an error value
4750
*/
48-
static <E extends Throwable> EffectHandler<E> ofError(final E error) {
49-
return new EffectHandler<>(error);
51+
static <E extends Throwable> EffectHandler<E> failure(final E error) {
52+
return new EffectHandler<>(
53+
isNull(error) // NOSONAR
54+
? Commons.cast(new NullPointerException("The \"Maybe<T>\" error was null"))
55+
: error
56+
);
5057
}
5158

5259
/**

Diff for: src/main/java/io/github/joselion/maybe/Maybe.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ public static <T, E extends Throwable> SolveHandler<T, E> from(
157157
final ThrowingSupplier<? extends T, ? extends E> solver
158158
) {
159159
try {
160-
return SolveHandler.ofSuccess(solver.get());
160+
return SolveHandler.from(solver.get());
161161
} catch (Throwable e) { // NOSONAR
162162
final var error = Commons.<E>cast(e);
163-
return SolveHandler.ofError(error);
163+
return SolveHandler.failure(error);
164164
}
165165
}
166166

@@ -199,7 +199,7 @@ public static <E extends Throwable> EffectHandler<E> from(final ThrowingRunnable
199199
return EffectHandler.empty();
200200
} catch (Throwable e) { // NOSONAR
201201
final var error = Commons.<E>cast(e);
202-
return EffectHandler.ofError(error);
202+
return EffectHandler.failure(error);
203203
}
204204
}
205205

@@ -444,7 +444,7 @@ public <U, E extends Throwable> SolveHandler<U, E> solve(
444444
.orElseThrow();
445445
} catch (final NoSuchElementException e) {
446446
final var error = Commons.<E>cast(e);
447-
return SolveHandler.ofError(error);
447+
return SolveHandler.failure(error);
448448
}
449449
}
450450

@@ -484,7 +484,7 @@ public <E extends Throwable> EffectHandler<E> effect(final ThrowingConsumer<? su
484484
.orElseThrow();
485485
} catch (final NoSuchElementException e) {
486486
final var error = Commons.<E>cast(e);
487-
return EffectHandler.ofError(error);
487+
return EffectHandler.failure(error);
488488
}
489489
}
490490

Diff for: src/main/java/io/github/joselion/maybe/SolveHandler.java

+25-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.github.joselion.maybe;
22

3+
import static java.util.Objects.isNull;
4+
35
import java.util.Optional;
46
import java.util.function.Consumer;
57
import java.util.function.Function;
@@ -39,8 +41,13 @@ private SolveHandler(final Either<E, T> value) {
3941
* @param success the success value to instantiate the SolveHandler
4042
* @return a SolveHandler instance with a success value
4143
*/
42-
static <T, E extends Throwable> SolveHandler<T, E> ofSuccess(final T success) {
43-
return new SolveHandler<>(Either.ofRight(success));
44+
static <T, E extends Throwable> SolveHandler<T, E> from(final T success) {
45+
final var nullException = new NullPointerException("The \"Maybe<T>\" value solved to null");
46+
final var either = isNull(success) // NOSONAR
47+
? Either.<E, T>ofLeft(Commons.cast(nullException))
48+
: Either.<E, T>ofRight(success);
49+
50+
return new SolveHandler<>(either);
4451
}
4552

4653
/**
@@ -51,8 +58,13 @@ static <T, E extends Throwable> SolveHandler<T, E> ofSuccess(final T success) {
5158
* @param error the error to instantiate the SolveHandler
5259
* @return a SolveHandler instance with an error value
5360
*/
54-
static <T, E extends Throwable> SolveHandler<T, E> ofError(final E error) {
55-
return new SolveHandler<>(Either.ofLeft(error));
61+
static <T, E extends Throwable> SolveHandler<T, E> failure(final E error) {
62+
final var nullException = new NullPointerException("The \"Maybe<T>\" error was null");
63+
final var either = isNull(error) // NOSONAR
64+
? Either.<E, T>ofLeft(Commons.cast(nullException))
65+
: Either.<E, T>ofLeft(error);
66+
67+
return new SolveHandler<>(either);
5668
}
5769

5870
/**
@@ -140,7 +152,7 @@ public <X extends Throwable> SolveHandler<T, E> catchError(
140152
.filter(ofType::isInstance)
141153
.map(ofType::cast)
142154
.map(handler)
143-
.map(SolveHandler::<T, E>ofSuccess)
155+
.map(SolveHandler::<T, E>from)
144156
.orElse(this);
145157
}
146158

@@ -156,7 +168,7 @@ public <X extends Throwable> SolveHandler<T, E> catchError(
156168
public SolveHandler<T, E> catchError(final Function<? super E, ? extends T> handler) {
157169
return this.value
158170
.mapLeft(handler)
159-
.mapLeft(SolveHandler::<T, E>ofSuccess)
171+
.mapLeft(SolveHandler::<T, E>from)
160172
.leftOrElse(this);
161173
}
162174

@@ -229,7 +241,7 @@ public <S, X extends Throwable> SolveHandler<S, X> solve(
229241
return this.value
230242
.mapLeft(Commons::<X>cast)
231243
.unwrap(
232-
SolveHandler::ofError,
244+
SolveHandler::failure,
233245
Maybe.partial(solver)
234246
);
235247
}
@@ -305,7 +317,7 @@ public <X extends Throwable> EffectHandler<X> effect(final ThrowingConsumer<? su
305317
return this.value
306318
.mapLeft(Commons::<X>cast)
307319
.unwrap(
308-
EffectHandler::ofError,
320+
EffectHandler::failure,
309321
Maybe.partial(effect)
310322
);
311323
}
@@ -341,8 +353,8 @@ public <U> SolveHandler<U, E> map(final Function<? super T, ? extends U> mapper)
341353
return this.value
342354
.mapRight(mapper)
343355
.unwrap(
344-
SolveHandler::ofError,
345-
SolveHandler::ofSuccess
356+
SolveHandler::failure,
357+
SolveHandler::from
346358
);
347359
}
348360

@@ -358,12 +370,12 @@ public <U> SolveHandler<U, E> map(final Function<? super T, ? extends U> mapper)
358370
*/
359371
public <U> SolveHandler<U, ClassCastException> cast(final Class<U> type) {
360372
return this.value.unwrap(
361-
error -> ofError(new ClassCastException(error.getMessage())),
373+
error -> failure(new ClassCastException(error.getMessage())),
362374
success -> {
363375
try {
364-
return ofSuccess(type.cast(success));
376+
return from(type.cast(success));
365377
} catch (ClassCastException error) {
366-
return ofError(error);
378+
return failure(error);
367379
}
368380
}
369381
);

Diff for: src/main/java17/io/github/joselion/maybe/Maybe.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ public static <T, E extends Throwable> SolveHandler<T, E> from(
157157
final ThrowingSupplier<? extends T, ? extends E> solver
158158
) {
159159
try {
160-
return SolveHandler.ofSuccess(solver.get());
160+
return SolveHandler.from(solver.get());
161161
} catch (Throwable e) { // NOSONAR
162162
final var error = Commons.<E>cast(e);
163-
return SolveHandler.ofError(error);
163+
return SolveHandler.failure(error);
164164
}
165165
}
166166

@@ -199,7 +199,7 @@ public static <E extends Throwable> EffectHandler<E> from(final ThrowingRunnable
199199
return EffectHandler.empty();
200200
} catch (Throwable e) { // NOSONAR
201201
final var error = Commons.<E>cast(e);
202-
return EffectHandler.ofError(error);
202+
return EffectHandler.failure(error);
203203
}
204204
}
205205

@@ -444,7 +444,7 @@ public <U, E extends Throwable> SolveHandler<U, E> solve(
444444
.orElseThrow();
445445
} catch (final NoSuchElementException e) {
446446
final var error = Commons.<E>cast(e);
447-
return SolveHandler.ofError(error);
447+
return SolveHandler.failure(error);
448448
}
449449
}
450450

@@ -484,7 +484,7 @@ public <E extends Throwable> EffectHandler<E> effect(final ThrowingConsumer<? su
484484
.orElseThrow();
485485
} catch (final NoSuchElementException e) {
486486
final var error = Commons.<E>cast(e);
487-
return EffectHandler.ofError(error);
487+
return EffectHandler.failure(error);
488488
}
489489
}
490490

Diff for: src/test/java/io/github/joselion/maybe/CloseableHandlerTests.java

+47
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,53 @@
3737
throw FAIL_EXCEPTION;
3838
};
3939

40+
@Nested class from {
41+
@Nested class when_the_resource_is_not_null {
42+
@Test void returns_a_handler_with_the_value() {
43+
final var fis = getFIS();
44+
final var handler = CloseableHandler.from(fis);
45+
46+
assertThat(handler.resource()).containsSame(fis);
47+
assertThat(handler.error()).isEmpty();
48+
}
49+
}
50+
51+
@Nested class when_the_resource_is_null {
52+
@Test void returns_a_handler_with_a_NullPointerException_error() {
53+
final var handler = CloseableHandler.from(null);
54+
55+
assertThat(handler.resource()).isEmpty();
56+
assertThat(handler.error())
57+
.get(THROWABLE)
58+
.isExactlyInstanceOf(NullPointerException.class)
59+
.hasMessage("The \"Maybe<T>\" resource solved to null");
60+
}
61+
}
62+
}
63+
64+
@Nested class failure {
65+
@Nested class when_the_error_is_not_null {
66+
@Test void returns_a_handler_with_the_error() {
67+
final var handler = CloseableHandler.failure(FAIL_EXCEPTION);
68+
69+
assertThat(handler.resource()).isEmpty();
70+
assertThat(handler.error()).containsSame(FAIL_EXCEPTION);
71+
}
72+
}
73+
74+
@Nested class when_the_error_is_null {
75+
@Test void returns_a_handler_with_a_NullPointerException_error() {
76+
final var handler = CloseableHandler.failure(null);
77+
78+
assertThat(handler.resource()).isEmpty();
79+
assertThat(handler.error())
80+
.get(THROWABLE)
81+
.isExactlyInstanceOf(NullPointerException.class)
82+
.hasMessage("The \"Maybe<T>\" error was null");
83+
}
84+
}
85+
}
86+
4087
@Nested class solve {
4188
@Nested class when_the_resource_is_present {
4289
@Nested class when_the_operation_succeeds {

Diff for: src/test/java/io/github/joselion/maybe/EffectHandlerTests.java

+30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatCode;
55
import static org.assertj.core.api.Assertions.assertThatThrownBy;
6+
import static org.assertj.core.api.InstanceOfAssertFactories.THROWABLE;
67
import static org.mockito.ArgumentMatchers.any;
78
import static org.mockito.Mockito.never;
89
import static org.mockito.Mockito.spy;
@@ -31,6 +32,35 @@
3132

3233
private final ThrowingRunnable<RuntimeException> noOp = () -> { };
3334

35+
@Nested class empty {
36+
@Test void returns_an_empty_handler() {
37+
final var handler = EffectHandler.empty();
38+
39+
assertThat(handler.error()).isEmpty();
40+
}
41+
}
42+
43+
@Nested class failure {
44+
@Nested class when_the_error_is_not_null {
45+
@Test void returns_a_handler_with_the_error() {
46+
final var handler = EffectHandler.failure(FAIL_EXCEPTION);
47+
48+
assertThat(handler.error()).containsSame(FAIL_EXCEPTION);
49+
}
50+
}
51+
52+
@Nested class when_the_error_is_null {
53+
@Test void returns_a_handler_with_a_NullPointerException_error() {
54+
final var handler = EffectHandler.failure(null);
55+
56+
assertThat(handler.error())
57+
.get(THROWABLE)
58+
.isExactlyInstanceOf(NullPointerException.class)
59+
.hasMessage("The \"Maybe<T>\" error was null");
60+
}
61+
}
62+
}
63+
3464
@Nested class doOnSuccess {
3565
@Nested class when_the_value_is_present {
3666
@Test void calls_the_effect_callback() {

0 commit comments

Comments
 (0)