1 /* 2 * Copyright (C) 2019 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.compatibility.common.util; 18 19 import androidx.annotation.NonNull; 20 import androidx.annotation.Nullable; 21 22 import java.util.function.Function; 23 24 /** 25 * Utilities to deal with exceptions 26 */ 27 public class ExceptionUtils { ExceptionUtils()28 private ExceptionUtils() {} 29 30 /** 31 * Rethrow a given exception, optionally wrapping it in a {@link RuntimeException} 32 */ propagate(@onNull Throwable t)33 public static RuntimeException propagate(@NonNull Throwable t) { 34 if (t == null) { 35 throw new NullPointerException(); 36 } 37 propagateIfInstanceOf(t, Error.class); 38 propagateIfInstanceOf(t, RuntimeException.class); 39 throw new RuntimeException(t); 40 } 41 42 /** 43 * Rethrow a given exception, if it's of type {@code E} 44 */ propagateIfInstanceOf( @ullable Throwable t, Class<E> c)45 public static <E extends Throwable> void propagateIfInstanceOf( 46 @Nullable Throwable t, Class<E> c) throws E { 47 if (t != null && c.isInstance(t)) { 48 throw c.cast(t); 49 } 50 } 51 52 /** 53 * Gets the root {@link Throwable#getCause() cause} of {@code t} 54 */ getRootCause(@onNull Throwable t)55 public static @NonNull Throwable getRootCause(@NonNull Throwable t) { 56 while (t.getCause() != null) t = t.getCause(); 57 return t; 58 } 59 60 /** 61 * Appends {@code cause} at the end of the causal chain of {@code t} 62 * 63 * @return {@code t} for convenience 64 */ appendCause(@onNull E t, @Nullable Throwable cause)65 public static @NonNull <E extends Throwable> E appendCause(@NonNull E t, @Nullable Throwable cause) { 66 if (cause != null) { 67 getRootCause(t).initCause(cause); 68 } 69 return t; 70 } 71 72 /** 73 * Runs the given {@code action}, and if any exceptions are thrown in the process, applies 74 * given {@code exceptionTransformer}, rethrowing the result. 75 */ wrappingExceptions( Function<Throwable, Throwable> exceptionTransformer, ThrowingSupplier<R> action)76 public static <R> R wrappingExceptions( 77 Function<Throwable, Throwable> exceptionTransformer, ThrowingSupplier<R> action) { 78 try { 79 return action.get(); 80 } catch (Throwable t) { 81 Throwable transformed; 82 try { 83 transformed = exceptionTransformer.apply(t); 84 } catch (Throwable t2) { 85 transformed = new RuntimeException("Failed to apply exception transformation", 86 ExceptionUtils.appendCause(t2, t)); 87 } 88 throw ExceptionUtils.propagate(transformed); 89 } 90 } 91 92 /** 93 * @see #wrappingExceptions(Function, ThrowingSupplier) 94 */ wrappingExceptions( Function<Throwable, Throwable> exceptionTransformer, ThrowingRunnable action)95 public static void wrappingExceptions( 96 Function<Throwable, Throwable> exceptionTransformer, ThrowingRunnable action) { 97 wrappingExceptions(exceptionTransformer, () -> { 98 action.run(); 99 return null; 100 }); 101 } 102 } 103