/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.adapter;

import java.util.concurrent.Flow;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Scannable;
import reactor.core.publisher.Flux;

/**
 * Convert a Java 9+ {@literal Flow.Publisher} to/from a Reactive Streams {@link Publisher}.
 *
 * @author Stephane Maldini
 * @author Sebastien Deleuze
 */
@SuppressWarnings("Since15")
public abstract class JdkFlowAdapter {

	/**
	 * Return a java {@code Flow.Publisher} from a {@link Flux}
	 * @param publisher the source Publisher to convert
	 * @param <T> the type of the publisher
	 * @return a java {@code Flow.Publisher} from the given {@link Publisher}
	 */
	public static <T> Flow.Publisher<T> publisherToFlowPublisher(final Publisher<T>
			publisher) {
		return new PublisherAsFlowPublisher<>(publisher);
	}

	/**
	 * Return a {@link Flux} from a java {@code Flow.Publisher}
	 *
	 * @param publisher the source Publisher to convert
	 * @param <T> the type of the publisher
	 * @return a {@link Flux} from a java {@code Flow.Publisher}
	 */
	public static <T> Flux<T> flowPublisherToFlux(Flow.Publisher<T> publisher) {
		return new FlowPublisherAsFlux<>(publisher);
	}

	private static class FlowPublisherAsFlux<T> extends Flux<T> implements Scannable {
        private final java.util.concurrent.Flow.Publisher<T> pub;

        private FlowPublisherAsFlux(java.util.concurrent.Flow.Publisher<T> pub) {
            this.pub = pub;
        }

        @Override
        public void subscribe(final CoreSubscriber<? super T> actual) {
        	pub.subscribe(new SubscriberToRS<>(actual));
        }

		@Override
		public Object scanUnsafe(Attr key) {
			return null; //no particular key to be represented, still useful in hooks
		}
    }

    private static class PublisherAsFlowPublisher<T> implements Flow.Publisher<T> {
        private final Publisher<T> pub;

        private PublisherAsFlowPublisher(Publisher<T> pub) {
            this.pub = pub;
        }

        @Override
        public void subscribe(Flow.Subscriber<? super T> subscriber) {
        	pub.subscribe(new FlowSubscriber<>(subscriber));
        }
    }

    private static class FlowSubscriber<T> implements CoreSubscriber<T>, Flow.Subscription {

		private final Flow.Subscriber<? super T> subscriber;

		Subscription subscription;

		public FlowSubscriber(Flow.Subscriber<? super T> subscriber) {
			this.subscriber = subscriber;
		}

		@Override
		public void onSubscribe(final Subscription s) {
		    this.subscription = s;
			subscriber.onSubscribe(this);
		}

		@Override
		public void onNext(T o) {
			subscriber.onNext(o);
		}

		@Override
		public void onError(Throwable t) {
			subscriber.onError(t);
		}

		@Override
		public void onComplete() {
			subscriber.onComplete();
		}

		@Override
		public void request(long n) {
		    subscription.request(n);
		}

		@Override
		public void cancel() {
		    subscription.cancel();
		}
	}

	private static class SubscriberToRS<T> implements Flow.Subscriber<T>, Subscription {

		private final Subscriber<? super T> s;

		Flow.Subscription subscription;

		public SubscriberToRS(Subscriber<? super T> s) {
			this.s = s;
		}

		@Override
		public void onSubscribe(final Flow.Subscription subscription) {
		    this.subscription = subscription;
			s.onSubscribe(this);
		}

		@Override
		public void onNext(T o) {
			s.onNext(o);
		}

		@Override
		public void onError(Throwable throwable) {
			s.onError(throwable);
		}

		@Override
		public void onComplete() {
			s.onComplete();
		}

		@Override
		public void request(long n) {
		    subscription.request(n);
		}

		@Override
		public void cancel() {
		    subscription.cancel();
		}
	}

	JdkFlowAdapter(){}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import org.reactivestreams.Publisher;
import reactor.core.Exceptions;
import reactor.core.publisher.FluxOnAssembly.AssemblySnapshot;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

/**
 * A set of overridable lifecycle hooks that can be used for cross-cutting
 * added behavior on {@link Flux}/{@link Mono} operators.
 */
public abstract class Hooks {

	/**
	 * Add a {@link Publisher} operator interceptor for each operator created
	 * ({@link Flux} or {@link Mono}). The passed function is applied to the original
	 * operator {@link Publisher} and can return a different {@link Publisher},
	 * on the condition that it generically maintains the same data type as the original.
	 * Use of the {@link Flux}/{@link Mono} APIs is discouraged as it will recursively
	 * call this hook, leading to {@link StackOverflowError}.
	 *

	 * Note that sub-hooks are cumulative, but invoking this method twice with the same instance
	 * (or any instance that has the same `toString`) will result in only a single instance
	 * being applied. See {@link #onEachOperator(String, Function)} for a variant that
	 * allows you to name the sub-hooks (and thus replace them or remove them individually
	 * later on). Can be fully reset via {@link #resetOnEachOperator()}.
	 *

	 * This pointcut function cannot make use of {@link Flux}, {@link Mono} or
	 * {@link ParallelFlux} APIs as it would lead to a recursive call to the hook: the
	 * operator calls would effectively invoke onEachOperator from onEachOperator.
	 *
	 * @param onEachOperator the sub-hook: a function to intercept each operation call
	 * (e.g. {@code map (fn)} and {@code map(fn2)} in {@code flux.map(fn).map(fn2).subscribe()})
	 *
	 * @see #onEachOperator(String, Function)
	 * @see #resetOnEachOperator(String)
	 * @see #resetOnEachOperator()
	 * @see #onLastOperator(Function)
	 */
	public static void onEachOperator(Function<? super Publisher<Object>, ? extends Publisher<Object>> onEachOperator) {
		onEachOperator(onEachOperator.toString(), onEachOperator);
	}

	/**
	 * Add or replace a named {@link Publisher} operator interceptor for each operator created
	 * ({@link Flux} or {@link Mono}). The passed function is applied to the original
	 * operator {@link Publisher} and can return a different {@link Publisher},
	 * on the condition that it generically maintains the same data type as the original.
	 * Use of the {@link Flux}/{@link Mono} APIs is discouraged as it will recursively
	 * call this hook, leading to {@link StackOverflowError}.
	 *

	 * Note that sub-hooks are cumulative. Invoking this method twice with the same key will
	 * replace the old sub-hook with that name, but keep the execution order (eg. A-h1, B-h2,
	 * A-h3 will keep A-B execution order, leading to hooks h3 then h2 being executed).
	 * Removing a particular key using {@link #resetOnEachOperator(String)} then adding it
	 * back will result in the execution order changing (the later sub-hook being executed
	 * last). Can be fully reset via {@link #resetOnEachOperator()}.
	 *

	 * This pointcut function cannot make use of {@link Flux}, {@link Mono} or
	 * {@link ParallelFlux} APIs as it would lead to a recursive call to the hook: the
	 * operator calls would effectively invoke onEachOperator from onEachOperator.
	 *
	 * @param key the key for the sub-hook to add/replace
	 * @param onEachOperator the sub-hook: a function to intercept each operation call
	 * (e.g. {@code map (fn)} and {@code map(fn2)} in {@code flux.map(fn).map(fn2).subscribe()})
	 *
	 * @see #onEachOperator(Function)
	 * @see #resetOnEachOperator(String)
	 * @see #resetOnEachOperator()
	 * @see #onLastOperator(String, Function)
	 */
	public static void onEachOperator(String key, Function<? super Publisher<Object>, ? extends Publisher<Object>> onEachOperator) {
		Objects.requireNonNull(key, "key");
		Objects.requireNonNull(onEachOperator, "onEachOperator");
		log.debug("Hooking onEachOperator: {}", key);

		synchronized (log) {
			onEachOperatorHooks.put(key, onEachOperator);
			onEachOperatorHook = createOrUpdateOpHook(onEachOperatorHooks.values());
		}
	}

	/**
	 * Remove the sub-hook with key {@code key} from the onEachOperator hook. No-op if
	 * no such key has been registered, and equivalent to calling {@link #resetOnEachOperator()}
	 * if it was the last sub-hook.
	 *
	 * @param key the key of the sub-hook to remove
	 */
	public static void resetOnEachOperator(String key) {
		Objects.requireNonNull(key, "key");
		log.debug("Reset onEachOperator: {}", key);

		synchronized (log) {
			onEachOperatorHooks.remove(key);
			onEachOperatorHook = createOrUpdateOpHook(onEachOperatorHooks.values());
		}
	}

	/**
	 * Reset global "assembly" hook tracking
	 */
	public static void resetOnEachOperator() {
		log.debug("Reset to factory defaults : onEachOperator");
		synchronized (log) {
			onEachOperatorHooks.clear();
			onEachOperatorHook = null;
		}
	}

	/**
	 * Override global error dropped strategy which by default bubble back the error.
	 *

	 * The hook is cumulative, so calling this method several times will set up the hook
	 * for as many consumer invocations (even if called with the same consumer instance).
	 *
	 * @param c the {@link Consumer} to apply to dropped errors
	 */
	public static void onErrorDropped(Consumer<? super Throwable> c) {
		Objects.requireNonNull(c, "onErrorDroppedHook");
		log.debug("Hooking new default : onErrorDropped");

		synchronized(log) {
			if (onErrorDroppedHook != null) {
				@SuppressWarnings("unchecked") Consumer<Throwable> _c =
						((Consumer<Throwable>)onErrorDroppedHook).andThen(c);
				onErrorDroppedHook = _c;
			}
			else {
				onErrorDroppedHook = c;
			}
		}
	}

	/**
	 * Add a {@link Publisher} operator interceptor for the last operator created
	 * in every flow ({@link Flux} or {@link Mono}). The passed function is applied
	 * to the original operator {@link Publisher} and can return a different {@link Publisher},
	 * on the condition that it generically maintains the same data type as the original.
	 *

	 * Note that sub-hooks are cumulative, but invoking this method twice with the same
	 * instance (or any instance that has the same `toString`) will result in only a single
	 * instance being applied. See {@link #onLastOperator(String, Function)} for a variant
	 * that allows you to name the sub-hooks (and thus replace them or remove them individually
	 * later on). Can be fully reset via {@link #resetOnLastOperator()}.
	 *

	 * This pointcut function cannot make use of {@link Flux}, {@link Mono} or
	 * {@link ParallelFlux} APIs as it would lead to a recursive call to the hook: the
	 * operator calls would effectively invoke onEachOperator from onEachOperator.
	 *
	 * @param onLastOperator the sub-hook: a function to intercept last operation call
	 * (e.g. {@code map(fn2)} in {@code flux.map(fn).map(fn2).subscribe()})
	 *
	 * @see #onLastOperator(String, Function)
	 * @see #resetOnLastOperator(String)
	 * @see #resetOnLastOperator()
	 * @see #onEachOperator(Function)
	 */
	public static void onLastOperator(Function<? super Publisher<Object>, ? extends Publisher<Object>> onLastOperator) {
		onLastOperator(onLastOperator.toString(), onLastOperator);
	}

