Skip to main content
summaryrefslogtreecommitdiffstats
blob: 096c0c73208df7b2666566f8ddfd9a5dc8b8b313 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*******************************************************************************
 * Copyright (c) 2004 - 2005 University Of British Columbia and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University Of British Columbia - initial API and implementation
 *******************************************************************************/
/*
 * Created on Dec 8, 2004
 */
package org.eclipse.mylar.tasks.bugzilla;

import java.util.ArrayList;
import java.util.List;

/**
 * Class to hold all of the information about a stack trace
 * 
 * @author Shawn Minto
 */
public class StackTrace {

    /** The length of the stack trace in the original string */
    private int length;

    /** The offset of the stack trace in the orignal string */
    private int offset;

    /** The string of the stack trace */
    private String stackTrace;
    
    /** This is the comment that the stack trace appeared in. String if desciption else Comment */
    private Object comment;

    /**
     * Constructor
     * 
     * @param stackTrace
     *            The stack trace string
     * @param offset
     *            The offset of the stack trace in the original string
     * @param length
     *            The length of the stack trace in the original string
     * @param comment
     *            The comment that the stack trace came from
     */
    public StackTrace(String stackTrace, int offset, int length, Object comment) {
        this.stackTrace = stackTrace;
        this.offset = offset;
        this.length = length;
        this.comment = comment;
    }

    /**
     * Get the offset for the stack trace
     * 
     * @return Returns the offset.
     */
    public int getOffset() {
        return offset;
    }

    /**
     * Get the stack trace for the bug
     * 
     * @return Returns the stackTrace.
     */
    public String getStackTrace() {
        return stackTrace;
    }

    /**
     * Get the length of the bug
     * 
     * @return Returns the length.
     */
    public int getLength() {
        return length;
    }
    
    /**
     * Get the Comment that this stack trace came from 
     * 
     * @return Returns the Comment if it was a comment else a String if it was the description
     */
    public Object getComment() {
        return comment;
    }
    
    /**
     * Find a standard java stack trace in the given string
     * 
     * 
     * @param s
     *            The string to search for stack traces
     * @param comment
     * 			  The comment that the text came from.<br>
     *            Comment if a comment else a String 
     * @return String[] of stack traces - each element is 1 trace
     */
    public static StackTrace[] getStackTrace(String s, Object comment) {

        // setup the regex used to determine if it looks like we are at a
        // stack trace and whether it is something that should be skipped
        String regexExceptionType = "^(.*\\.)+.+(Exception|Error|Throwable).*";
        String regexSkip = ".*\\.\\..*";

        // get all of the individual lines for the string
        String[] lines = s.split("\r\n");

        // the character start of the current stack trace
        int charStackStart = 0;

        // the current character in the string - used for the start and the
        // offset
        int[] charPos = { 0 }; // array so pass by reference

        boolean inStackTrace = false;
        List<String> stackTrace = null;
        List<StackTrace> stackTraces = new ArrayList<StackTrace>();

        // go through each of the lines of the string
        for (int i = 0; i < lines.length; i++) {

            if (lines[i].matches(regexSkip)){
                
                // update the current character position
                charPos[0] += lines[i].length() + 2;
                
            }else if (lines[i].trim().matches(regexExceptionType) && !inStackTrace) {

                // we have matched the stack trace and we are not already in one

                // add the old stack trace to the list of stack traces
                if (stackTrace != null && stackTrace.size() > 1) {
                    stackTraces.add(getStackTrace(stackTrace, charStackStart,
                            charPos[0] - charStackStart, comment));
                }

                // prepare for a new stack trace
                stackTrace = new ArrayList<String>();
                inStackTrace = true;

                // the current line is the start of our stack trace
                stackTrace.add(lines[i]);
                charStackStart = charPos[0];
                charPos[0] += lines[i].length() + 2;
            } else if (inStackTrace) {
                // we are in a stack trace

                int[] pos = { i }; // array so pass by reference

                // get the next at clause of the stack trace
                String stack = getNextAt(lines, pos, charPos);

                // check if there was an at
                if (stack == null) {

                    // there wasn't so we are done this stack trace
                    inStackTrace = false;
                    if (stackTrace != null && stackTrace.size() > 1) {
                        stackTraces.add(getStackTrace(stackTrace,
                                charStackStart, charPos[0] - charStackStart, comment));
                    }
                    stackTrace = null;
                } else {

                    // we had one, so add it to this stack trace
                    stackTrace.add(stack);
                }

                // update the position
                i = pos[0];
            } else {
                // update the current character position
                charPos[0] += lines[i].length() + 2;
            }
        }

        // make sure to add the stack trace if it was the last in the string
        if (stackTrace != null && stackTrace.size() > 1) {
            stackTraces.add(getStackTrace(stackTrace, charStackStart,
                    charPos[0] - charStackStart, comment));
        }

        if (stackTraces.size() == 0)
            return null;

        // get the string values of the stack traces and return it
        return getTracesFromList(stackTraces);
    }

