Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2007 Google Inc.
      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.google.common.base;
     18 
     19 import java.io.PrintWriter;
     20 import java.io.StringWriter;
     21 import java.util.List;
     22 import java.util.ArrayList;
     23 import java.util.Collections;
     24 
     25 /**
     26  * Static utility methods pertaining to instances of {@link Throwable}.
     27  *
     28  * @author Kevin Bourrillion
     29  * @author Ben Yu
     30  * @since 2009.09.15 <b>tentative</b>
     31  */
     32 public final class Throwables {
     33   private Throwables() {}
     34 
     35   /**
     36    * Propagates {@code throwable} exactly as-is, if and only if it is an
     37    * instance of {@code declaredType}.  Example usage:
     38    * <pre>
     39    *   try {
     40    *     someMethodThatCouldThrowAnything();
     41    *   } catch (IKnowWhatToDoWithThisException e) {
     42    *     handle(e);
     43    *   } catch (Throwable t) {
     44    *     Throwables.propagateIfInstanceOf(t, IOException.class);
     45    *     Throwables.propagateIfInstanceOf(t, SQLException.class);
     46    *     throw Throwables.propagate(t);
     47    *   }
     48    * </pre>
     49    */
     50   public static <X extends Throwable> void propagateIfInstanceOf(
     51       Throwable throwable, Class<X> declaredType) throws X {
     52     if (declaredType.isInstance(throwable)) {
     53       throw declaredType.cast(throwable);
     54     }
     55   }
     56 
     57   /**
     58    * Propagates {@code throwable} exactly as-is, if and only if it is an
     59    * instance of {@link RuntimeException} or {@link Error}.  Example usage:
     60    * <pre>
     61    *   try {
     62    *     someMethodThatCouldThrowAnything();
     63    *   } catch (IKnowWhatToDoWithThisException e) {
     64    *     handle(e);
     65    *   } catch (Throwable t) {
     66    *     Throwables.propagateIfPossible(t);
     67    *     throw new RuntimeException("unexpected", t);
     68    *   }
     69    * </pre>
     70    */
     71   public static void propagateIfPossible(Throwable throwable) {
     72     propagateIfInstanceOf(throwable, Error.class);
     73     propagateIfInstanceOf(throwable, RuntimeException.class);
     74   }
     75 
     76   /**
     77    * Propagates {@code throwable} exactly as-is, if and only if it is an
     78    * instance of {@link RuntimeException}, {@link Error}, or
     79    * {@code declaredType}. Example usage:
     80    * <pre>
     81    *   try {
     82    *     someMethodThatCouldThrowAnything();
     83    *   } catch (IKnowWhatToDoWithThisException e) {
     84    *     handle(e);
     85    *   } catch (Throwable t) {
     86    *     Throwables.propagateIfPossible(t, OtherException.class);
     87    *     throw new RuntimeException("unexpected", t);
     88    *   }
     89    * </pre>
     90    *
     91    * @param throwable the Throwable to possibly propagate
     92    * @param declaredType the single checked exception type declared by the
     93    *     calling method
     94    */
     95   public static <X extends Throwable> void propagateIfPossible(
     96       Throwable throwable, Class<X> declaredType) throws X {
     97     propagateIfInstanceOf(throwable, declaredType);
     98     propagateIfPossible(throwable);
     99   }
    100 
    101   /**
    102    * Propagates {@code throwable} exactly as-is, if and only if it is an
    103    * instance of {@link RuntimeException}, {@link Error}, {@code aDeclaredType},
    104    * or {@code anotherDeclaredType}.  In the unlikely case that you have three
    105    * or more declared checked exception types, you can handle them all by
    106    * invoking these methods repeatedly. See usage example in
    107    * {@link #propagateIfPossible(Throwable, Class)}.
    108    *
    109    * @param throwable the Throwable to possibly propagate
    110    * @param aDeclaredType any checked exception type declared by the calling
    111    *     method
    112    * @param anotherDeclaredType any other checked exception type declared by the
    113    *     calling method
    114    */
    115   public static <X1 extends Throwable, X2 extends Throwable> void
    116       propagateIfPossible(Throwable throwable, Class<X1> aDeclaredType,
    117           Class<X2> anotherDeclaredType) throws X1, X2 {
    118     propagateIfInstanceOf(throwable, aDeclaredType);
    119     propagateIfPossible(throwable, anotherDeclaredType);
    120   }
    121 
    122   /**
    123    * Propagates {@code throwable} as-is if it is an instance of
    124    * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps
    125    * it in a {@code RuntimeException} then propagates.
    126    * <p>
    127    * This method always throws an exception. The {@code RuntimeException} return
    128    * type is only for client code to make Java type system happy in case a
    129    * return value is required by the enclosing method. Example usage:
    130    * <pre>
    131    *   T doSomething() {
    132    *     try {
    133    *       return someMethodThatCouldThrowAnything();
    134    *     } catch (IKnowWhatToDoWithThisException e) {
    135    *       return handle(e);
    136    *     } catch (Throwable t) {
    137    *       throw Throwables.propagate(t);
    138    *     }
    139    *   }
    140    * </pre>
    141    *
    142    * @param throwable the Throwable to propagate
    143    * @return nothing will ever be returned
    144    */
    145   public static RuntimeException propagate(Throwable throwable) {
    146     propagateIfPossible(throwable);
    147     throw new RuntimeException(throwable);
    148   }
    149 
    150   /**
    151    * Returns the innermost cause of {@code throwable}. The first throwable in a
    152    * chain provides context from when the error or exception was initially
    153    * detected. Example usage:
    154    * <pre>
    155    *   assertEquals("Unable to assign a customer id",
    156    *       Throwables.getRootCause(e).getMessage());
    157    * </pre>
    158    */
    159   public static Throwable getRootCause(Throwable throwable) {
    160     Throwable cause;
    161     while ((cause = throwable.getCause()) != null) {
    162       throwable = cause;
    163     }
    164     return throwable;
    165   }
    166 
    167   /**
    168    * Gets a {@code Throwable} cause chain as a list.  The first entry in the
    169    * list will be {@code throwable} followed by its cause hierarchy.  Note
    170    * that this is a snapshot of the cause chain and will not reflect
    171    * any subsequent changes to the cause chain.
    172    *
    173    * <p>Here's an example of how it can be used to find specific types
    174    * of exceptions in the cause chain:
    175    *
    176    * <pre>
    177    * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
    178    * </pre>
    179    *
    180    * @param throwable the non-null {@code Throwable} to extract causes from
    181    * @return an unmodifiable list containing the cause chain starting with
    182    *     {@code throwable}
    183    */
    184   public static List<Throwable> getCausalChain(Throwable throwable) {
    185     Preconditions.checkNotNull(throwable);
    186     List<Throwable> causes = new ArrayList<Throwable>(4);
    187     while (throwable != null) {
    188       causes.add(throwable);
    189       throwable = throwable.getCause();
    190     }
    191     return Collections.unmodifiableList(causes);
    192   }
    193 
    194   /**
    195    * Returns a string containing the result of
    196    * {@link Throwable#toString() toString()}, followed by the full, recursive
    197    * stack trace of {@code throwable}. Note that you probably should not be
    198    * parsing the resulting string; if you need programmatic access to the stack
    199    * frames, you can call {@link Throwable#getStackTrace()}.
    200    */
    201   public static String getStackTraceAsString(Throwable throwable) {
    202     StringWriter stringWriter = new StringWriter();
    203     throwable.printStackTrace(new PrintWriter(stringWriter));
    204     return stringWriter.toString();
    205   }
    206 
    207   /**
    208    * Rethrows the cause exception of a given throwable, discarding the original
    209    * throwable. Optionally, the stack frames of the cause and the outer
    210    * exception are combined and the stack trace of the cause is set to this
    211    * combined trace. If there is no cause the original exception is rethrown
    212    * unchanged in all cases.
    213    *
    214    * @param exception the exception from which to extract the cause
    215    * @param combineStackTraces if true the stack trace of the cause will be
    216    *     replaced by the concatenation of the trace from the exception and the
    217    *     trace from the cause.
    218    */
    219   public static Exception throwCause(Exception exception, boolean combineStackTraces)
    220       throws Exception {
    221     Throwable cause = exception.getCause();
    222     if (cause == null) {
    223       throw exception;
    224     }
    225     if (combineStackTraces) {
    226       StackTraceElement[] causeTrace = cause.getStackTrace();
    227       StackTraceElement[] outerTrace = exception.getStackTrace();
    228       StackTraceElement[] combined = new StackTraceElement[causeTrace.length + outerTrace.length];
    229       System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length);
    230       System.arraycopy(outerTrace, 0, combined, causeTrace.length, outerTrace.length);
    231       cause.setStackTrace(combined);
    232     }
    233     if (cause instanceof Exception) {
    234       throw (Exception) cause;
    235     }
    236     if (cause instanceof Error) {
    237       throw (Error) cause;
    238     }
    239     // The cause is a weird kind of Throwable, so throw the outer exception
    240     throw exception;
    241   }
    242 }
    243