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