Futures2.java revision 3ac7a551f005d43f86617ae400b7a203c552b386
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.async;
18
19import com.google.common.util.concurrent.AsyncFunction;
20import com.google.common.util.concurrent.ForwardingListenableFuture;
21import com.google.common.util.concurrent.Futures;
22import com.google.common.util.concurrent.ListenableFuture;
23
24import java.util.List;
25
26import javax.annotation.Nullable;
27
28/**
29 * TODO Replace with Guava's com.google.common.util.concurrent.Futures once we
30 * can build with v. 15+
31 */
32public class Futures2 {
33    /**
34     * 2 parameter async function for joined async results.
35     */
36    public interface AsyncFunction2<T1, T2, TResult> {
37        ListenableFuture<TResult> apply(T1 value1, T2 value2) throws Exception;
38    }
39
40    /**
41     * 3 parameter async function for joined async results.
42     */
43    public interface AsyncFunction3<T1, T2, T3, TResult> {
44        ListenableFuture<TResult> apply(T1 value1, T2 value2, T3 value3) throws Exception;
45    }
46
47    /**
48     * 2 parameter function for joining multiple results into a single value.
49     */
50    public interface Function2<T1, T2, TResult> {
51        TResult apply(T1 value1, T2 value2);
52    }
53
54    /**
55     * 3 parameter function for joining multiple results into a single value.
56     */
57    public interface Function3<T1, T2, T3, TResult> {
58        TResult apply(T1 value1, T2 value2, T3 value3);
59    }
60
61    private Futures2() {
62    }
63
64    /**
65     * Creates a new ListenableFuture whose result is set from the supplied
66     * future when it completes. Cancelling the supplied future will also cancel
67     * the returned future, but cancelling the returned future will have no
68     * effect on the supplied future.
69     */
70    public static <T> ListenableFuture<T> nonCancellationPropagating(
71            final ListenableFuture<T> future) {
72        return new ForwardingListenableFuture.SimpleForwardingListenableFuture<T>(future) {
73            @Override
74            public boolean cancel(boolean mayInterruptIfNecessary) {
75                return false;
76            }
77        };
78    }
79
80    /**
81     * Create a new joined future from two existing futures and a joining function
82     * that combines the resulting outputs of the previous functions into a single
83     * result. The resulting future will fail if any of the dependent futures also
84     * fail.
85     */
86    public static <T1, T2, TResult> ListenableFuture<TResult> joinAll(
87          final ListenableFuture<T1> f1,
88          final ListenableFuture<T2> f2,
89          final AsyncFunction2<T1, T2, TResult> fn) {
90        ListenableFuture<?>[] futures = new ListenableFuture<?>[2];
91
92        futures[0] = f1;
93        futures[1] = f2;
94
95        // Futures.allAsList is used instead of Futures.successfulAsList because
96        // allAsList will propagate the failures instead of null values to the
97        // parameters of the supplied function.
98        ListenableFuture<List<Object>> result = Futures.allAsList(futures);
99        return Futures.transform(result, new AsyncFunction<List<Object>, TResult>() {
100            @Override
101            public ListenableFuture<TResult> apply(@Nullable List<Object> list) throws Exception {
102                T1 value1 = (T1) list.get(0);
103                T2 value2 = (T2) list.get(1);
104
105                return fn.apply(value1, value2);
106            }
107        });
108    }
109
110    /**
111     * Create a new joined future from two existing futures and an async function
112     * that combines the resulting outputs of the previous functions into a single
113     * result. The resulting future will fail if any of the dependent futures also
114     * fail.
115     */
116    public static <T1, T2, TResult> ListenableFuture<TResult> joinAll(
117          final ListenableFuture<T1> f1,
118          final ListenableFuture<T2> f2,
119          final Function2<T1, T2, TResult> fn) {
120        return joinAll(f1, f2, new ImmediateAsyncFunction2<>(fn));
121    }
122
123    /**
124     * Create a new joined future from three existing futures and a joining function
125     * that combines the resulting outputs of the previous functions into a single
126     * result. The resulting future will fail if any of the dependent futures also
127     * fail.
128     */
129    public static <T1, T2, T3, TResult> ListenableFuture<TResult> joinAll(
130          final ListenableFuture<T1> f1,
131          final ListenableFuture<T2> f2,
132          final ListenableFuture<T3> f3,
133          final AsyncFunction3<T1, T2, T3, TResult> fn) {
134        ListenableFuture<?>[] futures = new ListenableFuture<?>[3];
135
136        futures[0] = f1;
137        futures[1] = f2;
138        futures[2] = f3;
139
140        // Futures.allAsList is used instead of Futures.successfulAsList because
141        // allAsList will propagate the failures instead of null values to the
142        // parameters of the supplied function.
143        ListenableFuture<List<Object>> result = Futures.allAsList(futures);
144        return Futures.transform(result, new AsyncFunction<List<Object>, TResult>() {
145            @Override
146            public ListenableFuture<TResult> apply(@Nullable List<Object> list) throws Exception {
147                T1 value1 = (T1) list.get(0);
148                T2 value2 = (T2) list.get(1);
149                T3 value3 = (T3) list.get(2);
150
151                return fn.apply(value1, value2, value3);
152            }
153        });
154    }
155
156    /**
157     * Create a new joined future from three existing futures and an async function
158     * that combines the resulting outputs of the previous functions into a single
159     * result. The resulting future will fail if any of the dependent futures also
160     * fail.
161     */
162    public static <T1, T2, T3, TResult> ListenableFuture<TResult> joinAll(
163          final ListenableFuture<T1> f1,
164          final ListenableFuture<T2> f2,
165          final ListenableFuture<T3> f3,
166          final Function3<T1, T2, T3, TResult> fn) {
167        return joinAll(f1, f2, f3, new ImmediateAsyncFunction3<>(fn));
168    }
169
170    /**
171     * Wrapper class for turning a Function2 into an AsyncFunction2 by returning
172     * an immediate future when the function is applied.
173     */
174    private static final class ImmediateAsyncFunction2<T1, T2, TResult> implements
175          AsyncFunction2<T1, T2, TResult> {
176        private final Function2<T1, T2, TResult> mFn;
177
178        public ImmediateAsyncFunction2(Function2<T1, T2, TResult> fn) {
179            mFn = fn;
180        }
181
182        @Override
183        public ListenableFuture<TResult> apply(T1 value1, T2 value2) throws Exception {
184            return Futures.immediateFuture(mFn.apply(value1, value2));
185        }
186    }
187
188    /**
189     * Wrapper class for turning a Function3 into an AsyncFunction3 by returning
190     * an immediate future when the function is applied.
191     */
192    private static final class ImmediateAsyncFunction3<T1, T2, T3, TResult> implements
193          AsyncFunction3<T1, T2, T3, TResult> {
194        private final Function3<T1, T2, T3, TResult> mFn;
195
196        public ImmediateAsyncFunction3(Function3<T1, T2, T3, TResult> fn) {
197            mFn = fn;
198        }
199
200        @Override
201        public ListenableFuture<TResult> apply(T1 value1, T2 value2, T3 value3) throws Exception {
202            return Futures.immediateFuture(mFn.apply(value1, value2, value3));
203        }
204    }
205}
206