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

com.iodesystems.fn.thread.Async Maven / Gradle / Ivy

Go to download

Fn is a lazy Java Library that helps utilize some rudimentary functional concepts with more nounular objects

There is a newer version: 3.0.4
Show newest version
package com.iodesystems.fn.thread;

import com.iodesystems.fn.data.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class Async {

    public static final Executor INLINE = new Executor() {
        @Override
        public void execute(Runnable command) {
            command.run();
        }
    };
    protected final List> nexts = new ArrayList>();
    final Executor executor;
    protected Option result = null;
    protected Exception exception = null;
    protected int progress = -1;

    Async(Executor executor) {
        this.executor = executor;
    }

    public static  Async async(Callable initial) {
        return async(INLINE, initial);
    }

    public static  Async async(final A value) {
        return async(INLINE, new Callable() {
            @Override
            public A call() throws Exception {
                return value;
            }
        });
    }

    public static  Deferred defer() {
        return defer(INLINE);
    }

    public static  Deferred defer(Executor executor) {
        return new Deferred(executor);
    }

    public static  Async async(Executor executor, Callable initial) {
        return new Initial(executor, initial);
    }

    public static  Next, List> when(final Executor executor, final Async... asyncs) {
        final List results = new ArrayList(asyncs.length);
        final List> thens = new ArrayList>();
        final AtomicInteger countdown = new AtomicInteger(asyncs.length);
        final AtomicBoolean hasException = new AtomicBoolean(false);
        int index = 0;

        final Next, List> next = new Next, List>(executor, new Result>() {
            // Ignore this
        }, null) {
            @Override
            public void remove() {
                for (Next then : thens) {
                    then.remove();
                }
            }
        };

        for (Async async : asyncs) {
            final int asyncIndex = index++;
            results.add(null);
            thens.add(async.then(executor, new Result() {
                @Override
                public A onResult(A a) throws Exception {
                    results.set(asyncIndex, a);
                    if (countdown.decrementAndGet() == 0) {
                        next.onParentResult(executor, results);
                    }

                    return super.onResult(a);
                }

                @Override
                public Option onException(Exception e) {
                    if (hasException.compareAndSet(false, true)) {
                        next.onParentException(executor, e);
                    }
                    return Option.empty();
                }
            }));
        }
        return next;
    }

    public static  Async> when(Async... asyncs) {
        return when(INLINE, asyncs);
    }

    synchronized void exceptionInternal(Executor executor, Exception exception) {
        this.exception = exception;
        for (Next next : nexts) {
            next.onParentException(executor, exception);
        }
    }

    protected synchronized  Next then(Next next) {
        if (progress >= 0) {
            next.onParentProgress(executor, progress);
        }
        if (result != null) {
            next.onParentResult(executor, result.orElse(null));
        } else if (exception != null) {
            next.onParentException(executor, exception);
        }
        nexts.add(next);
        return next;
    }

    public  Next then(OnResult onResult) {
        return then(executor, onResult);
    }

    public  Next then(OnResult onResult, final OnException onException) {
        return then(executor, onResult, onException);
    }

    public  Next then(OnResult onResult, final OnException onException, final OnProgress onProgress) {
        return then(executor, onResult, onException, onProgress);
    }

    public  Next then(Executor executor, OnResult onResult) {
        return then(executor, onResult, null);
    }

    public  Next then(Executor executor, OnResult onResult, final OnException onException) {
        return then(executor, onResult, onException, null);
    }

    public  Next then(Executor executor, final OnResult onResult, final OnException onException, final OnProgress onProgress) {
        return then(new Next(executor, new From() {
            @Override
            public B onResult(A a) throws Exception {
                return onResult.onResult(a);
            }

            @Override
            public int onProgress(int progress) {
                if (onProgress != null) {
                    return onProgress.onProgress(progress);
                }
                return super.onProgress(progress);
            }

            @Override
            public Option onException(Exception e) {
                if (onException != null) {
                    return onException.onException(e);
                }
                return super.onException(e);
            }
        }, this));
    }

    public Next then(Result from) {
        return then(executor, from);
    }

    public  Next then(From from) {
        return then(executor, from);
    }

    public Next then(Executor executor, Result result) {
        return then(new Next(executor, result, this));
    }

    public  Next then(Executor executor, From from) {
        return then(new Next(executor, from, this));
    }

    synchronized void progressInternal(Executor executor, int progress) {
        this.progress = progress;
        for (Next next : nexts) {
            next.onParentProgress(executor, progress);
        }
    }

    synchronized void resultInternal(Executor executor, A result) {
        this.result = Option.of(result);
        for (Next next : nexts) {
            next.onParentResult(executor, result);
        }
    }

    public Next onProgress(OnProgress onProgress) {
        return then(executor, null, null, onProgress);
    }

    public  Next onException(OnException onException) {
        return then(executor, null, onException, null);
    }

    public Next onProgress(Executor executor, OnProgress onProgress) {
        return then(executor, null, null, onProgress);
    }

    public  Next onException(Executor executor, OnException onException) {
        return then(executor, null, onException, null);
    }

    private synchronized void remove(Next next) {
        nexts.remove(next);
    }

    public interface OnResult {
        B onResult(A a) throws Exception;
    }

    public interface OnException {
        Option onException(Exception e);

    }

    public interface OnProgress {
        int onProgress(int progress);
    }

    public static class Deferred extends Async {

        public Deferred(Executor executor) {
            super(executor);
        }

        @Override
        protected synchronized  Next then(Next next) {
            if (progress >= 0) {
                next.onParentProgress(null, progress);
            }
            if (result != null) {
                next.onParentResult(null, result.orElse(null));
            } else if (exception != null) {
                next.onParentException(null, exception);
            }
            nexts.add(next);
            return next;
        }

        public void progress(int progress) {
            if (progress > -1) {
                progressInternal(null, progress);
            }
        }

        public void result(A result) {
            resultInternal(null, result);
        }

        public void exception(Exception exception) {
            exceptionInternal(null, exception);
        }

    }

    public static class Initial extends Async {
        private Initial(final Executor executor, final Callable callable) {
            super(executor);
            if (executor == INLINE) {
                try {
                    resultInternal(executor, callable.call());
                } catch (Exception e) {
                    exceptionInternal(executor, e);
                }
            } else {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            resultInternal(executor, callable.call());
                        } catch (Exception e) {
                            exceptionInternal(executor, e);
                        }
                    }
                });
            }
        }
    }

    public static class Next extends Async {

        private final From from;
        private final Async parent;

        public Next(Executor executor, From from, Async parent) {
            super(executor);
            this.from = from;
            this.parent = parent;
        }

        public void remove() {
            parent.remove(this);
        }

        private void onParentProgressInternal(int progress) {
            progressInternal(executor, from.onProgress(progress));
        }

        private void onParentResultInternal(final A result) {
            try {
                resultInternal(executor, from.onResult(result));
            } catch (Exception e) {
                exceptionInternal(executor, e);
            }
        }

        void onParentProgress(Executor parentExecutor, final int progress) {
            if (isCurrentExecutor(parentExecutor)) {
                onParentProgressInternal(progress);
            } else {
                this.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        onParentProgressInternal(progress);
                    }
                });
            }
        }

        private boolean isCurrentExecutor(Executor executor) {
            return this.executor == INLINE || executor == this.executor;
        }

        void onParentResult(Executor parentExecutor, final A result) {
            if (isCurrentExecutor(parentExecutor)) {
                onParentResultInternal(result);
            } else {
                this.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        onParentResultInternal(result);
                    }
                });
            }

        }

        void onParentException(Executor parentExecutor, final Exception exception) {
            if (isCurrentExecutor(parentExecutor)) {
                onParentExceptionInternal(exception);
            } else {
                this.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        onParentExceptionInternal(exception);
                    }
                });
            }
        }

        private void onParentExceptionInternal(Exception exception) {
            try {
                Option recovery = from.onException(exception);
                if (recovery.isPresent()) {
                    resultInternal(executor, recovery.get());
                } else {
                    exceptionInternal(executor, exception);
                }
            } catch (Exception e) {
                exceptionInternal(executor, e);
            }
        }
    }

    public static abstract class Result extends From {
        @Override
        public A onResult(A a) throws Exception {
            return a;
        }
    }

    public static abstract class From implements OnResult, OnException, OnProgress {
        @Override
        public B onResult(A a) throws Exception {
            return null;
        }

        @Override
        public int onProgress(int progress) {
            return progress;
        }

        @Override
        public Option onException(Exception e) {
            return Option.empty();
        }
    }
}