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

play.libs.F Maven / Gradle / Ivy

There is a newer version: 1.5.0
Show newest version
package play.libs;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;

import play.exceptions.UnexpectedException;

public class F {

    public static class Promise implements Future, F.Action {

        final CountDownLatch taskLock = new CountDownLatch(1);
        boolean cancelled = false;

        public boolean cancel(boolean mayInterruptIfRunning) {
            return false;
        }

        public boolean isCancelled() {
            return false;
        }

        public boolean isDone() {
            return invoked;
        }

        public V getOrNull() {
            return result;
        }

        public V get() throws InterruptedException, ExecutionException {
            taskLock.await();
            if (exception != null) {
                // The result of the promise is an exception - throw it
                throw new ExecutionException(exception);
            }
            return result;
        }

        public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            taskLock.await(timeout, unit);
            if (exception != null) {
                // The result of the promise is an exception - throw it
                throw new ExecutionException(exception);
            }
            return result;
        }
        List>> callbacks = new ArrayList>>();
        boolean invoked = false;
        V result = null;
        Throwable exception = null;

        public void invoke(V result) {
            invokeWithResultOrException(result, null);
        }

        public void invokeWithException(Throwable t) {
            invokeWithResultOrException(null, t);
        }

        protected void invokeWithResultOrException(V result, Throwable t) {
            synchronized (this) {
                if (!invoked) {
                    invoked = true;
                    this.result = result;
                    this.exception = t;
                    taskLock.countDown();
                } else {
                    return;
                }
            }
            for (F.Action> callback : callbacks) {
                callback.invoke(this);
            }
        }

        public void onRedeem(F.Action> callback) {
            synchronized (this) {
                if (!invoked) {
                    callbacks.add(callback);
                }
            }
            if (invoked) {
                callback.invoke(this);
            }
        }

        public static  Promise> waitAll(final Promise... promises) {
            return waitAll(Arrays.asList(promises));
        }

