7
7
import java .io .StringWriter ;
8
8
import java .util .AbstractList ;
9
9
import java .util .ArrayList ;
10
+ import java .util .Arrays ;
10
11
import java .util .Collections ;
11
12
import java .util .List ;
12
13
13
14
/**
14
15
* Utility class for working with stack traces.
15
16
*/
16
17
public class StackTraces {
18
+
17
19
private StackTraces () {
18
20
}
19
21
@@ -24,34 +26,64 @@ private StackTraces() {
24
26
* @return a trimmed stack trace, or the original trace if trimming wasn't possible
25
27
*/
26
28
public static String getTrimmedStackTrace (Throwable exception ) {
27
- String fullTrace = getFullStackTrace (exception );
28
- BufferedReader reader = new BufferedReader (
29
- new StringReader (fullTrace .substring (exception .toString ().length ())));
29
+ StringBuilder result = new StringBuilder (exception .toString ());
30
+ List <String > trimmedStackTraceLines = getTrimmedStackTraceLines (exception );
31
+ if (trimmedStackTraceLines .isEmpty ()) {
32
+ return getFullStackTrace (exception );
33
+ }
30
34
31
- try {
32
- // Collect the stack trace lines for "exception" (but not the cause).
33
- List <String > stackTraceLines = new ArrayList <String >();
34
- List <String > causedByLines = new ArrayList <String >();
35
- collectStackTraceLines (reader , stackTraceLines , causedByLines );
35
+ appendStackTraceLines (trimmedStackTraceLines , result );
36
+ appendStackTraceLines (getCauseStackTraceLines (exception ), result );
37
+ return result .toString ();
38
+ }
36
39
37
- if (stackTraceLines .isEmpty ()) {
38
- // No stack trace?
39
- return fullTrace ;
40
- }
41
- boolean hasCause = !causedByLines .isEmpty ();
42
- stackTraceLines = trimStackTraceLines (stackTraceLines , hasCause );
43
- if (stackTraceLines .isEmpty ()) {
44
- // Could not trim stack trace lines.
45
- return fullTrace ;
40
+ private static List <String > getTrimmedStackTraceLines (Throwable exception ) {
41
+ List <StackTraceElement > stackTraceElements = Arrays .asList (exception .getStackTrace ());
42
+ int linesToInclude = stackTraceElements .size ();
43
+
44
+ State state = State .PROCESSING_OTHER_CODE ;
45
+ for (StackTraceElement stackTraceElement : asReversedList (stackTraceElements )) {
46
+ state = state .processStackTraceElement (stackTraceElement );
47
+ if (state == State .DONE ) {
48
+ List <String > trimmedLines = new ArrayList <String >(linesToInclude + 2 );
49
+ trimmedLines .add ("" );
50
+ for (StackTraceElement each : stackTraceElements .subList (0 , linesToInclude )) {
51
+ trimmedLines .add ("\t at " + each );
52
+ }
53
+ if (exception .getCause () != null ) {
54
+ trimmedLines .add ("\t ... " + (stackTraceElements .size () - trimmedLines .size ()) + " trimmed" );
55
+ }
56
+ return trimmedLines ;
46
57
}
58
+ linesToInclude --;
59
+ }
60
+ return Collections .emptyList ();
61
+ }
47
62
48
- StringBuilder trimmedTrace = new StringBuilder (exception .toString ());
49
- appendStackTraceLines (stackTraceLines , trimmedTrace );
50
- appendStackTraceLines (causedByLines , trimmedTrace );
51
- return trimmedTrace .toString ();
52
- } catch (IOException e ) {
63
+ private static List <String > getCauseStackTraceLines (Throwable exception ) {
64
+ if (exception .getCause () != null ) {
65
+ String fullTrace = getFullStackTrace (exception );
66
+ BufferedReader reader = new BufferedReader (
67
+ new StringReader (fullTrace .substring (exception .toString ().length ())));
68
+ List <String > causedByLines = new ArrayList <String >();
69
+
70
+ try {
71
+ String line ;
72
+ while ((line = reader .readLine ()) != null ) {
73
+ if (line .startsWith ("Caused by: " )) {
74
+ causedByLines .add (line );
75
+ while ((line = reader .readLine ()) != null ) {
76
+ causedByLines .add (line );
77
+ }
78
+ return causedByLines ;
79
+ }
80
+ }
81
+ } catch (IOException e ) {
82
+ // We should never get here, because we are reading from a StringReader
83
+ }
53
84
}
54
- return fullTrace ;
85
+
86
+ return Collections .emptyList ();
55
87
}
56
88
57
89
private static String getFullStackTrace (Throwable exception ) {
@@ -61,51 +93,13 @@ private static String getFullStackTrace(Throwable exception) {
61
93
return stringWriter .toString ();
62
94
}
63
95
64
- private static void collectStackTraceLines (
65
- BufferedReader reader ,
66
- List <String > stackTraceLines ,
67
- List <String > remainingLines ) throws IOException {
68
- String line ;
69
- while ((line = reader .readLine ()) != null ) {
70
- if (line .startsWith ("Caused by: " )) {
71
- remainingLines .add (line );
72
- while ((line = reader .readLine ()) != null ) {
73
- remainingLines .add (line );
74
- }
75
- return ;
76
- }
77
- stackTraceLines .add (line );
78
- }
79
- }
80
-
81
96
private static void appendStackTraceLines (
82
97
List <String > stackTraceLines , StringBuilder destBuilder ) {
83
98
for (String stackTraceLine : stackTraceLines ) {
84
99
destBuilder .append (String .format ("%s%n" , stackTraceLine ));
85
100
}
86
101
}
87
102
88
- private static List <String > trimStackTraceLines (
89
- List <String > stackTraceLines , boolean hasCause ) {
90
- State state = State .PROCESSING_OTHER_CODE ;
91
- int linesToInclude = stackTraceLines .size ();
92
- for (String stackTraceLine : asReversedList (stackTraceLines )) {
93
- state = state .processLine (stackTraceLine );
94
- if (state == State .DONE ) {
95
- List <String > trimmedLines = stackTraceLines .subList (0 , linesToInclude );
96
- if (!hasCause ) {
97
- return trimmedLines ;
98
- }
99
- List <String > copy = new ArrayList <String >(trimmedLines .size () + 1 );
100
- copy .addAll (trimmedLines );
101
- copy .add ("\t ... " + (stackTraceLines .size () - trimmedLines .size ()) + " trimmed" );
102
- return copy ;
103
- }
104
- linesToInclude --;
105
- }
106
- return Collections .emptyList ();
107
- }
108
-
109
103
private static <T > List <T > asReversedList (final List <T > list ) {
110
104
return new AbstractList <T >() {
111
105
@@ -123,42 +117,47 @@ public int size() {
123
117
124
118
private enum State {
125
119
PROCESSING_OTHER_CODE {
126
- @ Override public State processLine (String line ) {
127
- if (isTestFrameworkStackTraceLine ( line )) {
120
+ @ Override public State processLine (String methodName ) {
121
+ if (isTestFrameworkMethod ( methodName )) {
128
122
return PROCESSING_TEST_FRAMEWORK_CODE ;
129
123
}
130
124
return this ;
131
125
}
132
126
},
133
127
PROCESSING_TEST_FRAMEWORK_CODE {
134
- @ Override public State processLine (String line ) {
135
- if (isReflectionStackTraceLine ( line )) {
128
+ @ Override public State processLine (String methodName ) {
129
+ if (isReflectionMethod ( methodName )) {
136
130
return PROCESSING_REFLECTION_CODE ;
137
- } else if (isTestFrameworkStackTraceLine ( line )) {
131
+ } else if (isTestFrameworkMethod ( methodName )) {
138
132
return this ;
139
133
}
140
134
return PROCESSING_OTHER_CODE ;
141
135
}
142
136
},
143
137
PROCESSING_REFLECTION_CODE {
144
- @ Override public State processLine (String line ) {
145
- if (isReflectionStackTraceLine ( line )) {
138
+ @ Override public State processLine (String methodName ) {
139
+ if (isReflectionMethod ( methodName )) {
146
140
return this ;
147
- } else if (isTestFrameworkStackTraceLine ( line )) {
141
+ } else if (isTestFrameworkMethod ( methodName )) {
148
142
// This is here to handle TestCase.runBare() calling TestCase.runTest().
149
143
return PROCESSING_TEST_FRAMEWORK_CODE ;
150
144
}
151
145
return DONE ;
152
146
}
153
147
},
154
148
DONE {
155
- @ Override public State processLine (String line ) {
149
+ @ Override public State processLine (String methodName ) {
156
150
return this ;
157
151
}
158
152
};
159
153
160
- /** Processes a stack trace line, possibly moving to a new state. */
161
- public abstract State processLine (String line );
154
+ /** Processes a stack trace element method name, possibly moving to a new state. */
155
+ protected abstract State processLine (String methodName );
156
+
157
+ /** Processes a stack trace element, possibly moving to a new state. */
158
+ public final State processStackTraceElement (StackTraceElement element ) {
159
+ return processLine (element .getClassName () + "." + element .getMethodName () + "()" );
160
+ }
162
161
}
163
162
164
163
private static final String [] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = {
@@ -173,9 +172,9 @@ private enum State {
173
172
"org.junit.internal.StackTracesTest" ,
174
173
};
175
174
176
- private static boolean isTestFrameworkStackTraceLine (String line ) {
177
- return isMatchingStackTraceLine ( line , TEST_FRAMEWORK_METHOD_NAME_PREFIXES ) &&
178
- !isMatchingStackTraceLine ( line , TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES );
175
+ private static boolean isTestFrameworkMethod (String methodName ) {
176
+ return isMatchingMethod ( methodName , TEST_FRAMEWORK_METHOD_NAME_PREFIXES ) &&
177
+ !isMatchingMethod ( methodName , TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES );
179
178
}
180
179
181
180
private static final String [] REFLECTION_METHOD_NAME_PREFIXES = {
@@ -187,17 +186,13 @@ private static boolean isTestFrameworkStackTraceLine(String line) {
187
186
"junit.framework.TestCase.runBare(" , // runBare() directly calls setUp() and tearDown()
188
187
};
189
188
190
- private static boolean isReflectionStackTraceLine (String line ) {
191
- return isMatchingStackTraceLine ( line , REFLECTION_METHOD_NAME_PREFIXES );
189
+ private static boolean isReflectionMethod (String methodName ) {
190
+ return isMatchingMethod ( methodName , REFLECTION_METHOD_NAME_PREFIXES );
192
191
}
193
192
194
- private static boolean isMatchingStackTraceLine (String line , String [] methodNamePrefixes ) {
195
- if (!line .startsWith ("\t at " )) {
196
- return false ;
197
- }
198
- line = line .substring (4 );
193
+ private static boolean isMatchingMethod (String methodName , String [] methodNamePrefixes ) {
199
194
for (String methodNamePrefix : methodNamePrefixes ) {
200
- if (line .startsWith (methodNamePrefix )) {
195
+ if (methodName .startsWith (methodNamePrefix )) {
201
196
return true ;
202
197
}
203
198
}
0 commit comments