All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.core.publisher.FluxMapSignal Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

The newest version!
/*
 * Copyright (c) 2016-2022 VMware Inc. or its affiliates, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.core.publisher;

import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;

import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.util.annotation.Nullable;

/**
 * Maps the values of the source publisher one-on-one via a mapper function.
 *
 * @param  the source value type
 * @param  the result value type
 * @author Stephane Maldini
 */
final class FluxMapSignal extends InternalFluxOperator {

    final Function mapperNext;
    final Function mapperError;
    final Supplier            mapperComplete;

    /**
     * Constructs a FluxMapSignal instance with the given source and mappers.
     *
     * @param source the source Publisher instance
     * @param mapperNext the next mapper function
     * @param mapperError the error mapper function
     * @param mapperComplete the complete mapper function
     *
     * @throws NullPointerException if either {@code source} is null or all {@code mapper} are null.
     */
    FluxMapSignal(Flux source,
		    @Nullable Function mapperNext,
		    @Nullable Function mapperError,
		    @Nullable Supplier mapperComplete) {
        super(source);
	    if(mapperNext == null && mapperError == null && mapperComplete == null){
		    throw new IllegalArgumentException("Map Signal needs at least one valid mapper");
	    }

        this.mapperNext = mapperNext;
        this.mapperError = mapperError;
        this.mapperComplete = mapperComplete;
    }

    @Override
    public CoreSubscriber subscribeOrReturn(CoreSubscriber actual) {
        return new FluxMapSignalSubscriber<>(actual,
                mapperNext,
                mapperError,
                mapperComplete);
    }

	@Override
	public Object scanUnsafe(Attr key) {
    	if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;
		return super.scanUnsafe(key);
	}

	static final class FluxMapSignalSubscriber
    extends AbstractQueue
		    implements InnerOperator,
		               BooleanSupplier {

	    final CoreSubscriber                actual;
	    final Function         mapperNext;
	    final Function mapperError;
	    final Supplier                    mapperComplete;

        boolean done;

        Subscription s;
        
        R value;
        
        volatile long requested;
        @SuppressWarnings("rawtypes")
        static final AtomicLongFieldUpdater REQUESTED =
                AtomicLongFieldUpdater.newUpdater(FluxMapSignalSubscriber.class, "requested");

        volatile boolean cancelled;
        
        long produced;

	    FluxMapSignalSubscriber(CoreSubscriber actual,
		        @Nullable Function mapperNext,
		        @Nullable Function mapperError,
		        @Nullable Supplier            mapperComplete) {
            this.actual = actual;
            this.mapperNext = mapperNext;
            this.mapperError = mapperError;
            this.mapperComplete = mapperComplete;
        }

        @Override
        public void onSubscribe(Subscription s) {
            if (Operators.validate(this.s, s)) {
                this.s = s;

                actual.onSubscribe(this);

                if (mapperNext == null) {
		            s.request(Long.MAX_VALUE);
	            }
	        }
        }

        @Override
        public void onNext(T t) {
	        if (done) {
	            Operators.onNextDropped(t, actual.currentContext());
                return;
            }

	        if (mapperNext == null) {
		        return;
		    }

            R v;

            try {
                v = mapperNext.apply(t);
                if (v == null) {
                    throw new NullPointerException("The mapper [" + mapperNext.getClass().getName() + "] returned a null value.");
                }
            }
            catch (Throwable e) {
	            done = true;
	            actual.onError(Operators.onOperatorError(s, e, t, actual.currentContext()));
                return;
            }

            produced++;
            actual.onNext(v);
        }

        @Override
        public void onError(Throwable t) {
            if (done) {
                Operators.onErrorDropped(t, actual.currentContext());
                return;
            }

            done = true;

	        if(mapperError == null){
		        actual.onError(t);
		        return;
	        }

	        R v;

	        try {
                v = mapperError.apply(t);
                if (v == null) {
                    throw new NullPointerException("The mapper [" + mapperError.getClass().getName() + "] returned a null value.");
                }
	        }
	        catch (Throwable e) {
		        done = true;
		        actual.onError(Operators.onOperatorError(s, e, t, actual.currentContext()));
		        return;
	        }

	        value = v;
            long p = produced;
            if (p != 0L) {
	            Operators.addCap(REQUESTED, this, -p);
            }
	        DrainUtils.postComplete(actual, this, REQUESTED, this, this);
        }

        @Override
        public void onComplete() {
            if (done) {
                return;
            }
            done = true;

	        if(mapperComplete == null){
		        actual.onComplete();
		        return;
	        }

	        R v;

	        try {
                v = mapperComplete.get();
                if (v == null) {
                    throw new NullPointerException("The mapper [" + mapperComplete.getClass().getName() + "] returned a null value.");
                }
	        }
	        catch (Throwable e) {
		        done = true;
		        actual.onError(Operators.onOperatorError(s, e, actual.currentContext()));
		        return;
	        }

            value = v;
            long p = produced;
            if (p != 0L) {
	            Operators.addCap(REQUESTED, this, -p);
            }
            DrainUtils.postComplete(actual, this, REQUESTED, this, this);
        }

        @Override
        public CoreSubscriber actual() {
            return actual;
        }

	    @Override
	    public void request(long n) {
	        if (Operators.validate(n)) {
	            if (!DrainUtils.postCompleteRequest(n, actual, this, REQUESTED, this, this)) {
	                s.request(n);
	            }
	        }
	    }

        @Override
        public boolean offer(R e) {
            throw new UnsupportedOperationException();
        }

        @Override
        @Nullable
        public R poll() {
            R v = value;
            if (v != null) {
                value = null;
                return v;
            }
            return null;
        }

	    @Override
	    @Nullable
	    public Object scanUnsafe(Attr key) {
		    if (key == Attr.PARENT) return s;
		    if (key == Attr.TERMINATED) return done;
		    if (key == Attr.CANCELLED) return getAsBoolean();
		    if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
		    if (key == Attr.BUFFERED) return size();
		    if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;

		    return InnerOperator.super.scanUnsafe(key);
	    }

	    @Override
	    @Nullable
        public R peek() {
            return value;
        }

        @Override
        public boolean getAsBoolean() {
            return cancelled;
        }

        @Override
        public void cancel() {
            cancelled = true;
            s.cancel();
        }

        @Override
        public Iterator iterator() {
            throw new UnsupportedOperationException();
        }

        @Override
        public int size() {
            return value == null ? 0 : 1;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy