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