001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.common.internal;
014
015import java.io.*;
016
017import org.apache.juneau.common.utils.*;
018
019/**
020 * Various utility methods for creating and working with throwables.
021 */
022public class ThrowableUtils {
023
024   /**
025    * Creates a new {@link RuntimeException}.
026    *
027    * @param cause The caused-by exception.
028    * @return A new {@link RuntimeException}, or the same exception if it's already of that type.
029    */
030   public static RuntimeException asRuntimeException(Throwable cause) {
031      return cast(RuntimeException.class, cause);
032   }
033
034   /**
035    * Casts or wraps the specified throwable to the specified type.
036    *
037    * @param <T> The class to cast to.
038    * @param type The class to cast to.
039    * @param t The throwable to cast.
040    * @return Either the same exception if it's already the specified type, or a wrapped exception.
041    */
042   public static <T> T cast(Class<T> type, Throwable t) {
043      try {
044         return type.isInstance(t) ? type.cast(t) : type.getConstructor(Throwable.class).newInstance(t);
045      } catch (Exception e) {
046         throw new IllegalArgumentException(e);
047      }
048   }
049
050   /**
051    * Same as {@link Throwable#getCause()} but searches the throwable chain for an exception of the specified type.
052    *
053    * @param c The throwable type to search for.
054    * @param <T> The throwable type to search for.
055    * @param t The throwable to search.
056    * @return The exception, or <jk>null</jk> if not found.
057    */
058   public static <T extends Throwable> T getCause(Class<T> c, Throwable t) {
059      while (t != null) {
060         t = t.getCause();
061         if (c.isInstance(t))
062            return c.cast(t);
063      }
064      return null;
065   }
066
067   /**
068    * Allows you to wrap a supplier that throws an exception so that it can be used in a fluent interface.
069    *
070    * @param <T> The supplier type.
071    * @param supplier The supplier throwing an exception.
072    * @return The supplied result.
073    * @throws RuntimeException if supplier threw an exception.
074    */
075   public static <T> T safeSupplier(SupplierWithThrowable<T> supplier) {
076      try {
077         return supplier.get();
078      } catch (RuntimeException t) {
079         throw t;
080      } catch (Throwable t) {
081         throw asRuntimeException(t);
082      }
083   }
084
085   /**
086    * Interface used with {@link #safeSupplier(SupplierWithThrowable)}.
087    */
088   @SuppressWarnings("javadoc")
089   @FunctionalInterface
090   public interface SupplierWithThrowable<T> {
091      T get() throws Throwable;
092   }
093
094   /**
095    * Runs a snippet of code and encapsulates any throwable inside a {@link RuntimeException}.
096    *
097    * @param snippet The snippet of code to run.
098    */
099   public static void safeRun(Snippet snippet) {
100      try {
101         snippet.run();
102      } catch (RuntimeException t) {
103         throw t;
104      } catch (Throwable t) {
105         throw asRuntimeException(t);
106      }
107   }
108
109   /**
110    * Convenience method for getting a stack trace as a string.
111    *
112    * @param t The throwable to get the stack trace from.
113    * @return The same content that would normally be rendered via <c>t.printStackTrace()</c>
114    */
115   public static String getStackTrace(Throwable t) {
116      StringWriter sw = new StringWriter();
117      try (PrintWriter pw = new PrintWriter(sw)) {
118         t.printStackTrace(pw);
119      }
120      return sw.toString();
121   }
122
123   /**
124    * Calculates a 16-bit hash for the specified throwable based on it's stack trace.
125    *
126    * @param t The throwable to calculate the stack trace on.
127    * @param stopClass Optional stop class on which to stop calculation of a stack trace beyond when found.
128    * @return A calculated hash.
129    */
130   public static int hash(Throwable t, String stopClass) {
131      int i = 0;
132      while (t != null) {
133         for (StackTraceElement e : t.getStackTrace()) {
134            if (e.getClassName().equals(stopClass))
135               break;
136            if (e.getClassName().indexOf('$') == -1)
137               i ^= e.hashCode();
138         }
139         t = t.getCause();
140      }
141      return i;
142   }
143}