diff --git a/build.gradle b/build.gradle index c821280..59932c5 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { subprojects { group = 'dev.tommyjs' - version = '2.5.3' + version = '2.5.4' apply plugin: 'java-library' apply plugin: 'com.github.johnrengelman.shadow' diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java index 2faeaf7..fd9e682 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java @@ -20,12 +20,12 @@ public class CompletionJoiner extends PromiseJoiner, Void, Void, List } @Override - protected Void getChildKey(Promise value) { + protected Void getChildKey(@NotNull Promise value) { return null; } @Override - protected @NotNull Promise getChildPromise(Promise value) { + protected @NotNull Promise getChildPromise(@NotNull Promise value) { //noinspection unchecked return (Promise) value; } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java index a2a5ad9..4b679cc 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java @@ -5,6 +5,7 @@ import dev.tommyjs.futur.promise.PromiseCompletion; import dev.tommyjs.futur.promise.PromiseFactory; import dev.tommyjs.futur.util.ConcurrentResultArray; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -20,12 +21,12 @@ public class MappedResultJoiner extends PromiseJoiner> entry) { + protected K getChildKey(@NotNull Map.Entry> entry) { return entry.getKey(); } @Override - protected @NotNull Promise getChildPromise(Map.Entry> entry) { + protected @Nullable Promise getChildPromise(@NotNull Map.Entry> entry) { return entry.getValue(); } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/PromiseJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/PromiseJoiner.java index 56b465f..c33a39d 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/PromiseJoiner.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/PromiseJoiner.java @@ -6,6 +6,7 @@ import dev.tommyjs.futur.promise.PromiseCompletion; import dev.tommyjs.futur.promise.PromiseFactory; import dev.tommyjs.futur.util.PromiseUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; @@ -18,35 +19,40 @@ public abstract class PromiseJoiner { this.joined = factory.unresolved(); } - protected abstract Key getChildKey(T value); + protected abstract Key getChildKey(@NotNull T value); - protected abstract @NotNull Promise getChildPromise(T value); + protected abstract @Nullable Promise getChildPromise(@NotNull T value); protected abstract void onChildComplete(int index, Key key, @NotNull PromiseCompletion completion); protected abstract Result getResult(); - protected void join(@NotNull Iterator promises) { + protected void join(@NotNull Iterator<@Nullable T> promises) { + assert !joined.isCompleted(); + AtomicInteger count = new AtomicInteger(); int i = 0; do { - if (joined.isCompleted()) { - promises.forEachRemaining(v -> getChildPromise(v).cancel()); - return; + T value = promises.next(); + if (value == null) { + continue; } - T value = promises.next(); - Promise p = getChildPromise(value); - if (!p.isCompleted()) { - PromiseUtil.cancelOnComplete(joined, p); + Promise promise = getChildPromise(value); + if (promise == null) { + continue; + } + + if (!promise.isCompleted()) { + PromiseUtil.cancelOnComplete(joined, promise); } count.incrementAndGet(); Key key = getChildKey(value); int index = i++; - p.addAsyncListener(res -> { + promise.addAsyncListener(res -> { onChildComplete(index, key, res); if (res.isError()) { assert res.getException() != null; diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java index 3f53887..b3009e5 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java @@ -20,12 +20,12 @@ public class ResultJoiner extends PromiseJoiner, Void, T, List> } @Override - protected Void getChildKey(Promise value) { + protected Void getChildKey(@NotNull Promise value) { return null; } @Override - protected @NotNull Promise getChildPromise(Promise value) { + protected @NotNull Promise getChildPromise(@NotNull Promise value) { return value; } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/VoidJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/VoidJoiner.java index 56a58d5..5584150 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/VoidJoiner.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/VoidJoiner.java @@ -15,12 +15,12 @@ public class VoidJoiner extends PromiseJoiner, Void, Void, Void> { } @Override - protected Void getChildKey(Promise value) { + protected Void getChildKey(@NotNull Promise value) { return null; } @Override - protected @NotNull Promise getChildPromise(Promise value) { + protected @NotNull Promise getChildPromise(@NotNull Promise value) { //noinspection unchecked return (Promise) value; } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java index 91688fb..a8cf488 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java @@ -70,7 +70,7 @@ public abstract class AbstractPromiseFactory implements PromiseFactory { return resolve(Collections.emptyList()); } - return new ResultJoiner<>(this, promises, expectedSize).joined(); + return new ResultJoiner(this, promises, expectedSize).joined(); } @Override diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java index 6780e16..4da6302 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java @@ -183,7 +183,7 @@ public interface PromiseFactory { * @param promises the input promises * @return the combined promise */ - default @NotNull Promise> combineMapped(@NotNull Map.Entry>... promises) { + default @NotNull Promise> combineMapped(@Nullable Map.Entry>... promises) { return combineMapped(Arrays.spliterator(promises)); } @@ -298,7 +298,7 @@ public interface PromiseFactory { * @param promises the input promises * @return the combined promise */ - default @NotNull Promise> combine(@NotNull Promise... promises) { + default @NotNull Promise> combine(@Nullable Promise... promises) { return combine(Arrays.spliterator(promises)); } @@ -352,7 +352,7 @@ public interface PromiseFactory { * @param promises the input promises * @return the combined promise */ - default @NotNull Promise>> allSettled(@NotNull Promise... promises) { + default @NotNull Promise>> allSettled(@Nullable Promise... promises) { return allSettled(Arrays.spliterator(promises)); } @@ -398,7 +398,7 @@ public interface PromiseFactory { * @param promises the input promises * @return the combined promise */ - default @NotNull Promise all(@NotNull Promise... promises) { + default @NotNull Promise all(@Nullable Promise... promises) { return all(Arrays.asList(promises).iterator()); } @@ -482,7 +482,7 @@ public interface PromiseFactory { * @param ignoreErrors whether to ignore promises that complete exceptionally * @return the combined promise */ - default @NotNull Promise race(boolean ignoreErrors, @NotNull Promise... promises) { + default @NotNull Promise race(boolean ignoreErrors, @Nullable Promise... promises) { return race(Arrays.asList(promises), ignoreErrors); } @@ -494,7 +494,7 @@ public interface PromiseFactory { * @param promises the input promises * @return the combined promise */ - default @NotNull Promise race(@NotNull Promise... promises) { + default @NotNull Promise race(@Nullable Promise... promises) { return race(false, promises); } diff --git a/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java index cbe1983..c9a47de 100644 --- a/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java +++ b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.AbstractMap; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -116,6 +117,19 @@ public final class PromiseTests { assert result.equals(unsizedIntStream(1000).boxed().toList()); } + @Test + public void testCombineNull() { + var result1 = promises.combine(Arrays.asList(null, null, null)).await(); + var result2 = promises.combineMapped(Arrays.asList(null, null, null)).await(); + var result3 = promises.combineMapped(List.of(new AbstractMap.SimpleEntry<>(null, null))).await(); + var result4 = promises.combineMapped(List.of(new AbstractMap.SimpleEntry<>(null, promises.resolve(true)))).await(); + + assert result1.isEmpty(); + assert result2.isEmpty(); + assert result3.isEmpty(); + assert result4.get(null) == true; + } + @Test public void testCombineUtil() throws TimeoutException, ExecutionException, InterruptedException { promises.all(