package com.google.common.util.concurrent;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.getDone;
import static com.google.common.util.concurrent.MoreExecutors.rejectionPropagatingExecutor;
import static com.google.common.util.concurrent.Platform.isInstanceOfThrowableClass;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.errorprone.annotations.ForOverride;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import org.checkerframework.checker.nullness.qual.Nullable;
@GwtCompatible
abstract class AbstractCatchingFuture<V, X extends Throwable, F, T>
extends FluentFuture.TrustedFuture<V> implements Runnable {
static <V, X extends Throwable> ListenableFuture<V> create(
ListenableFuture<? extends V> input,
Class<X> exceptionType,
Function<? super X, ? extends V> fallback,
Executor executor) {
CatchingFuture<V, X> future = new CatchingFuture<>(input, exceptionType, fallback);
input.addListener(future, rejectionPropagatingExecutor(executor, future));
return future;
}
static <X extends Throwable, V> ListenableFuture<V> create(
ListenableFuture<? extends V> input,
Class<X> exceptionType,
AsyncFunction<? super X, ? extends V> fallback,
Executor executor) {
AsyncCatchingFuture<V, X> future = new AsyncCatchingFuture<>(input, exceptionType, fallback);
input.addListener(future, rejectionPropagatingExecutor(executor, future));
return future;
}
@Nullable ListenableFuture<? extends V> inputFuture;
@Nullable Class<X> exceptionType;
@Nullable F fallback;
AbstractCatchingFuture(
ListenableFuture<? extends V> inputFuture, Class<X> exceptionType, F fallback) {
this.inputFuture = checkNotNull(inputFuture);
this.exceptionType = checkNotNull(exceptionType);
this.fallback = checkNotNull(fallback);
}
@Override
public final void run() {
ListenableFuture<? extends V> localInputFuture = inputFuture;
Class<X> localExceptionType = exceptionType;
F localFallback = fallback;
if (localInputFuture == null
| localExceptionType == null
| localFallback == null
| isCancelled()) {
return;
}
inputFuture = null;
V sourceResult = null;
Throwable throwable = null;
try {
sourceResult = getDone(localInputFuture);
} catch (ExecutionException e) {
throwable = checkNotNull(e.getCause());
} catch (Throwable e) {
throwable = e;
}
if (throwable == null) {
set(sourceResult);
return;
}
if (!isInstanceOfThrowableClass(throwable, localExceptionType)) {
setFuture(localInputFuture);
return;
}
@SuppressWarnings("unchecked")
X castThrowable = (X) throwable;
T fallbackResult;
try {
fallbackResult = doFallback(localFallback, castThrowable);
} catch (Throwable t) {
setException(t);
return;
} finally {
exceptionType = null;
fallback = null;
}
setResult(fallbackResult);
}
@Override
protected String pendingToString() {
ListenableFuture<? extends V> localInputFuture = inputFuture;
Class<X> localExceptionType = exceptionType;
F localFallback = fallback;
String superString = super.pendingToString();
String resultString = "";
if (localInputFuture != null) {
resultString = "inputFuture=[" + localInputFuture + "], ";
}
if (localExceptionType != null && localFallback != null) {
return resultString
+ "exceptionType=["
+ localExceptionType
+ "], fallback=["
+ localFallback
+ "]";
} else if (superString != null) {
return resultString + superString;
}
return null;
}
@ForOverride
abstract @Nullable T doFallback(F fallback, X throwable) throws Exception;
@ForOverride
abstract void setResult(@Nullable T result);
@Override
protected final void afterDone() {
maybePropagateCancellationTo(inputFuture);
this.inputFuture = null;
this.exceptionType = null;
this.fallback = null;
}
private static final class AsyncCatchingFuture<V, X extends Throwable>
extends AbstractCatchingFuture<
V, X, AsyncFunction<? super X, ? extends V>, ListenableFuture<? extends V>> {
AsyncCatchingFuture(
ListenableFuture<? extends V> input,
Class<X> exceptionType,
AsyncFunction<? super X, ? extends V> fallback) {
super(input, exceptionType, fallback);
}
@Override
ListenableFuture<? extends V> doFallback(
AsyncFunction<? super X, ? extends V> fallback, X cause) throws Exception {
ListenableFuture<? extends V> replacement = fallback.apply(cause);
checkNotNull(
replacement,
"AsyncFunction.apply returned null instead of a Future. "
+ "Did you mean to return immediateFuture(null)? %s",
fallback);
return replacement;
}
@Override
void setResult(ListenableFuture<? extends V> result) {
setFuture(result);
}
}
private static final class CatchingFuture<V, X extends Throwable>
extends AbstractCatchingFuture<V, X, Function<? super X, ? extends V>, V> {
CatchingFuture(
ListenableFuture<? extends V> input,
Class<X> exceptionType,
Function<? super X, ? extends V> fallback) {
super(input, exceptionType, fallback);
}
@Override
@Nullable
V doFallback(Function<? super X, ? extends V> fallback, X cause) throws Exception {
return fallback.apply(cause);
}
@Override
void setResult(@Nullable V result) {
set(result);
}
}
}