	/**
	 * Add or replace a named {@link Publisher} operator interceptor for the last operator created
	 * in every flow ({@link Flux} or {@link Mono}). The passed function is applied
	 * to the original operator {@link Publisher} and can return a different {@link Publisher},
	 * on the condition that it generically maintains the same data type as the original.
	 * Use of the {@link Flux}/{@link Mono} APIs is discouraged as it will recursively
	 * call this hook, leading to {@link StackOverflowError}.
	 *

	 * Note that sub-hooks are cumulative. Invoking this method twice with the same key will
	 * replace the old sub-hook with that name, but keep the execution order (eg. A-h1, B-h2,
	 * A-h3 will keep A-B execution order, leading to hooks h3 then h2 being executed).
	 * Removing a particular key using {@link #resetOnLastOperator(String)} then adding it
	 * back will result in the execution order changing (the later sub-hook being executed
	 * last). Can be fully reset via {@link #resetOnLastOperator()}.
	 *

	 * This pointcut function cannot make use of {@link Flux}, {@link Mono} or
	 * {@link ParallelFlux} APIs as it would lead to a recursive call to the hook: the
	 * operator calls would effectively invoke onEachOperator from onEachOperator.
	 *
	 * @param key the key for the sub-hook to add/replace
	 * @param onLastOperator the sub-hook: a function to intercept last operation call
	 * (e.g. {@code map(fn2)} in {@code flux.map(fn).map(fn2).subscribe()})
	 *
	 * @see #onLastOperator(Function)
	 * @see #resetOnLastOperator(String)
	 * @see #resetOnLastOperator()
	 * @see #onEachOperator(String, Function)
	 */
	public static void onLastOperator(String key, Function<? super Publisher<Object>, ? extends Publisher<Object>> onLastOperator) {
		Objects.requireNonNull(key, "key");
		Objects.requireNonNull(onLastOperator, "onLastOperator");
		log.debug("Hooking onLastOperator: {}", key);

		synchronized (log) {
			onLastOperatorHooks.put(key, onLastOperator);
			onLastOperatorHook = createOrUpdateOpHook(onLastOperatorHooks.values());
		}
	}

	/**
	 * Remove the sub-hook with key {@code key} from the onLastOperator hook. No-op if
	 * no such key has been registered, and equivalent to calling {@link #resetOnLastOperator()}
	 * if it was the last sub-hook.
	 *
	 * @param key the key of the sub-hook to remove
	 */
	public static void resetOnLastOperator(String key) {
		Objects.requireNonNull(key, "key");
		log.debug("Reset onLastOperator: {}", key);

		synchronized (log) {
			onLastOperatorHooks.remove(key);
			onLastOperatorHook = createOrUpdateOpHook(onLastOperatorHooks.values());
		}
	}

	/**
	 * Reset global "subscriber" hook tracking
	 */
	public static void resetOnLastOperator() {
		log.debug("Reset to factory defaults : onLastOperator");
		synchronized (log) {
			onLastOperatorHooks.clear();
			onLastOperatorHook = null;
		}
	}

	/**
	 * Override global data dropped strategy which by default logs at DEBUG level.
	 *

	 * The hook is cumulative, so calling this method several times will set up the hook
	 * for as many consumer invocations (even if called with the same consumer instance).
	 *
	 * @param c the {@link Consumer} to apply to data (onNext) that is dropped
	 * @see #onNextDroppedFail()
	 */
	public static void onNextDropped(Consumer<Object> c) {
		Objects.requireNonNull(c, "onNextDroppedHook");
		log.debug("Hooking new default : onNextDropped");

		synchronized(log) {
			if (onNextDroppedHook != null) {
				onNextDroppedHook = onNextDroppedHook.andThen(c);
			}
			else {
				onNextDroppedHook = c;
			}
		}
	}

	/**
	 * Resets {@link #resetOnNextDropped() onNextDropped hook(s)} and
	 * apply a strategy of throwing {@link Exceptions#failWithCancel()} instead.
	 *

	 * Use {@link #resetOnNextDropped()} to reset to the default strategy of logging.
	 */
	public static void onNextDroppedFail() {
		log.debug("Enabling failure mode for onNextDropped");

		synchronized(log) {
			onNextDroppedHook = n -> {throw Exceptions.failWithCancel();};
		}
	}

	/**
	 * Enable operator stack recorder that captures a declaration stack whenever an
	 * operator is instantiated. When errors are observed later on, they will be
	 * enriched with a Suppressed Exception detailing the original assembly line stack.
	 * Must be called before producers (e.g. Flux.map, Mono.fromCallable) are actually
	 * called to intercept the right stack information.
	 *

	 * This is added as a specifically-keyed sub-hook in {@link #onEachOperator(String, Function)}.
	 */
	public static void onOperatorDebug() {
		log.debug("Enabling stacktrace debugging via onOperatorDebug");
		GLOBAL_TRACE = true;
	}

	/**
	 * Reset global operator debug.
	 */
	public static void resetOnOperatorDebug() {
		GLOBAL_TRACE = false;
	}

	/**
	 * Set the custom global error mode hook for operators that support resuming
	 * during an error in their {@link org.reactivestreams.Subscriber#onNext(Object)}.
	 *

	 * The hook is a {@link BiFunction} of {@link Throwable} and potentially null {@link Object}.
	 * If it is also a {@link java.util.function.BiPredicate}, its
	 * {@link java.util.function.BiPredicate#test(Object, Object) test} method should be
	 * used to determine if an error should be processed (matching predicate) or completely
	 * skipped (non-matching predicate). Typical usage, as in {@link Operators}, is to
	 * check if the predicate matches and fallback to {@link Operators#onOperatorError(Throwable, Context)}
	 * if it doesn't.
	 *
	 * @param onNextError the new {@link BiFunction} to use.
	 */
	public static void onNextError(BiFunction<? super Throwable, Object, ? extends Throwable> onNextError) {
		Objects.requireNonNull(onNextError, "onNextError");
		log.debug("Hooking new default : onNextError");

		if (onNextError instanceof OnNextFailureStrategy) {
			synchronized(log) {
				onNextErrorHook = (OnNextFailureStrategy) onNextError;
			}
		}
		else {
			synchronized(log) {
				onNextErrorHook = new OnNextFailureStrategy.LambdaOnNextErrorStrategy(onNextError);
			}
		}
	}

	/**
	 * Add a custom error mapping, overriding the default one. Custom mapping can be an
	 * accumulation of several sub-hooks each subsequently added via this method.
	 *

	 * Note that sub-hooks are cumulative, but invoking this method twice with the same
	 * instance (or any instance that has the same `toString`) will result in only a single
	 * instance being applied. See {@link #onOperatorError(String, BiFunction)} for a variant
	 * that allows you to name the sub-hooks (and thus replace them or remove them individually
	 * later on). Can be fully reset via {@link #resetOnOperatorError()}.
	 *

	 * For reference, the default mapping is to unwrap the exception and, if the second
	 * parameter is another exception, to add it to the first as suppressed.
	 *
	 * @param onOperatorError an operator error {@link BiFunction} mapper, returning an arbitrary exception
	 * given the failure and optionally some original context (data or error).
	 *
	 * @see #onOperatorError(String, BiFunction)
	 * @see #resetOnOperatorError(String)
	 * @see #resetOnOperatorError()
	 */
	public static void onOperatorError(BiFunction<? super Throwable, Object, ? extends Throwable> onOperatorError) {
		onOperatorError(onOperatorError.toString(), onOperatorError);
	}

	/**
	 * Add or replace a named custom error mapping, overriding the default one. Custom
	 * mapping can be an accumulation of several sub-hooks each subsequently added via this
	 * method.
	 *

	 * Note that invoking this method twice with the same key will replace the old sub-hook
	 * with that name, but keep the execution order (eg. A-h1, B-h2, A-h3 will keep A-B
	 * execution order, leading to hooks h3 then h2 being executed). Removing a particular
	 * key using {@link #resetOnOperatorError(String)} then adding it back will result in
	 * the execution order changing (the later sub-hook being executed last).
	 * Can be fully reset via {@link #resetOnOperatorError()}.
	 *

	 * For reference, the default mapping is to unwrap the exception and, if the second
	 * parameter is another exception, to add it to the first as a suppressed.
	 *
	 * @param key the key for the sub-hook to add/replace
	 * @param onOperatorError an operator error {@link BiFunction} mapper, returning an arbitrary exception
	 * given the failure and optionally some original context (data or error).
	 *
	 * @see #onOperatorError(String, BiFunction)
	 * @see #resetOnOperatorError(String)
	 * @see #resetOnOperatorError()
	 */
	public static void onOperatorError(String key, BiFunction<? super Throwable, Object, ? extends Throwable> onOperatorError) {
		Objects.requireNonNull(key, "key");
		Objects.requireNonNull(onOperatorError, "onOperatorError");
		log.debug("Hooking onOperatorError: {}", key);
		synchronized (log) {
			onOperatorErrorHooks.put(key, onOperatorError);
			onOperatorErrorHook = createOrUpdateOpErrorHook(onOperatorErrorHooks.values());
		}
	}

	/**
	 * Remove the sub-hook with key {@code key} from the onOperatorError hook. No-op if
	 * no such key has been registered, and equivalent to calling {@link #resetOnOperatorError()}
	 * if it was the last sub-hook.
	 *
	 * @param key the key of the sub-hook to remove
	 */
	public static void resetOnOperatorError(String key) {
		Objects.requireNonNull(key, "key");
		log.debug("Reset onOperatorError: {}", key);
		synchronized (log) {
			onOperatorErrorHooks.remove(key);
			onOperatorErrorHook = createOrUpdateOpErrorHook(onOperatorErrorHooks.values());
		}
	}

	/**
	 * Reset global operator error mapping to the default behavior.
	 *

	 * For reference, the default mapping is to unwrap the exception and, if the second
	 * parameter is another exception, to add it to the first as a suppressed.
	 */
	public static void resetOnOperatorError() {
		log.debug("Reset to factory defaults : onOperatorError");
		synchronized (log) {
			onOperatorErrorHooks.clear();
			onOperatorErrorHook = null;
		}
	}

	/**
	 * Reset global error dropped strategy to bubbling back the error.
	 */
	public static void resetOnErrorDropped() {
		log.debug("Reset to factory defaults : onErrorDropped");
		synchronized (log) {
			onErrorDroppedHook = null;
		}
	}