        public static  Promise> waitAll(final Collection> promises) {
            final CountDownLatch waitAllLock = new CountDownLatch(promises.size());
            final Promise> result = new Promise>() {

                @Override
                public boolean cancel(boolean mayInterruptIfRunning) {
                    boolean r = true;
                    for (Promise f : promises) {
                        r = r & f.cancel(mayInterruptIfRunning);
                    }
                    return r;
                }

                @Override
                public boolean isCancelled() {
                    boolean r = true;
                    for (Promise f : promises) {
                        r = r & f.isCancelled();
                    }
                    return r;
                }

                @Override
                public boolean isDone() {
                    boolean r = true;
                    for (Promise f : promises) {
                        r = r & f.isDone();
                    }
                    return r;
                }

                @Override
                public List get() throws InterruptedException, ExecutionException {
                    waitAllLock.await();
                    List r = new ArrayList();
                    for (Promise f : promises) {
                        r.add(f.get());
                    }
                    return r;
                }

                @Override
                public List get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                    waitAllLock.await(timeout, unit);
                    return get();
                }
            };
            final F.Action> action = new F.Action>() {

                public void invoke(Promise completed) {
                    waitAllLock.countDown();
                    if (waitAllLock.getCount() == 0) {
                        try {
                            result.invoke(result.get());
                        } catch (Exception e) {
                            result.invokeWithException(e);
                        }
                    }
                }
            };
            for (Promise f : promises) {
                f.onRedeem(action);
            }
            if(promises.isEmpty()) {
              result.invoke(Collections.emptyList());
            }
            return result;
        }

        public static  Promise> wait2(Promise tA, Promise tB) {
            final Promise> result = new Promise>();
            final Promise> t = waitAll(new Promise[]{tA, tB});
            t.onRedeem(new F.Action>>() {

                public void invoke(Promise> completed) {
                    List values = completed.getOrNull();
                    if(values != null) {
                        result.invoke(new F.Tuple((A) values.get(0), (B) values.get(1)));
                    }
                    else {
                        result.invokeWithException(completed.exception);
                    }
                }
            });
            return result;
        }

        public static  Promise> wait3(Promise tA, Promise tB, Promise tC) {
            final Promise> result = new Promise>();
            final Promise> t = waitAll(new Promise[]{tA, tB, tC});
            t.onRedeem(new F.Action>>() {

                public void invoke(Promise> completed) {
                    List values = completed.getOrNull();
                    if(values != null) {
                        result.invoke(new F.T3((A) values.get(0), (B) values.get(1), (C) values.get(2)));
                    }
                    else {
                        result.invokeWithException(completed.exception);
                    }
                }
            });
            return result;
        }

        public static  Promise> wait4(Promise tA, Promise tB, Promise tC, Promise tD) {
            final Promise> result = new Promise>();
            final Promise> t = waitAll(new Promise[]{tA, tB, tC, tD});
            t.onRedeem(new F.Action>>() {

                public void invoke(Promise> completed) {
                    List values = completed.getOrNull();
                    if(values != null) {
                        result.invoke(new F.T4((A) values.get(0), (B) values.get(1), (C) values.get(2), (D) values.get(3)));
                    }
                    else {
                        result.invokeWithException(completed.exception);
                    }
                }
            });
            return result;
        }

        public static  Promise> wait5(Promise tA, Promise tB, Promise tC, Promise tD, Promise tE) {
            final Promise> result = new Promise>();
            final Promise> t = waitAll(new Promise[]{tA, tB, tC, tD, tE});
            t.onRedeem(new F.Action>>() {

                public void invoke(Promise> completed) {
                    List values = completed.getOrNull();
                    if(values != null) {
                        result.invoke(new F.T5((A) values.get(0), (B) values.get(1), (C) values.get(2), (D) values.get(3), (E) values.get(4)));
                    }
                    else {
                        result.invokeWithException(completed.exception);
                    }
                }
            });
            return result;
        }

        private static Promise>> waitEitherInternal(final Promise... futures) {
            final Promise>> result = new Promise>>();
            for (int i = 0; i < futures.length; i++) {
                final int index = i + 1;
                ((Promise) futures[i]).onRedeem(new F.Action>() {

                    public void invoke(Promise completed) {
                        result.invoke(new F.Tuple(index, completed));
                    }
                });
            }
            return result;
        }

        public static  Promise> waitEither(final Promise tA, final Promise tB) {
            final Promise> result = new Promise>();
            final Promise>> t = waitEitherInternal(tA, tB);

            t.onRedeem(new F.Action>>>() {

                public void invoke(Promise>> completed) {
                    F.Tuple> value = completed.getOrNull();
                    switch (value._1) {
                        case 1:
                            result.invoke(F.Either._1((A) value._2.getOrNull()));
                            break;
                        case 2:
                            result.invoke(F.Either._2((B) value._2.getOrNull()));
                            break;
                    }

                }
            });

            return result;
        }

        public static  Promise> waitEither(final Promise tA, final Promise tB, final Promise tC) {
            final Promise> result = new Promise>();
            final Promise>> t = waitEitherInternal(tA, tB, tC);

            t.onRedeem(new F.Action>>>() {

                public void invoke(Promise>> completed) {
                    F.Tuple> value = completed.getOrNull();
                    switch (value._1) {
                        case 1:
                            result.invoke(F.E3._1((A) value._2.getOrNull()));
                            break;
                        case 2:
                            result.invoke(F.E3._2((B) value._2.getOrNull()));
                            break;
                        case 3:
                            result.invoke(F.E3._3((C) value._2.getOrNull()));
                            break;
                    }

                }
            });

            return result;
        }

        public static  Promise> waitEither(final Promise tA, final Promise tB, final Promise tC, final Promise tD) {
            final Promise> result = new Promise>();
            final Promise>> t = waitEitherInternal(tA, tB, tC, tD);

            t.onRedeem(new F.Action>>>() {

                public void invoke(Promise>> completed) {
                    F.Tuple> value = completed.getOrNull();
                    switch (value._1) {
                        case 1:
                            result.invoke(F.E4._1((A) value._2.getOrNull()));
                            break;
                        case 2:
                            result.invoke(F.E4._2((B) value._2.getOrNull()));
                            break;
                        case 3:
                            result.invoke(F.E4._3((C) value._2.getOrNull()));
                            break;
                        case 4:
                            result.invoke(F.E4._4((D) value._2.getOrNull()));
                            break;
                    }

                }
            });

            return result;
        }

        public static  Promise> waitEither(final Promise tA, final Promise tB, final Promise tC, final Promise tD, final Promise tE) {
            final Promise> result = new Promise>();
            final Promise>> t = waitEitherInternal(tA, tB, tC, tD, tE);

            t.onRedeem(new F.Action>>>() {

                public void invoke(Promise>> completed) {
                    F.Tuple> value = completed.getOrNull();
                    switch (value._1) {
                        case 1:
                            result.invoke(F.E5._1((A) value._2.getOrNull()));
                            break;
                        case 2:
                            result.invoke(F.E5._2((B) value._2.getOrNull()));
                            break;
                        case 3:
                            result.invoke(F.E5._3((C) value._2.getOrNull()));
                            break;
                        case 4:
                            result.invoke(F.E5._4((D) value._2.getOrNull()));
                            break;
                        case 5:
                            result.invoke(F.E5._5((E) value._2.getOrNull()));
                            break;

                    }

                }
            });

            return result;
        }

        public static  Promise waitAny(final Promise... futures) {
            final Promise result = new Promise();

            final F.Action> action = new F.Action>() {

                public void invoke(Promise completed) {
                    synchronized (this) {
                        if (result.isDone()) {
                            return;
                        }
                    }
                    T resultOrNull = completed.getOrNull();
                    if(resultOrNull != null) {
                      result.invoke(resultOrNull);
                    }
                    else {
                      result.invokeWithException(completed.exception);
                    }
                }
            };

            for (Promise f : futures) {
                f.onRedeem(action);
            }

            return result;
        }
    }

    public static class Timeout extends Promise {

        static Timer timer = new Timer("F.Timeout", true);
        final public String token;
        final public long delay;

        public Timeout(String delay) {
            this(Time.parseDuration(delay) * 1000);
        }

        public Timeout(String token, String delay) {
            this(token, Time.parseDuration(delay) * 1000);
        }

        public Timeout(long delay) {
            this("timeout", delay);
        }

        public Timeout(String token, long delay) {
            this.delay = delay;
            this.token = token;
            final Timeout timeout = this;
            timer.schedule(new TimerTask() {

                @Override
                public void run() {
                    timeout.invoke(timeout);
                }
            }, delay);
        }

        @Override
        public String toString() {
            return "Timeout(" + delay + ")";
        }

    }

    public static Timeout Timeout(String delay) {
        return new Timeout(delay);
    }

    public static Timeout Timeout(String token, String delay) {
        return new Timeout(token, delay);
    }

    public static Timeout Timeout(long delay) {
        return new Timeout(delay);
    }

    public static Timeout Timeout(String token, long delay) {
        return new Timeout(token, delay);
    }

    public static class EventStream {

        final int bufferSize;
        final ConcurrentLinkedQueue events = new ConcurrentLinkedQueue();
        final List> waiting = Collections.synchronizedList(new ArrayList>());

        public EventStream() {
            this.bufferSize = 100;
        }

        public EventStream(int maxBufferSize) {
            this.bufferSize = maxBufferSize;
        }

        public synchronized Promise nextEvent() {
            if (events.isEmpty()) {
                LazyTask task = new LazyTask();
                waiting.add(task);
                return task;
            }
            return new LazyTask(events.peek());
        }

        public synchronized void publish(T event) {
            if (events.size() > bufferSize) {
                events.poll();
            }
            events.offer(event);
            notifyNewEvent();
        }

        void notifyNewEvent() {
            T value = events.peek();
            for (Promise task : waiting) {
                task.invoke(value);
            }
            waiting.clear();
        }

        class LazyTask extends Promise {

            public LazyTask() {
            }

            public LazyTask(T value) {
                invoke(value);
            }

            @Override
            public T get() throws InterruptedException, ExecutionException {
                T value = super.get();
                markAsRead(value);
                return value;
            }

            @Override
            public T getOrNull() {
                T value = super.getOrNull();
                markAsRead(value);
                return value;
            }

            private void markAsRead(T value) {
                if (value != null) {
                    events.remove(value);
                }
            }
        }
    }

    public static class IndexedEvent {

        private static final AtomicLong idGenerator = new AtomicLong(1);
        final public M data;
        final public Long id;

        public IndexedEvent(M data) {
            this.data = data;
            this.id = idGenerator.getAndIncrement();
        }

        @Override
        public String toString() {
            return "Event(id: " + id + ", " + data + ")";
        }

        public static void resetIdGenerator() {
            idGenerator.set(1);
        }
    }

    public static class ArchivedEventStream {

        final int archiveSize;
        final ConcurrentLinkedQueue> events = new ConcurrentLinkedQueue>();
        final List> waiting = Collections.synchronizedList(new ArrayList>());
        final List> pipedStreams = new ArrayList>();

        public ArchivedEventStream(int archiveSize) {
            this.archiveSize = archiveSize;
        }

        public synchronized EventStream eventStream() {
            final EventStream stream = new EventStream(archiveSize);
            for (IndexedEvent event : events) {
                stream.publish(event.data);
            }
            pipedStreams.add(stream);
            return stream;
        }

        public synchronized Promise>> nextEvents(long lastEventSeen) {
            FilterTask filter = new FilterTask(lastEventSeen);
            waiting.add(filter);
            notifyNewEvent();
            return filter;
        }

        public synchronized List availableEvents(long lastEventSeen) {
            List result = new ArrayList();
            for (IndexedEvent event : events) {
                if (event.id > lastEventSeen) {
                    result.add(event);
                }
            }
            return result;
        }

        public List archive() {
            List result = new ArrayList();
            for (IndexedEvent event : events) {
                result.add(event.data);
            }
            return result;
        }

        public synchronized void publish(T event) {
            if (events.size() >= archiveSize) {
                events.poll();
            }
            events.offer(new IndexedEvent(event));
            notifyNewEvent();
            for (EventStream eventStream : pipedStreams) {
                eventStream.publish(event);
            }
        }

        void notifyNewEvent() {
            for (ListIterator> it = waiting.listIterator(); it.hasNext();) {
                FilterTask filter = it.next();
                for (IndexedEvent event : events) {
                    filter.propose(event);
                }
                if (filter.trigger()) {
                    it.remove();
                }
            }
        }

        static class FilterTask extends Promise>> {

            final Long lastEventSeen;
            final List> newEvents = new ArrayList>();

            public FilterTask(Long lastEventSeen) {
                this.lastEventSeen = lastEventSeen;
            }

            public void propose(IndexedEvent event) {
                if (event.id > lastEventSeen) {
                    newEvents.add(event);
                }
            }

            public boolean trigger() {
                if (newEvents.isEmpty()) {
                    return false;
                }
                invoke(newEvents);
                return true;
            }
        }
    }

    public static interface Action0 {

        void invoke();
    }

    public static interface Action {

        void invoke(T result);
    }

    public static abstract class Option implements Iterable {

        public abstract boolean isDefined();

        public abstract T get();

        public static  None None() {
            return (None) (Object) None;
        }

        public static  Some Some(T value) {
            return new Some(value);
        }
    }

    public static  Some Some(A a) {
        return new Some(a);
    }

    public static class None extends Option {

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

        @Override
        public T get() {
            throw new IllegalStateException("No value");
        }

        public Iterator iterator() {
            return Collections.emptyList().iterator();
        }

        @Override
        public String toString() {
            return "None";
        }
    }
    public static None None = new None();

    public static class Some extends Option {

        final T value;

        public Some(T value) {
            this.value = value;
        }

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

        @Override
        public T get() {
            return value;
        }

        public Iterator iterator() {
            return Collections.singletonList(value).iterator();
        }

        @Override
        public String toString() {
            return "Some(" + value + ")";
        }
    }

    public static class Either {

        final public Option _1;
        final public Option _2;

        private Either(Option _1, Option _2) {
            this._1 = _1;
            this._2 = _2;
        }

        public static  Either _1(A value) {
            return new Either(Some(value), None);
        }

        public static  Either _2(B value) {
            return new Either(None, Some(value));
        }

        @Override
        public String toString() {
            return "E2(_1: " + _1 + ", _2: " + _2 + ")";
        }
    }

    public static class E2 extends Either {

        private E2(Option _1, Option _2) {
            super(_1, _2);
        }
    }

    public static class E3 {

        final public Option _1;
        final public Option _2;
        final public Option _3;

        private E3(Option _1, Option _2, Option _3) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
        }

        public static  E3 _1(A value) {
            return new E3(Some(value), None, None);
        }

        public static  E3 _2(B value) {
            return new E3(None, Some(value), None);
        }

        public static  E3 _3(C value) {
            return new E3(None, None, Some(value));
        }

        @Override
        public String toString() {
            return "E3(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ")";
        }
    }

    public static class E4 {

        final public Option _1;
        final public Option _2;
        final public Option _3;
        final public Option _4;

        private E4(Option _1, Option _2, Option _3, Option _4) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
            this._4 = _4;
        }

        public static  E4 _1(A value) {
            return new E4(Option.Some(value), None, None, None);
        }

        public static  E4 _2(B value) {
            return new E4(None, Some(value), None, None);
        }

        public static  E4 _3(C value) {
            return new E4(None, None, Some(value), None);
        }

        public static  E4 _4(D value) {
            return new E4(None, None, None, Some(value));
        }

        @Override
        public String toString() {
            return "E4(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ", _4:" + _4 + ")";
        }
    }

    public static class E5 {

        final public Option _1;
        final public Option _2;
        final public Option _3;
        final public Option _4;
        final public Option _5;

        private E5(Option _1, Option _2, Option _3, Option _4, Option _5) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
            this._4 = _4;
            this._5 = _5;
        }

        public static  E5 _1(A value) {
            return new E5(Option.Some(value), None, None, None, None);
        }

        public static  E5 _2(B value) {
            return new E5(None, Option.Some(value), None, None, None);
        }

        public static  E5 _3(C value) {
            return new E5(None, None, Option.Some(value), None, None);
        }

        public static  E5 _4(D value) {
            return new E5(None, None, None, Option.Some(value), None);
        }

        public static  E5 _5(E value) {
            return new E5(None, None, None, None, Option.Some(value));
        }

        @Override
        public String toString() {
            return "E5(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ", _4:" + _4 + ", _5:" + _5 + ")";
        }
    }

    public static class Tuple {

        final public A _1;
        final public B _2;

        public Tuple(A _1, B _2) {
            this._1 = _1;
            this._2 = _2;
        }

        @Override
        public String toString() {
            return "T2(_1: " + _1 + ", _2: " + _2 + ")";
        }
    }

    public static  Tuple Tuple(A a, B b) {
        return new Tuple(a, b);
    }

    public static class T2 extends Tuple {

        public T2(A _1, B _2) {
            super(_1, _2);
        }
    }

    public static  T2 T2(A a, B b) {
        return new T2(a, b);
    }

    public static class T3 {

        final public A _1;
        final public B _2;
        final public C _3;

        public T3(A _1, B _2, C _3) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
        }

        @Override
        public String toString() {
            return "T3(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ")";
        }
    }

    public static  T3 T3(A a, B b, C c) {
        return new T3(a, b, c);
    }

    public static class T4 {

        final public A _1;
        final public B _2;
        final public C _3;
        final public D _4;

        public T4(A _1, B _2, C _3, D _4) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
            this._4 = _4;
        }

        @Override
        public String toString() {
            return "T4(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ", _4:" + _4 + ")";
        }
    }

    public static  T4 T4(A a, B b, C c, D d) {
        return new T4(a, b, c, d);
    }

    public static class T5 {

        final public A _1;
        final public B _2;
        final public C _3;
        final public D _4;
        final public E _5;

        public T5(A _1, B _2, C _3, D _4, E _5) {
            this._1 = _1;
            this._2 = _2;
            this._3 = _3;
            this._4 = _4;
            this._5 = _5;
        }

        @Override
        public String toString() {
            return "T5(_1: " + _1 + ", _2: " + _2 + ", _3:" + _3 + ", _4:" + _4 + ", _5:" + _5 + ")";
        }
    }

    public static  T5 T5(A a, B b, C c, D d, E e) {
        return new T5(a, b, c, d, e);
    }

    public static abstract class Matcher {

        public abstract Option match(T o);

        public Option match(Option o) {
            if (o.isDefined()) {
                return match(o.get());
            }
            return Option.None();
        }

        public  Matcher and(final Matcher nextMatcher) {
            final Matcher firstMatcher = this;
            return new Matcher() {

                @Override
                public Option match(T o) {
                    for (R r : firstMatcher.match(o)) {
                        return nextMatcher.match(r);
                    }
                    return Option.None();
                }
            };
        }
        public static Matcher String = new Matcher() {

            @Override
            public Option match(Object o) {
                if (o instanceof String) {
                    return Option.Some((String) o);
                }
                return Option.None();
            }
        };

        public static  Matcher ClassOf(final Class clazz) {
            return new Matcher() {

                @Override
                public Option match(Object o) {
                    if (o instanceof Option && ((Option) o).isDefined()) {
                        o = ((Option) o).get();
                    }
                    if (clazz.isInstance(o)) {
                        return Option.Some((K) o);
                    }
                    return Option.None();
                }
            };
        }

        public static Matcher StartsWith(final String prefix) {
            return new Matcher() {

                @Override
                public Option match(String o) {
                    if (o.startsWith(prefix)) {
                        return Option.Some(o);
                    }
                    return Option.None();
                }
            };
        }

        public static Matcher Re(final String pattern) {
            return new Matcher() {

                @Override
                public Option match(String o) {
                    if (o.matches(pattern)) {
                        return Option.Some(o);
                    }
                    return Option.None();
                }
            };
        }

        public static  Matcher Equals(final X other) {
            return new Matcher() {

                @Override
                public Option match(X o) {
                    if (o.equals(other)) {
                        return Option.Some(o);
                    }
                    return Option.None();
                }
            };
        }
    }
}