1 /*
2  * Copyright 2024 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 
17 package androidx.pdf.data;
18 
19 import androidx.annotation.RestrictTo;
20 import androidx.annotation.WorkerThread;
21 import androidx.pdf.util.ObservableValue;
22 import androidx.pdf.util.Preconditions;
23 
24 import org.jspecify.annotations.NonNull;
25 import org.jspecify.annotations.Nullable;
26 
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.HashSet;
31 import java.util.Set;
32 import java.util.concurrent.Semaphore;
33 
34 /** Basic {@link FutureValue}s and related helpers. */
35 @RestrictTo(RestrictTo.Scope.LIBRARY)
36 public class FutureValues {
FutureValues()37     private FutureValues() {
38     }
39 
40     /** Creates a {@link FutureValue} with the given value. */
newImmediateValue(T value)41     public static <T> @NonNull FutureValue<T> newImmediateValue(T value) {
42         return new ImmediateValue<T>(value);
43     }
44 
45     /**
46      * Creates a {@link FutureValue} with an immediate failure (the passed value).
47      *
48      * @param error the exception to be delivered on fail.
49      */
immediateFail(final @NonNull Exception error)50     public static <T> @NonNull FutureValue<T> immediateFail(final @NonNull Exception error) {
51         return callback -> callback.failed(error);
52     }
53 
54     /** Creates a {@link FutureValue} that decouples setting and reading progress and results. */
newSettableValue()55     public static <T> @NonNull SettableFutureValue<T> newSettableValue() {
56         return new SettableFutureValue<T>();
57     }
58 
59     /** A {@link FutureValue.Callback} wrapper interface around a {@link SettableFutureValue}. */
setterCallback( final @NonNull SettableFutureValue<T> targetFuture)60     public static <T> FutureValue.@NonNull Callback<T> setterCallback(
61             final @NonNull SettableFutureValue<T> targetFuture) {
62         return new FutureValue.Callback<T>() {
63 
64             @Override
65             public void available(T value) {
66                 targetFuture.set(value);
67             }
68 
69             @Override
70             public void failed(@NonNull Throwable thrown) {
71                 targetFuture.fail(thrown);
72             }
73 
74             @Override
75             public void progress(float progress) {
76                 targetFuture.progress(progress);
77             }
78         };
79     }
80 
81     /**
82      * Creates a {@link FutureValue} that converts the results of a source {@link FutureValue} on
83      * the fly. If the conversion fails, the resulting future will report a failure with the
84      * corresponding exception.
85      *
86      * @param sourceFuture the source {@link FutureValue}.
87      * @param converter    the {@link Converter} used to convert results from source to returned
88      */
89     public static <F, T> @NonNull FutureValue<T> convert(final @NonNull FutureValue<F> sourceFuture,
90             final @NonNull Converter<F, T> converter) {
91         return new FutureValue<T>() {
92             @Override
93             public void get(final FutureValue.Callback<T> target) {
94                 final FutureValue.Callback<F> convertCallback = new FutureValue.Callback<F>() {
95                     @Override
96                     public void progress(float progress) {
97                         target.progress(progress);
98                     }
99 
100                     @Override
101                     public void failed(@NonNull Throwable thrown) {
102                         target.failed(thrown);
103                     }
104 
105                     @Override
106                     public void available(F sourceValue) {
107                         try {
108                             target.available(converter.convert(sourceValue));
109                         } catch (Exception e) {
110                             target.failed(e);
111                         }
112                     }
113                 };
114 
115                 sourceFuture.get(convertCallback);
116             }
117         };
118     }
119 
120     /** Creates a new Converter that chains 2 other converters. */
121     public static <F, T, V> @NonNull Converter<F, T> combine(
122             final @NonNull Converter<F, V> converter1, final @NonNull Converter<V, T> converter2) {
123         return new Converter<F, T>() {
124             @Override
125             public T convert(F from) {
126                 return converter2.convert(converter1.convert(from));
127             }
128         };
129     }
130 
131     /**
132      * Observe an {@link ObservableValue} as a {@link FutureValue}: wait for it to next hit one of
133      * the given target values. It will start observing the observable value after
134      * {@link FutureValue#get} is called. The Future might complete immediately (if the observable
135      * value already equals the target), in the future (when it changes to it), or never. It never
136      * fails (Note that there is not time-out to trigger an automatic fail in case the condition
137      * never materializes).
138      *
139      * <p>This {@link FutureValue} cannot be used more than once (i.e. a second call to {@link
140      * FutureValue#get} is illegal).
141      *
142      * @param obs    the observable value.
143      * @param target the value(s) that will trigger the {@link FutureValue}'s completion.
144      * @return A {@link FutureValue} that completes when the observable value changes to target.
145      */
146     @SafeVarargs
147     public static <T> @NonNull FutureValue<T> observeAsFuture(final @NonNull ObservableValue<T> obs,
148             T @NonNull ... target) {
149         Preconditions.checkNotNull(obs);
150         final Set<T> targetSet = new HashSet<>(Arrays.asList(target));
151         return new FutureValue<T>() {
152 
153             /**
154              * key is null as long as this Future hasn't been used (before
155              * {@link FutureValue#get} is called). It becomes non-null after that (holds the
156              * {@link ValueObserver}'s registration key if applicable).
157              */
158             private @Nullable Object mKey;
159 
160             @Override
161             public void get(final FutureValue.Callback<T> callback) {
162                 Preconditions.checkState(mKey == null, "Can't reuse this future twice: " + mKey);
163                 if (targetSet.contains(obs.get())) {
164                     mKey = Boolean.TRUE;
165                     callback.available(obs.get());
166                 } else {
167                     mKey = obs.addObserver((oldValue, newValue) -> {
168                         if (targetSet.contains(newValue)) {
169                             obs.removeObserver(mKey);
170                             mKey = Boolean.FALSE;
171                             callback.available(newValue);
172                         }
173                     });
174                 }
175             }
176         };
177     }
178 
179     /**
180      * An object that can be used for converting an object of type F to one of type T.
181      *
182      * @param <F> original type of the object
183      * @param <T> type to be converted to
184      */
185     public interface Converter<F, T> {
186 
187         /** Converts object of type F to type T */
188         T convert(F from);
189     }
190 
191     /**
192      * A convenient base implementation of {@link FutureValue.Callback} that does nothing. It
193      * assumes no failures are reported and thus handles them.
194      * Subclasses should override {@link #failed} if failures are expected.
195      *
196      * @param <T> the type of result
197      */
198     public static class SimpleCallback<T> implements FutureValue.Callback<T> {
199 
200         @Override
201         public void available(T value) {
202         }
203 
204         @Override
205         public void failed(@NonNull Throwable thrown) {
206         }
207 
208         @Override
209         public void progress(float progress) {
210         }
211 
212         @Override
213         public @NonNull String toString() {
214             return "SimpleCallback (unspecified)";
215         }
216     }
217 
218     /**
219      * Allows for a blocking get.
220      *
221      * <p>Useful for when waiting for an execution to complete.
222      *
223      * @param <T> the type of result
224      */
225     public static class BlockingCallback<T> extends SimpleCallback<T> {
226         Semaphore mSemaphore = new Semaphore(0);
227         T mSuccess = null;
228         Throwable mFail = null;
229 
230         @Override
231         public void available(T value) {
232             mSuccess = value;
233             mSemaphore.release();
234         }
235 
236         @Override
237         public void failed(@NonNull Throwable thrown) {
238             mFail = thrown;
239             mSemaphore.release();
240         }
241 
242         /** Acquires semaphore until data comes through callback. */
243         @WorkerThread
244         public T getBlocking() {
245             try {
246                 mSemaphore.acquire(); // wait until data comes through the callback.
247             } catch (InterruptedException e) {
248                 throw new IllegalStateException(e);
249             } finally {
250                 // Release as someone else is probably now waiting
251                 mSemaphore.release();
252             }
253             if (mFail != null) {
254                 throw new IllegalStateException(mFail);
255             }
256             return mSuccess;
257         }
258     }
259 
260     /**
261      * A simple {@link FutureValue} that is constructed with a value and returns it immediately when
262      * {@link #get(Callback)} is called.
263      */
264     private static class ImmediateValue<T> implements FutureValue<T> {
265         private final T mValue;
266 
267         ImmediateValue(T value) {
268             this.mValue = value;
269         }
270 
271         @Override
272         public void get(Callback<T> callback) {
273             callback.available(mValue);
274         }
275     }
276 
277     /**
278      * A {@link FutureValue} which wraps a {@link Supplier} for another {@link FutureValue}. This
279      * is designed so that a computation which returns a {@link FutureValue} is not started until
280      * {@link #get(Callback)} is called for the first time (a "lazy" future).
281      *
282      * @param <T> the type of the value
283      */
284     public static class DeferredFutureValue<T> implements FutureValue<T> {
285 
286         /**
287          * Represents an operation which should not be started until {@link #get(Callback)} is
288          * called for the first time.
289          */
290         private final Supplier<FutureValue<T>> mComputation;
291 
292         public DeferredFutureValue(@NonNull Supplier<FutureValue<T>> computation) {
293             this.mComputation = computation;
294         }
295 
296         @Override
297         public void get(@Nullable Callback<T> callback) {
298             try {
299                 mComputation.supply(progress -> {
300                 }).get(callback);
301             } catch (Exception e) {
302                 // Most errors should be piped through the computation's Future to the callback.
303                 // This captures errors thrown during the supply.
304                 callback.failed(e);
305             }
306         }
307     }
308 
309     /**
310      * A {@link FutureValue} that accepts multiple {@link FutureValue.Callback}s and can be set.
311      *
312      * @param <T> the type of the value to be returned
313      */
314     public static class SettableFutureValue<T> implements FutureValue<T> {
315         private final Collection<Callback<T>> mCallbacks = new ArrayList<>(2);
316         private T mValue;
317         private Throwable mThrown;
318 
319         /**
320          * Set the successful value resulting from the asynchronous operation. Any callbacks will be
321          * called immediately. The value will be retained to call any new callbacks. All callback
322          * references will be removed.
323          *
324          * @param value The result of the asynchronous operation.
325          */
326         public void set(T value) {
327             Preconditions.checkNotNull(value);
328             checkNotSet(value.toString());
329             this.mValue = value;
330             for (Callback<T> callback : mCallbacks) {
331                 callback.available(value);
332             }
333             mCallbacks.clear();
334         }
335 
336         /**
337          * Notify all the callbacks of a progress update. This implementation will not store
338          * intermediate progress values. So if a callback is registered after the last progress
339          * value has been sent, it will not get the previous progress values. Progress is no longer
340          * reported after this future completes (i.e. if {@link #isSet()}).
341          *
342          * @param progress is a measure of how much progress has been done [0-1].
343          */
344         public void progress(float progress) {
345             if (!isSet()) {
346                 for (Callback<T> callback : mCallbacks) {
347                     callback.progress(progress);
348                 }
349             }
350         }
351 
352         /**
353          * Set the exception thrown while getting the value. Any callbacks will be called
354          * immediately. The exception will be retained to call any new callbacks. All callback
355          * references will be removed.
356          *
357          * @param thrown The problem encountered while getting the result of the asynchronous
358          *               operation.
359          */
360         public void fail(@NonNull Throwable thrown) {
361             checkNotSet(thrown.toString());
362             this.mThrown = Preconditions.checkNotNull(thrown);
363             for (Callback<T> callback : mCallbacks) {
364                 callback.failed(thrown);
365             }
366             mCallbacks.clear();
367         }
368 
369         /**
370          * Wraps a {@link String} error message in an {@link Exception}. Otherwise, it has the same
371          * behaviour as {@link SettableFutureValue#fail(Throwable)}.
372          */
373         public void fail(@NonNull String errorMessage) {
374             fail(new Exception(errorMessage));
375         }
376 
377         @Override
378         public void get(@Nullable Callback<T> callback) {
379             if (mValue != null) {
380                 callback.available(mValue);
381             } else if (mThrown != null) {
382                 callback.failed(mThrown);
383             } else {
384                 Preconditions.checkNotNull(callback);
385                 mCallbacks.add(callback);
386             }
387         }
388 
389         public boolean isSet() {
390             return (mValue != null || mThrown != null);
391         }
392 
393         private void checkNotSet(String newValue) {
394             Preconditions.checkState(mValue == null,
395                     String.format("Value has already been set (%s) : %s", mValue, newValue));
396             Preconditions.checkState(mThrown == null,
397                     String.format("Exception was already set (%s) : %s", mThrown, newValue));
398         }
399     }
400 }
401