	/**
	 * Reset global data dropped strategy to throwing via {@link
	 * reactor.core.Exceptions#failWithCancel()}
	 */
	public static void resetOnNextDropped() {
		log.debug("Reset to factory defaults : onNextDropped");
		synchronized (log) {
			onNextDroppedHook = null;
		}
	}

	/**
	 * Reset global onNext error handling strategy to terminating the sequence with
	 * an onError and cancelling upstream ({@link OnNextFailureStrategy#STOP}).
	 */
	public static void resetOnNextError() {
		log.debug("Reset to factory defaults : onNextError");
		synchronized (log) {
			onNextErrorHook = null;
		}
	}

	@Nullable
	@SuppressWarnings("unchecked")
	static Function<Publisher, Publisher> createOrUpdateOpHook(Collection<Function<? super Publisher<Object>, ? extends Publisher<Object>>> hooks) {
		Function<Publisher, Publisher> composite = null;
		for (Function<? super Publisher<Object>, ? extends Publisher<Object>> function : hooks) {
			Function<? super Publisher, ? extends Publisher> op = (Function<? super Publisher, ? extends Publisher>) function;
			if (composite != null) {
				composite = composite.andThen(op);
			}
			else {
				composite = (Function<Publisher, Publisher>) op;
			}
		}
		return composite;
	}

	@Nullable
	static BiFunction<? super Throwable, Object, ? extends Throwable> createOrUpdateOpErrorHook(Collection<BiFunction<? super Throwable, Object, ? extends Throwable>> hooks) {
		BiFunction<? super Throwable, Object, ? extends Throwable> composite = null;
		for (BiFunction<? super Throwable, Object, ? extends Throwable> function : hooks) {
			if (composite != null) {
				BiFunction<? super Throwable, Object, ? extends Throwable> ff = composite;
				composite = (e, data) -> function.apply(ff.apply(e, data), data);
			}
			else {
				composite = function;
			}
		}
		return composite;
	}

	//Hooks that are transformative
	static volatile Function<Publisher, Publisher>                             onEachOperatorHook;
	static volatile Function<Publisher, Publisher>                             onLastOperatorHook;
	static volatile BiFunction<? super Throwable, Object, ? extends Throwable> onOperatorErrorHook;

	//Hooks that are just callbacks
	static volatile Consumer<? super Throwable> onErrorDroppedHook;
	static volatile Consumer<Object>            onNextDroppedHook;

	//Special hook that is between the two (strategy can be transformative, but not named)
	static volatile OnNextFailureStrategy onNextErrorHook;

	//For transformative hooks, allow to name them, keep track in an internal Map that retains insertion order
	//internal use only as it relies on external synchronization
	private static final LinkedHashMap<String, Function<? super Publisher<Object>, ? extends Publisher<Object>>> onEachOperatorHooks;
	private static final LinkedHashMap<String, Function<? super Publisher<Object>, ? extends Publisher<Object>>> onLastOperatorHooks;
	private static final LinkedHashMap<String, BiFunction<? super Throwable, Object, ? extends Throwable>> onOperatorErrorHooks;

	//Immutable views on hook trackers, for testing purpose
	static final Map<String, Function<? super Publisher<Object>, ? extends Publisher<Object>>> getOnEachOperatorHooks() {
		return Collections.unmodifiableMap(onEachOperatorHooks);
	}
	static final Map<String, Function<? super Publisher<Object>, ? extends Publisher<Object>>> getOnLastOperatorHooks() {
		return Collections.unmodifiableMap(onLastOperatorHooks);
	}
	static final Map<String, BiFunction<? super Throwable, Object, ? extends Throwable>> getOnOperatorErrorHooks() {
		return Collections.unmodifiableMap(onOperatorErrorHooks);
	}

	static final Logger log = Loggers.getLogger(Hooks.class);

	/**
	 * A key that can be used to store a sequence-specific {@link Hooks#onErrorDropped(Consumer)}
	 * hook in a {@link Context}, as a {@link Consumer Consumer<Throwable>}.
	 */
	static final String KEY_ON_ERROR_DROPPED = "reactor.onErrorDropped.local";
	/**
	 * A key that can be used to store a sequence-specific {@link Hooks#onNextDropped(Consumer)}
	 * hook in a {@link Context}, as a {@link Consumer Consumer<Object>}.
	 */
	static final String KEY_ON_NEXT_DROPPED = "reactor.onNextDropped.local";
	/**
	 * A key that can be used to store a sequence-specific {@link Hooks#onOperatorError(BiFunction)}
	 * hook in a {@link Context}, as a {@link BiFunction BiFunction<Throwable, Object, Throwable>}.
	 */
	static final String KEY_ON_OPERATOR_ERROR = "reactor.onOperatorError.local";

	/**
	 * A key that can be used to store a sequence-specific onDiscard(Consumer)
	 * hook in a {@link Context}, as a {@link Consumer Consumer<Object>}.
	 */
	static final String KEY_ON_DISCARD = "reactor.onDiscard.local";

	/**
	 * A key that can be used to store a sequence-specific {@link Hooks#onOperatorError(BiFunction)}
	 * hook THAT IS ONLY APPLIED TO Operators{@link Operators#onRejectedExecution(Throwable, Context) onRejectedExecution}
	 * in a {@link Context}, as a {@link BiFunction BiFunction<Throwable, Object, Throwable>}.
	 */
	static final String KEY_ON_REJECTED_EXECUTION = "reactor.onRejectedExecution.local";

	static boolean GLOBAL_TRACE =
			Boolean.parseBoolean(System.getProperty("reactor.trace.operatorStacktrace",
					"false"));

	static {
		onEachOperatorHooks = new LinkedHashMap<>(1);
		onLastOperatorHooks = new LinkedHashMap<>(1);
		onOperatorErrorHooks = new LinkedHashMap<>(1);
	}

	Hooks() {
	}

