View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.util;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   
9   import java.io.IOException;
10  
11  /**
12   * Provides utility methods to extract and filter stack trace information.
13   */
14  public final class StackTrace {
15      @NonNull
16      private final Throwable throwable;
17      @NonNull
18      private StackTraceElement[] elements;
19  
20      public StackTrace(@NonNull Throwable throwable) {
21          this.throwable = throwable;
22          elements = throwable.getStackTrace();
23      }
24  
25      public int getDepth() {
26          return elements.length;
27      }
28  
29      @NonNull
30      public StackTraceElement getElement(int index) {
31          return elements[index];
32      }
33  
34      public static void filterStackTrace(@NonNull Throwable t) {
35          new StackTrace(t).filter();
36      }
37  
38      public void filter() {
39          StackTraceElement[] filteredST = new StackTraceElement[elements.length];
40          int i = 0;
41  
42          for (StackTraceElement ste : elements) {
43              if (ste.getFileName() != null) {
44                  String where = ste.getClassName();
45  
46                  if (!isJDKOr3rdPartyLibraryInternalMethod(ste) && !isTestFrameworkMethod(where)
47                          && !isJMockitMethod(where)) {
48                      filteredST[i] = ste;
49                      i++;
50                  }
51              }
52          }
53  
54          StackTraceElement[] newStackTrace = new StackTraceElement[i];
55          System.arraycopy(filteredST, 0, newStackTrace, 0, i);
56          throwable.setStackTrace(newStackTrace);
57          elements = newStackTrace;
58  
59          Throwable cause = throwable.getCause();
60  
61          if (cause != null) {
62              new StackTrace(cause).filter();
63          }
64      }
65  
66      private static boolean isJDKOr3rdPartyLibraryInternalMethod(@NonNull StackTraceElement ste) {
67          String className = ste.getClassName();
68  
69          return className.startsWith("sun.") && !ste.isNativeMethod() || className.startsWith("jdk.")
70                  || className.startsWith("java.util.") || className.contains(".reflect.")
71                  || className.contains(".surefire.") || className.contains(".gradle.")
72                  || className.contains(".intellij.") || className.contains(".jdt.");
73      }
74  
75      private static boolean isTestFrameworkMethod(@NonNull String where) {
76          return where.startsWith("org.junit.") || where.startsWith("org.testng.");
77      }
78  
79      private static boolean isJMockitMethod(@NonNull String where) {
80          if (!where.startsWith("mockit.")) {
81              return false;
82          }
83  
84          int p = where.indexOf('$');
85  
86          if (p < 0) {
87              int q = where.lastIndexOf("Test");
88              return q < 0 || q + 4 < where.length();
89          }
90  
91          int q = where.lastIndexOf("Test", p - 4);
92  
93          if (q < 0) {
94              return true;
95          }
96  
97          q += 4;
98          return q < where.length() && where.charAt(q) != '$';
99      }
100 
101     public void print(@NonNull Appendable output) {
102         String previousFileName = null;
103         int previousLineNumber = 0;
104         String sep = "";
105 
106         for (int i = 0, d = getDepth(); i < d; i++) {
107             StackTraceElement ste = elements[i];
108 
109             if (ste.getLineNumber() != previousLineNumber || !ste.getFileName().equals(previousFileName)) {
110                 try {
111                     output.append(sep).append("\tat ").append(ste.toString());
112                 } catch (IOException ignore) {
113                 }
114                 sep = "\n";
115                 previousFileName = ste.getFileName();
116                 previousLineNumber = ste.getLineNumber();
117             }
118         }
119     }
120 }