    /**
     * Get the next at clause from a potential stack trace -- looks ahead 4
     * lines
     * 
     * @param lines
     *            The array of all of the lines in the bug
     * @param i
     *            The current position to start at 
     * @param charPos
     *            The current character position in the original string 
     * @return The next at clause, or <code>null</code><br>
     *         If an at line is matched, but the end isn't within the 4 lines,
     *         only the first line is returned. Also, charPos is updated as well
     *         as i
     */
    private static String getNextAt(String[] lines, int[] i, int[] charPos) {
        String regexAtString = "^at.*";
        String regexEndString = ".*:\\d+\\)$";
        int index = i[0];
        String l1, l2, l3, l4;
        l1 = l2 = l3 = l4 = null;
        String res = null;

        // get the first line to look at
        if (lines.length > index) {
            l1 = lines[index];
        } else {
            // if the first line doesn't exist, we are done and should
            // return
            return null;
        }

        // get the next 3 lines
        if (lines.length > index + 1) {
            l2 = lines[index + 1];
        }
        if (lines.length > index + 2) {
            l3 = lines[index + 2];
        }
        if (lines.length > index + 3) {
            l4 = lines[index + 3];
        }

        // make sure that the first line is the start of an at
        // if not, return null
        if (l1.trim().matches(regexAtString)) {
            charPos[0] += l1.length() + 2;
            res = l1;
        } else
            return null;

        // now determine where the end is if it wasn't on 1 line
        if (!res.trim().matches(regexEndString)) {

            if (l2 != null && l2.trim().matches(regexEndString)) {

                // it was on the second line
                // update the current position and the result string
                i[0] = index + 1;
                charPos[0] += l2.length() + 2;
                res += l2.trim();
            } else if (l3 != null && l3.trim().matches(regexEndString)) {

                // it was on the third line
                // update the current position and the result string
                i[0] = index + 2;
                charPos[0] += l2.length() + l3.length() + 4;
                res += l2.trim();
                res += l3.trim();
            } else if (l4 != null && l4.trim().matches(regexEndString)) {

                // it was on the fourth line
                // update the current position and the result string
                i[0] = index + 3;
                charPos[0] += l2.length() + l3.length() + l4.length() + 6;
                res += l2.trim();
                res += l3.trim();
                res += l4.trim();
            }
        }

        // return the result
        return res;
    }

    /**
     * Get the StackTrace
     * 
     * @param l
     *            the list of lines that contain the trace
     * @param start
     *            the start of the stack trace
     * @param offset
     *            the offset of the stack trace
     * @param comment
     *            The comment that the stack trace came from
     * @return The StackTrace for the given data
     */
    private static StackTrace getStackTrace(List<String> l, int offset, int length, Object comment) {
        String s = "";
        for(String s2 : l) {
            s += s2 + "\r\n";
        }

        return new StackTrace(s, offset, length, comment);
    }

    /**
     * Convert a List StackTraces to a StackTrace[] <br>
     * 
     * @param l
     *            The List of StackTraces
     * @return StackTrace[] of the List
     */
    private static StackTrace[] getTracesFromList(List<StackTrace> l) {

        // make sure that there is something to convert, else return null
        if (l == null || l.size() == 0)
            return null;

        // convert the list of strings to an array of strings
        int i = 0;
        StackTrace[] s = new StackTrace[l.size()];

        for(StackTrace st : l) {
            s[i] = st;
            i++;
        }

        // return the string array
        return s;
    }
    
    /**
     * Escape all of the special regex characters from the string
     * 
     * @param s
     *            The string to escape the characters for
     * @return A string with all of the special characters escaped <br>
     *         <code>
     * 				. => \.<br>
     * 				$ => \$<br>
     * 				? => \?<br>
     * 				{ => \{<br>
     * 				} => \}<br>
     * 				( => \(<br>
     * 				) => \)<br>
     * 				[ => \[<br>
     * 				] => \]<br>
     * 				+ => \+<br>
     * 				* => \*<br>
     * 				| => \|<br>
     * 				^ => \^<br>
     * 				\ => \\<br>
     * 				/ => \/<br>
     * 			</code>
     */
    public static String escapeForRegex(String s) {
        String sFixed = s;

        // replace all special regex characters
        sFixed = sFixed.replaceAll("\\\\", "\\\\\\\\");
        sFixed = sFixed.replaceAll("\\$", "\\\\\\$");
        sFixed = sFixed.replaceAll("\\.", "\\\\.");
        sFixed = sFixed.replaceAll("\\?", "\\\\?");
        sFixed = sFixed.replaceAll("\\{", "\\\\{");
        sFixed = sFixed.replaceAll("\\}", "\\\\}");
        sFixed = sFixed.replaceAll("\\(", "\\\\(");
        sFixed = sFixed.replaceAll("\\)", "\\\\)");
        sFixed = sFixed.replaceAll("\\[", "\\\\[");
        sFixed = sFixed.replaceAll("\\]", "\\\\]");
        sFixed = sFixed.replaceAll("\\+", "\\\\+");
        sFixed = sFixed.replaceAll("\\*", "\\\\*");
        sFixed = sFixed.replaceAll("\\|", "\\\\|");
        sFixed = sFixed.replaceAll("\\^", "\\\\^");
        sFixed = sFixed.replaceAll("\\/", "\\\\/");

        return sFixed;
    }
}

Back to the top