	static <T, P extends Publisher<T>> Publisher<T> addAssemblyInfo(P publisher, AssemblySnapshot stacktrace) {
		if (publisher instanceof Callable) {
			if (publisher instanceof Mono) {
				return new MonoCallableOnAssembly<>((Mono<T>) publisher, stacktrace);
			}
			return new FluxCallableOnAssembly<>((Flux<T>) publisher, stacktrace);
		}
		if (publisher instanceof Mono) {
			return new MonoOnAssembly<>((Mono<T>) publisher, stacktrace);
		}
		if (publisher instanceof ParallelFlux) {
			return new ParallelFluxOnAssembly<>((ParallelFlux<T>) publisher, stacktrace);
		}
		if (publisher instanceof ConnectableFlux) {
			return new ConnectableFluxOnAssembly<>((ConnectableFlux<T>) publisher, stacktrace);
		}
		return new FluxOnAssembly<>((Flux<T>) publisher, stacktrace);
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.Arrays;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Exceptions;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

import static reactor.core.Fuseable.ASYNC;
import static reactor.core.Fuseable.SYNC;

/**
 * Repeatedly takes one item from all source Publishers and
 * runs it through a function to produce the output item.
 *
 * @param <T> the common input type
 * @param <R> the output value type
 *
 * @see Reactive-Streams-Commons
 */
final class FluxZip<T, R> extends Flux<R> implements SourceProducer<R> {

	final Publisher<? extends T>[] sources;

	final Iterable<? extends Publisher<? extends T>> sourcesIterable;

	final Function<? super Object[], ? extends R> zipper;

	final Supplier<? extends Queue<T>> queueSupplier;

	final int prefetch;

	@SuppressWarnings("unchecked")
	 FluxZip(Publisher<? extends T> p1,
			Publisher<? extends U> p2,
			BiFunction<? super T, ? super U, ? extends R> zipper2,
			Supplier<? extends Queue<T>> queueSupplier,
			int prefetch) {
		this(new Publisher[]{Objects.requireNonNull(p1, "p1"),
						Objects.requireNonNull(p2, "p2")},
				new PairwiseZipper<>(new BiFunction[]{
						Objects.requireNonNull(zipper2, "zipper2")}),
				queueSupplier,
				prefetch);
	}

	FluxZip(Publisher<? extends T>[] sources,
			Function<? super Object[], ? extends R> zipper,
			Supplier<? extends Queue<T>> queueSupplier,
			int prefetch) {
		if (prefetch <= 0) {
			throw new IllegalArgumentException("prefetch > 0 required but it was " + prefetch);
		}
		this.sources = Objects.requireNonNull(sources, "sources");
		if (sources.length == 0) {
			throw new IllegalArgumentException("at least one source is required");
		}
		this.sourcesIterable = null;
		this.zipper = Objects.requireNonNull(zipper, "zipper");
		this.queueSupplier = Objects.requireNonNull(queueSupplier, "queueSupplier");
		this.prefetch = prefetch;
	}

	FluxZip(Iterable<? extends Publisher<? extends T>> sourcesIterable,
			Function<? super Object[], ? extends R> zipper,
			Supplier<? extends Queue<T>> queueSupplier,
			int prefetch) {
		if (prefetch <= 0) {
			throw new IllegalArgumentException("prefetch > 0 required but it was " + prefetch);
		}
		this.sources = null;
		this.sourcesIterable = Objects.requireNonNull(sourcesIterable, "sourcesIterable");
		this.zipper = Objects.requireNonNull(zipper, "zipper");
		this.queueSupplier = Objects.requireNonNull(queueSupplier, "queueSupplier");
		this.prefetch = prefetch;
	}

	@Override
	public int getPrefetch() {
		return prefetch;
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	@Nullable
	FluxZip<T, R> zipAdditionalSource(Publisher source, BiFunction zipper) {
		Publisher[] oldSources = sources;
		if (oldSources != null && this.zipper instanceof PairwiseZipper) {
			int oldLen = oldSources.length;
			Publisher<? extends T>[] newSources = new Publisher[oldLen + 1];
			System.arraycopy(oldSources, 0, newSources, 0, oldLen);
			newSources[oldLen] = source;

			Function<Object[], R> z = ((PairwiseZipper<R>) this.zipper).then(zipper);

			return new FluxZip<>(newSources, z, queueSupplier, prefetch);
		}
		return null;
	}

	@Override
	public void subscribe(CoreSubscriber<? super R> actual) {
		Publisher<? extends T>[] srcs = sources;
		if (srcs != null) {
			handleArrayMode(actual, srcs);
		}
		else {
			handleIterableMode(actual, sourcesIterable);
		}
	}

	@SuppressWarnings("unchecked")
	void handleIterableMode(CoreSubscriber<? super R> s,
			Iterable<? extends Publisher<? extends T>> sourcesIterable) {
		Object[] scalars = new Object[8];
		Publisher<? extends T>[] srcs = new Publisher[8];

		int n = 0;
		int sc = 0;

		for (Publisher<? extends T> p : sourcesIterable) {
			if (p == null) {
				Operators.error(s,
						Operators.onOperatorError(new NullPointerException(
								"The sourcesIterable returned a null Publisher"),
								s.currentContext()));
				return;
			}

			if (p instanceof Callable) {
				Callable<T> callable = (Callable<T>) p;

				T v;

				try {
					v = callable.call();
				}
				catch (Throwable e) {
					Operators.error(s, Operators.onOperatorError(e,
							s.currentContext()));
					return;
				}

				if (v == null) {
					Operators.complete(s);
					return;
				}

				if (n == scalars.length) {
					Object[] b = new Object[n + (n >> 1)];
					System.arraycopy(scalars, 0, b, 0, n);

					Publisher<T>[] c = new Publisher[b.length];
					//noinspection SuspiciousSystemArraycopy
					System.arraycopy(srcs, 0, c, 0, n);

					scalars = b;
					srcs = c;
				}

				scalars[n] = v;
				sc++;
			}
			else {
				if (n == srcs.length) {
					Object[] b = new Object[n + (n >> 1)];
					System.arraycopy(scalars, 0, b, 0, n);

					Publisher<T>[] c = new Publisher[b.length];
					//noinspection SuspiciousSystemArraycopy
					System.arraycopy(srcs, 0, c, 0, n);

					scalars = b;
					srcs = c;
				}
				srcs[n] = p;
			}
			n++;
		}

		if (n == 0) {
			Operators.complete(s);
			return;
		}

		if (n < scalars.length) {
			scalars = Arrays.copyOfRange(scalars, 0, n, scalars.getClass());
		}

		handleBoth(s, srcs, scalars, n, sc);
	}

	@SuppressWarnings("unchecked")
	void handleArrayMode(CoreSubscriber<? super R> s, Publisher<? extends T>[] srcs) {

		Object[] scalars = null; //optimisation: if no scalar source, no array creation
		int n = srcs.length;

		int sc = 0;

		for (int j = 0; j < n; j++) {
			Publisher<? extends T> p = srcs[j];

			if (p == null) {
				Operators.error(s,
						new NullPointerException("The sources contained a null Publisher"));
				return;
			}

			if (p instanceof Callable) {
				Object v;

				try {
					v = ((Callable<? extends T>) p).call();
				}
				catch (Throwable e) {
					Operators.error(s, Operators.onOperatorError(e,
							s.currentContext()));
					return;
				}

				if (v == null) {
					Operators.complete(s);
					return;
				}

				if (scalars == null) {
					scalars = new Object[n];
				}

				scalars[j] = v;
				sc++;
			}
		}

		handleBoth(s, srcs, scalars, n, sc);
	}

	/**
	 * Handle values either from the iterable mode or array mode, taking into account the
	 * possibility that some sources were already resolved (being {@link Callable}):
	 *
	 *  - if all sources have been resolved (sc == n), simply apply the mapper
	 *  - if some sources have been resolved (n > sc > 0), use a coordinator with the sparse
	 *  array, which will subscribe to unresolved sources only
	 *  - if no source is resolved, none was callable: use a coordinator without the sparse
	 *  array, resulting on an inner subscription for each source
	 *
	 * @param s the subscriber
	 * @param srcs the array of sources, some of which can be callable
	 * @param scalars a sparse array of values resolved for the callable sources, null if not resolved
	 * @param n the number of sources
	 * @param sc the number of already resolved sources in the scalars array
	 */
	void handleBoth(CoreSubscriber<? super R> s,
			Publisher<? extends T>[] srcs,
			@Nullable Object[] scalars,
			int n,
			int sc) {
		if (sc != 0 && scalars != null) {
			if (n != sc) {
				ZipSingleCoordinator<T, R> coordinator =
						new ZipSingleCoordinator<>(s, scalars, n, zipper);

				s.onSubscribe(coordinator);

				coordinator.subscribe(n, sc, srcs);
			}
			else {
				Operators.MonoSubscriber<R, R> sds = new Operators.MonoSubscriber<>(s);

				s.onSubscribe(sds);

				R r;

				try {
					r = Objects.requireNonNull(zipper.apply(scalars),
							"The zipper returned a null value");
				}
				catch (Throwable e) {
					s.onError(Operators.onOperatorError(e, s.currentContext()));
					return;
				}

				sds.complete(r);
			}

		}
		else {
			ZipCoordinator<T, R> coordinator =
					new ZipCoordinator<>(s, zipper, n, queueSupplier, prefetch);

			s.onSubscribe(coordinator);

			coordinator.subscribe(srcs, n);
		}
	}

	@Override
	public Object scanUnsafe(Attr key) {
		if (key == Attr.PREFETCH) return prefetch;
		return null;
	}

	static final class ZipSingleCoordinator<T, R> extends Operators.MonoSubscriber<R, R> {

		final Function<? super Object[], ? extends R> zipper;

		final Object[] scalars;

		final ZipSingleSubscriber<T>[] subscribers;

		volatile int wip;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater<ZipSingleCoordinator> WIP =
				AtomicIntegerFieldUpdater.newUpdater(ZipSingleCoordinator.class, "wip");

		@SuppressWarnings("unchecked")
		ZipSingleCoordinator(CoreSubscriber<? super R> subscriber,
				Object[] scalars,
				int n,
				Function<? super Object[], ? extends R> zipper) {
			super(subscriber);
			this.zipper = zipper;
			this.scalars = scalars;
			ZipSingleSubscriber<T>[] a = new ZipSingleSubscriber[n];
			for (int i = 0; i < n; i++) {
				if (scalars[i] == null) {
					a[i] = new ZipSingleSubscriber<>(this, i);
				}
			}
			this.subscribers = a;
		}

		void subscribe(int n, int sc, Publisher<? extends T>[] sources) {
			WIP.lazySet(this, n - sc);
			ZipSingleSubscriber<T>[] a = subscribers;
			for (int i = 0; i < n; i++) {
				if (wip <= 0 || isCancelled()) {
					break;
				}
				ZipSingleSubscriber<T> s = a[i];
				if (s != null) {
					sources[i].subscribe(s);
				}
			}
		}

		void next(T value, int index) {
			Object[] a = scalars;
			a[index] = value;
			if (WIP.decrementAndGet(this) == 0) {
				R r;

				try {
					r = Objects.requireNonNull(zipper.apply(a),
							"The zipper returned a null value");
				}
				catch (Throwable e) {
					actual.onError(Operators.onOperatorError(this, e, value,
							actual.currentContext()));
					return;
				}

				complete(r);
			}
		}

		void error(Throwable e, int index) {
			if (WIP.getAndSet(this, 0) > 0) {
				cancelAll();
				actual.onError(e);
			}
			else {
				Operators.onErrorDropped(e, actual.currentContext());
			}
		}

		void complete(int index) {
			if (WIP.getAndSet(this, 0) > 0) {
				cancelAll();
				actual.onComplete();
			}
		}

		@Override
		public void cancel() {
			super.cancel();
			cancelAll();
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.TERMINATED) return wip == 0;
			if (key == Attr.BUFFERED) return wip > 0 ? scalars.length : 0;

			return super.scanUnsafe(key);
		}

		@Override
		public Stream<? extends Scannable> inners() {
			return Stream.of(subscribers);
		}

		void cancelAll() {
			for (ZipSingleSubscriber<T> s : subscribers) {
				if (s != null) {
					s.dispose();
				}
			}
		}
	}

	static final class ZipSingleSubscriber<T>
			implements InnerConsumer<T>, Disposable {

		final ZipSingleCoordinator<T, ?> parent;

		final int index;

		volatile Subscription s;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<ZipSingleSubscriber, Subscription> S =
				AtomicReferenceFieldUpdater.newUpdater(ZipSingleSubscriber.class,
						Subscription.class,
						"s");

		boolean done;

		ZipSingleSubscriber(ZipSingleCoordinator<T, ?> parent, int index) {
			this.parent = parent;
			this.index = index;
		}

		@Override
		public Context currentContext() {
			return parent.currentContext();
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return s;
			if (key == Attr.TERMINATED) return done;
			if (key == Attr.ACTUAL) return parent;
			if (key == Attr.CANCELLED) return s == Operators.cancelledSubscription();
			if (key == Attr.BUFFERED) return parent.scalars[index] == null ? 0 : 1;

			return null;
		}

		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.setOnce(S, this, s)) {
				this.s = s;
				s.request(Long.MAX_VALUE);
			}
		}

		@Override
		public void onNext(T t) {
			if (done) {
				Operators.onNextDropped(t, parent.currentContext());
				return;
			}
			done = true;
			Operators.terminate(S, this);
			parent.next(t, index);
		}

		@Override
		public void onError(Throwable t) {
			if (done) {
				Operators.onErrorDropped(t, parent.currentContext());
				return;
			}
			done = true;
			parent.error(t, index);
		}

		@Override
		public void onComplete() {
			if (done) {
				return;
			}
			done = true;
			parent.complete(index);
		}

		@Override
		public void dispose() {
			Operators.terminate(S, this);
		}
	}

