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

org.teavm.common.Promise Maven / Gradle / Ivy

There is a newer version: 0.2.8
Show newest version
/*
 *  Copyright 2018 Alexey Andreev.
 *
 *  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
 *
 *       http://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 org.teavm.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

public class Promise {
    public static final Promise VOID = Promise.of(null);

    private T value;
    private Throwable error;
    private State state = State.PENDING;
    private List> thenList;
    private List catchList;

    Promise() {
    }

    public static  Promise of(T value) {
        Promise promise = new Promise<>();
        promise.complete(value);
        return promise;
    }

    public static Promise error(Throwable e) {
        Promise promise = new Promise<>();
        promise.completeWithError(e);
        return promise;
    }

    public static Promise allVoid(Collection> promises) {
        if (promises.isEmpty()) {
            return Promise.VOID;
        }
        AllVoidFunction all = new AllVoidFunction(promises.size());

        for (Promise promise : promises) {
            promise.then(all.thenF).catchError(all.catchF);
        }

        return all.result;
    }


    public static  Promise> all(Collection> promises) {
        if (promises.isEmpty()) {
            return Promise.of(Collections.emptyList());
        }
        AllFunction all = new AllFunction<>(promises.size());

        int i = 0;
        for (Promise promise : promises) {
            promise.then(all.thenF(i++)).catchError(all.catchF);
        }

        return all.result;
    }

    static class AllVoidFunction {
        Promise result = new Promise<>();
        int count;
        boolean error;

        AllVoidFunction(int count) {
            this.count = count;
        }

        Function thenF = v -> {
            if (!error && --count == 0) {
                result.complete(null);
            }
            return null;
        };

        Function catchF = e -> {
            if (!error) {
                error = true;
                result.completeWithError(e);
            }
            return null;
        };
    }


    static class AllFunction {
        Promise> result = new Promise<>();
        List list = new ArrayList<>();
        int count;
        boolean error;

        AllFunction(int count) {
            this.count = count;
            list.addAll(Collections.nCopies(count, null));
        }

        Function thenF(int index) {
            return v -> {
                if (!error) {
                    list.set(index, v);
                    if (--count == 0) {
                        result.complete(list);
                    }
                }
                return null;
            };
        }

        Function catchF = e -> {
            if (!error) {
                error = true;
                result.completeWithError(e);
            }
            return null;
        };
    }

    public  Promise then(Function f) {
        Promise result = new Promise<>();
        if (state == State.PENDING || state == State.WAITING_PROMISE) {
            if (thenList == null) {
                thenList = new ArrayList<>();
                thenList.add(new Then<>(f, result, false));
            }
        } else {
            passValue(f, result);
        }
        return result;
    }

    public Promise thenVoid(Consumer f) {
        return then(r -> {
            f.accept(r);
            return null;
        });
    }

    public  Promise thenAsync(Function> f) {
        Promise result = new Promise<>();
        if (state == State.PENDING || state == State.WAITING_PROMISE) {
            if (thenList == null) {
                thenList = new ArrayList<>();
                thenList.add(new Then<>(f, result, true));
            }
        } else if (state == State.COMPLETED) {
            passValueAsync(f, result);
        }
        return result;
    }

    public  Promise catchError(Function f) {
        Promise result = new Promise<>();
        if (state == State.PENDING || state == State.WAITING_PROMISE) {
            if (catchList == null) {
                catchList = new ArrayList<>();
                catchList.add(new Catch(f, result));
            }
        } else if (state == State.ERRORED) {
            passError(f, result);
        }
        return result;
    }


    public Promise catchVoid(Consumer f) {
        return catchError(e -> {
            f.accept(e);
            return null;
        });
    }

     void passValue(Function f, Promise target) {
        if (state == State.COMPLETED) {
            S next;
            try {
                next = f.apply(value);
            } catch (Throwable e) {
                target.completeWithError(e);
                return;
            }
            target.complete(next);
        } else {
            target.completeWithError(error);
        }
    }

     void passValueAsync(Function> f, Promise target) {
        if (state == State.COMPLETED) {
            target.completeAsync(f.apply(value));
        } else {
            target.completeWithError(error);
        }
    }

     void passError(Function f, Promise target) {
        S next;
        try {
            next = f.apply(error);
        } catch (Throwable e) {
            target.completeWithError(e);
            return;
        }
        target.complete(next);
    }

    void complete(T value) {
        if (state != State.PENDING) {
            throw new IllegalStateException("Already completed");
        }
        completeImpl(value);
    }

    void completeAsync(Promise value) {
        if (state != State.PENDING) {
            throw new IllegalStateException("Already completed");
        }
        state = State.WAITING_PROMISE;

        value
                .then(result -> {
                    completeImpl(result);
                    return null;
                })
                .catchError(e -> {
                    completeWithErrorImpl(e);
                    return null;
                });
    }

    private void completeImpl(T value) {
        state = State.COMPLETED;
        this.value = value;

        if (thenList != null) {
            List> list = thenList;
            thenList = null;
            for (Then then : list) {
                if (then.promise) {
                    passValueAsync((Function>) then.f, (Promise) then.target);
                } else {
                    passValue(then.f, (Promise) then.target);
                }
            }
        }
        catchList = null;
    }

    void completeWithError(Throwable e) {
        if (state != State.PENDING) {
            throw new IllegalStateException("Already completed");
        }
        completeWithErrorImpl(e);
    }

    void completeWithErrorImpl(Throwable e) {
        state = State.ERRORED;
        this.error = e;

        if (catchList != null) {
            List list = catchList;
            thenList = null;
            for (Catch c : list) {
                passError(c.f, (Promise) c.target);
            }
        } else {
            e.printStackTrace();
        }
        thenList = null;
    }

    enum State {
        PENDING,
        WAITING_PROMISE,
        COMPLETED,
        ERRORED
    }

    static class Then {
        Function f;
        Promise target;
        boolean promise;

        Then(Function f, Promise target, boolean promise) {
            this.f = f;
            this.target = target;
            this.promise = promise;
        }
    }

    static class Catch {
        Function f;
        Promise target;

        Catch(Function f, Promise target) {
            this.f = f;
            this.target = target;
        }
    }
}