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

com.englishtown.promises.internal.TrustedPromise Maven / Gradle / Ivy

The newest version!
package com.englishtown.promises.internal;

import com.englishtown.promises.Promise;
import com.englishtown.promises.PromiseResolver;
import com.englishtown.promises.State;
import com.englishtown.promises.Thenable;
import com.englishtown.promises.exceptions.RejectException;
import com.englishtown.promises.internal.handlers.DeferredHandler;
import com.englishtown.promises.internal.handlers.Handler;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.englishtown.promises.HandlerState.FULFILLED;

/**
 * A trusted {@link com.englishtown.promises.Promise}
 */
public class TrustedPromise implements Promise {

    public final Handler _handler;
    private final PromiseHelper helper;

    /**
     * Create a promise whose fate is determined by handler
     *
     * @param handler handler used to fulfill/reject
     * @param helper  promise helper methods
     */
    public TrustedPromise(Handler handler, PromiseHelper helper) {
        this._handler = handler;
        this.helper = helper;
    }

    /**
     * Create a promise whose fate is determined by the resolver
     *
     * @param resolver a promise resolver to fulfill/reject
     * @param helper   promise helper methods
     */
    public TrustedPromise(PromiseResolver resolver, PromiseHelper helper) {
        this.helper = helper;
        this._handler = init(resolver);
    }

    /**
     * Run the supplied resolver
     *
     * @param resolver a promise resolver to fulfill/reject
     * @return {makePromise.DeferredHandler}
     */
    private DeferredHandler init(PromiseResolver resolver) {
        DeferredHandler handler = new DeferredHandler<>(helper, null);

//        /**
//         * Issue a progress event, notifying all progress listeners
//         * @param {*} x progress event payload to pass to all listeners
//         */
//        function promiseNotify(x) {
//                handler.notify(x);
//        }

        try {
            resolver.resolve(
                    /**
                     * Transition from pre-resolution state to post-resolution state, notifying
                     * all listeners of the ultimate fulfillment or rejection
                     * @param {*} x resolution value
                     */
                    handler::resolve,
                    /**
                     * Reject this promise with reason, which will be used verbatim
                     * @param {Error|*} reason rejection reason, strongly suggested
                     *   to be an Error type
                     */
                    handler::reject
            );

        } catch (Throwable e) {
            handler.reject(e);
        }

        return handler;

    }

    // Creation

//    Promise.resolve = resolve;
//    Promise.reject = reject;
//    Promise.never = never;
//
//    Promise._defer = defer;

    @Override
    public  Promise then(Function> onFulfilled) {
        return then(onFulfilled, null);
    }

    /**
     * Transform this promise's fulfillment value, returning a new Promise
     * for the transformed result.  If the promise cannot be fulfilled, onRejected
     * is called with the reason.  onProgress *may* be called with updates toward
     * this promise's fulfillment.
     *
     * @param onFulfilled fulfillment handler
     * @param onRejected  rejection handler
     * @return new promise
     */
    @Override
    public  Promise then(Function> onFulfilled, Function> onRejected) {
        Handler parent = this._handler;

        if ((onFulfilled == null) && (parent.join().state() == FULFILLED)) {
            // Short circuit: value will not change, simply share handler
            //noinspection unchecked
            return new TrustedPromise<>((Handler) parent, helper); // TODO: Check this cast
        }

        TrustedPromise p = this._beget();
        Handler child = p._handler;

        Continuation cont = new Continuation<>();
        cont.resolve = child::resolve;
//        cont.notify = child::notify;
        cont.context = child;
//        cont.receiver = parent.receiver;
        cont.fulfilled = onFulfilled;
        cont.rejected = onRejected;
//        cont.progress = arguments.length > 2 ? arguments[2] : null;

        parent.when(cont);

        return p;
    }

    /**
     * Creates a new, pending promise of the same type as this promise
     *
     * @return {Promise}
     */
    private  TrustedPromise _beget() {
        Handler parent = this._handler;
        DeferredHandler child = new DeferredHandler<>(helper, parent.join().context);
        return new TrustedPromise<>(child, helper);
    }

    /**
     * Check if x is a rejected promise, and if so, delegate to handler._fatal
     *
     * @param x a thenable
     */
    private void _maybeFatal(Thenable x) {
        if (!helper.maybeThenable(x)) {
            return;
        }

        Handler handler = helper.getHandler(x);
        Object context = this._handler.context;
        handler.catchError(t -> {
            handler._fatal(context);
        }, handler);
    }

    @Override
    public State inspect() {
        return _handler.inspect();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  void done(Function> onResult, Function> onError) {
        Handler h = this._handler;

        Continuation cont = new Continuation<>();
        cont.resolve = this::_maybeFatal;
//        cont.notify = noop;
        cont.context = this;
        cont.fulfilled = onResult;
        cont.rejected = onError;
//        cont.progress = null;

//        h.when({ resolve: this._maybeFatal, notify: noop, context: this,
//                receiver: h.receiver, fulfilled: onResult, rejected: onError,
//                progress: void 0 });

        h.when(cont);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Promise otherwise(Function> onRejected) {
        return this.then(null, onRejected);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Promise otherwise(Predicate predicate, Function> onRejected) {
        if (predicate == null) {
            return otherwise(onRejected);
        } else {
            if (onRejected == null) {
                //noinspection unchecked
                return (Promise) this.ensure(this::rejectInvalidPredicate);
            }

            return this.otherwise(this.createCatchFilter(onRejected, predicate));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Promise otherwise(Class type, Function> onRejected) {
        return otherwise(type::isInstance, onRejected);
    }

    /**
     * Wraps the provided catch handler, so that it will only be called
     * if the predicate evaluates truthy
     *
     * @param handler   catch function
     * @param predicate predicate to check if handler should be called
     * @return conditional catch handler
     */
    private  Function> createCatchFilter(Function> handler, Predicate predicate) {
        return (e) -> {
            return predicate.test(e)
                    ? handler.apply(e)
                    : helper.reject(e);
        };
    }

    /**
     * Ensures that onFulfilledOrRejected will be called regardless of whether
     * this promise is fulfilled or rejected.  onFulfilledOrRejected WILL NOT
     * receive the promises' value or reason.  Any returned value will be disregarded.
     * onFulfilledOrRejected may throw or return a rejected promise to signal
     * an additional error.
     *
     * @param handler handler to be called regardless of
     *                fulfillment or rejection
     * @return promise
     */
    @Override
    public Promise ensure(Runnable handler) {
        if (handler == null) {
            // Optimization: result will not change, return same promise
            return this;
        }

        return this.then(
                x -> {
                    handler.run();
                    return this;
                },
                t -> {
                    handler.run();
                    return this;
                });

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Promise orElse(Thenable defaultValue) {
        return this.then(null, (t) -> {
            return defaultValue;
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Promise yield(Thenable value) {
        return this.then((x) -> {
            return value;
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Promise tap(Function> onFulfilledSideEffect) {
        return this.then(onFulfilledSideEffect).yield(this);
    }

    private void rejectInvalidPredicate() {
        throw new RejectException("catch predicate must be a function");
    }

    @Override
    public  Promise fold(BiFunction> fn, Thenable arg) {
        TrustedPromise promise = this._beget();
        this._handler.fold(promise._handler, fn, arg);
        return promise;
    }

}