blob: 3b7379850d54e3386e296fef29c557487756840a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.bugzilla.ui.tasklist;
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 final int length;
/** The offset of the stack trace in the orignal string */
private final int offset;
/** The string of the stack trace */
private final String stackTrace;
/**
* This is the comment that the stack trace appeared in. String if desciption else Comment
*/
private final 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 summary
*/
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).*"; //$NON-NLS-1$
String regexSkip = ".*\\.\\..*"; //$NON-NLS-1$
// get all of the individual lines for the string
String[] lines = s.split("\r\n|\n"); //$NON-NLS-1$
// 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 if (stackTrace != null) {
// 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.*"; //$NON-NLS-1$
String regexEndString = ".*:\\d+\\)$"; //$NON-NLS-1$
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 (l2 != null && 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 (l2 != null && l3 != null && 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 = ""; //$NON-NLS-1$
for (String s2 : l) {
s += s2 + "\r\n"; //$NON-NLS-1$
}
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("\\\\", "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\$", "\\\\\\$"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\.", "\\\\."); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\?", "\\\\?"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\{", "\\\\{"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\}", "\\\\}"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\(", "\\\\("); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\)", "\\\\)"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\[", "\\\\["); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\]", "\\\\]"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\+", "\\\\+"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\*", "\\\\*"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\|", "\\\\|"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\^", "\\\\^"); //$NON-NLS-1$ //$NON-NLS-2$
sFixed = sFixed.replaceAll("\\/", "\\\\/"); //$NON-NLS-1$ //$NON-NLS-2$
return sFixed;
}
}