1919import java .util .stream .Stream ;
2020
2121import io .reactivex .rxjava4 .annotations .*;
22- import io .reactivex .rxjava4 .core .config .StandardConcurrentConfig ;
22+ import io .reactivex .rxjava4 .core .config .* ;
2323import io .reactivex .rxjava4 .disposables .*;
2424import io .reactivex .rxjava4 .exceptions .Exceptions ;
2525import io .reactivex .rxjava4 .functions .*;
3737 * @param <T> the element type of the stream.
3838 * @since 4.0.0
3939 */
40+ @ FunctionalInterface
4041public interface Streamable <@ NonNull T > {
4142
4243 // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
@@ -394,52 +395,83 @@ static Streamable<Long> rangeLong(long start, long count) {
394395 // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
395396
396397 /**
397- * Converts the streamable into a Flowable representation, running
398- * on the default Executors.newVirtualThreadPerTaskExecutor() virtual thread.
399- * @return the new Flowable instance
398+ * Calls the given consumer whenever an upstream item becomes available.
399+ * @param consumer the callback to invoke with the next item from upstream
400+ * @return the new {@code Streamable} instance
401+ * @throws NullPointerException if {@code consumer} is {@code null}
402+ */
403+ default Streamable <T > doOnNext (Consumer <? super T > consumer ) {
404+ Objects .requireNonNull (consumer , "consumer is null" );
405+ return intercept (new StreamableInterceptConfig <>(v -> { consumer .accept (v ); return v ; } ));
406+ }
407+
408+ /**
409+ * Maps each upstream item onto a {@code Streamable} and runs them concurrently while
410+ * relaying inner items as first-come-first-served manner.
411+ * @param <R> the element type of the output sequence
412+ * @param mapper the function that turns an upstream item into a {@code Streamable} inner sequence
413+ * @param config the configuration record for this operator
414+ * @return the new {@code Streamable} instance
415+ * @throws NullPointerException if {@code mapper} or {@code config} is {@code null}
400416 */
401417 @ CheckReturnValue
402418 @ NonNull
403- default Flowable <T > toFlowable () {
404- return toFlowable (Executors .newVirtualThreadPerTaskExecutor ());
419+ default <R > Streamable <R > flatMap (@ NonNull Function <? super T , ? extends Streamable <? extends R >> mapper ,
420+ @ NonNull StandardConcurrentConfig config ) {
421+ Objects .requireNonNull (mapper , "mapper is null" );
422+ Objects .requireNonNull (config , "config is null" );
423+ return RxJavaPlugins .onAssembly (new StreamableFlatMap <>(this , mapper , config .maxConcurrency ()));
405424 }
406425
407426 /**
408- * Converts the streamable into a Flowable representation, running
409- * on the provided executor service.
410- * @param executor the executor to use
411- * @return the new Flowable instance
427+ * Hides the identity of this {@code Streamable} and its {@link Streamer}.
428+ * <p>
429+ * Use it to break optimizations or hide concrete implementations.
430+ * @return the new {@code Streamable} instance
412431 */
413432 @ CheckReturnValue
414433 @ NonNull
415- default Flowable <T > toFlowable (@ NonNull ExecutorService executor ) {
416- Objects .requireNonNull (executor , "executir is null" );
417- var me = this ;
418- return Flowable .virtualCreate (emitter -> me .forEach (emitter ::emit ).await (emitter .canceller ()), executor );
434+ default Streamable <T > hide () {
435+ return RxJavaPlugins .onAssembly (new StreamableHide <>(this ));
419436 }
420437
421438 /**
422- * Transforms the upstream sequence into zero or more elements for the downstream.
423- * @param <R> the result element type
424- * @param transformer the interface to implement the transforming logic
439+ * Intercepts the lifecycle method calls of {@code Streamable} and {@link Streamer}
440+ * and allows the modification of them via Function callbacks.
441+ * @param config the configuration record for this operator
425442 * @return the new {@code Streamable} instance
443+ * @thros NullPointerException if {@code config} is {@code null}
426444 */
427445 @ CheckReturnValue
428446 @ NonNull
429- default <@ NonNull R > Streamable <R > transform (@ NonNull VirtualTransformer <T , R > transformer ) {
430- return transform (transformer , Executors .newVirtualThreadPerTaskExecutor ());
447+ default Streamable <T > intercept (StreamableInterceptConfig <T > config ) {
448+ Objects .requireNonNull (config , "config is null" );
449+ return RxJavaPlugins .onAssembly (new StreamableIntercept <>(this ,
450+ config .onStream (), config .onNext (), config .onCurrent (), config .onFinish ()));
431451 }
432452
433453 /**
434- * Hides the identity of this {@code Streamable} and its {@link Streamer}.
454+ * <strong>This method requires advanced knowledge about building operators, please consider
455+ * other standard composition methods first;</strong>
456+ * Returns a {@code Streamable} instance which when its {@link #stream(DisposableContainer)} is invoked,
457+ * applies the specified operator callback to the upstream {@link Streamer} to produce
458+ * an actual {@code Streamer} instance to be handed downstream.
435459 * <p>
436- * Use it to break optimizations or hide concrete implementations.
460+ * Use it to implement operators without creating the surrounding {@code Streamable} class.
461+ * <p>
462+ * If the {@code lifter} returns {@code null} or throws, the downstream will receive a
463+ * standard error streamer.
464+ * @param <R> the downstream type of the sequence
465+ * @param lifter the callback that will be invoked with the upstream {@code Streamer} and is expected
466+ * to produce a {@code Streamer} for the downstream.
437467 * @return the new {@code Streamable} instance
468+ * @throws NullPointerException if {@code lifter} is {@code null}
438469 */
439470 @ CheckReturnValue
440471 @ NonNull
441- default Streamable <T > hide () {
442- return RxJavaPlugins .onAssembly (new StreamableHide <>(this ));
472+ default <@ NonNull R > Streamable <R > lift (@ NonNull StreamableOperator <? super T , ? extends R > lifter ) {
473+ Objects .requireNonNull (lifter , "lifter is null" );
474+ return RxJavaPlugins .onAssembly (new StreamableLift <T , R >(this , lifter ));
443475 }
444476
445477 /**
@@ -472,27 +504,6 @@ default Streamable<T> hide() {
472504 return RxJavaPlugins .onAssembly (new StreamableMapOptional <>(this , mapper ));
473505 }
474506
475- /**
476- * Transforms the upstream sequence into zero or more elements for the downstream.
477- * @param <R> the result element type
478- * @param transformer the interface to implement the transforming logic
479- * @param executor where to run the transform and blocking operations
480- * @return the new Streamable instance
481- */
482- @ CheckReturnValue
483- @ NonNull
484- default <@ NonNull R > Streamable <R > transform (@ NonNull VirtualTransformer <T , R > transformer ,
485- @ NonNull ExecutorService executor ) {
486- Objects .requireNonNull (transformer , "transformer is null" );
487- Objects .requireNonNull (executor , "executor is null" );
488- var me = this ;
489- return create (emitter -> me .forEach ((item , stopper ) -> {
490- // System.out.println("item " + item);
491- transformer .transform (item , emitter , stopper );
492- }, emitter .canceller (), executor )
493- .await (emitter .canceller ()), executor );
494- }
495-
496507 /**
497508 * Takes at most the given number of items from the upstream and relays it to the downstream,
498509 * then cancels the rest of the sequence.
@@ -515,21 +526,76 @@ default Streamable<T> take(long n) {
515526 }
516527
517528 /**
518- * Maps each upstream item onto a {@code Streamable} and runs them concurrently while
519- * relaying inner items as first-come-first-served manner.
520- * @param <R> the element type of the output sequence
521- * @param mapper the function that turns an upstream item into a {@code Streamable} inner sequence
522- * @param config the configuration record for this operator
529+ * Calls the specified converter function during assembly time and returns its resulting value.
530+ * <p>
531+ * This allows fluent conversion to any other type.
532+ * @param <R> the resulting object type
533+ * @param converter the function that receives the current {@code Observable} instance and returns a value
534+ * @return the converted value
535+ * @throws NullPointerException if {@code converter} is {@code null}
536+ */
537+ @ CheckReturnValue
538+ @ NonNull
539+ default <@ NonNull R > R to (@ NonNull StreamableConverter <T , ? extends R > converter ) {
540+ return Objects .requireNonNull (converter , "converter is null" ).apply (this );
541+ }
542+
543+ /**
544+ * Converts the streamable into a Flowable representation, running
545+ * on the default Executors.newVirtualThreadPerTaskExecutor() virtual thread.
546+ * @return the new Flowable instance
547+ */
548+ @ CheckReturnValue
549+ @ NonNull
550+ default Flowable <T > toFlowable () {
551+ return toFlowable (Executors .newVirtualThreadPerTaskExecutor ());
552+ }
553+
554+ /**
555+ * Converts the streamable into a Flowable representation, running
556+ * on the provided executor service.
557+ * @param executor the executor to use
558+ * @return the new Flowable instance
559+ */
560+ @ CheckReturnValue
561+ @ NonNull
562+ default Flowable <T > toFlowable (@ NonNull ExecutorService executor ) {
563+ Objects .requireNonNull (executor , "executir is null" );
564+ var me = this ;
565+ return Flowable .virtualCreate (emitter -> me .forEach (emitter ::emit ).await (emitter .canceller ()), executor );
566+ }
567+
568+ /**
569+ * Transforms the upstream sequence into zero or more elements for the downstream.
570+ * @param <R> the result element type
571+ * @param transformer the interface to implement the transforming logic
523572 * @return the new {@code Streamable} instance
524- * @throws NullPointerException if {@code mapper} or {@code config} is {@code null}
525573 */
526574 @ CheckReturnValue
527575 @ NonNull
528- default <R > Streamable <R > flatMap (@ NonNull Function <? super T , ? extends Streamable <? extends R >> mapper ,
529- @ NonNull StandardConcurrentConfig config ) {
530- Objects .requireNonNull (mapper , "mapper is null" );
531- Objects .requireNonNull (config , "config is null" );
532- return RxJavaPlugins .onAssembly (new StreamableFlatMap <>(this , mapper , config .maxConcurrency ()));
576+ default <@ NonNull R > Streamable <R > transform (@ NonNull VirtualTransformer <T , R > transformer ) {
577+ return transform (transformer , Executors .newVirtualThreadPerTaskExecutor ());
578+ }
579+
580+ /**
581+ * Transforms the upstream sequence into zero or more elements for the downstream.
582+ * @param <R> the result element type
583+ * @param transformer the interface to implement the transforming logic
584+ * @param executor where to run the transform and blocking operations
585+ * @return the new Streamable instance
586+ */
587+ @ CheckReturnValue
588+ @ NonNull
589+ default <@ NonNull R > Streamable <R > transform (@ NonNull VirtualTransformer <T , R > transformer ,
590+ @ NonNull ExecutorService executor ) {
591+ Objects .requireNonNull (transformer , "transformer is null" );
592+ Objects .requireNonNull (executor , "executor is null" );
593+ var me = this ;
594+ return create (emitter -> me .forEach ((item , stopper ) -> {
595+ // System.out.println("item " + item);
596+ transformer .transform (item , emitter , stopper );
597+ }, emitter .canceller (), executor )
598+ .await (emitter .canceller ()), executor );
533599 }
534600
535601 // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
0 commit comments