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