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    
017    package com.google.common.base;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.Beta;
022    
023    import java.io.PrintWriter;
024    import java.io.StringWriter;
025    import java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.List;
028    
029    import 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     */
038    public 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    }