	static final class ZipCoordinator<T, R>
			implements InnerProducer<R> {

		final CoreSubscriber<? super R> actual;

		final ZipInner<T>[] subscribers;

		final Function<? super Object[], ? extends R> zipper;

		volatile int wip;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater<ZipCoordinator> WIP =
				AtomicIntegerFieldUpdater.newUpdater(ZipCoordinator.class, "wip");

		volatile long requested;
		@SuppressWarnings("rawtypes")
		static final AtomicLongFieldUpdater<ZipCoordinator> REQUESTED =
				AtomicLongFieldUpdater.newUpdater(ZipCoordinator.class, "requested");

		volatile Throwable error;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<ZipCoordinator, Throwable> ERROR =
				AtomicReferenceFieldUpdater.newUpdater(ZipCoordinator.class,
						Throwable.class,
						"error");

		volatile boolean cancelled;

		final Object[] current;

		ZipCoordinator(CoreSubscriber<? super R> actual,
				Function<? super Object[], ? extends R> zipper,
				int n,
				Supplier<? extends Queue<T>> queueSupplier,
				int prefetch) {
			this.actual = actual;
			this.zipper = zipper;
			@SuppressWarnings("unchecked") ZipInner<T>[] a = new ZipInner[n];
			for (int i = 0; i < n; i++) {
				a[i] = new ZipInner<>(this, prefetch, i, queueSupplier);
			}
			this.current = new Object[n];
			this.subscribers = a;
		}

		void subscribe(Publisher<? extends T>[] sources, int n) {
			ZipInner<T>[] a = subscribers;
			for (int i = 0; i < n; i++) {
				if (cancelled || error != null) {
					return;
				}
				sources[i].subscribe(a[i]);
			}
		}

		@Override
		public void request(long n) {
			if (Operators.validate(n)) {
				Operators.addCap(REQUESTED, this, n);
				drain();
			}
		}

		@Override
		public void cancel() {
			if (!cancelled) {
				cancelled = true;

				cancelAll();
			}
		}

		@Override
		public CoreSubscriber<? super R> actual() {
			return actual;
		}

		@Override
		public Stream<? extends Scannable> inners() {
			return Stream.of(subscribers);
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
			if (key == Attr.ERROR) return error;
			if (key == Attr.CANCELLED) return cancelled;

			return InnerProducer.super.scanUnsafe(key);
		}

		void error(Throwable e, int index) {
			if (Exceptions.addThrowable(ERROR, this, e)) {
				drain();
			}
			else {
				Operators.onErrorDropped(e, actual.currentContext());
			}
		}

		void cancelAll() {
			for (ZipInner<T> s : subscribers) {
				s.cancel();
			}
		}

		void drain() {

			if (WIP.getAndIncrement(this) != 0) {
				return;
			}

			final CoreSubscriber<? super R> a = actual;
			final ZipInner<T>[] qs = subscribers;
			final int n = qs.length;
			Object[] values = current;

			int missed = 1;

			for (; ; ) {

				long r = requested;
				long e = 0L;

				while (r != e) {

					if (cancelled) {
						return;
					}

					if (error != null) {
						cancelAll();

						Throwable ex = Exceptions.terminate(ERROR, this);

						a.onError(ex);

						return;
					}

					boolean empty = false;

					for (int j = 0; j < n; j++) {
						ZipInner<T> inner = qs[j];
						if (values[j] == null) {
							try {
								boolean d = inner.done;
								Queue<T> q = inner.queue;

								T v = q != null ? q.poll() : null;

								boolean sourceEmpty = v == null;
								if (d && sourceEmpty) {
									cancelAll();

									a.onComplete();
									return;
								}
								if (!sourceEmpty) {
									values[j] = v;
								}
								else {
									empty = true;
								}
							}
							catch (Throwable ex) {
								ex = Operators.onOperatorError(ex,
										actual.currentContext());

								cancelAll();

								Exceptions.addThrowable(ERROR, this, ex);
								//noinspection ConstantConditions
								ex = Exceptions.terminate(ERROR, this);

								a.onError(ex);

								return;
							}
						}
					}

					if (empty) {
						break;
					}

					R v;
					try {
						v = Objects.requireNonNull(zipper.apply(values.clone()),
								"The zipper returned a null value");
					}
					catch (Throwable ex) {

						ex = Operators.onOperatorError(null, ex, values.clone(),
								actual.currentContext());
						cancelAll();

						Exceptions.addThrowable(ERROR, this, ex);
						//noinspection ConstantConditions
						ex = Exceptions.terminate(ERROR, this);

						a.onError(ex);

						return;
					}

					a.onNext(v);

					e++;

					Arrays.fill(values, null);
				}

				if (r == e) {
					if (cancelled) {
						return;
					}

					if (error != null) {
						cancelAll();

						Throwable ex = Exceptions.terminate(ERROR, this);

						a.onError(ex);

						return;
					}

					for (int j = 0; j < n; j++) {
						ZipInner<T> inner = qs[j];
						if (values[j] == null) {
							try {
								boolean d = inner.done;
								Queue<T> q = inner.queue;
								T v = q != null ? q.poll() : null;

								boolean empty = v == null;
								if (d && empty) {
									cancelAll();

									a.onComplete();
									return;
								}
								if (!empty) {
									values[j] = v;
								}
							}
							catch (Throwable ex) {
								ex = Operators.onOperatorError(null, ex, values,
										actual.currentContext());

								cancelAll();

								Exceptions.addThrowable(ERROR, this, ex);
								//noinspection ConstantConditions
								ex = Exceptions.terminate(ERROR, this);

								a.onError(ex);

								return;
							}
						}
					}

				}

				if (e != 0L) {

					for (int j = 0; j < n; j++) {
						ZipInner<T> inner = qs[j];
						inner.request(e);
					}

					if (r != Long.MAX_VALUE) {
						REQUESTED.addAndGet(this, -e);
					}
				}

				missed = WIP.addAndGet(this, -missed);
				if (missed == 0) {
					break;
				}
			}
		}
	}

	static final class ZipInner<T>
			implements InnerConsumer<T> {

		final ZipCoordinator<T, ?> parent;

		final int prefetch;

		final int limit;

		final int index;

		final Supplier<? extends Queue<T>> queueSupplier;

		volatile Queue<T> queue;

		volatile Subscription s;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<ZipInner, Subscription> S =
				AtomicReferenceFieldUpdater.newUpdater(ZipInner.class,
						Subscription.class,
						"s");

		long produced;

		volatile boolean done;

		int sourceMode;

		ZipInner(ZipCoordinator<T, ?> parent,
				int prefetch,
				int index,
				Supplier<? extends Queue<T>> queueSupplier) {
			this.parent = parent;
			this.prefetch = prefetch;
			this.index = index;
			this.queueSupplier = queueSupplier;
			this.limit = Operators.unboundedOrLimit(prefetch);
		}

		@SuppressWarnings("unchecked")
		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.setOnce(S, this, s)) {
				if (s instanceof Fuseable.QueueSubscription) {
					Fuseable.QueueSubscription<T> f = (Fuseable.QueueSubscription<T>) s;

					int m = f.requestFusion(Fuseable.ANY | Fuseable.THREAD_BARRIER);

					if (m == SYNC) {
						sourceMode = SYNC;
						queue = f;
						done = true;
						parent.drain();
						return;
					}
					else if (m == ASYNC) {
						sourceMode = ASYNC;
						queue = f;
					}
					else {
						queue = queueSupplier.get();
					}
				}
				else {
					queue = queueSupplier.get();
				}
				s.request(Operators.unboundedOrPrefetch(prefetch));
			}
		}

		@Override
		public void onNext(T t) {
			if (sourceMode != ASYNC) {
				if (!queue.offer(t)) {
					onError(Operators.onOperatorError(s, Exceptions.failWithOverflow
							(Exceptions.BACKPRESSURE_ERROR_QUEUE_FULL), currentContext()));
					return;
				}
			}
			parent.drain();
		}

		@Override
		public Context currentContext() {
			return parent.actual.currentContext();
		}

		@Override
		public void onError(Throwable t) {
			if (done) {
				Operators.onErrorDropped(t, currentContext());
				return;
			}
			done = true;
			parent.error(t, index);
		}

		@Override
		public void onComplete() {
			done = true;
			parent.drain();
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return  s;
			if (key == Attr.ACTUAL) return parent;
			if (key == Attr.CANCELLED) return s == Operators.cancelledSubscription();
			if (key == Attr.BUFFERED) return queue != null ? queue.size() : 0;
			if (key == Attr.TERMINATED) return done && (queue == null || queue.isEmpty());
			if (key == Attr.PREFETCH) return prefetch;

			return null;
		}

		void cancel() {
			Operators.terminate(S, this);
		}

		void request(long n) {
			if (sourceMode != SYNC) {
				long p = produced + n;
				if (p >= limit) {
					produced = 0L;
					s.request(p);
				}
				else {
					produced = p;
				}
			}
		}
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	static final class PairwiseZipper<R> implements Function<Object[], R> {

		final BiFunction[] zippers;

		PairwiseZipper(BiFunction[] zippers) {
			this.zippers = zippers;
		}

		@Override
		public R apply(Object[] args) {
			Object o = zippers[0].apply(args[0], args[1]);
			for (int i = 1; i < zippers.length; i++) {
				o = zippers[i].apply(o, args[i + 1]);
			}
			return (R) o;
		}

		public PairwiseZipper then(BiFunction zipper) {
			BiFunction[] zippers = this.zippers;
			int n = zippers.length;
			BiFunction[] newZippers = new BiFunction[n + 1];
			System.arraycopy(zippers, 0, newZippers, 0, n);
			newZippers[n] = zipper;

			return new PairwiseZipper(newZippers);
		}
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.Objects;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Function;
import java.util.stream.Stream;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.core.Scannable;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

/**
 * Waits for a Mono source to terminate or produce a value, in which case the value is
 * mapped to a Publisher used as a delay: its termination will trigger the actual emission
 * of the value downstream. If the Mono source didn't produce a value, terminate with the
 * same signal (empty completion or error).
 *
 * @param <T> the value type
 *
 * @author Simon Baslé
 */
final class MonoDelayUntil<T> extends Mono<T> implements Scannable {

	final Mono<T> source;

	Function<? super T, ? extends Publisher<?>>[] otherGenerators;

	@SuppressWarnings("unchecked")
	MonoDelayUntil(Mono<T> monoSource,
			Function<? super T, ? extends Publisher<?>> triggerGenerator) {
		this.source = Objects.requireNonNull(monoSource, "monoSource");
		this.otherGenerators = new Function[] { Objects.requireNonNull(triggerGenerator, "triggerGenerator")};
	}

	MonoDelayUntil(Mono<T> monoSource,
			Function<? super T, ? extends Publisher<?>>[] triggerGenerators) {
		this.source = Objects.requireNonNull(monoSource, "monoSource");
		this.otherGenerators = triggerGenerators;
	}

	/**
	 * Add a trigger generator to wait for.
	 * @param delayError the delayError parameter for the trigger being added, ignored if already true
	 * @param triggerGenerator the new trigger to add to the copy of the operator
	 * @return a new {@link MonoDelayUntil} instance with same source but additional trigger generator
	 */
	@SuppressWarnings("unchecked")
	MonoDelayUntil<T> copyWithNewTriggerGenerator(boolean delayError,
			Function<? super T, ? extends Publisher<?>> triggerGenerator) {
		Objects.requireNonNull(triggerGenerator, "triggerGenerator");
		Function<? super T, ? extends Publisher<?>>[] oldTriggers = this.otherGenerators;
		Function<? super T, ? extends Publisher<?>>[] newTriggers = new Function[oldTriggers.length + 1];
		System.arraycopy(oldTriggers, 0, newTriggers, 0, oldTriggers.length);
		newTriggers[oldTriggers.length] = triggerGenerator;
		return new MonoDelayUntil<>(this.source, newTriggers);
	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		DelayUntilCoordinator<T> parent = new DelayUntilCoordinator<>(actual, otherGenerators);
		actual.onSubscribe(parent);
		source.subscribe(parent);
	}

	@Override
	public Object scanUnsafe(Attr key) {
		return null; //no particular key to be represented, still useful in hooks
	}

	static final class DelayUntilCoordinator<T>
			extends Operators.MonoSubscriber<T, T> {

		static final DelayUntilTrigger[] NO_TRIGGER = new DelayUntilTrigger[0];

		final int                                                n;
		final Function<? super T, ? extends Publisher<?>>[] otherGenerators;

		volatile int done;
		static final AtomicIntegerFieldUpdater<DelayUntilCoordinator> DONE =
				AtomicIntegerFieldUpdater.newUpdater(DelayUntilCoordinator.class, "done");

		volatile Subscription s;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<DelayUntilCoordinator, Subscription> S =
				AtomicReferenceFieldUpdater.newUpdater(DelayUntilCoordinator.class, Subscription.class, "s");

		DelayUntilTrigger[] triggerSubscribers;

		DelayUntilCoordinator(CoreSubscriber<? super T> subscriber,
				Function<? super T, ? extends Publisher<?>>[] otherGenerators) {
			super(subscriber);
			this.otherGenerators = otherGenerators;
			//don't consider the source as this is only used from when there is a value
			this.n = otherGenerators.length;
			triggerSubscribers = NO_TRIGGER;
		}

		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.setOnce(S, this, s)) {
				s.request(Long.MAX_VALUE);
			} else {
				s.cancel();
			}
		}

		@Override
		public void onNext(T t) {
			if (value == null) {
				setValue(t);
				subscribeNextTrigger(t, done);
			}
		}

		@Override
		public void onError(Throwable t) {
			actual.onError(t);
		}

		@Override
		public void onComplete() {
			if (value == null && state < HAS_REQUEST_HAS_VALUE) {
				actual.onComplete();
			}
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.TERMINATED) return done == n;

			return super.scanUnsafe(key);
		}

