1 /*
2  * Copyright (C) 2017 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 com.android.internal.util;
18 
19 import android.annotation.NonNull;
20 import android.os.RemoteException;
21 import android.util.ExceptionUtils;
22 
23 import java.util.function.BiConsumer;
24 import java.util.function.BiFunction;
25 import java.util.function.Consumer;
26 import java.util.function.Function;
27 import java.util.function.Supplier;
28 
29 /**
30  * Utilities specific to functional programming
31  */
32 public class FunctionalUtils {
FunctionalUtils()33     private FunctionalUtils() {}
34 
35     /**
36      * Converts a lambda expression that throws a checked exception(s) into a regular
37      * {@link Consumer} by propagating any checked exceptions as {@link RuntimeException}
38      */
uncheckExceptions(ThrowingConsumer<T> action)39     public static <T> Consumer<T> uncheckExceptions(ThrowingConsumer<T> action) {
40         return action;
41     }
42 
43     /**
44      * @see #uncheckExceptions(ThrowingConsumer)
45      */
uncheckExceptions(ThrowingFunction<I, O> action)46     public static <I, O> Function<I, O> uncheckExceptions(ThrowingFunction<I, O> action) {
47         return action;
48     }
49 
50     /**
51      * @see #uncheckExceptions(ThrowingConsumer)
52      */
uncheckExceptions(ThrowingRunnable action)53     public static Runnable uncheckExceptions(ThrowingRunnable action) {
54         return action;
55     }
56 
57     /**
58      * @see #uncheckExceptions(ThrowingConsumer)
59      */
uncheckExceptions(ThrowingBiConsumer<A, B> action)60     public static <A, B> BiConsumer<A, B> uncheckExceptions(ThrowingBiConsumer<A, B> action) {
61         return action;
62     }
63 
64     /**
65      * @see #uncheckExceptions(ThrowingConsumer)
66      */
uncheckExceptions(ThrowingSupplier<T> action)67     public static <T> Supplier<T> uncheckExceptions(ThrowingSupplier<T> action) {
68         return action;
69     }
70 
71     /**
72      * Wraps a given {@code action} into one that ignores any {@link RemoteException}s
73      */
ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action)74     public static <T> Consumer<T> ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action) {
75         return action;
76     }
77 
78     /**
79      * Wraps the given {@link ThrowingRunnable} into one that handles any exceptions using the
80      * provided {@code handler}
81      */
handleExceptions(ThrowingRunnable r, Consumer<Throwable> handler)82     public static Runnable handleExceptions(ThrowingRunnable r, Consumer<Throwable> handler) {
83         return () -> {
84             try {
85                 r.run();
86             } catch (Throwable t) {
87                 handler.accept(t);
88             }
89         };
90     }
91 
92     /**
93      * An equivalent of {@link Runnable} that allows throwing checked exceptions
94      *
95      * This can be used to specify a lambda argument without forcing all the checked exceptions
96      * to be handled within it
97      */
98     @FunctionalInterface
99     @SuppressWarnings("FunctionalInterfaceMethodChanged")
100     public interface ThrowingRunnable extends Runnable {
101         void runOrThrow() throws Exception;
102 
103         @Override
104         default void run() {
105             try {
106                 runOrThrow();
107             } catch (Exception ex) {
108                 throw ExceptionUtils.propagate(ex);
109             }
110         }
111     }
112 
113     /**
114      * An equivalent of {@link Supplier} that allows throwing checked exceptions
115      *
116      * This can be used to specify a lambda argument without forcing all the checked exceptions
117      * to be handled within it
118      */
119     @FunctionalInterface
120     @SuppressWarnings("FunctionalInterfaceMethodChanged")
121     public interface ThrowingSupplier<T> extends Supplier<T> {
122         T getOrThrow() throws Exception;
123 
124         @Override
125         default T get() {
126             try {
127                 return getOrThrow();
128             } catch (Exception ex) {
129                 throw ExceptionUtils.propagate(ex);
130             }
131         }
132     }
133     /**
134      * A {@link Consumer} that allows throwing checked exceptions from its single abstract method.
135      *
136      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
137      * that throws a checked exception into a regular {@link Consumer}
138      */
139     @FunctionalInterface
140     @SuppressWarnings("FunctionalInterfaceMethodChanged")
141     public interface ThrowingConsumer<T> extends Consumer<T> {
142         void acceptOrThrow(T t) throws Exception;
143 
144         @Override
145         default void accept(T t) {
146             try {
147                 acceptOrThrow(t);
148             } catch (Exception ex) {
149                 throw ExceptionUtils.propagate(ex);
150             }
151         }
152     }
153 
154     /**
155      * A {@link Consumer} that automatically ignores any {@link RemoteException}s.
156      *
157      * Used by {@link #ignoreRemoteException}
158      */
159     @FunctionalInterface
160     @SuppressWarnings("FunctionalInterfaceMethodChanged")
161     public interface RemoteExceptionIgnoringConsumer<T> extends Consumer<T> {
162         void acceptOrThrow(T t) throws RemoteException;
163 
164         @Override
165         default void accept(T t) {
166             try {
167                 acceptOrThrow(t);
168             } catch (RemoteException ex) {
169                 // ignore
170             }
171         }
172     }
173 
174     /**
175      * A {@link Function} that allows throwing checked exceptions from its single abstract method.
176      *
177      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
178      * that throws a checked exception into a regular {@link Function}
179      *
180      * @param <T> see {@link Function}
181      * @param <R> see {@link Function}
182      */
183     @FunctionalInterface
184     @SuppressWarnings("FunctionalInterfaceMethodChanged")
185     public interface ThrowingFunction<T, R> extends Function<T, R> {
186         /** @see ThrowingFunction */
187         R applyOrThrow(T t) throws Exception;
188 
189         @Override
190         default R apply(T t) {
191             try {
192                 return applyOrThrow(t);
193             } catch (Exception ex) {
194                 throw ExceptionUtils.propagate(ex);
195             }
196         }
197     }
198 
199     /**
200      * A {@link BiFunction} that allows throwing checked exceptions from its single abstract method.
201      *
202      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
203      * that throws a checked exception into a regular {@link BiFunction}
204      *
205      * @param <T> see {@link BiFunction}
206      * @param <U> see {@link BiFunction}
207      * @param <R> see {@link BiFunction}
208      */
209     @FunctionalInterface
210     @SuppressWarnings("FunctionalInterfaceMethodChanged")
211     public interface ThrowingBiFunction<T, U, R> extends BiFunction<T, U, R> {
212         /** @see ThrowingFunction */
213         R applyOrThrow(T t, U u) throws Exception;
214 
215         @Override
216         default R apply(T t, U u) {
217             try {
218                 return applyOrThrow(t, u);
219             } catch (Exception ex) {
220                 throw ExceptionUtils.propagate(ex);
221             }
222         }
223     }
224 
225     /**
226      * A {@link BiConsumer} that allows throwing checked exceptions from its single abstract method.
227      *
228      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
229      * that throws a checked exception into a regular {@link Function}
230      *
231      * @param <A> see {@link BiConsumer}
232      * @param <B> see {@link BiConsumer}
233      */
234     @FunctionalInterface
235     @SuppressWarnings("FunctionalInterfaceMethodChanged")
236     public interface ThrowingBiConsumer<A, B> extends BiConsumer<A, B> {
237         /** @see ThrowingFunction */
238         void acceptOrThrow(A a, B b) throws Exception;
239 
240         @Override
241         default void accept(A a, B b) {
242             try {
243                 acceptOrThrow(a, b);
244             } catch (Exception ex) {
245                 throw ExceptionUtils.propagate(ex);
246             }
247         }
248     }
249 
250     /**
251      * A {@link Supplier} that allows the caller to specify a custom checked {@link Exception} that
252      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
253      * different classes.
254      *
255      * @param <Output> Method return type
256      * @param <ExceptionType> Checked exception type
257      */
258     @FunctionalInterface
259     public interface ThrowingCheckedSupplier<Output, ExceptionType extends Exception> {
260         Output get() throws ExceptionType;
261     }
262 
263     /**
264      * A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
265      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
266      * different classes.
267      *
268      * @param <Input> Method parameter type
269      * @param <ExceptionType> Checked exception type
270      */
271     @FunctionalInterface
272     public interface ThrowingCheckedConsumer<Input, ExceptionType extends Exception> {
273         void accept(Input input) throws ExceptionType;
274     }
275 
276     /**
277      * A {@link Consumer} that allows the caller to specify 2 different custom checked
278      * {@link Exception}s that can be thrown by the implementer. This is usually used when
279      * proxying/wrapping calls between different classes.
280      *
281      * @param <Input> Method parameter type
282      * @param <ExceptionOne> First checked exception type
283      * @param <ExceptionTwo> Second checked exception type
284      */
285     @FunctionalInterface
286     public interface ThrowingChecked2Consumer<Input, ExceptionOne extends Exception,
287             ExceptionTwo extends Exception> {
288         void accept(Input input) throws ExceptionOne, ExceptionTwo;
289     }
290 
291     /**
292      * A {@link Function} that allows the caller to specify a custom checked {@link Exception} that
293      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
294      * different classes.
295      *
296      * @param <Input> Method parameter type
297      * @param <Output> Method return type
298      * @param <ExceptionType> Checked exception type
299      */
300     @FunctionalInterface
301     public interface ThrowingCheckedFunction<Input, Output, ExceptionType extends Exception> {
302         Output apply(Input input) throws ExceptionType;
303     }
304 
305     // TODO: add unit test
306     /**
307      * Gets a user-friendly name for a lambda function.
308      */
309     @NonNull
310     public static String getLambdaName(@NonNull Object function) {
311         // Full function has one of the following formats:
312         //   package-$$Lambda$class$randomId
313         //   package-$$Lambda$randomId
314         //
315         // We just want just package.class$Lambda (or package$Lambda) respectively
316 
317         final String fullFunction = function.toString();
318 
319         final int endPkgIdx = fullFunction.indexOf("-$$");
320         if (endPkgIdx == -1) return fullFunction;
321 
322         // firstDollarIdx could be either beginning of class or beginning of the random id
323         final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3);
324         if (firstDollarIdx == -1) return fullFunction;
325 
326         final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1);
327         if (endClassIdx == -1) {
328             // Just package
329             return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda";
330         }
331 
332         // Package + class
333         return fullFunction.substring(0, endPkgIdx)
334                 + fullFunction.substring(firstDollarIdx + 1, endClassIdx)
335                 + "$Lambda";
336     }
337 }
338