From 8b175153216b527e735a07feda4cb5d822caa7c8 Mon Sep 17 00:00:00 2001 From: Andrei Korzhevskii Date: Fri, 14 Oct 2016 16:11:51 +0300 Subject: [PATCH] Emulate CompletableFuture Change-Id: I06df161c6e5dbf3101a876b56874307b9b762ed4 --- .../emul/java/lang/InterruptedException.java | 30 + .../emul/java/util/concurrent/Callable.java | 25 + .../concurrent/CancellationException.java | 28 + .../util/concurrent/CompletableFuture.java | 707 +++++++++++++ .../util/concurrent/CompletionException.java | 36 + .../java/util/concurrent/CompletionStage.java | 121 +++ .../emul/java/util/concurrent/Delayed.java | 23 + .../util/concurrent/ExecutionException.java | 36 + .../emul/java/util/concurrent/Executor.java | 23 + .../emul/java/util/concurrent/Executors.java | 52 + .../gwt/emul/java/util/concurrent/Future.java | 38 + .../RejectedExecutionException.java | 35 + .../java/util/concurrent/RunnableFuture.java | 22 + .../concurrent/RunnableScheduledFuture.java | 26 + .../java/util/concurrent/ScheduledFuture.java | 24 + .../emul/java/util/concurrent/TimeUnit.java | 208 ++++ .../util/concurrent/TimeoutException.java | 28 + .../concurrent/impl/DeferredExecutor.java | 30 + .../emul/java/util/concurrent/impl/Impl.java | 48 + .../java/util/concurrent/impl/JsPromise.java | 75 ++ .../concurrent/impl/NativePromiseImpl.java | 67 ++ .../concurrent/impl/NativePromisesImpl.java | 53 + .../java/util/concurrent/impl/Promise.java | 32 + .../util/concurrent/impl/PromiseImpl.java | 90 ++ .../java/util/concurrent/impl/Promises.java | 31 + .../util/concurrent/impl/PromisesImpl.java | 78 ++ .../google/gwt/emultest/EmulJava8Suite.java | 4 + .../concurrent/CompletableFutureTest.java | 971 ++++++++++++++++++ 28 files changed, 2941 insertions(+) create mode 100644 user/super/com/google/gwt/emul/java/lang/InterruptedException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/Callable.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/CancellationException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/CompletableFuture.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/CompletionException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/CompletionStage.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/Delayed.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/ExecutionException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/Executor.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/Executors.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/Future.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/RejectedExecutionException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/RunnableFuture.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/RunnableScheduledFuture.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/ScheduledFuture.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/TimeUnit.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/TimeoutException.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/DeferredExecutor.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/Impl.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/JsPromise.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromiseImpl.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromisesImpl.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/Promise.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/PromiseImpl.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/Promises.java create mode 100644 user/super/com/google/gwt/emul/java/util/concurrent/impl/PromisesImpl.java create mode 100644 user/test/com/google/gwt/emultest/java8/util/concurrent/CompletableFutureTest.java diff --git a/user/super/com/google/gwt/emul/java/lang/InterruptedException.java b/user/super/com/google/gwt/emul/java/lang/InterruptedException.java new file mode 100644 index 0000000000..f2139490dc --- /dev/null +++ b/user/super/com/google/gwt/emul/java/lang/InterruptedException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.lang; + +/** + * Minimal emulation of {@link java.lang.InterruptedException}, that should + * only be used in method signatures. New GWT code should not reference this + * class at all. It is here only to ease the GWTification of common code. + * + */ +public class InterruptedException extends Exception { + public InterruptedException() { } + + public InterruptedException(String message) { + super(message); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/Callable.java b/user/super/com/google/gwt/emul/java/util/concurrent/Callable.java new file mode 100644 index 0000000000..26cb4fc54b --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/Callable.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of Callable. + * + */ +@FunctionalInterface +public interface Callable { + V call() throws Exception; +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/CancellationException.java b/user/super/com/google/gwt/emul/java/util/concurrent/CancellationException.java new file mode 100644 index 0000000000..6e25a31b46 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/CancellationException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of CancellationException. + */ +public class CancellationException extends IllegalStateException { + + public CancellationException() {} + + public CancellationException(String message) { + super(message); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/CompletableFuture.java b/user/super/com/google/gwt/emul/java/util/concurrent/CompletableFuture.java new file mode 100644 index 0000000000..77d63a81bc --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/CompletableFuture.java @@ -0,0 +1,707 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +import java.util.concurrent.impl.DeferredExecutor; +import java.util.concurrent.impl.Impl; +import java.util.concurrent.impl.Promise; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import static javaemul.internal.InternalPreconditions.checkNotNull; + +/** + * Emulation of CompletableFuture. + * See + * + * the official Java API doc for details. + */ +public class CompletableFuture implements Future, CompletionStage { + + public interface AsynchronousCompletionTask { + } + + public static CompletableFuture runAsync(Runnable action) { + return runAsync(action, DEFAULT_EXECUTOR); + } + + public static CompletableFuture runAsync(Runnable action, Executor executor) { + return supplyAsync(() -> { + action.run(); + return null; + }, executor); + } + + public static CompletableFuture supplyAsync(Supplier supplier) { + return supplyAsync(supplier, DEFAULT_EXECUTOR); + } + + public static CompletableFuture supplyAsync(Supplier supplier, Executor executor) { + checkNotNull(supplier); + checkNotNull(executor); + + CompletableFuture future = new CompletableFuture<>(); + executor.execute(() -> { + try { + future.tryCompleteValue(supplier.get()); + } catch (Throwable t) { + future.tryCompleteThrowable(t); + } + }); + return future; + } + + public static CompletableFuture completedFuture(T value) { + return new CompletableFuture<>(value, null); + } + + public static CompletableFuture allOf(CompletableFuture... futures) { + if (futures.length == 0) { + return completedFuture(null); + } + + CompletableFuture completedFuture = null; + for (CompletableFuture future : futures) { + if (!future.isDone()) { + completedFuture = null; + break; + } + if (completedFuture == null) { + completedFuture = future; + } + } + if (completedFuture != null) { + return new CompletableFuture<>(null, completedFuture.reason); + } + + CompletableFuture future = new CompletableFuture<>(); + and(futures).then((value, reason) -> { + if (reason != null) { + future.tryCompleteThrowable(reason); + } else { + future.tryCompleteValue(null); + } + }); + return future; + } + + public static CompletableFuture anyOf(CompletableFuture... futures) { + if (futures.length == 0) { + return new CompletableFuture<>(); + } + + for (CompletableFuture future : futures) { + if (future.isDone()) { + return new CompletableFuture<>(future.value, future.reason); + } + } + + CompletableFuture future = new CompletableFuture<>(); + or(futures).then((value, reason) -> { + if (reason != null) { + future.tryCompleteThrowable(reason); + } else { + future.tryCompleteValue(value); + } + }); + return future; + } + + private static Promise and(CompletableFuture... futures) { + return Impl.IMPL.allOf(toPromises(futures)); + } + + private static Promise or(CompletableFuture... futures) { + return Impl.IMPL.anyOf(toPromises(futures)); + } + + private static Promise[] toPromises(CompletableFuture... futures) { + int length = futures.length; + Promise[] promises = new Promise[length]; + for (int i = 0; i < length; i++) { + promises[i] = futures[i].promise; + } + return promises; + } + + private static BiConsumer runAsync( + Executor executor, BiConsumer action) { + + if (executor == null) { + return action; + } else { + return (r, e) -> executor.execute(() -> action.accept(r, e)); + } + } + + private static final Executor DEFAULT_EXECUTOR = new DeferredExecutor(); + + private final Promise promise; + private boolean done; + private T value; + private Throwable reason; + + public CompletableFuture() { + promise = Impl.IMPL.incomplete(); + } + + private CompletableFuture(T value, Throwable reason) { + this(); + completeStage(value, reason); // TODO + } + + @Override + public CompletableFuture thenApply(Function fn) { + return thenApplyAsync0(fn, null); + } + + @Override + public CompletableFuture thenApplyAsync(Function fn) { + return thenApplyAsync0(fn, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenApplyAsync(Function fn, Executor executor) { + checkNotNull(executor); + return thenApplyAsync0(fn, executor); + } + + private CompletableFuture thenApplyAsync0(Function fn, Executor executor) { + checkNotNull(fn); + CompletableFuture future = new CompletableFuture<>(); + onStageComplete((r, e) -> { + if (e != null) { + future.tryCompleteThrowable(e); + } else { + try { + future.tryCompleteValue(fn.apply(r)); + } catch (Throwable ex) { + future.tryCompleteThrowable(ex); + } + } + }, executor); + return future; + } + + @Override + public CompletableFuture thenAccept(Consumer action) { + return thenAcceptAsync0(action, null); + } + + @Override + public CompletableFuture thenAcceptAsync(Consumer action) { + return thenAcceptAsync0(action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenAcceptAsync(Consumer action, Executor executor) { + checkNotNull(executor); + return thenAcceptAsync0(action, executor); + } + + private CompletableFuture thenAcceptAsync0(Consumer action, Executor executor) { + checkNotNull(action); + return thenApplyAsync0((r) -> { + action.accept(r); + return null; + }, executor); + } + + @Override + public CompletableFuture thenRun(Runnable action) { + return thenRunAsync0(action, null); + } + + @Override + public CompletableFuture thenRunAsync(Runnable action) { + return thenRunAsync0(action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenRunAsync(Runnable action, Executor executor) { + checkNotNull(executor); + return thenRunAsync0(action, executor); + } + + private CompletableFuture thenRunAsync0(Runnable action, Executor executor) { + checkNotNull(action); + return thenApplyAsync0((r) -> { + action.run(); + return null; + }, executor); + } + + @Override + public CompletableFuture thenCombine(CompletionStage other, + BiFunction fn) { + + return thenCombineAsync0(other, fn, null); + } + + @Override + public CompletableFuture thenCombineAsync(CompletionStage other, + BiFunction fn) { + + return thenCombineAsync0(other, fn, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenCombineAsync(CompletionStage other, + BiFunction fn, Executor executor) { + checkNotNull(executor); + return thenCombineAsync0(other, fn, executor); + } + + private CompletableFuture thenCombineAsync0(CompletionStage other, + BiFunction fn, Executor executor) { + + checkNotNull(fn); + CompletableFuture future = new CompletableFuture<>(); + CompletableFuture first = this; + CompletableFuture second = other.toCompletableFuture(); + and(first, second).then(runAsync(executor, (ignored, e) -> { + if (e != null) { + future.tryCompleteThrowable(e); + } else { + try { + future.tryCompleteValue(fn.apply(first.get(), second.get())); + } catch (Throwable ex) { + future.tryCompleteThrowable(ex); + } + } + })); + return future; + } + + @Override + public CompletableFuture thenAcceptBoth(CompletionStage other, + BiConsumer action) { + + return thenAcceptBothAsync0(other, action, null); + } + + @Override + public CompletableFuture thenAcceptBothAsync(CompletionStage other, + BiConsumer action) { + + return thenAcceptBothAsync0(other, action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenAcceptBothAsync(CompletionStage other, + BiConsumer action, Executor executor) { + + checkNotNull(executor); + return thenAcceptBothAsync0(other, action, executor); + } + + private CompletableFuture thenAcceptBothAsync0(CompletionStage other, + BiConsumer action, Executor executor) { + + checkNotNull(action); + return thenCombineAsync0(other, (a, b) -> { + action.accept(a, b); + return null; + }, executor); + } + + @Override + public CompletableFuture runAfterBoth(CompletionStage other, Runnable action) { + return runAfterBothAsync0(other, action, null); + } + + @Override + public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action) { + return runAfterBothAsync0(other, action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) { + checkNotNull(executor); + return runAfterBothAsync0(other, action, executor); + } + + private CompletableFuture runAfterBothAsync0(CompletionStage other, Runnable action, Executor executor) { + checkNotNull(action); + return thenCombineAsync0(other, (a, b) -> { + action.run(); + return null; + }, executor); + } + + @Override + public CompletableFuture applyToEither(CompletionStage other, Function fn) { + return applyToEitherAsync0(other, fn, null); + } + + @Override + public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn) { + return applyToEitherAsync0(other, fn, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture applyToEitherAsync(CompletionStage other, + Function fn, Executor executor) { + + checkNotNull(executor); + return applyToEitherAsync0(other, fn, executor); + } + + @SuppressWarnings("unchecked") + private CompletableFuture applyToEitherAsync0(CompletionStage other, + Function fn, Executor executor) { + + checkNotNull(fn); + CompletableFuture future = new CompletableFuture<>(); + or(this, other.toCompletableFuture()).then(runAsync(executor, (r, e) -> { + if (e != null) { + future.tryCompleteThrowable(e); + } else { + try { + future.tryCompleteValue(fn.apply((T) r)); + } catch (Throwable ex) { + future.tryCompleteThrowable(ex); + } + } + })); + return future; + } + + @Override + public CompletableFuture acceptEither(CompletionStage other, Consumer action) { + return acceptEitherAsync0(other, action, null); + } + + @Override + public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action) { + return acceptEitherAsync0(other, action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture acceptEitherAsync(CompletionStage other, + Consumer action, Executor executor) { + + checkNotNull(executor); + return acceptEitherAsync0(other, action, executor); + } + + private CompletableFuture acceptEitherAsync0(CompletionStage other, + Consumer action, Executor executor) { + checkNotNull(action); + return applyToEitherAsync0(other, (r) -> { + action.accept(r); + return null; + }, executor); + } + + @Override + public CompletableFuture runAfterEither(CompletionStage other, Runnable action) { + return runAfterEitherAsync0(other, action, null); + } + + @Override + public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action) { + return runAfterEitherAsync0(other, action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) { + checkNotNull(executor); + return runAfterEitherAsync0(other, action, executor); + } + + @SuppressWarnings("unchecked") + private CompletableFuture runAfterEitherAsync0(CompletionStage other, Runnable action, Executor executor) { + checkNotNull(action); + return ((CompletableFuture) this).applyToEitherAsync0(other, (r) -> { + action.run(); + return null; + }, executor); + } + + @Override + public CompletableFuture thenCompose(Function> fn) { + return thenComposeAsync0(fn, null); + } + + @Override + public CompletableFuture thenComposeAsync(Function> fn) { + return thenComposeAsync0(fn, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture thenComposeAsync(Function> fn, Executor executor) { + checkNotNull(executor); + return thenComposeAsync0(fn, executor); + } + + private CompletableFuture thenComposeAsync0(Function> fn, Executor executor) { + checkNotNull(fn); + CompletableFuture future = new CompletableFuture<>(); + onStageComplete((r, e) -> { + if (e != null) { + future.tryCompleteThrowable(e); + } else { + try { + CompletableFuture newFuture = fn.apply(r).toCompletableFuture(); + // TODO: async? + // TODO: whenCompleteAsync0? + newFuture.whenCompleteAsync0((r1, ex) -> { + if (ex != null) { + future.tryCompleteThrowable(ex); + } else { + future.tryCompleteValue(r1); + } + }, executor); + } catch (Throwable ex) { + future.tryCompleteThrowable(ex); + } + } + }, null); // TODO: executor? + return future; + } + + @Override + public CompletableFuture exceptionally(Function fn) { + checkNotNull(fn); + return handle((r, e) -> e != null ? fn.apply(e) : r); + } + + @Override + public CompletableFuture whenComplete(BiConsumer action) { + return whenCompleteAsync0(action, null); + } + + @Override + public CompletableFuture whenCompleteAsync(BiConsumer action) { + return whenCompleteAsync0(action, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture whenCompleteAsync(BiConsumer action, Executor executor) { + checkNotNull(executor); + return whenCompleteAsync0(action, executor); + } + + private CompletableFuture whenCompleteAsync0(BiConsumer action, Executor executor) { + checkNotNull(action); + return handleAsync0((r, e) -> { + action.accept(r, e); + return r; + }, executor); + } + + @Override + public CompletableFuture handle(BiFunction fn) { + return handleAsync0(fn, null); + } + + @Override + public CompletableFuture handleAsync(BiFunction fn) { + return handleAsync0(fn, DEFAULT_EXECUTOR); + } + + @Override + public CompletableFuture handleAsync(BiFunction fn, Executor executor) { + checkNotNull(executor); + return handleAsync0(fn, executor); + } + + private CompletableFuture handleAsync0(BiFunction fn, Executor executor) { + checkNotNull(fn); + CompletableFuture future = new CompletableFuture<>(); + onStageComplete((r, e) -> { + try { + future.tryCompleteValue(fn.apply(r, e)); + } catch (Throwable ex) { + future.tryCompleteThrowable(ex); + } + }, executor); + return future; + } + + @Override + public CompletableFuture toCompletableFuture() { + return this; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return tryCompleteStage(null, new CancellationException()); + } + + @Override + public boolean isCancelled() { + return reason instanceof CancellationException; + } + + @Override + public boolean isDone() { + return done; + } + + public boolean isCompletedExceptionally() { + return reason != null; + } + + /** + * This method does not implement blocking behaviour on CompletableFuture because + * it's not possible to implement that in single thread browser environment. + * Instead a method call, which would block in JVM, will act as if calling thread is interrupted + * immediately and will throw InterruptedException. + */ + @Override + public T get() throws InterruptedException, ExecutionException { + if (!isDone()) { + // TODO: according to GWT Future's javadoc +// throw new IllegalStateException("blocking on CompletableFuture is not supported"); + throw new InterruptedException("blocking on CompletableFuture is not supported"); + } + + if (reason != null) { + if (reason instanceof CancellationException) { + throw (CancellationException) reason; + } + Throwable cause = null; + if (reason instanceof CompletionException) { + cause = reason.getCause(); + } + if (cause == null) { + cause = reason; + } + throw new ExecutionException(cause); + } + return value; + } + + /** + * This method does not implement blocking behaviour on CompletableFuture because + * it's not possible to implement that in single thread browser environment. + * Instead a method call, which would block in JVM, will act as if calling thread is interrupted + * immediately and will throw InterruptedException. + * Timeout parameters are ignored and considered indefinite. + */ + @Override + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return get(); + } + + /** + * This method does not implement blocking behaviour on CompletableFuture because + * it's not possible to implement that in single thread browser environment. + * Instead a method call, which would block in JVM, will act as if calling thread is interrupted + * immediately and will return null as a result. + */ + public T join() { +// if (!isDone()) { +// TODO: according to GWT Future's javadoc +// throw new IllegalStateException("blocking on CompletableFuture is not supported"); +// } + return getNow(null); + } + + public T getNow(T valueIfAbsent) { + return isDone() ? getJoinValue() : valueIfAbsent; + } + + private T getJoinValue() { + if (reason == null) { + return value; + } + + if (reason instanceof CancellationException) { + throw (CancellationException) reason; + } + if (reason instanceof CompletionException) { + throw (CompletionException) reason; + } + throw new CompletionException(reason); + } + + /** + * This method is a simple stub which returns 0. + * It's done for simplicity because this method is designed to be used in monitoring systems + * and has no use in GWT. + */ + public int getNumberOfDependents() { + return 0; + } + + public boolean complete(T value) { + return tryCompleteStage(value, null); + } + + public boolean completeExceptionally(Throwable e) { + checkNotNull(e); + return tryCompleteStage(null, e); + } + + public void obtrudeValue(T value) { + completeStage(value, null); + } + + public void obtrudeException(Throwable e) { + checkNotNull(e); + completeStage(null, e); + } + + private void tryCompleteValue(T value) { + tryCompleteStage(value, null); + } + + private void tryCompleteThrowable(Throwable reason) { + tryCompleteStage(null, wrap(reason)); + } + + private boolean tryCompleteStage(T value, Throwable reason) { + if (done) { + return false; + } + + completeStage(value, reason); + return true; + } + + private void completeStage(T value, Throwable reason) { + this.value = value; + this.reason = reason; + done = true; + if (reason == null) { + promise.resolve(value); + } else { + promise.reject(reason); + } + } + + private void onStageComplete(BiConsumer action, Executor executor) { + if (done) { + runAsync(executor, action).accept(value, reason); // TODO + } else { + promise.then(runAsync(executor, action)); + } + } + + private static RuntimeException wrap(Throwable t) { + if (t instanceof CompletionException) { + return (CompletionException) t; + } + return new CompletionException(t); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/CompletionException.java b/user/super/com/google/gwt/emul/java/util/concurrent/CompletionException.java new file mode 100644 index 0000000000..9d0a03e6dc --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/CompletionException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of CompletionException. + */ +public class CompletionException extends IllegalStateException { + + public CompletionException() { } + + public CompletionException(String message) { + super(message); + } + + public CompletionException(String message, Throwable cause) { + super(message, cause); + } + + public CompletionException(Throwable cause) { + super(cause); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/CompletionStage.java b/user/super/com/google/gwt/emul/java/util/concurrent/CompletionStage.java new file mode 100644 index 0000000000..34aec24316 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/CompletionStage.java @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Emulation of CompletionStage. + * See + * + * the official Java API doc for details. + */ +public interface CompletionStage { + + CompletionStage applyToEither(CompletionStage other, + Function fn); + + CompletionStage applyToEitherAsync(CompletionStage other, + Function fn); + + CompletionStage applyToEitherAsync(CompletionStage other, + Function fn, Executor executor); + + CompletionStage acceptEither(CompletionStage other, Consumer action); + + CompletionStage acceptEitherAsync(CompletionStage other, + Consumer action); + + CompletionStage acceptEitherAsync(CompletionStage other, + Consumer action, Executor executor); + + CompletionStage thenAccept(Consumer action); + + CompletionStage thenAcceptAsync(Consumer action); + + CompletionStage thenAcceptAsync(Consumer action, Executor executor); + + CompletionStage thenAcceptBoth(CompletionStage other, + BiConsumer action); + + CompletionStage thenAcceptBothAsync(CompletionStage other, + BiConsumer action); + + CompletionStage thenAcceptBothAsync(CompletionStage other, + BiConsumer action, Executor executor); + + CompletionStage thenApply(Function fn); + + CompletionStage thenApplyAsync(Function fn); + + CompletionStage thenApplyAsync(Function fn, Executor executor); + + CompletionStage thenCombine(CompletionStage other, + BiFunction fn); + + CompletionStage thenCombineAsync(CompletionStage other, + BiFunction fn); + + CompletionStage thenCombineAsync(CompletionStage other, + BiFunction fn, Executor executor); + + CompletionStage thenCompose(Function> fn); + + CompletionStage thenComposeAsync(Function> fn); + + CompletionStage thenComposeAsync(Function> fn, + Executor executor); + + CompletionStage thenRun(Runnable action); + + CompletionStage thenRunAsync(Runnable action); + + CompletionStage thenRunAsync(Runnable action, Executor executor); + + CompletionStage runAfterBoth(CompletionStage other, Runnable action); + + CompletionStage runAfterBothAsync(CompletionStage other, Runnable action); + + CompletionStage runAfterBothAsync(CompletionStage other, Runnable action, Executor executor); + + CompletionStage runAfterEither(CompletionStage other, Runnable action); + + CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action); + + CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action, + Executor executor); + + CompletionStage whenComplete(BiConsumer action); + + CompletionStage whenCompleteAsync(BiConsumer action); + + CompletionStage whenCompleteAsync(BiConsumer action, + Executor executor); + + CompletionStage exceptionally(Function fn); + + CompletionStage handle(BiFunction fn); + + CompletionStage handleAsync(BiFunction fn); + + CompletionStage handleAsync(BiFunction fn, + Executor executor); + + CompletableFuture toCompletableFuture(); +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/Delayed.java b/user/super/com/google/gwt/emul/java/util/concurrent/Delayed.java new file mode 100644 index 0000000000..5c3e19aa24 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/Delayed.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of Delayed. + */ +public interface Delayed extends Comparable { + long getDelay(TimeUnit unit); +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/ExecutionException.java b/user/super/com/google/gwt/emul/java/util/concurrent/ExecutionException.java new file mode 100644 index 0000000000..c567892987 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/ExecutionException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of ExecutionException. + * + */ +public class ExecutionException extends Exception { + protected ExecutionException() { } + + protected ExecutionException(String message) { + super(message); + } + + public ExecutionException(String message, Throwable cause) { + super(message, cause); + } + + public ExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/Executor.java b/user/super/com/google/gwt/emul/java/util/concurrent/Executor.java new file mode 100644 index 0000000000..a4e53310bc --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/Executor.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of Executor. + */ +public interface Executor { + void execute(Runnable command); +} \ No newline at end of file diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/Executors.java b/user/super/com/google/gwt/emul/java/util/concurrent/Executors.java new file mode 100644 index 0000000000..24070bf7ca --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/Executors.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +import static javaemul.internal.InternalPreconditions.checkNotNull; + +/** + * Emulation of executors. + */ +public class Executors { + + public static Callable callable(Runnable task, T result) { + return new RunnableAdapter<>(task, result); + } + + public static Callable callable(Runnable task) { + return callable(task, null); + } + + private static class RunnableAdapter implements Callable { + + private final Runnable task; + private final T result; + + private RunnableAdapter(Runnable task, T result) { + this.task = checkNotNull(task); + this.result = result; + } + + @Override + public T call() { + task.run(); + return result; + } + } + + private Executors() { + } +} \ No newline at end of file diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/Future.java b/user/super/com/google/gwt/emul/java/util/concurrent/Future.java new file mode 100644 index 0000000000..32b09122e8 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/Future.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of Future. Since GWT environment is single threaded, attempting to block on the future + * by calling {@link #get()} or {@link #get(long, TimeUnit)} when it is not yet done is + * considered illegal because it would lead to a deadlock. Future implementations must throw + * {@link IllegalStateException} to avoid a deadlock. + * See + * + * the official Java API doc for details. + */ +public interface Future { + boolean cancel(boolean mayInterruptIfRunning); + + boolean isCancelled(); + + boolean isDone(); + + V get() throws InterruptedException, ExecutionException; + + V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException; +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/RejectedExecutionException.java b/user/super/com/google/gwt/emul/java/util/concurrent/RejectedExecutionException.java new file mode 100644 index 0000000000..6e7fab0c25 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/RejectedExecutionException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * GWT emulation of RejectedExecutionException. + */ +public class RejectedExecutionException extends RuntimeException { + public RejectedExecutionException() { } + + public RejectedExecutionException(String message) { + super(message); + } + + public RejectedExecutionException(String message, Throwable cause) { + super(message, cause); + } + + public RejectedExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/RunnableFuture.java b/user/super/com/google/gwt/emul/java/util/concurrent/RunnableFuture.java new file mode 100644 index 0000000000..3d5531d111 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/RunnableFuture.java @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of RunnableFuture. + */ +public interface RunnableFuture extends Runnable, Future { +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/RunnableScheduledFuture.java b/user/super/com/google/gwt/emul/java/util/concurrent/RunnableScheduledFuture.java new file mode 100644 index 0000000000..ce834b0659 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/RunnableScheduledFuture.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of RunnableScheduledFuture. + * + * @param value type returned by the future. + */ +public interface RunnableScheduledFuture extends RunnableFuture, ScheduledFuture { + + boolean isPeriodic(); +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/ScheduledFuture.java b/user/super/com/google/gwt/emul/java/util/concurrent/ScheduledFuture.java new file mode 100644 index 0000000000..5ca3f63f18 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/ScheduledFuture.java @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of ScheduleFuture. + * + * @param value type returned by the future. + */ +public interface ScheduledFuture extends Delayed, Future { +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/TimeUnit.java b/user/super/com/google/gwt/emul/java/util/concurrent/TimeUnit.java new file mode 100644 index 0000000000..6de8274fb8 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/TimeUnit.java @@ -0,0 +1,208 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * GWT emulation of TimeUnit, created by removing unsupported operations from + * Doug Lea's public domain version. + */ +public enum TimeUnit { + NANOSECONDS { + public long toNanos(long d) { return d; } + public long toMicros(long d) { return d / C1_C0; } + public long toMillis(long d) { return d / C2_C0; } + public long toSeconds(long d) { return d / C3_C0; } + public long toMinutes(long d) { return d / C4_C0; } + public long toHours(long d) { return d / C5_C0; } + public long toDays(long d) { return d / C6_C0; } + public long convert(long d, TimeUnit u) { return u.toNanos(d); } + int excessNanos(long d, long m) { return (int)(d - (m*C2)); } + }, + MICROSECONDS { + public long toNanos(long d) { return x(d, C1_C0, MAX_C1_C0); } + public long toMicros(long d) { return d; } + public long toMillis(long d) { return d / C2_C1; } + public long toSeconds(long d) { return d / C3_C1; } + public long toMinutes(long d) { return d / C4_C1; } + public long toHours(long d) { return d / C5_C1; } + public long toDays(long d) { return d / C6_C1; } + public long convert(long d, TimeUnit u) { return u.toMicros(d); } + int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } + }, + MILLISECONDS { + public long toNanos(long d) { return x(d, C2_C0, MAX_C2_C0); } + public long toMicros(long d) { return x(d, C2_C1, MAX_C2_C1); } + public long toMillis(long d) { return d; } + public long toSeconds(long d) { return d / C3_C2; } + public long toMinutes(long d) { return d / C4_C2; } + public long toHours(long d) { return d / C5_C2; } + public long toDays(long d) { return d / C6_C2; } + public long convert(long d, TimeUnit u) { return u.toMillis(d); } + int excessNanos(long d, long m) { return 0; } + }, + SECONDS { + public long toNanos(long d) { return x(d, C3_C0, MAX_C3_C0); } + public long toMicros(long d) { return x(d, C3_C1, MAX_C3_C1); } + public long toMillis(long d) { return x(d, C3_C2, MAX_C3_C2); } + public long toSeconds(long d) { return d; } + public long toMinutes(long d) { return d / C4_C3; } + public long toHours(long d) { return d / C5_C3; } + public long toDays(long d) { return d / C6_C3; } + public long convert(long d, TimeUnit u) { return u.toSeconds(d); } + int excessNanos(long d, long m) { return 0; } + }, + MINUTES { + public long toNanos(long d) { return x(d, C4_C0, MAX_C4_C0); } + public long toMicros(long d) { return x(d, C4_C1, MAX_C4_C1); } + public long toMillis(long d) { return x(d, C4_C2, MAX_C4_C2); } + public long toSeconds(long d) { return x(d, C4_C3, MAX_C4_C3); } + public long toMinutes(long d) { return d; } + public long toHours(long d) { return d / C5_C4; } + public long toDays(long d) { return d / C6_C4; } + public long convert(long d, TimeUnit u) { return u.toMinutes(d); } + int excessNanos(long d, long m) { return 0; } + }, + HOURS { + public long toNanos(long d) { return x(d, C5_C0, MAX_C5_C0); } + public long toMicros(long d) { return x(d, C5_C1, MAX_C5_C1); } + public long toMillis(long d) { return x(d, C5_C2, MAX_C5_C2); } + public long toSeconds(long d) { return x(d, C5_C3, MAX_C5_C3); } + public long toMinutes(long d) { return x(d, C5_C4, MAX_C5_C4); } + public long toHours(long d) { return d; } + public long toDays(long d) { return d / C6_C5; } + public long convert(long d, TimeUnit u) { return u.toHours(d); } + int excessNanos(long d, long m) { return 0; } + }, + DAYS { + public long toNanos(long d) { return x(d, C6_C0, MAX_C6_C0); } + public long toMicros(long d) { return x(d, C6_C1, MAX_C6_C1); } + public long toMillis(long d) { return x(d, C6_C2, MAX_C6_C2); } + public long toSeconds(long d) { return x(d, C6_C3, MAX_C6_C3); } + public long toMinutes(long d) { return x(d, C6_C4, MAX_C6_C4); } + public long toHours(long d) { return x(d, C6_C5, MAX_C6_C5); } + public long toDays(long d) { return d; } + public long convert(long d, TimeUnit u) { return u.toDays(d); } + int excessNanos(long d, long m) { return 0; } + }; + + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 60L; + static final long C5 = C4 * 60L; + static final long C6 = C5 * 24L; + + static final long MAX = Long.MAX_VALUE; + + static final long C6_C0 = C6 / C0; + static final long C6_C1 = C6 / C1; + static final long C6_C2 = C6 / C2; + static final long C6_C3 = C6 / C3; + static final long C6_C4 = C6 / C4; + static final long C6_C5 = C6 / C5; + + static final long C5_C0 = C5 / C0; + static final long C5_C1 = C5 / C1; + static final long C5_C2 = C5 / C2; + static final long C5_C3 = C5 / C3; + static final long C5_C4 = C5 / C4; + + static final long C4_C0 = C4 / C0; + static final long C4_C1 = C4 / C1; + static final long C4_C2 = C4 / C2; + static final long C4_C3 = C4 / C3; + + static final long C3_C0 = C3 / C0; + static final long C3_C1 = C3 / C1; + static final long C3_C2 = C3 / C2; + + static final long C2_C0 = C2 / C0; + static final long C2_C1 = C2 / C1; + + static final long C1_C0 = C1 / C0; + + static final long MAX_C6_C0 = MAX / C6_C0; + static final long MAX_C6_C1 = MAX / C6_C1; + static final long MAX_C6_C2 = MAX / C6_C2; + static final long MAX_C6_C3 = MAX / C6_C3; + static final long MAX_C6_C4 = MAX / C6_C4; + static final long MAX_C6_C5 = MAX / C6_C5; + + static final long MAX_C5_C0 = MAX / C5_C0; + static final long MAX_C5_C1 = MAX / C5_C1; + static final long MAX_C5_C2 = MAX / C5_C2; + static final long MAX_C5_C3 = MAX / C5_C3; + static final long MAX_C5_C4 = MAX / C5_C4; + + static final long MAX_C4_C0 = MAX / C4_C0; + static final long MAX_C4_C1 = MAX / C4_C1; + static final long MAX_C4_C2 = MAX / C4_C2; + static final long MAX_C4_C3 = MAX / C4_C3; + + static final long MAX_C3_C0 = MAX / C3_C0; + static final long MAX_C3_C1 = MAX / C3_C1; + static final long MAX_C3_C2 = MAX / C3_C2; + + static final long MAX_C2_C0 = MAX / C2_C0; + static final long MAX_C2_C1 = MAX / C2_C1; + + static final long MAX_C1_C0 = MAX / C1_C0; + + static long x(long d, long m, long over) { + if (d > over) return Long.MAX_VALUE; + if (d < -over) return Long.MIN_VALUE; + return d * m; + } + + // exceptions below changed from AbstractMethodError for GWT + + public long convert(long sourceDuration, TimeUnit sourceUnit) { + throw new AssertionError(); + } + + public long toNanos(long duration) { + throw new AssertionError(); + } + + public long toMicros(long duration) { + throw new AssertionError(); + } + + public long toMillis(long duration) { + throw new AssertionError(); + } + + public long toSeconds(long duration) { + throw new AssertionError(); + } + + public long toMinutes(long duration) { + throw new AssertionError(); + } + + public long toHours(long duration) { + throw new AssertionError(); + } + + public long toDays(long duration) { + throw new AssertionError(); + } + + abstract int excessNanos(long d, long m); +} + diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/TimeoutException.java b/user/super/com/google/gwt/emul/java/util/concurrent/TimeoutException.java new file mode 100644 index 0000000000..337c485ef5 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/TimeoutException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent; + +/** + * Emulation of TimeoutException. + * + */ +public class TimeoutException extends Exception { + public TimeoutException() {} + + public TimeoutException(String message) { + super(message); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/DeferredExecutor.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/DeferredExecutor.java new file mode 100644 index 0000000000..7a8a280d16 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/DeferredExecutor.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import java.util.concurrent.Executor; + +/** + * + */ +public final class DeferredExecutor implements Executor { + private final Promise resolvedPromise = Impl.IMPL.completed(null); + + @Override + public void execute(Runnable command) { + resolvedPromise.then(command); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/Impl.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Impl.java new file mode 100644 index 0000000000..fdada6d815 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Impl.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +/** + * + */ +public class Impl { + + public static final Promises IMPL = isSupported() ? new NativePromisesImpl() : + new PromisesImpl(); + + /* + * Implementation taken from: + * https://github.com/jakearchibald/es6-promise/blob/master/lib/promise/polyfill.js + */ + private static native boolean isSupported() /*-{ + return typeof Promise === "function" + // Some of these methods are missing from + // Firefox/Chrome experimental implementations + && "resolve" in Promise + && "reject" in Promise + && "all" in Promise + && "race" in Promise + // Older version of the spec had a resolver object + // as the arg rather than a function + && (function() { + var resolve; + new Promise(function(r) { resolve = r; }); + return typeof resolve === "function"; + }()); + }-*/; + + private Impl() { } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/JsPromise.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/JsPromise.java new file mode 100644 index 0000000000..bb0dd930be --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/JsPromise.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import jsinterop.annotations.JsConstructor; +import jsinterop.annotations.JsFunction; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +/** + * ECMA 6 Promise API. + * See + * + * MDN Promise documentation. + */ +@JsType(isNative = true, name = "Promise", namespace = JsPackage.GLOBAL) +final class JsPromise { + + public static native JsPromise all(JsPromise[] promises); + + public static native JsPromise race(JsPromise[] promises); + + public static native JsPromise reject(Object reason); + + public static native JsPromise resolve(Object value); + + @JsConstructor + public JsPromise(Executor executor) { } + + /* + * Method has no return value for simplicity because the return value of the method, + * onFulfilled and onRejected (OnSettledCallback) are not used. + */ + // TODO: $entry ? + public native void then(OnSettledCallback onFulfilled, + OnSettledCallback onRejected); + + @FunctionalInterface + @JsFunction + interface Executor { + void executor(Resolver resolve, Rejector reject); + } + + @JsFunction + interface Resolver { + void resolve(Object value); + } + + @JsFunction + interface Rejector { + void reject(Object reason); + } + + /* + * Single interface for onFulfilled and onRejected callbacks are used for simplicity. + */ + @FunctionalInterface + @JsFunction + interface OnSettledCallback { + void onSettled(Object value); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromiseImpl.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromiseImpl.java new file mode 100644 index 0000000000..439fc4bb80 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromiseImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import java.util.function.BiConsumer; + +/** + * + */ +final class NativePromiseImpl implements Promise { + + final JsPromise jsPromise; + private JsPromise.Resolver resolver; + private JsPromise.Rejector rejector; + + NativePromiseImpl() { + jsPromise = new JsPromise((resolve, reject) -> { + resolver = resolve; + rejector = reject; + }); + } + + NativePromiseImpl(JsPromise promise) { + assert promise != null; + this.jsPromise = promise; + } + + @Override + public void resolve(V value) { + resolver.resolve(value); + } + + @Override + public void reject(Throwable reason) { + assert reason != null; + rejector.reject(reason); + } + + @SuppressWarnings("unchecked") + @Override + public void then(BiConsumer callback) { + assert callback != null; + jsPromise.then( + value -> callback.accept((V) value, null), + reason -> callback.accept(null, (Throwable) reason)); + } + + @Override + public void then(Runnable callback) { + assert callback != null; + JsPromise.OnSettledCallback func = value -> callback.run(); + jsPromise.then(func, func); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromisesImpl.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromisesImpl.java new file mode 100644 index 0000000000..a9be1e984d --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/NativePromisesImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +/** + * + */ +final class NativePromisesImpl implements Promises { + + @Override + public Promise allOf(Promise[] promises) { + assert promises.length > 0; + return new NativePromiseImpl<>(JsPromise.all(unwrap(promises))); + } + + @Override + public Promise anyOf(Promise[] promises) { + assert promises.length > 0; + return new NativePromiseImpl<>(JsPromise.race(unwrap(promises))); + } + + @Override + public Promise completed(V value) { + return new NativePromiseImpl<>(JsPromise.resolve(value)); + } + + @Override + public Promise incomplete() { + return new NativePromiseImpl<>(); + } + + private static JsPromise[] unwrap(Promise[] promises) { + int length = promises.length; + JsPromise[] jsPromises = new JsPromise[length]; + for (int i = 0; i < length; ++i) { + jsPromises[i] = ((NativePromiseImpl) promises[i]).jsPromise; + } + return jsPromises; + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promise.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promise.java new file mode 100644 index 0000000000..c324696ab0 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promise.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import java.util.function.BiConsumer; + +/** + * + */ +public interface Promise { + + void resolve(V value); + + void reject(Throwable reason); + + void then(BiConsumer callback); + + void then(Runnable callback); +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromiseImpl.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromiseImpl.java new file mode 100644 index 0000000000..e729f9ad18 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromiseImpl.java @@ -0,0 +1,90 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import jsinterop.annotations.JsFunction; +import jsinterop.annotations.JsMethod; +import jsinterop.annotations.JsPackage; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +/** + * + */ +final class PromiseImpl implements Promise { + private V value; + private Throwable reason; + private boolean done; + private final List callbacks = new ArrayList<>(); + + @Override + public void resolve(V value) { + complete(value, null); + } + + @Override + public void reject(Throwable reason) { + assert reason != null; + complete(null, reason); + } + + @Override + public void then(BiConsumer callback) { + assert callback != null; + then(() -> callback.accept(value, reason)); + } + + @Override + public void then(Runnable callback) { + assert callback != null; + callbacks.add(callback); + if (done) { + runCallbacks(); + } + } + + private void complete(V value, Throwable reason) { + if (!done) { + this.value = value; + this.reason = reason; + done = true; + runCallbacks(); + } + } + + private void runCallbacks() { + if (!callbacks.isEmpty()) { + setTimeout(() -> { + for (Runnable callback : callbacks) { + callback.run(); + } + callbacks.clear(); + }, 0); + } + } + + // TODO: use $entry? + @JsMethod(namespace = JsPackage.GLOBAL) + private static native int setTimeout(TimerCallback callback, int time); + + @FunctionalInterface + @JsFunction + private interface TimerCallback { + void onTick(); + } +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promises.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promises.java new file mode 100644 index 0000000000..a35cd6c7d2 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/Promises.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +/** + * + */ +public interface Promises { + + Promise allOf(Promise[] promises); + + Promise anyOf(Promise[] promises); + + Promise completed(V value); + + Promise incomplete(); + +} diff --git a/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromisesImpl.java b/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromisesImpl.java new file mode 100644 index 0000000000..092ddc4e1b --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/concurrent/impl/PromisesImpl.java @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 java.util.concurrent.impl; + +import java.util.function.BiConsumer; + +/** + * + */ +final class PromisesImpl implements Promises { + @Override + public Promise allOf(Promise[] promises) { + assert promises.length > 0; + + Promise andPromise = new PromiseImpl<>(); + BiConsumer callback = new BiConsumer() { + int counter = promises.length; + + @Override + public void accept(Object value, Throwable e) { + if (e != null) { + andPromise.reject(e); + } else if (--counter == 0) { + andPromise.resolve(null); + } + } + }; + + for (Promise promise : promises) { + promise.then(callback); + } + return andPromise; + } + + @Override + public Promise anyOf(Promise[] promises) { + assert promises.length > 0; + + Promise orPromise = new PromiseImpl<>(); + BiConsumer callback = (value, e) -> { + if (e != null) { + orPromise.reject(e); + } else { + orPromise.resolve(value); + } + }; + + for (Promise promise : promises) { + promise.then(callback); + } + return orPromise; + } + + @Override + public Promise completed(V value) { + PromiseImpl promise = new PromiseImpl<>(); + promise.resolve(value); + return promise; + } + + @Override + public Promise incomplete() { + return new PromiseImpl<>(); + } +} diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java index 572eb86bd1..158cd6a8d3 100644 --- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java +++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java @@ -42,6 +42,7 @@ import com.google.gwt.emultest.java8.util.StringJoinerTest; import com.google.gwt.emultest.java8.util.TreeMapTest; import com.google.gwt.emultest.java8.util.VectorTest; +import com.google.gwt.emultest.java8.util.concurrent.CompletableFutureTest; import com.google.gwt.emultest.java8.util.stream.CollectorsTest; import com.google.gwt.emultest.java8.util.stream.DoubleStreamTest; import com.google.gwt.emultest.java8.util.stream.IntStreamTest; @@ -93,6 +94,9 @@ public static Test suite() { suite.addTestSuite(IntSummaryStatisticsTest.class); suite.addTestSuite(LongSummaryStatisticsTest.class); + //-- java.util.concurrent + suite.addTestSuite(CompletableFutureTest.class); + //-- java.util.stream suite.addTestSuite(CollectorsTest.class); suite.addTestSuite(DoubleStreamTest.class); diff --git a/user/test/com/google/gwt/emultest/java8/util/concurrent/CompletableFutureTest.java b/user/test/com/google/gwt/emultest/java8/util/concurrent/CompletableFutureTest.java new file mode 100644 index 0000000000..20c6935c6a --- /dev/null +++ b/user/test/com/google/gwt/emultest/java8/util/concurrent/CompletableFutureTest.java @@ -0,0 +1,971 @@ +/* + * Copyright 2016 Google Inc. + * + * 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 com.google.gwt.emultest.java8.util.concurrent; + +import com.google.gwt.emultest.java.util.EmulTestBase; +import com.google.gwt.user.client.Timer; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +/** + * Tests for java.util.concurrent.CompletableFutureTest. + */ +public class CompletableFutureTest extends EmulTestBase { + + public void testRunAsync_ShouldCompleteWithNullValue_WhenComplete() { + assertThat(runAsync()) + .willComplete() + .withResult(null); + } + + public void testRunAsync_ShouldCompleteWithCancellationException_WhenCancelled() { + CompletableFuture future = runAsync(); + + assertThat(future) + .willComplete() + .withException(new CancellationException()); + + future.cancel(true); + } + + public void testRunAsync_ShouldCompleteWithCompletionException_WhenThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = runAsyncWithException(taskException); + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testRunAsync_ShouldCompleteWithValue_WhenCalledObtrudeValue() { + CompletableFuture future = runAsync(); + + assertThat(future) + .willComplete() + .withResult(null); + + future.obtrudeValue(null); + } + + public void testRunAsync_ShouldCompleteWithException_WhenCalledObtrudeException() { + RuntimeException taskException1 = new RuntimeException(); + RuntimeException taskException2 = new RuntimeException(); + CompletableFuture future = runAsyncWithException(taskException1); + + assertThat(future) + .willComplete() + .withException(taskException2); + + future.obtrudeException(taskException2); + } + + public void testRunAsync_ShouldCompleteWithValue2_WhenCalledCompleteValue2() { + CompletableFuture future = runAsyncWithException(new RuntimeException()); + + assertThat(future) + .willComplete() + .withResult(null); + + future.complete(null); + } + + public void testRunAsync_ShouldCompleteWithException_WhenCalledCompleteExceptionally() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = runAsync(); + + assertThat(future) + .willComplete() + .withException(taskException); + + future.completeExceptionally(taskException); + } + + public void testRunAsync_ShouldComplete_WhenCalledExceptionally() { + CompletableFuture future = runAsyncWithException(new RuntimeException()) + .exceptionally(e -> null); + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_ShouldCompleteWithValue_WhenComplete() { + Object result = new Object(); + CompletableFuture future = supplyAsyncWithResult(result); + + assertThat(future) + .willComplete() + .withResult(result); + } + + public void testSupplyAsync_ShouldCompleteWithCancellationException_WhenCancelled() { + CompletableFuture future = supplyAsyncWithResult(null); + + assertThat(future) + .willComplete() + .withException(new CancellationException()); + + future.cancel(true); + } + + public void testSupplyAsync_ShouldCompleteWithCompletionException_WhenThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException); + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_ShouldCompleteWithValue_WhenCalledObtrudeValue() { + Object result = new Object(); + CompletableFuture future = supplyAsyncWithResult(null); + + assertThat(future) + .willComplete() + .withResult(result); + + future.obtrudeValue(result); + } + + public void testSupplyAsync_ShouldCompleteWithException_WhenCalledObtrudeException() { + RuntimeException taskException1 = new RuntimeException(); + RuntimeException taskException2 = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException1); + + assertThat(future) + .willComplete() + .withException(taskException2); + + future.obtrudeException(taskException2); + } + + public void testSupplyAsync_ShouldCompleteWithValue2_WhenCalledCompleteValue2() { + Object value1 = new Object(); + Object value2 = new Object(); + CompletableFuture future = supplyAsyncWithResult(value1); + + assertThat(future) + .willComplete() + .withResult(value2); + + future.complete(value2); + } + + public void testSupplyAsync_ShouldCompleteWithException_WhenCalledCompleteExceptionally() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithResult(new Object()); + + assertThat(future) + .willComplete() + .withException(taskException); + + future.completeExceptionally(taskException); + } + + public void testSupplyAsync_ShouldComplete_WhenCalledExceptionally() { + String result = "result"; + RuntimeException runException = new RuntimeException(result); + CompletableFuture future = supplyAsyncWithException(runException) + .exceptionally(e -> e.getCause().getMessage()); + + assertThat(future) + .willComplete() + .withResult(result); + } + + public void testSupplyAsync_thenApply() { + CompletableFuture future = supplyAsyncWithResult("a") + .thenApply(v -> v + "b"); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_thenApplyShouldFail_WhenThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithResult("a") + .thenApply(v -> { + throw taskException; + }); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenApplyShouldFail_WhenFutureThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException) + .thenApply(v -> { + fail(getTag() + "should not be called"); + return null; + }); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenAccept() { + CompletableFuture future = supplyAsyncWithResult("a") + .thenAccept(v -> assertEquals("a", v)); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_thenAcceptShouldFail_WhenThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithResult("a") + .thenAccept(v -> { + throw taskException; + }); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenAcceptShouldFail_WhenFutureThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException) + .thenAccept(v -> fail(getTag() + "should not be called")); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenRun() { + CompletableFuture future = supplyAsyncWithResult("a") + .thenRun(() -> {}); // TODO: test that it's called + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_thenRunShouldFail_WhenThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithResult("a") + .thenRun(() -> { + throw taskException; + }); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenRunShouldFail_WhenFutureThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException) + .thenRun(() -> fail(getTag() + "should not be called")); + + // TODO: also check original future result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenCombine() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a") + .thenCombine(other, (v1, v2) -> v1 + v2); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_thenCombineWithCompletedFuture() { + CompletableFuture other = CompletableFuture.completedFuture("b"); + CompletableFuture future = supplyAsyncWithResult("a") + .thenCombine(other, (v1, v2) -> v1 + v2); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_CompletedFutureThenCombine() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = CompletableFuture.completedFuture("a") + .thenCombine(other, (v1, v2) -> v1 + v2); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_thenCombineShouldComplete_WhenCompletedFutures() { + CompletableFuture other = CompletableFuture.completedFuture("b"); + CompletableFuture future = CompletableFuture.completedFuture("a") + .thenCombine(other, (v1, v2) -> v1 + v2); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_thenCombineShouldCompleteExceptionally_WhenFirstFutureThrowsException() { + CompletableFuture other = supplyAsyncWithResult("b"); + RuntimeException taskException = new RuntimeException(); + CompletableFuture future = supplyAsyncWithException(taskException) + .thenCombine(other, (v1, v2) -> { + fail(getTag() + "should not be called"); + return null; + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenCombineShouldCompleteExceptionally_WhenOtherFutureThrowsException() { + RuntimeException taskException = new RuntimeException(); + CompletableFuture other = supplyAsyncWithException(taskException); + CompletableFuture future = CompletableFuture.completedFuture("a") + .thenCombine(other, (v1, v2) -> { + fail(getTag() + "should not be called"); + return null; + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withException(new CompletionException(taskException)); + } + + public void testSupplyAsync_thenAcceptBoth() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a") + .thenAcceptBoth(other, (v1, v2) -> { + assertEquals("a", v1); + assertEquals("b", v2); + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_runAfterBoth() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a") + .runAfterBoth(other, () -> {}); // TODO: test that it's called + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_applyToEither() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a", LONGER_DELAY) + .applyToEither(other, v -> { + assertEquals("b", v); + return v; + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("b"); + } + + public void testSupplyAsync_completedFutureApplyToEither() { + CompletableFuture other = CompletableFuture.completedFuture("b"); + CompletableFuture future = supplyAsyncWithResult("a") + .applyToEither(other, v -> { + assertEquals("b", v); + return v; + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("b"); + } + + public void testSupplyAsync_acceptEither() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a", LONGER_DELAY) + .acceptEither(other, v -> assertEquals("b", v)); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_runAfterEither() { + CompletableFuture other = supplyAsyncWithResult("b"); + CompletableFuture future = supplyAsyncWithResult("a", LONGER_DELAY) + .runAfterEither(other, () -> {});// TODO: test that it's called + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testSupplyAsync_thenCompose() { + CompletableFuture future = supplyAsyncWithResult("a") + .thenCompose(v -> CompletableFuture.completedFuture(v));// TODO: rewrite the test + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("a"); + } + + public void testSupplyAsync_handle() { + CompletableFuture future = supplyAsyncWithResult("a") + .handle((r, e) -> { + assertEquals("a", r); + assertNull(e); + return r + "b"; + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("ab"); + } + + public void testSupplyAsync_whenComplete() { + CompletableFuture future = supplyAsyncWithResult("a") + .whenComplete((r, e) -> { + assertEquals("a", r); + assertNull(e); + }); + + // TODO: also check original futures result? + + assertThat(future) + .willComplete() + .withResult("a"); + } + + public void testSupplyAsync_exceptionally() { + Object result = new Object(); + CompletableFuture future = supplyAsyncWithException(new RuntimeException()) + .exceptionally(e -> result); + + assertThat(future) + .willComplete() + .withResult(result); + } + + public void testCompletedFuture_ShouldBeCompletedWithResult() { + Object result = new Object(); + CompletableFuture future = CompletableFuture.completedFuture(result); + + assertThat(future) + .completed() + .withResult(result); + } + + public void testCompletedFuture_ShouldCompleteWithResult_WhenComplete() { + Object result = new Object(); + CompletableFuture future = CompletableFuture.completedFuture(result); + + assertThat(future) + .willComplete() + .withResult(result); + } + + public void testCompletedFuture_ShouldBeCompletedWithResult2_WhenCalledObtrudeResult2() { + Object result1 = new Object(); + CompletableFuture future = CompletableFuture.completedFuture(result1); + + Object result2 = new Object(); + future.obtrudeValue(result2); + + assertThat(future) + .completed() + .withResult(result2); + } + + public void testCompletedFuture_ShouldBeCompletedWithResult_WhenCalledObtrudeException() { + CompletableFuture future = CompletableFuture.completedFuture(new Object()); + + Exception taskException = new Exception(); + future.obtrudeException(taskException); + + assertThat(future) + .completed() + .withException(new ExecutionException(taskException)); + } + + public void testAllOf_ShouldComplete_WhenEmpty() throws Exception { + assertThat(CompletableFuture.allOf()) + .willComplete() + .withResult(null); + } + + public void testAllOf_ShouldBeCompleted_WhenEmpty() throws Exception { + assertThat(CompletableFuture.allOf()) + .willComplete() + .withResult(null); + } + + public void testAllOf_ShouldComplete_WhenCalledWithFutures() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGEST_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, SHORT_DELAY); + + assertThat(CompletableFuture.allOf(future1, future2, future3)) + .willComplete() + .withResult(null); + } + + public void testAllOf_ShouldCompleteExceptionally_WhenFutureCompletesExceptionally() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + RuntimeException failure = new RuntimeException(); + CompletableFuture future3 = runAsyncWithException(failure); + + assertThat(CompletableFuture.allOf(future1, future2, future3)) + .willComplete() + .withException(new CompletionException(failure)); + } + + public void testAllOf_ShouldCompleteExceptionally_WhenFutureCancelled() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, LONGER_DELAY); + + assertThat(CompletableFuture.allOf(future1, future2, future3)) + .willComplete() + .withException(new CompletionException(new CancellationException())); + + future2.cancel(true); + } + + public void testAllOf_ShouldCompleteExceptionally_WhenCancelled() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, LONGER_DELAY); + CompletableFuture future = CompletableFuture.allOf(future1, future2, future3); + + assertThat(future) + .willComplete() + .withException(new CancellationException()); + + future.cancel(true); + } + + public void testAllOf_ShouldBeCompleted_WhenCalledWithCompletedFutures() { + CompletableFuture future = CompletableFuture.allOf( + CompletableFuture.completedFuture(1), + CompletableFuture.completedFuture(2), + CompletableFuture.completedFuture(3)); + + assertThat(future) + .completed() + .withResult(null); + } + + public void testAllOf_ShouldBeCompleted_WhenCalledWithExceptionallyCompletedFuture() { + CompletableFuture exceptionallyCompleted = runAsync(); + RuntimeException taskException = new RuntimeException(); + exceptionallyCompleted.completeExceptionally(taskException); + CompletableFuture future = CompletableFuture.allOf( + exceptionallyCompleted, + CompletableFuture.completedFuture(3)); + + assertThat(future) + .completed() + .withException(new ExecutionException(taskException)); + } + + public void testAllOf_ShouldComplete_WhenContainsCompletedFuture() { + CompletableFuture future = CompletableFuture.allOf( + CompletableFuture.completedFuture(1), + supplyAsyncWithResult(2)); + + assertThat(future) + .willComplete() + .withResult(null); + } + + public void testAnyOf_ShouldBeIncomplete_WhenEmpty() { + CompletableFuture future = CompletableFuture.anyOf(); + assertFalse(future.isDone()); + finishTest(); + } + + public void testAnyOf_ShouldComplete_WhenCalledWithFutures() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGEST_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, SHORT_DELAY); + + assertThat(CompletableFuture.anyOf(future1, future2, future3)) + .willComplete() + .withResult(result3); + } + + public void testAnyOf_ShouldCompleteExceptionally_WhenFutureCompletesExceptionally() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + RuntimeException failure = new RuntimeException(); + CompletableFuture future3 = runAsyncWithException(failure); + + assertThat(CompletableFuture.anyOf(future1, future2, future3)) + .willComplete() + .withException(new CompletionException(failure)); + } + + public void testAnyOf_ShouldCompleteExceptionally_WhenFutureCancelled() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, LONGER_DELAY); + + delay(() -> future2.cancel(true), SHORT_DELAY); + + assertThat(CompletableFuture.anyOf(future1, future2, future3)) + .willComplete() + .withException(new CompletionException(new CancellationException())); + } + + public void testAnyOf_ShouldCompleteExceptionally_WhenCancelled() { + Object result1 = new Object(); + CompletableFuture future1 = supplyAsyncWithResult(result1, LONGER_DELAY); + Object result2 = new Object(); + CompletableFuture future2 = supplyAsyncWithResult(result2, LONGER_DELAY); + Object result3 = new Object(); + CompletableFuture future3 = supplyAsyncWithResult(result3, LONGER_DELAY); + CompletableFuture future = CompletableFuture.anyOf(future1, future2, future3); + + assertThat(future) + .willComplete() + .withException(new CancellationException()); + + future.cancel(true); + } + + public void testAnyOf_ShouldBeCompleted_WhenCalledWithCompletedFutures() { + CompletableFuture future = CompletableFuture.anyOf( + CompletableFuture.completedFuture(1), + CompletableFuture.completedFuture(2), + CompletableFuture.completedFuture(3) + ); + + assertThat(future) + .completed() + .withResult(1); + } + + public void testAnyOf_ShouldComplete_WhenContainsCompletedFuture() { + CompletableFuture future = CompletableFuture.anyOf( + CompletableFuture.completedFuture(1), + supplyAsyncWithResult(2)); + + assertThat(future) + .willComplete() + .withResult(1); + } + + public void testAnyOf_ShouldBeCompleted_WhenContainsCompletedFuture() { + CompletableFuture future = CompletableFuture.anyOf( + CompletableFuture.completedFuture(1), + supplyAsyncWithResult(2)); + + assertThat(future) + .completed() + .withResult(1); + } + + public void testAnyOf_ShouldBeCompleted_WhenCalledWithExceptionallyCompletedFuture() { + CompletableFuture exceptionallyCompleted = runAsync(); + RuntimeException taskException = new RuntimeException(); + exceptionallyCompleted.completeExceptionally(taskException); + CompletableFuture future = CompletableFuture.anyOf( + supplyAsyncWithResult(new Object()), + exceptionallyCompleted, + CompletableFuture.completedFuture(3)); + + assertThat(future) + .completed() + .withException(new ExecutionException(taskException)); + } + + private FutureSubject assertThat(CompletableFuture future) { + return new FutureSubject(future); + } + + private class FutureSubject { + private final CompletableFuture future; + + public FutureSubject(CompletableFuture future) { + this.future = future; + } + + public IncompleteFutureSubject willComplete() { + return new IncompleteFutureSubject(future); + } + + public CompletedFutureSubject completed() { + return new CompletedFutureSubject(future); + } + } + + private class CompletedFutureSubject { + private final CompletableFuture future; + + public CompletedFutureSubject(CompletableFuture future) { + this.future = future; + } + + public void withResult(Object expected) { + assertCompletedSuccessfully(future); + // TODO: use whenComplete? + try { + assertEquals(getTag() + "future has different result", expected, future.get()); + } catch (InterruptedException | ExecutionException e) { + fail(getTag() + "failed to get completed future result: " + e.getMessage()); + } + // TODO + finishTest(); + } + + public void withException(Throwable expected) { + assertCompletedExceptionally(future); + // TODO: use whenComplete? + try { + Object result = future.get(); + fail(getTag() + "future has been completed exceptionally but has result: " + result); + } catch (InterruptedException e) { + fail(getTag() + "failed to get completed future result: " + e.getMessage()); + } catch (ExecutionException e) { + assertEquals(getTag() + "failed future should have thrown " + expected + + " but threw " + e, expected, e); + } + // TODO + finishTest(); + } + } + + private class IncompleteFutureSubject { + private final CompletableFuture future; + + public IncompleteFutureSubject(CompletableFuture future) { + this.future = future; + } + + public void withResult(Object expected) { + future.whenComplete((r, e) -> { + assertCompletedWithResult(future, expected, r, e); + finishTest(); + }); + } + + public void withException(Throwable expected) { + future.whenComplete((r, e) -> { + assertFailed(future, expected, r, e); + finishTest(); + }); + } + } + + private static final int SHORT_DELAY = 100; + private static final int LONGER_DELAY = 200; + private static final int LONGEST_DELAY = 300; + + private static final int TEST_FINISH_DELAY = 10000; + + @Override + protected void gwtSetUp() throws Exception { + delayTestFinish(TEST_FINISH_DELAY); + } + + private static CompletableFuture supplyAsyncWithResult(T result) { + return supplyAsyncWithResult(result, SHORT_DELAY); + } + + private static CompletableFuture supplyAsyncWithResult(T result, int delay) { + return CompletableFuture.supplyAsync(() -> result, delayExecutionBy(delay)); + } + + private static CompletableFuture supplyAsyncWithException(RuntimeException e) { + return supplyAsyncWithException(e, SHORT_DELAY); + } + + private static CompletableFuture supplyAsyncWithException(RuntimeException e, int delay) { + return CompletableFuture.supplyAsync(() -> { + throw e; + }, delayExecutionBy(delay)); + } + + private static CompletableFuture runAsync() { + return runAsync(SHORT_DELAY); + } + + private static CompletableFuture runAsync(int delay) { + return CompletableFuture.runAsync(() -> {}, delayExecutionBy(delay)); + } + + private static CompletableFuture runAsyncWithException(RuntimeException e) { + return runAsyncWithException(e, SHORT_DELAY); + } + + private static CompletableFuture runAsyncWithException(RuntimeException e, int delay) { + return CompletableFuture.runAsync(() -> { + throw e; + }, delayExecutionBy(delay)); + } + + private void assertCompletedWithResult(CompletableFuture future, Object expected, + Object actual, Throwable e) { + assertCompletedSuccessfully(future); + // TODO: assertSame? + assertEquals(getTag() + "future should have returned " + expected + " but it returned " + + actual + " instead", expected, actual); + assertNull(getTag() + "future should not have thrown exception but it threw " + e, e); + } + + private void assertFailed(CompletableFuture future, Throwable expected, + Object r, Throwable actual) { + + assertTrue(getTag() + "failed future should have been completed", future.isDone()); + assertTrue(getTag() + "failed future should have been completed exceptionally", + future.isCompletedExceptionally()); + assertNull(getTag() + "failed future should have no result", r); + assertEquals(getTag() + "failed future should have thrown " + expected + " but threw " + actual, + expected, actual); + } + + private static void assertEquals(String message, Throwable expected, Throwable actual) { + if (expected == null) { + assertNull(message, actual); + return; + } + + assertNotNull(message, actual); + if (expected instanceof CancellationException + || expected instanceof CompletionException + || expected instanceof ExecutionException) { + assertEquals(message, expected.getClass(), actual.getClass()); + assertEquals(message, expected.getMessage(), actual.getMessage()); + assertEquals(message, expected.getCause(), actual.getCause()); + } else { + assertEquals(message, (Object) expected, (Object) actual); + } + } + + private void assertCompletedSuccessfully(CompletableFuture future) { + assertTrue(getTag() + "future should have been completed", future.isDone()); + assertFalse(getTag() + "future should not have been cancelled", future.isCancelled()); + assertFalse(getTag() + "future should not have been completed exceptionally", + future.isCompletedExceptionally()); + } + + private void assertCompletedExceptionally(CompletableFuture future) { + assertTrue(getTag() + "future should have been completed", future.isDone()); + assertFalse(getTag() + "future should not have been cancelled", future.isCancelled()); + assertTrue(getTag() + "future should have been completed exceptionally", + future.isCompletedExceptionally()); + } + + private String getTag() { + return "[" + getName() + "] "; + } + + private static Executor delayExecutionBy(final int millis) { + return new Executor() { + @Override + public void execute(Runnable command) { + delay(command, millis); + } + }; + } + + private static void delay(final Runnable runnable, int delay) { + Timer timer = new Timer() { + @Override + public void run() { + runnable.run(); + } + }; + timer.schedule(delay); + } +}