/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.util; import java.io.*; import javax.annotation.*; /** * Provides utility methods to extract and filter stack trace information. */ public final class StackTrace { @Nonnull private final Throwable throwable; @Nonnull private StackTraceElement[] elements; public StackTrace(@Nonnull Throwable throwable) { this.throwable = throwable; elements = throwable.getStackTrace(); } public int getDepth() { return elements.length; } @Nonnull public StackTraceElement getElement(int index) { return elements[index]; } public static void filterStackTrace(@Nonnull Throwable t) { new StackTrace(t).filter(); } public void filter() { StackTraceElement[] filteredST = new StackTraceElement[elements.length]; int i = 0; for (StackTraceElement ste : elements) { if (ste.getFileName() != null) { String where = ste.getClassName(); if (!isJDKInternalMethod(ste) && !isTestFrameworkMethod(where) && !isJMockitMethod(where)) { filteredST[i] = ste; i++; } } } StackTraceElement[] newStackTrace = new StackTraceElement[i]; System.arraycopy(filteredST, 0, newStackTrace, 0, i); throwable.setStackTrace(newStackTrace); elements = newStackTrace; Throwable cause = throwable.getCause(); if (cause != null) { new StackTrace(cause).filter(); } } private static boolean isJDKInternalMethod(@Nonnull StackTraceElement ste) { String className = ste.getClassName(); return className.startsWith("sun.") && !ste.isNativeMethod() || className.startsWith("jdk.") || className.contains(".reflect.") || className.contains(".surefire.") || className.contains(".intellij.") || className.contains(".jdt."); } private static boolean isTestFrameworkMethod(@Nonnull String where) { return where.startsWith("org.junit.") || where.startsWith("org.testng."); } private static boolean isJMockitMethod(@Nonnull String where) { if (!where.startsWith("mockit.")) { return false; } int p = where.indexOf('$'); if (p < 0) { int q = where.lastIndexOf("Test"); return q < 0 || q + 4 < where.length(); } int q = where.lastIndexOf("Test", p - 4); if (q < 0) { return true; } q += 4; return q < where.length() && where.charAt(q) != '$'; } public void print(@Nonnull Appendable output) { String previousFileName = null; int previousLineNumber = 0; String sep = ""; for (int i = 0, d = getDepth(); i < d; i++) { StackTraceElement ste = elements[i]; if (ste.getLineNumber() != previousLineNumber || !ste.getFileName().equals(previousFileName)) { try { output.append(sep).append("\tat ").append(ste.toString()); } catch (IOException ignore) {} sep = "\n"; previousFileName = ste.getFileName(); previousLineNumber = ste.getLineNumber(); } } } }