001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.base;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.Beta;
022
023import java.io.PrintWriter;
024import java.io.StringWriter;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.List;
028
029import javax.annotation.Nullable;
030
031/**
032 * Static utility methods pertaining to instances of {@link Throwable}.
033 *
034 * @author Kevin Bourrillion
035 * @author Ben Yu
036 * @since 1
037 */
038public final class Throwables {
039  private Throwables() {}
040
041  /**
042   * Propagates {@code throwable} exactly as-is, if and only if it is an
043   * instance of {@code declaredType}.  Example usage:
044   * <pre>
045   *   try {
046   *     someMethodThatCouldThrowAnything();
047   *   } catch (IKnowWhatToDoWithThisException e) {
048   *     handle(e);
049   *   } catch (Throwable t) {
050   *     Throwables.propagateIfInstanceOf(t, IOException.class);
051   *     Throwables.propagateIfInstanceOf(t, SQLException.class);
052   *     throw Throwables.propagate(t);
053   *   }
054   * </pre>
055   */
056  public static <X extends Throwable> void propagateIfInstanceOf(
057      @Nullable Throwable throwable, Class<X> declaredType) throws X {
058    if (declaredType.isInstance(throwable)) {
059      throw declaredType.cast(throwable);
060    }
061  }
062
063  /**
064   * Propagates {@code throwable} exactly as-is, if and only if it is an
065   * instance of {@link RuntimeException} or {@link Error}.  Example usage:
066   * <pre>
067   *   try {
068   *     someMethodThatCouldThrowAnything();
069   *   } catch (IKnowWhatToDoWithThisException e) {
070   *     handle(e);
071   *   } catch (Throwable t) {
072   *     Throwables.propagateIfPossible(t);
073   *     throw new RuntimeException("unexpected", t);
074   *   }
075   * </pre>
076   */
077  public static void propagateIfPossible(@Nullable Throwable throwable) {
078    propagateIfInstanceOf(throwable, Error.class);
079    propagateIfInstanceOf(throwable, RuntimeException.class);
080  }
081
082  /**
083   * Propagates {@code throwable} exactly as-is, if and only if it is an
084   * instance of {@link RuntimeException}, {@link Error}, or
085   * {@code declaredType}. Example usage:
086   * <pre>
087   *   try {
088   *     someMethodThatCouldThrowAnything();
089   *   } catch (IKnowWhatToDoWithThisException e) {
090   *     handle(e);
091   *   } catch (Throwable t) {
092   *     Throwables.propagateIfPossible(t, OtherException.class);
093   *     throw new RuntimeException("unexpected", t);
094   *   }
095   * </pre>
096   *
097   * @param throwable the Throwable to possibly propagate
098   * @param declaredType the single checked exception type declared by the
099   *     calling method
100   */
101  public static <X extends Throwable> void propagateIfPossible(
102      @Nullable Throwable throwable, Class<X> declaredType) throws X {
103    propagateIfInstanceOf(throwable, declaredType);
104    propagateIfPossible(throwable);
105  }
106
107  /**
108   * Propagates {@code throwable} exactly as-is, if and only if it is an
109   * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1},
110   * or {@code declaredType2}.  In the unlikely case that you have three or more
111   * declared checked exception types, you can handle them all by invoking these
112   * methods repeatedly. See usage example in {@link
113   * #propagateIfPossible(Throwable, Class)}.
114   *
115   * @param throwable the Throwable to possibly propagate
116   * @param declaredType1 any checked exception type declared by the calling
117   *     method
118   * @param declaredType2 any other checked exception type declared by the
119   *     calling method
120   */
121  public static <X1 extends Throwable, X2 extends Throwable>
122      void propagateIfPossible(@Nullable Throwable throwable,
123          Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 {
124    checkNotNull(declaredType2);
125    propagateIfInstanceOf(throwable, declaredType1);
126    propagateIfPossible(throwable, declaredType2);
127  }
128
129  /**
130   * Propagates {@code throwable} as-is if it is an instance of
131   * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps
132   * it in a {@code RuntimeException} then propagates.
133   * <p>
134   * This method always throws an exception. The {@code RuntimeException} return
135   * type is only for client code to make Java type system happy in case a
136   * return value is required by the enclosing method. Example usage:
137   * <pre>
138   *   T doSomething() {
139   *     try {
140   *       return someMethodThatCouldThrowAnything();
141   *     } catch (IKnowWhatToDoWithThisException e) {
142   *       return handle(e);
143   *     } catch (Throwable t) {
144   *       throw Throwables.propagate(t);
145   *     }
146   *   }
147   * </pre>
148   *
149   * @param throwable the Throwable to propagate
150   * @return nothing will ever be returned; this return type is only for your
151   *     convenience, as illustrated in the example above
152   */
153  public static RuntimeException propagate(Throwable throwable) {
154    propagateIfPossible(checkNotNull(throwable));
155    throw new RuntimeException(throwable);
156  }
157
158  /**
159   * Returns the innermost cause of {@code throwable}. The first throwable in a
160   * chain provides context from when the error or exception was initially
161   * detected. Example usage:
162   * <pre>
163   *   assertEquals("Unable to assign a customer id",
164   *       Throwables.getRootCause(e).getMessage());
165   * </pre>
166   */
167  public static Throwable getRootCause(Throwable throwable) {
168    Throwable cause;
169    while ((cause = throwable.getCause()) != null) {
170      throwable = cause;
171    }
172    return throwable;
173  }
174
175  /**
176   * Gets a {@code Throwable} cause chain as a list.  The first entry in the
177   * list will be {@code throwable} followed by its cause hierarchy.  Note
178   * that this is a snapshot of the cause chain and will not reflect
179   * any subsequent changes to the cause chain.
180   *
181   * <p>Here's an example of how it can be used to find specific types
182   * of exceptions in the cause chain:
183   *
184   * <pre>
185   * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
186   * </pre>
187   *
188   * @param throwable the non-null {@code Throwable} to extract causes from
189   * @return an unmodifiable list containing the cause chain starting with
190   *     {@code throwable}
191   */
192  @Beta // TODO(kevinb): decide best return type
193  public static List<Throwable> getCausalChain(Throwable throwable) {
194    checkNotNull(throwable);
195    List<Throwable> causes = new ArrayList<Throwable>(4);
196    while (throwable != null) {
197      causes.add(throwable);
198      throwable = throwable.getCause();
199    }
200    return Collections.unmodifiableList(causes);
201  }
202
203  /**
204   * Returns a string containing the result of
205   * {@link Throwable#toString() toString()}, followed by the full, recursive
206   * stack trace of {@code throwable}. Note that you probably should not be
207   * parsing the resulting string; if you need programmatic access to the stack
208   * frames, you can call {@link Throwable#getStackTrace()}.
209   */
210  public static String getStackTraceAsString(Throwable throwable) {
211    StringWriter stringWriter = new StringWriter();
212    throwable.printStackTrace(new PrintWriter(stringWriter));
213    return stringWriter.toString();
214  }
215
216  /**
217   * Rethrows the cause exception of a given throwable, discarding the original
218   * throwable. Optionally, the stack frames of the cause and the outer
219   * exception are combined and the stack trace of the cause is set to this
220   * combined trace. If there is no cause the original exception is rethrown
221   * unchanged in all cases.
222   *
223   * @param exception the exception from which to extract the cause
224   * @param combineStackTraces if true the stack trace of the cause will be
225   *     replaced by the concatenation of the trace from the exception and the
226   *     trace from the cause.
227   */
228  @Beta
229  public static Exception throwCause(Exception exception,
230      boolean combineStackTraces) throws Exception {
231    Throwable cause = exception.getCause();
232    if (cause == null) {
233      throw exception;
234    }
235    if (combineStackTraces) {
236      StackTraceElement[] causeTrace = cause.getStackTrace();
237      StackTraceElement[] outerTrace = exception.getStackTrace();
238      StackTraceElement[] combined =
239          new StackTraceElement[causeTrace.length + outerTrace.length];
240      System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length);
241      System.arraycopy(outerTrace, 0, combined,
242          causeTrace.length, outerTrace.length);
243      cause.setStackTrace(combined);
244    }
245    if (cause instanceof Exception) {
246      throw (Exception) cause;
247    }
248    if (cause instanceof Error) {
249      throw (Error) cause;
250    }
251    // The cause is a weird kind of Throwable, so throw the outer exception
252    throw exception;
253  }
254}