		@Override
		public Stream<? extends Scannable> inners() {
			return Stream.of(triggerSubscribers);
		}

		@SuppressWarnings("unchecked")
		void subscribeNextTrigger(T value, int triggerIndex) {
			if (triggerSubscribers == NO_TRIGGER) {
				triggerSubscribers = new DelayUntilTrigger[otherGenerators.length];
			}

			Function<? super T, ? extends Publisher<?>> generator = otherGenerators[triggerIndex];
			Publisher<?> p = generator.apply(value);
			DelayUntilTrigger triggerSubscriber = new DelayUntilTrigger<>(this);

			this.triggerSubscribers[triggerIndex] = triggerSubscriber;
			p.subscribe(triggerSubscriber);
		}

		void signal() {
			int nextIndex = DONE.incrementAndGet(this);
			if (nextIndex != n) {
				subscribeNextTrigger(this.value, nextIndex);
				return;
			}

			Throwable error = null;
			Throwable compositeError = null;

			//check for errors in the triggers
			for (int i = 0; i < n; i++) {
				DelayUntilTrigger mt = triggerSubscribers[i];
				Throwable e = mt.error;
				if (e != null) {
					if (compositeError != null) {
						//this is ok as the composite created by multiple is never a singleton
						compositeError.addSuppressed(e);
					} else
					if (error != null) {
						compositeError = Exceptions.multiple(error, e);
					} else {
						error = e;
					}
					//else the trigger publisher was empty, but we'll ignore that
				}
			}

			if (compositeError != null) {
				actual.onError(compositeError);
			}
			else if (error != null) {
				actual.onError(error);
			} else {
				//emit the value downstream
				complete(this.value);
			}
		}

		@Override
		public void cancel() {
			if (!isCancelled()) {
				super.cancel();
				//source is always cancellable...
				Operators.terminate(S, this);
				//...but triggerSubscribers could be partially initialized
				for (int i = 0; i < triggerSubscribers.length; i++) {
					DelayUntilTrigger ts = triggerSubscribers[i];
					if (ts != null) ts.cancel();
				}
			}
		}
	}

	static final class DelayUntilTrigger<T> implements InnerConsumer<T> {

		final DelayUntilCoordinator<?> parent;

		volatile Subscription s;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<DelayUntilTrigger, Subscription> S =
				AtomicReferenceFieldUpdater.newUpdater(DelayUntilTrigger.class, Subscription.class, "s");

		boolean done;
		volatile Throwable error;

		DelayUntilTrigger(DelayUntilCoordinator<?> parent) {
			this.parent = parent;
		}

		@Override
		public Context currentContext() {
			return parent.currentContext();
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) return s == Operators.cancelledSubscription();
			if (key == Attr.PARENT) return s;
			if (key == Attr.ACTUAL) return parent;
			if (key == Attr.ERROR) return error;
			if (key == Attr.PREFETCH) return Integer.MAX_VALUE;

			return null;
		}

		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.setOnce(S, this, s)) {
				s.request(Long.MAX_VALUE);
			} else {
				s.cancel();
			}
		}

		@Override
		public void onNext(Object t) {
			//NO-OP
		}

		@Override
		public void onError(Throwable t) {
			error = t;
			if (DelayUntilCoordinator.DONE.getAndSet(parent, parent.n) != parent.n) {
				parent.cancel();
				parent.actual.onError(t);
			}
		}

		@Override
		public void onComplete() {
			if (!done) {
				done = true;
				parent.signal();
			}
		}

		void cancel() {
			Operators.terminate(S, this);
		}
	}
}


/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, 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.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

import org.reactivestreams.Subscriber;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Exceptions;
import reactor.util.annotation.Nullable;

/**
 * Abstract base class for subscribers that hold another subscriber, a queue
 * and requires queue-drain behavior.
 *
 * @param <T> the source type to which this subscriber will be subscribed
 * @param <U> the value type in the queue
 * @param <V> the value type the child subscriber accepts
 *
 * @author Simon Baslé
 * @author David Karnok
 */
abstract class QueueDrainSubscriber<T, U, V> extends QueueDrainSubscriberPad4
		implements InnerOperator<T, V> {

	final CoreSubscriber<? super V> actual;
	final Queue<U>              queue;

	volatile boolean cancelled;

	volatile boolean done;
	Throwable error;

	QueueDrainSubscriber(CoreSubscriber<? super V> actual, Queue<U> queue) {
		this.actual = actual;
		this.queue = queue;
	}

	@Override
	public CoreSubscriber<? super V> actual() {
		return actual;
	}

	public final boolean cancelled() {
		return cancelled;
	}

	public final boolean done() {
		return done;
	}

	public final boolean enter() {
		return wip.getAndIncrement() == 0;
	}

	public final boolean fastEnter() {
		return wip.get() == 0 && wip.compareAndSet(0, 1);
	}

	protected final void fastPathEmitMax(U value, boolean delayError, Disposable dispose) {
		final Subscriber<? super V> s = actual;
		final Queue<U> q = queue;

		if (wip.get() == 0 && wip.compareAndSet(0, 1)) {
			long r = requested;
			if (r != 0L) {
				if (accept(s, value)) {
					if (r != Long.MAX_VALUE) {
						produced(1);
					}
				}
				if (leave(-1) == 0) {
					return;
				}
			} else {
				dispose.dispose();
				s.onError(Exceptions.failWithOverflow("Could not emit buffer due to lack of requests"));
				return;
			}
		} else {
			q.offer(value);
			if (!enter()) {
				return;
			}
		}
		drainMaxLoop(q, s, delayError, dispose, this);
	}

	protected final void fastPathOrderedEmitMax(U value, boolean delayError, Disposable dispose) {
		final Subscriber<? super V> s = actual;
		final Queue<U> q = queue;

		if (wip.get() == 0 && wip.compareAndSet(0, 1)) {
			long r = requested;
			if (r != 0L) {
				if (q.isEmpty()) {
					if (accept(s, value)) {
						if (r != Long.MAX_VALUE) {
							produced(1);
						}
					}
					if (leave(-1) == 0) {
						return;
					}
				} else {
					q.offer(value);
				}
			} else {
				cancelled = true;
				dispose.dispose();
				s.onError(Exceptions.failWithOverflow("Could not emit buffer due to lack of requests"));
				return;
			}
		} else {
			q.offer(value);
			if (!enter()) {
				return;
			}
		}
		drainMaxLoop(q, s, delayError, dispose, this);
	}

	/**
	 * Accept the value and return true if forwarded.
	 * @param a the subscriber
	 * @param v the value
	 * @return true if the value was delivered
	 */
	public boolean accept(Subscriber<? super V> a, U v) {
		return false;
	}

	public final Throwable error() {
		return error;
	}

	/**
	 * Adds m to the wip counter.
	 * @param m the value to add
	 * @return the current value after adding m
	 */
	public final int leave(int m) {
		return wip.addAndGet(m);
	}

	public final long requested() {
		return requested;
	}

	public final long produced(long n) {
		return REQUESTED.addAndGet(this, -n);
	}

	public final void requested(long n) {
		if (Operators.validate(n)) {
			Operators.addCap(REQUESTED, this, n);
		}
	}

	@Override
	@Nullable
	public Object scanUnsafe(Attr key) {
		if (key == Attr.TERMINATED) return done;
		if (key == Attr.CANCELLED) return cancelled;
		if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
		if (key == Attr.ERROR) return error;

		return InnerOperator.super.scanUnsafe(key);
	}

	/**
	 * Drain the queue but give up with an error if there aren't enough requests.
	 * @param <Q> the queue value type
	 * @param <S> the emission value type
	 * @param q the queue
	 * @param a the subscriber
	 * @param delayError true if errors should be delayed after all normal items
	 * @param dispose the disposable to call when termination happens and cleanup is necessary
	 * @param qd the QueueDrain instance that gives status information to the drain logic
	 */
	static <Q, S> void drainMaxLoop(Queue<Q> q, Subscriber<? super S> a, boolean delayError,
			Disposable dispose, QueueDrainSubscriber<?, Q, S> qd) {
		int missed = 1;

		for (;;) {
			for (;;) {
				boolean d = qd.done();

				Q v = q.poll();

				boolean empty = v == null;

				if (checkTerminated(d, empty, a, delayError, q, qd)) {
					if (dispose != null) {
						dispose.dispose();
					}
					return;
				}

				if (empty) {
					break;
				}

				long r = qd.requested();
				if (r != 0L) {
					if (qd.accept(a, v)) {
						if (r != Long.MAX_VALUE) {
							qd.produced(1);
						}
					}
				} else {
					q.clear();
					if (dispose != null) {
						dispose.dispose();
					}
					a.onError(Exceptions.failWithOverflow("Could not emit value due to lack of requests."));
					return;
				}
			}

			missed = qd.leave(-missed);
			if (missed == 0) {
				break;
			}
		}
	}

	static <Q, S> boolean checkTerminated(boolean d, boolean empty,
			Subscriber<?> s, boolean delayError, Queue<?> q, QueueDrainSubscriber<?, Q, S> qd) {
		if (qd.cancelled()) {
			q.clear();
			return true;
		}

		if (d) {
			if (delayError) {
				if (empty) {
					Throwable err = qd.error();
					if (err != null) {
						s.onError(err);
					} else {
						s.onComplete();
					}
					return true;
				}
			} else {
				Throwable err = qd.error();
				if (err != null) {
					q.clear();
					s.onError(err);
					return true;
				} else
				if (empty) {
					s.onComplete();
					return true;
				}
			}
		}

		return false;
	}

}

