FutureChain.kt
/*
* Copyright 2023 The Android Open Source Project
*
* 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 androidx.camera.impl.utils.futures
import androidx.arch.core.util.Function
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.core.util.Preconditions
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
/**
* A [ListenableFuture] that supports chains of operations. For example:
*
* <pre>`ListenableFuture<Boolean> adminIsLoggedIn =
* FutureChain.from(usersDatabase.getAdminUser())
* .transform(User::getId, directExecutor())
* .transform(ActivityService::isLoggedIn, threadPool);
`</pre> *
* @param <V>
</V> */
open class FutureChain<V> : ListenableFuture<V> {
private val mDelegate: ListenableFuture<V>
private var mCompleter: CallbackToFutureAdapter.Completer<V>? = null
/**
* Returns a new `Future` whose result is derived from the result of this `Future`.
* If this input `Future` fails, the returned `Future` fails with the same
* exception (and the function is not invoked).
*
* @param function A Function to transform the results of this future to the results of the
* returned future.
* @param executor Executor to run the function in.
* @return A future that holds result of the transformation.
*/
fun <T> transform(
function: Function<in V?, out T>,
executor: Executor
): FutureChain<T> {
return Futures.transform(
this,
function,
executor
) as FutureChain<T>
}
internal constructor(delegate: ListenableFuture<V>) {
mDelegate = Preconditions.checkNotNull(delegate)
}
internal constructor() {
mDelegate = CallbackToFutureAdapter.getFuture { completer ->
Preconditions.checkState(
mCompleter == null,
"The result can only set once!"
)
mCompleter = completer
"FutureChain[" + this@FutureChain + "]"
}
}
override fun addListener(listener: Runnable, executor: Executor) {
mDelegate.addListener(listener, executor)
}
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
return mDelegate.cancel(mayInterruptIfRunning)
}
override fun isCancelled(): Boolean {
return mDelegate.isCancelled
}
override fun isDone(): Boolean {
return mDelegate.isDone
}
@Throws(InterruptedException::class, ExecutionException::class)
override fun get(): V? {
return mDelegate.get()
}
@Throws(
InterruptedException::class,
ExecutionException::class,
TimeoutException::class
)
override fun get(timeout: Long, unit: TimeUnit): V? {
return mDelegate[timeout, unit]
}
fun set(value: V?): Boolean {
return if (mCompleter != null) {
mCompleter!!.set(value)
} else false
}
fun setException(throwable: Throwable): Boolean {
return if (mCompleter != null) {
mCompleter!!.setException(throwable)
} else false
}
companion object {
/**
* Converts the given `ListenableFuture` to an equivalent `FutureChain`.
*
*
* If the given `ListenableFuture` is already a `FutureChain`, it is returned
* directly. If not, it is wrapped in a `FutureChain` that delegates all calls to the
* original `ListenableFuture`.
*
* @return directly if input a FutureChain or a ListenableFuture wrapped by FutureChain.
*/
fun <V> from(future: ListenableFuture<V>): FutureChain<V> {
return if (future is FutureChain<*>) future as FutureChain<V> else FutureChain(future)
}
}
}