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