001 /*
002 * Created on Dec 13, 2008
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005 * 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
010 * an "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 *
013 * Copyright @2008-2011-2010 the original author or authors.
014 */
015 package org.fest.util;
016
017 import static org.fest.util.Collections.list;
018
019 import java.util.*;
020
021 /**
022 * Utility methods related to <code>{@link Throwable}</code>s.
023 *
024 * @author Alex Ruiz
025 */
026 public final class Throwables {
027
028 /**
029 * Appends the stack trace of the current thread to the one in the given <code>{@link Throwable}</code>.
030 * @param t the given {@code Throwable}.
031 * @param methodToStartFrom the name of the method used as the starting point of the current thread's stack trace.
032 */
033 public static void appendCurrentThreadStackTraceToThrowable(Throwable t, String methodToStartFrom) {
034 List<StackTraceElement> stackTrace = list(t.getStackTrace());
035 stackTrace.addAll(currentThreadStackTrace(methodToStartFrom));
036 t.setStackTrace(stackTrace.toArray(new StackTraceElement[stackTrace.size()]));
037 }
038
039 /**
040 * Removes the Fest elements from the <code>{@link Throwable}</code> stack trace that have little value for end user.
041 * <pre>So instead of seeing this:
042
043 org.junit.ComparisonFailure: expected:<'[Ronaldo]'> but was:<'[Messi]'>
044 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
045 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
046 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
047 at java.lang.reflect.Constructor.newInstance(Constructor.java:501)
048 at org.fest.assertions.error.ConstructorInvoker.newInstance(ConstructorInvoker.java:34)
049 at org.fest.assertions.error.ShouldBeEqual.newComparisonFailure(ShouldBeEqual.java:111)
050 at org.fest.assertions.error.ShouldBeEqual.comparisonFailure(ShouldBeEqual.java:103)
051 at org.fest.assertions.error.ShouldBeEqual.newAssertionError(ShouldBeEqual.java:81)
052 at org.fest.assertions.internal.Failures.failure(Failures.java:76)
053 at org.fest.assertions.internal.Objects.assertEqual(Objects.java:116)
054 at org.fest.assertions.api.AbstractAssert.isEqualTo(AbstractAssert.java:74)
055 at examples.StackTraceFilterExample.main(StackTraceFilterExample.java:13)
056
057 We get this:
058
059 org.junit.ComparisonFailure: expected:<'[Ronaldo]'> but was:<'[Messi]'>
060 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
061 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
062 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
063 at examples.StackTraceFilterExample.main(StackTraceFilterExample.java:20)
064 * </pre>
065 * @param throwable the {@code Throwable} to filter stack trace.
066 */
067 public static void removeFestRelatedElementsFromStackTrace(Throwable throwable) {
068 List<StackTraceElement> filteredStackTrace = list(throwable.getStackTrace());
069 StackTraceElement previousStackTraceElement = null;
070 for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {
071 if (stackTraceElement.getClassName().contains("org.fest")) {
072 filteredStackTrace.remove(stackTraceElement);
073 // handle the case when Fest builds a ComparisonFailure by reflection (see ShouldBeEqual.newAssertionError
074 // method), the stack trace looks like :
075 // java.lang.reflect.Constructor.newInstance(Constructor.java:501),
076 // org.fest.assertions.error.ConstructorInvoker.newInstance(ConstructorInvoker.java:34),
077 // we want to get rid of java.lang.reflect.Constructor.newInstance element because it is related to Fest.
078 if (stackTraceElementClassNameIs(previousStackTraceElement, "java.lang.reflect.Constructor")
079 && stackTraceElement.getClassName().contains("org.fest.assertions.error.ConstructorInvoker")) {
080 filteredStackTrace.remove(previousStackTraceElement);
081 }
082 }
083 previousStackTraceElement = stackTraceElement;
084 }
085 throwable.setStackTrace(filteredStackTrace.toArray(new StackTraceElement[filteredStackTrace.size()]));
086 }
087
088 private static boolean stackTraceElementClassNameIs(StackTraceElement stackTraceElement, String className) {
089 if (stackTraceElement == null) return false;
090 return stackTraceElement.getClassName().equals(className);
091 }
092
093 private static List<StackTraceElement> currentThreadStackTrace(String methodToStartFrom) {
094 List<StackTraceElement> filtered = stackTraceInCurrentThread();
095 List<StackTraceElement> toRemove = new ArrayList<StackTraceElement>();
096 for (StackTraceElement e : filtered) {
097 if (methodToStartFrom.equals(e.getMethodName())) break;
098 toRemove.add(e);
099 }
100 filtered.removeAll(toRemove);
101 return filtered;
102 }
103
104 private static List<StackTraceElement> stackTraceInCurrentThread() {
105 return list(StackTraces.instance().stackTraceInCurrentThread());
106 }
107
108 private Throwables() {}
109 }