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