// -------------------------------------------------------------------
// Padding superclasses
//-------------------------------------------------------------------

/** Pads the header away from other fields. */
class QueueDrainSubscriberPad0 {
	volatile long p1, p2, p3, p4, p5, p6, p7;
	volatile long p8, p9, p10, p11, p12, p13, p14, p15;
}

/** The WIP counter. */
class QueueDrainSubscriberWip extends QueueDrainSubscriberPad0 {
	final AtomicInteger wip = new AtomicInteger();
}

/** Pads away the wip from the other fields. */
class QueueDrainSubscriberPad2 extends QueueDrainSubscriberWip {
	volatile long p1a, p2a, p3a, p4a, p5a, p6a, p7a;
	volatile long p8a, p9a, p10a, p11a, p12a, p13a, p14a, p15a;
}

/** Contains the requested field. */
class QueueDrainSubscriberPad3 extends QueueDrainSubscriberPad2 {

	static final AtomicLongFieldUpdater<QueueDrainSubscriberPad3> REQUESTED =
			AtomicLongFieldUpdater.newUpdater(QueueDrainSubscriberPad3.class, "requested");

	volatile long requested;
}

/** Pads away the requested from the other fields. */
class QueueDrainSubscriberPad4 extends QueueDrainSubscriberPad3 {
	volatile long q1, q2, q3, q4, q5, q6, q7;
	volatile long q8, q9, q10, q11, q12, q13, q14, q15;
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.stream.Stream;

import reactor.util.annotation.Nullable;

/**
 * A support class that offers factory methods for implementations of the specialized
 * {@link Disposable} sub-interfaces ({@link Disposable.Composite Disposable.Composite},
 * {@link Disposable.Swap Disposable.Swap}).
 *
 * @author Simon Baslé
 * @author Stephane Maldini
 */
public final class Disposables {

	private Disposables() { }

	/**
	 * Create a new empty {@link Disposable.Composite} with atomic guarantees on all mutative
	 * operations.
	 *
	 * @return an empty atomic {@link Disposable.Composite}
	 */
	public static Disposable.Composite composite() {
		return new ListCompositeDisposable();
	}

	/**
	 * Create and initialize a new {@link Disposable.Composite} with atomic guarantees on
	 * all mutative operations.
	 *
	 * @return a pre-filled atomic {@link Disposable.Composite}
	 */
	public static Disposable.Composite composite(Disposable... disposables) {
		return new ListCompositeDisposable(disposables);
	}

	/**
	 * Create and initialize a new {@link Disposable.Composite} with atomic guarantees on
	 * all mutative operations.
	 *
	 * @return a pre-filled atomic {@link Disposable.Composite}
	 */
	public static Disposable.Composite composite(
			Iterable<? extends Disposable> disposables) {
		return new ListCompositeDisposable(disposables);
	}

	/**
	 * Return a new {@link Disposable} that is already disposed.
	 *
	 * @return a new disposed {@link Disposable}.
	 */
	public static Disposable disposed() {
		return new AlwaysDisposable();
	}

	/**
	 * Return a new {@link Disposable} that can never be disposed. Calling {@link Disposable#dispose()}
	 * is a NO-OP and {@link Disposable#isDisposed()} always return false.
	 *
	 * @return a new {@link Disposable} that can never be disposed.
	 */
	public static Disposable never() {
		return new NeverDisposable();
	}

	/**
	 * Return a new simple {@link Disposable} instance that is initially not disposed but
	 * can be by calling {@link Disposable#dispose()}.
	 *
	 * @return a new {@link Disposable} initially not yet disposed.
	 */
	public static Disposable single() {
		return new SimpleDisposable();
	}

	/**
	 * Create a new empty {@link Disposable.Swap} with atomic guarantees on all mutative
	 * operations.
	 *
	 * @return an empty atomic {@link Disposable.Swap}
	 */
	public static Disposable.Swap swap() {
		return new SwapDisposable();
	}

	//==== STATIC package private implementations ====

	/**
	 * @author David Karnok
	 * @author Simon Baslé
	 */
	static final class ListCompositeDisposable implements Disposable.Composite, Scannable {

		@Nullable
		List<Disposable> resources;

		volatile boolean disposed;

		ListCompositeDisposable() {
		}

		ListCompositeDisposable(Disposable... resources) {
			Objects.requireNonNull(resources, "resources is null");
			this.resources = new LinkedList<>();
			for (Disposable d : resources) {
				Objects.requireNonNull(d, "Disposable item is null");
				this.resources.add(d);
			}
		}

		ListCompositeDisposable(Iterable<? extends Disposable> resources) {
			Objects.requireNonNull(resources, "resources is null");
			this.resources = new LinkedList<>();
			for (Disposable d : resources) {
				Objects.requireNonNull(d, "Disposable item is null");
				this.resources.add(d);
			}
		}

		@Override
		public void dispose() {
			if (disposed) {
				return;
			}
			List<Disposable> set;
			synchronized (this) {
				if (disposed) {
					return;
				}
				disposed = true;
				set = resources;
				resources = null;
			}

			dispose(set);
		}

		@Override
		public boolean isDisposed() {
			return disposed;
		}

		@Override
		public boolean add(Disposable d) {
			Objects.requireNonNull(d, "d is null");
			if (!disposed) {
				synchronized (this) {
					if (!disposed) {
						List<Disposable> set = resources;
						if (set == null) {
							set = new LinkedList<>();
							resources = set;
						}
						set.add(d);
						return true;
					}
				}
			}
			d.dispose();
			return false;
		}

		@Override
		public boolean addAll(Collection<? extends Disposable> ds) {
			Objects.requireNonNull(ds, "ds is null");
			if (!disposed) {
				synchronized (this) {
					if (!disposed) {
						List<Disposable> set = resources;
						if (set == null) {
							set = new LinkedList<>();
							resources = set;
						}
						for (Disposable d : ds) {
							Objects.requireNonNull(d, "d is null");
							set.add(d);
						}
						return true;
					}
				}
			}
			for (Disposable d : ds) {
				d.dispose();
			}
			return false;
		}

		@Override
		public boolean remove(Disposable d) {
			Objects.requireNonNull(d, "Disposable item is null");
			if (disposed) {
				return false;
			}
			synchronized (this) {
				if (disposed) {
					return false;
				}

				List<Disposable> set = resources;
				if (set == null || !set.remove(d)) {
					return false;
				}
			}
			return true;
		}

		@Override
		public int size() {
			List<Disposable> r = resources;
			return r == null ? 0 : r.size();
		}

		public void clear() {
			if (disposed) {
				return;
			}
			List<Disposable> set;
			synchronized (this) {
				if (disposed) {
					return;
				}

				set = resources;
				resources = null;
			}

			dispose(set);
		}

		void dispose(@Nullable List<Disposable> set) {
			if (set == null) {
				return;
			}
			List<Throwable> errors = null;
			for (Disposable o : set) {
				try {
					o.dispose();
				} catch (Throwable ex) {
					Exceptions.throwIfFatal(ex);
					if (errors == null) {
						errors = new ArrayList<>();
					}
					errors.add(ex);
				}
			}
			if (errors != null) {
				if (errors.size() == 1) {
					throw Exceptions.propagate(errors.get(0));
				}
				throw Exceptions.multiple(errors);
			}
		}

		@Override
		public Stream<? extends Scannable> inners() {
			List<Disposable> r = resources;
			if (r == null) return Stream.empty();
			return r.stream()
			        .filter(Objects::nonNull)
			        .map(Scannable::from);
		}

		@Nullable
		@Override
		public Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) {
				return isDisposed();
			}
			return null;
		}
	}

/**
 * Not public API. Implementation of a {@link Swap}.
 *
 * @author Simon Baslé
 * @author David Karnok
 */
static final class SwapDisposable implements Disposable.Swap {

	volatile Disposable inner;
	static final AtomicReferenceFieldUpdater<SwapDisposable, Disposable>
	                    INNER =
			AtomicReferenceFieldUpdater.newUpdater(SwapDisposable.class, Disposable.class, "inner");

	@Override
	public boolean update(@Nullable Disposable next) {
		return Disposables.set(INNER, this, next);
	}

	@Override
	public boolean replace(@Nullable Disposable next) {
		return Disposables.replace(INNER, this, next);
	}

	@Override
	@Nullable
	public Disposable get() {
		return inner;
	}

	@Override
	public void dispose() {
		Disposables.dispose(INNER, this);
	}

	@Override
	public boolean isDisposed() {
		return Disposables.isDisposed(INNER.get(this));
	}
}

/**
 * A very simple {@link Disposable} that only wraps a mutable boolean for
 * {@link #isDisposed()}.
 */
static final class SimpleDisposable extends AtomicBoolean implements Disposable {

	@Override
	public void dispose() {
		set(true);
	}

	@Override
	public boolean isDisposed() {
		return get();
	}
}

/**
 * Immutable disposable that is always {@link #isDisposed() disposed}. Calling
 * {@link #dispose()} does nothing, and {@link #isDisposed()} always return true.
 */
static final class AlwaysDisposable implements Disposable {

	@Override
	public void dispose() {
		//NO-OP
	}

	@Override
	public boolean isDisposed() {
		return true;
	}
}

/**
 * Immutable disposable that is never {@link #isDisposed() disposed}. Calling
 * {@link #dispose()} does nothing, and {@link #isDisposed()} always return false.
 */
static final class NeverDisposable implements Disposable {

	@Override
	public void dispose() {
		//NO-OP
	}

