/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.promises.client.js; import com.google.gwt.core.client.JavaScriptObject; import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.FunctionException; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.api.promises.client.Thenable; /** * Implementation of {@link Promise} around ES6 promises. * * @param <V> * the type of the promised value * @author Mickaƫl Leduque * @author Artem Zatsarynnyi */ public class JsPromise<V> extends JavaScriptObject implements Promise<V> { /** * JSO mandated protected constructor. */ protected JsPromise() { } private static void applyOperationToError(Operation<PromiseError> onRejected, Object reason) throws OperationException { final Throwable cause = getCause(reason); if (cause != null) { onRejected.apply(JsPromiseError.create(cause)); } else { onRejected.apply(JsPromiseError.create(reason instanceof Throwable ? (Throwable)reason : null)); } } private static <V> V applyFunctionToError(Function<PromiseError, V> onRejected, Object reason) throws FunctionException { final Throwable cause = getCause(reason); if (cause != null) { return onRejected.apply(JsPromiseError.create(cause)); } else { return onRejected.apply(JsPromiseError.create(reason instanceof Throwable ? (Throwable)reason : null)); } } private final static <B> Thenable<B> staticThen(Thenable<B> thenable, B arg) { return thenable.then(arg); } @Override public final native <B> Thenable<B> then(V arg) /*-{ return this.then(arg); }-*/; @Override public final native <B> Promise<B> then(Function<V, B> onFulfilled) /*-{ return this.then(function (value) { return onFulfilled.@org.eclipse.che.api.promises.client.Function::apply(*)(value); }); }-*/; @Override public final native <B> Promise<B> thenPromise(Function<V, Promise<B>> onFulfilled) /*-{ return this.then(function (value) { return onFulfilled.@org.eclipse.che.api.promises.client.Function::apply(*)(value); }); }-*/; @Override public final <B> Promise<B> then(Function<V, B> onFulfilled, Function<PromiseError, B> onRejected) { if (onFulfilled != null) { return this.internalThen(onFulfilled, onRejected); } else { return this.catchError(onRejected); } } private final native <B> Promise<B> internalThen(Function<V, B> onFulfilled, Function<PromiseError, B> onRejected) /*-{ return this.then(function (value) { return onFulfilled.@org.eclipse.che.api.promises.client.Function::apply(*)(value); }, function (reason) { return @org.eclipse.che.api.promises.client.js.JsPromise::applyFunctionToError(*)(onRejected, reason); }); }-*/; @Override public final native <B> Promise<B> catchError(Function<PromiseError, B> onRejected) /*-{ return this.then(undefined, function (reason) { return @org.eclipse.che.api.promises.client.js.JsPromise::applyFunctionToError(*)(onRejected, reason); }); }-*/; @Override public final native <B> Promise<B> catchErrorPromise(Function<PromiseError, Promise<B>> onRejected) /*-{ return this.then(undefined, function (reason) { return @org.eclipse.che.api.promises.client.js.JsPromise::applyFunctionToError(*)(onRejected, reason); }); }-*/; @Override public final native Promise<V> then(Operation<V> onFulfilled) /*-{ return this.then(function (value) { onFulfilled.@org.eclipse.che.api.promises.client.Operation::apply(*)(value); }); }-*/; @Override public final Promise<V> then(Operation<V> onFulfilled, Function<PromiseError, V> onRejected) { if (onFulfilled != null) { return this.internalThen(onFulfilled, onRejected); } else { return this.catchError(onRejected); } } private final native <B> Promise<B> internalThen(Operation<V> onFulfilled, Function<PromiseError, V> onRejected) /*-{ return this.then(function (value) { onFulfilled.@org.eclipse.che.api.promises.client.Operation::apply(*)(value); }, function (reason) { return @org.eclipse.che.api.promises.client.js.JsPromise::applyFunctionToError(*)(onRejected, reason); }); }-*/; @Override public final Promise<V> then(Operation<V> onFulfilled, Operation<PromiseError> onRejected) { if (onFulfilled != null) { return this.internalThen(onFulfilled, onRejected); } else { return this.catchError(onRejected); } } private final native Promise<V> internalThen(Operation<V> onFulfilled, Operation<PromiseError> onRejected) /*-{ return this.then(function (value) { onFulfilled.@org.eclipse.che.api.promises.client.Operation::apply(*)(value); }, function (reason) { @org.eclipse.che.api.promises.client.js.JsPromise::applyOperationToError(*)(onRejected, reason); }); }-*/; @Override public final <B> Promise<B> then(final Thenable<B> thenable) { if (thenable instanceof JavaScriptObject) { return this.thenJs((JavaScriptObject)thenable); } else { return this.thenJava(thenable); } } @Override public final native Promise<V> catchError(Operation<PromiseError> onRejected) /*-{ return this.then(undefined, function (reason) { @org.eclipse.che.api.promises.client.js.JsPromise::applyOperationToError(*)(onRejected, reason); }); }-*/; private final native <B> Promise<B> thenJs(JavaScriptObject thenable) /*-{ return this.then(thenable); }-*/; private final native <B> Promise<B> thenJava(Thenable<B> thenable) /*-{ return this.then(new Object() { then: function (arg) { return @org.eclipse.che.api.promises.client.js.JsPromise::staticThen(*)(thenable, arg); } } ) ; }-*/; public static Throwable getCause(Object object) { return object instanceof JsPromiseError ? ((JsPromiseError)object).getCause() : null; } }