|
19 | 19 | import java.util.stream.Stream; |
20 | 20 |
|
21 | 21 | import io.reactivex.rxjava4.annotations.*; |
| 22 | +import io.reactivex.rxjava4.core.config.StandardConcurrentConfig; |
22 | 23 | import io.reactivex.rxjava4.disposables.*; |
23 | 24 | import io.reactivex.rxjava4.exceptions.Exceptions; |
24 | 25 | import io.reactivex.rxjava4.functions.*; |
@@ -84,6 +85,20 @@ public interface Streamable<@NonNull T> { |
84 | 85 | return RxJavaPlugins.onAssembly(new StreamableJust<>(item)); |
85 | 86 | } |
86 | 87 |
|
| 88 | + /** |
| 89 | + * Filters out the upstream items that do not pass the given predicate |
| 90 | + * @param predicate the callback that should return {@code true} to let the upstream value pass |
| 91 | + * or {@code false} to ignore it and continue with the next upstream item |
| 92 | + * @return the new {@code Streamable} instance |
| 93 | + * @throw NullPointerException if {@code predicate} is {@code null} |
| 94 | + */ |
| 95 | + @CheckReturnValue |
| 96 | + @NonNull |
| 97 | + default Streamable<T> filter(@NonNull Predicate<? super T> predicate) { |
| 98 | + Objects.requireNonNull(predicate, "predicate is null"); |
| 99 | + return RxJavaPlugins.onAssembly(new StreamableFilter<>(this, predicate)); |
| 100 | + } |
| 101 | + |
87 | 102 | /** |
88 | 103 | * Streams all elements of the given items array. |
89 | 104 | * @param <T> the element type of the items |
@@ -427,6 +442,32 @@ default Streamable<T> hide() { |
427 | 442 | return RxJavaPlugins.onAssembly(new StreamableHide<>(this)); |
428 | 443 | } |
429 | 444 |
|
| 445 | + /** |
| 446 | + * Maps each upstream item into another item via a mapper function. |
| 447 | + * @param <R> the element type of the mapping |
| 448 | + * @param mapper the function that takes an upstream item and returns an item to be emitted |
| 449 | + * to the downstream |
| 450 | + * @return the new {@code Streamable} instance |
| 451 | + * @throw NullPointerException if {@code mapper} is {@code null} |
| 452 | + */ |
| 453 | + default <@NonNull R> Streamable<R> map(@NonNull Function<? super T, ? extends R> mapper) { |
| 454 | + Objects.requireNonNull(mapper, "mapper is null"); |
| 455 | + return RxJavaPlugins.onAssembly(new StreamableMap<>(this, mapper)); |
| 456 | + } |
| 457 | + |
| 458 | + /** |
| 459 | + * Maps each upstream item into another, optional item via a mapper function that skips the empty optionals. |
| 460 | + * @param <R> the element type of the mapping |
| 461 | + * @param mapper the function that takes an upstream item and returns an optional item to be emitted / skipped |
| 462 | + * to the downstream |
| 463 | + * @return the new {@code Streamable} instance |
| 464 | + * @throw NullPointerException if {@code mapper} is {@code null} |
| 465 | + */ |
| 466 | + default <@NonNull R> Streamable<R> mapOptional(@NonNull Function<? super T, ? extends Optional<? extends R>> mapper) { |
| 467 | + Objects.requireNonNull(mapper, "mapper is null"); |
| 468 | + return RxJavaPlugins.onAssembly(new StreamableMapOptional<>(this, mapper)); |
| 469 | + } |
| 470 | + |
430 | 471 | /** |
431 | 472 | * Transforms the upstream sequence into zero or more elements for the downstream. |
432 | 473 | * @param <R> the result element type |
@@ -469,6 +510,24 @@ default Streamable<T> take(long n) { |
469 | 510 | }); |
470 | 511 | } |
471 | 512 |
|
| 513 | + /** |
| 514 | + * Maps each upstream item onto a {@code Streamable} and runs them concurrently while |
| 515 | + * relaying inner items as first-come-first-served manner. |
| 516 | + * @param <R> the element type of the output sequence |
| 517 | + * @param mapper the function that turns an upstream item into a {@code Streamable} inner sequence |
| 518 | + * @param config the configuration record for this operator |
| 519 | + * @return the new {@code Streamable} instance |
| 520 | + * @throws NullPointerException if {@code mapper} or {@code config} is {@code null} |
| 521 | + */ |
| 522 | + @CheckReturnValue |
| 523 | + @NonNull |
| 524 | + default <R> Streamable<R> flatMap(@NonNull Function<? super T, ? extends Streamable<? extends R>> mapper, |
| 525 | + @NonNull StandardConcurrentConfig config) { |
| 526 | + Objects.requireNonNull(mapper, "mapper is null"); |
| 527 | + Objects.requireNonNull(config, "config is null"); |
| 528 | + return RxJavaPlugins.onAssembly(new StreamableFlatMap<>(this, mapper, config.maxConcurrency())); |
| 529 | + } |
| 530 | + |
472 | 531 | // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo |
473 | 532 | // Consumption methods and outgoing converters |
474 | 533 | // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo |
@@ -525,17 +584,21 @@ default CompletionStageDisposable<Void> forEach(@NonNull Consumer<? super T> con |
525 | 584 | Objects.requireNonNull(executor, "executor is null"); |
526 | 585 | final Streamable<T> me = this; |
527 | 586 | var future = CompletableFuture.<Void>supplyAsync(() -> { |
528 | | - try (var str = me.stream(canceller)) { |
529 | | - while (!canceller.isDisposed()) { |
530 | | - if (str.awaitNext(canceller)) { |
531 | | - // System.out.println("Received " + str.current()); |
532 | | - consumer.accept(Objects.requireNonNull(str.current(), "The upstream Streamable " + me.getClass() + " produced a null element!")); |
533 | | - } else { |
534 | | - // System.out.println("EOF "); |
535 | | - break; |
| 587 | + var str = me.stream(canceller); |
| 588 | + try { |
| 589 | + try { |
| 590 | + while (!canceller.isDisposed()) { |
| 591 | + if (str.awaitNext(canceller)) { |
| 592 | + // System.out.println("Received " + str.current()); |
| 593 | + consumer.accept(Objects.requireNonNull(str.current(), "The upstream Streamable " + me.getClass() + " produced a null element!")); |
| 594 | + } else { |
| 595 | + // System.out.println("EOF "); |
| 596 | + break; |
| 597 | + } |
536 | 598 | } |
| 599 | + } finally { |
| 600 | + str.awaitFinish(canceller); |
537 | 601 | } |
538 | | - // System.out.println("Canceller status after loop: " + canceller.isDisposed()); |
539 | 602 | } catch (final Throwable crash) { |
540 | 603 | Exceptions.throwIfFatal(crash); |
541 | 604 | if (crash instanceof CompletionException ce) { |
@@ -567,19 +630,23 @@ default CompletionStageDisposable<Void> forEach( |
567 | 630 | Objects.requireNonNull(executor, "executor is null"); |
568 | 631 | final Streamable<T> me = this; |
569 | 632 | var future = CompletableFuture.<Void>supplyAsync(() -> { |
570 | | - try (var str = me.stream(canceller)) { |
| 633 | + var str = me.stream(canceller); |
| 634 | + try { |
| 635 | + try { |
571 | 636 | var stopper = Disposable.empty(); |
572 | | - while (!canceller.isDisposed() && !stopper.isDisposed()) { |
573 | | - if (str.awaitNext(canceller)) { |
574 | | - // System.out.println("Received " + str.current()); |
575 | | - var v = Objects.requireNonNull(str.current(), "The upstream Streamable " + me.getClass() + " produced a null element!"); |
576 | | - consumer.accept(v, stopper); |
577 | | - } else { |
578 | | - // System.out.println("EOF "); |
579 | | - break; |
| 637 | + while (!canceller.isDisposed() && !stopper.isDisposed()) { |
| 638 | + if (str.awaitNext(canceller)) { |
| 639 | + // System.out.println("Received " + str.current()); |
| 640 | + var v = Objects.requireNonNull(str.current(), "The upstream Streamable " + me.getClass() + " produced a null element!"); |
| 641 | + consumer.accept(v, stopper); |
| 642 | + } else { |
| 643 | + // System.out.println("EOF "); |
| 644 | + break; |
| 645 | + } |
580 | 646 | } |
| 647 | + } finally { |
| 648 | + str.awaitFinish(canceller); |
581 | 649 | } |
582 | | - // System.out.println("Canceller status after loop: " + canceller.isDisposed()); |
583 | 650 | } catch (final Throwable crash) { |
584 | 651 | Exceptions.throwIfFatal(crash); |
585 | 652 | if (crash instanceof CompletionException ce) { |
|
0 commit comments