• 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  * Utilities specific to functional programming
31  */
32 public final 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         /** TODO add javadoc */
102         void runOrThrow() throws Exception;
103 
104         @Override
105         default void run() {
106             try {
107                 runOrThrow();
108             } catch (Exception ex) {
109                 throw ExceptionUtils.propagate(ex);
110             }
111         }
112     }
113 
114     /**
115      * An equivalent of {@link Supplier} that allows throwing checked exceptions
116      *
117      * This can be used to specify a lambda argument without forcing all the checked exceptions
118      * to be handled within it
119      *
120      * @param <T>
121      */
122     @FunctionalInterface
123     @SuppressWarnings("FunctionalInterfaceMethodChanged")
124     public interface ThrowingSupplier<T> extends Supplier<T> {
125         /** TODO add javadoc */
126         T getOrThrow() throws Exception;
127 
128         @Override
129         default T get() {
130             try {
131                 return getOrThrow();
132             } catch (Exception ex) {
133                 throw ExceptionUtils.propagate(ex);
134             }
135         }
136     }
137     /**
138      * A {@link Consumer} that allows throwing checked exceptions from its single abstract method.
139      *
140      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
141      * that throws a checked exception into a regular {@link Consumer}
142      *
143      * @param <T>
144      */
145     @FunctionalInterface
146     @SuppressWarnings("FunctionalInterfaceMethodChanged")
147     public interface ThrowingConsumer<T> extends Consumer<T> {
148         /** TODO add javadoc */
149         void acceptOrThrow(T t) throws Exception;
150 
151         @Override
152         default void accept(T t) {
153             try {
154                 acceptOrThrow(t);
155             } catch (Exception ex) {
156                 throw ExceptionUtils.propagate(ex);
157             }
158         }
159     }
160 
161     /**
162      * A {@link Consumer} that automatically ignores any {@link RemoteException}s.
163      *
164      * Used by {@link #ignoreRemoteException}
165      *
166      * @param <T>
167      */
168     @FunctionalInterface
169     @SuppressWarnings("FunctionalInterfaceMethodChanged")
170     public interface RemoteExceptionIgnoringConsumer<T> extends Consumer<T> {
171         /** TODO add javadoc */
172         void acceptOrThrow(T t) throws RemoteException;
173 
174         @Override
175         default void accept(T t) {
176             try {
177                 acceptOrThrow(t);
178             } catch (RemoteException ex) {
179                 // ignore
180             }
181         }
182     }
183 
184     /**
185      * A {@link Function} that allows throwing checked exceptions from its single abstract method.
186      *
187      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
188      * that throws a checked exception into a regular {@link Function}
189      *
190      * @param <T> see {@link Function}
191      * @param <R> see {@link Function}
192      */
193     @FunctionalInterface
194     @SuppressWarnings("FunctionalInterfaceMethodChanged")
195     public interface ThrowingFunction<T, R> extends Function<T, R> {
196         /** @see ThrowingFunction */
197         R applyOrThrow(T t) throws Exception;
198 
199         @Override
200         default R apply(T t) {
201             try {
202                 return applyOrThrow(t);
203             } catch (Exception ex) {
204                 throw ExceptionUtils.propagate(ex);
205             }
206         }
207     }
208 
209     /**
210      * A {@link BiFunction} that allows throwing checked exceptions from its single abstract method.
211      *
212      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
213      * that throws a checked exception into a regular {@link BiFunction}
214      *
215      * @param <T> see {@link BiFunction}
216      * @param <U> see {@link BiFunction}
217      * @param <R> see {@link BiFunction}
218      */
219     @FunctionalInterface
220     @SuppressWarnings("FunctionalInterfaceMethodChanged")
221     public interface ThrowingBiFunction<T, U, R> extends BiFunction<T, U, R> {
222         /** @see ThrowingFunction */
223         R applyOrThrow(T t, U u) throws Exception;
224 
225         @Override
226         default R apply(T t, U u) {
227             try {
228                 return applyOrThrow(t, u);
229             } catch (Exception ex) {
230                 throw ExceptionUtils.propagate(ex);
231             }
232         }
233     }
234 
235     /**
236      * A {@link BiConsumer} that allows throwing checked exceptions from its single abstract method.
237      *
238      * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
239      * that throws a checked exception into a regular {@link Function}
240      *
241      * @param <A> see {@link BiConsumer}
242      * @param <B> see {@link BiConsumer}
243      */
244     @FunctionalInterface
245     @SuppressWarnings("FunctionalInterfaceMethodChanged")
246     public interface ThrowingBiConsumer<A, B> extends BiConsumer<A, B> {
247         /** @see ThrowingFunction */
248         void acceptOrThrow(A a, B b) throws Exception;
249 
250         @Override
251         default void accept(A a, B b) {
252             try {
253                 acceptOrThrow(a, b);
254             } catch (Exception ex) {
255                 throw ExceptionUtils.propagate(ex);
256             }
257         }
258     }
259 
260     /**
261      * A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
262      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
263      * different classes.
264      *
265      * @param <Input> Method parameter type
266      * @param <ExceptionType> Checked exception type
267      */
268     @FunctionalInterface
269     public interface ThrowingCheckedConsumer<Input, ExceptionType extends Exception> {
270         /** TODO add javadoc */
271         void accept(Input input) throws ExceptionType;
272     }
273 
274     /**
275      * A {@link Consumer} that allows the caller to specify 2 different custom checked
276      * {@link Exception}s that can be thrown by the implementer. This is usually used when
277      * proxying/wrapping calls between different classes.
278      *
279      * @param <Input> Method parameter type
280      * @param <ExceptionOne> First checked exception type
281      * @param <ExceptionTwo> Second checked exception type
282      */
283     @FunctionalInterface
284     public interface ThrowingChecked2Consumer<Input, ExceptionOne extends Exception,
285             ExceptionTwo extends Exception> {
286         /** TODO add javadoc */
287         void accept(Input input) throws ExceptionOne, ExceptionTwo;
288     }
289 
290     /**
291      * A {@link Function} that allows the caller to specify a custom checked {@link Exception} that
292      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
293      * different classes.
294      *
295      * @param <Input> Method parameter type
296      * @param <Output> Method return type
297      * @param <ExceptionType> Checked exception type
298      */
299     @FunctionalInterface
300     public interface ThrowingCheckedFunction<Input, Output, ExceptionType extends Exception> {
301         /** TODO add javadoc */
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