	@Override
	public boolean isDisposed() {
		return false;
	}
}

	//==== STATIC ATOMIC UTILS copied from Disposables ====

	/**
	 * A singleton {@link Disposable} that represents a disposed instance. Should not be
	 * leaked to clients.
	 */
	//NOTE: There is a private similar DISPOSED singleton in Disposables as well
	static final Disposable DISPOSED = disposed();

	/**
	 * Atomically set the field to a {@link Disposable} and dispose the old content.
	 *
	 * @param updater the target field updater
	 * @param holder the target instance holding the field
	 * @param newValue the new Disposable to set
	 * @return true if successful, false if the field contains the {@link #DISPOSED} instance.
	 */
	static <T> boolean set(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, @Nullable Disposable newValue) {
		for (;;) {
			Disposable current = updater.get(holder);
			if (current == DISPOSED) {
				if (newValue != null) {
					newValue.dispose();
				}
				return false;
			}
			if (updater.compareAndSet(holder, current, newValue)) {
				if (current != null) {
					current.dispose();
				}
				return true;
			}
		}
	}

	/**
	 * Atomically replace the {@link Disposable} in the field with the given new Disposable
	 * but do not dispose the old one.
	 *
	 * @param updater the target field updater
	 * @param holder the target instance holding the field
	 * @param newValue the new Disposable to set, null allowed
	 * @return true if the operation succeeded, false if the target field contained
	 * the common {@link #DISPOSED} instance and the given disposable is not null but is disposed.
	 */
	static <T> boolean replace(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, @Nullable Disposable newValue) {
		for (;;) {
			Disposable current = updater.get(holder);
			if (current == DISPOSED) {
				if (newValue != null) {
					newValue.dispose();
				}
				return false;
			}
			if (updater.compareAndSet(holder, current, newValue)) {
				return true;
			}
		}
	}

	/**
	 * Atomically dispose the {@link Disposable} in the field if not already disposed.
	 *
	 * @param updater the target field updater
	 * @param holder the target instance holding the field
	 * @return true if the {@link Disposable} held by the field was properly disposed
	 */
	static <T> boolean dispose(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder) {
		Disposable current = updater.get(holder);
		Disposable d = DISPOSED;
		if (current != d) {
			current = updater.getAndSet(holder, d);
			if (current != d) {
				if (current != null) {
					current.dispose();
				}
				return true;
			}
		}
		return false;
	}

	/**
	 * Check if the given {@link Disposable} is the singleton {@link #DISPOSED}.
	 *
	 * @param d the disposable to check
	 * @return true if d is {@link #DISPOSED}
	 */
	static boolean isDisposed(Disposable d) {
		return d == DISPOSED;
	}
	//=====================================================
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.function.Consumer;
import java.util.function.LongConsumer;

import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.core.Fuseable.ConditionalSubscriber;
import reactor.core.publisher.FluxPeekFuseable.PeekConditionalSubscriber;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;

/**
 * Peek into the lifecycle events and signals of a sequence.
 *

 *

 * The callbacks are all optional.
 *

 *

 * Crashes by the lambdas are ignored.
 *
 * @param <T> the value type
 * @see Reactive-Streams-Commons
 */
final class FluxPeek<T> extends FluxOperator<T, T> implements SignalPeek<T> {

	final Consumer<? super Subscription> onSubscribeCall;

	final Consumer<? super T> onNextCall;

	final Consumer<? super Throwable> onErrorCall;

	final Runnable onCompleteCall;

	final Runnable onAfterTerminateCall;

	final LongConsumer onRequestCall;

	final Runnable onCancelCall;

	FluxPeek(Flux<? extends T> source,
			@Nullable Consumer<? super Subscription> onSubscribeCall,
			@Nullable Consumer<? super T> onNextCall,
			@Nullable Consumer<? super Throwable> onErrorCall,
			@Nullable Runnable onCompleteCall,
			@Nullable Runnable onAfterTerminateCall,
			@Nullable LongConsumer onRequestCall,
			@Nullable Runnable onCancelCall) {
		super(source);
		this.onSubscribeCall = onSubscribeCall;
		this.onNextCall = onNextCall;
		this.onErrorCall = onErrorCall;
		this.onCompleteCall = onCompleteCall;
		this.onAfterTerminateCall = onAfterTerminateCall;
		this.onRequestCall = onRequestCall;
		this.onCancelCall = onCancelCall;
	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		if (actual instanceof ConditionalSubscriber) {
			@SuppressWarnings("unchecked") // javac, give reason to suppress because inference anomalies
					ConditionalSubscriber<T> s2 = (ConditionalSubscriber<T>) actual;
			source.subscribe(new PeekConditionalSubscriber<>(s2, this));
			return;
		}
		source.subscribe(new PeekSubscriber<>(actual, this));
	}

	static final class PeekSubscriber<T> implements InnerOperator<T, T> {

		final CoreSubscriber<? super T> actual;

		final SignalPeek<T> parent;

		Subscription s;

		boolean done;

		PeekSubscriber(CoreSubscriber<? super T> actual, SignalPeek<T> parent) {
			this.actual = actual;
			this.parent = parent;
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return s;
			if (key == Attr.TERMINATED) return done;

			return InnerOperator.super.scanUnsafe(key);
		}

		@Override
		public Context currentContext() {
			Context c = actual.currentContext();
			if(!c.isEmpty() && parent.onCurrentContextCall() != null) {
				parent.onCurrentContextCall().accept(c);
			}
			return c;
		}

		@Override
		public void request(long n) {
			final LongConsumer requestHook = parent.onRequestCall();
			if (requestHook != null) {
				try {
					requestHook.accept(n);
				}
				catch (Throwable e) {
					Operators.onOperatorError(e, actual.currentContext());
				}
			}
			s.request(n);
		}

		@Override
		public void cancel() {
			final Runnable cancelHook = parent.onCancelCall();
			if (cancelHook != null) {
				try {
					cancelHook.run();
				}
				catch (Throwable e) {
					onError(Operators.onOperatorError(s, e, actual.currentContext()));
					return;
				}
			}
			s.cancel();
		}

		@Override
		public void onSubscribe(Subscription s) {
			if(Operators.validate(this.s, s)) {
				final Consumer<? super Subscription> subscribeHook = parent.onSubscribeCall();
				if (subscribeHook != null) {
					try {
						subscribeHook.accept(s);
					}
					catch (Throwable e) {
						Operators.error(actual, Operators.onOperatorError(s, e,
								actual.currentContext()));
						return;
					}
				}
				this.s = s;
				actual.onSubscribe(this);
			}
		}

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

			final Consumer<? super T> nextHook = parent.onNextCall();
			if(nextHook != null) {
				try {
					nextHook.accept(t);
				}
				catch (Throwable e) {
					Throwable e_ = Operators.onNextError(t, e, actual.currentContext(), s);
					if (e_ == null) {
						request(1);
						return;
					}
					else {
						onError(e_);
						return;
					}
				}
			}

			actual.onNext(t);
		}

		@Override
		public void onError(Throwable t) {
			if (done) {
				Operators.onErrorDropped(t, actual.currentContext());
				return;
			}
			done = true;
			final Consumer<? super Throwable> errorHook = parent.onErrorCall();
			if(errorHook != null) {
				try {
					errorHook.accept(t);
				}
				catch (Throwable e) {
					//this performs a throwIfFatal or suppresses t in e
					t = Operators.onOperatorError(null, e, t, actual.currentContext());
				}
			}

			try {
				actual.onError(t);
			}
			catch (UnsupportedOperationException use){
				if(errorHook == null
						|| !Exceptions.isErrorCallbackNotImplemented(use) && use.getCause() != t){
					throw use;
				}
				//ignore if missing callback
			}

			final Runnable afterTerminateHook = parent.onAfterTerminateCall();
			if(afterTerminateHook != null) {
				try {
					afterTerminateHook.run();
				}
				catch (Throwable e) {
					afterErrorWithFailure(parent, e, t, actual.currentContext());
				}
			}
		}

		@Override
		public void onComplete() {
			if (done) {
				return;
			}
			final Runnable completeHook = parent.onCompleteCall();
			if(completeHook != null) {
				try {
					completeHook.run();
				}
				catch (Throwable e) {
					onError(Operators.onOperatorError(s, e, actual.currentContext()));
					return;
				}
			}
			done = true;

			actual.onComplete();

			final Runnable afterTerminateHook = parent.onAfterTerminateCall();
			if(afterTerminateHook != null) {
				try {
					afterTerminateHook.run();
				}
				catch (Throwable e) {
					afterCompleteWithFailure(parent, e, actual.currentContext());
				}
			}
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return actual;
		}

	}

	@Override
	@Nullable
	public Consumer<? super Subscription> onSubscribeCall() {
		return onSubscribeCall;
	}

	@Override
	@Nullable
	public Consumer<? super T> onNextCall() {
		return onNextCall;
	}

	@Override
	@Nullable
	public Consumer<? super Throwable> onErrorCall() {
		return onErrorCall;
	}

	@Override
	@Nullable
	public Runnable onCompleteCall() {
		return onCompleteCall;
	}

	@Override
	@Nullable
	public Runnable onAfterTerminateCall() {
		return onAfterTerminateCall;
	}

	@Override
	@Nullable
	public LongConsumer onRequestCall() {
		return onRequestCall;
	}

	@Override
	@Nullable
	public Runnable onCancelCall() {
		return onCancelCall;
	}

	/**
	 * Common method for FluxPeek and FluxPeekFuseable to deal with a doAfterTerminate
	 * callback that fails during onComplete. It drops the error to the global hook.
	 *

	 *
The callback failure is thrown immediately if fatal.

	 *
{@link Operators#onOperatorError(Throwable, Context)} is called

	 *
{@link Operators#onErrorDropped(Throwable, Context)} is called

	 *

	 *

	 *
	 * @param parent the {@link SignalPeek} from which to get the callbacks
	 * @param callbackFailure the afterTerminate callback failure
	 * @param context subscriber context
	 */
	//see https://github.com/reactor/reactor-core/issues/270
	static <T> void afterCompleteWithFailure(SignalPeek<T> parent,
			Throwable callbackFailure, Context context) {

		Exceptions.throwIfFatal(callbackFailure);
		Throwable _e = Operators.onOperatorError(callbackFailure, context);
		Operators.onErrorDropped(_e, context);
	}

	/**
	 * Common method for FluxPeek and FluxPeekFuseable to deal with a doAfterTerminate
	 * callback that fails during onError. It drops the error to the global hook.
	 *

	 *
The callback failure is thrown immediately if fatal.

	 *
{@link Operators#onOperatorError(Subscription, Throwable, Object, Context)} is
	 *     called, adding the original error as suppressed

	 *
{@link Operators#onErrorDropped(Throwable, Context)} is called

	 *

	 *

	 *
	 * @param parent the {@link SignalPeek} from which to get the callbacks
	 * @param callbackFailure the afterTerminate callback failure
	 * @param originalError the onError throwable
	 * @param context subscriber context
	 */
	//see https://github.com/reactor/reactor-core/issues/270
	static <T> void afterErrorWithFailure(SignalPeek<T> parent,
			Throwable callbackFailure, Throwable originalError, Context context) {
		Exceptions.throwIfFatal(callbackFailure);
		Throwable _e = Operators.onOperatorError(null, callbackFailure, originalError, context);
		Operators.onErrorDropped(_e, context);
	}

}