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