blob: f0874b6b909b7b6d3be78298ebc76620de578ef5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 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
*
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.java.ui;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.mylyn.context.core.IInteractionContext;
/**
* A strategy that computes Java items based on a Java stack trace in the task description.
*
* @author David Green
*/
public class JavaStackTraceContextComputationStrategy extends AbstractJavaContextComputationStrategy {
private static final String PACKAGE_PART = "([a-z][a-z0-9]*)"; //$NON-NLS-1$
private static final String CLASS_PART = "[A-Za-z][a-zA-Z0-9_$]*"; //$NON-NLS-1$
private static final String FQN_PART = "((" + PACKAGE_PART + "\\.)+" + CLASS_PART + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private static final Pattern STACK_TRACE_PATTERN = Pattern.compile("\\s*((" + "((Caused by:\\s+)|(at\\s+))?" //$NON-NLS-1$//$NON-NLS-2$
+ FQN_PART + "((:\\s+\\w.*)|(\\.((\\<(?:cl)?init\\>)|([a-zA-Z0-9_$]+))\\(.*?\\)))?" //$NON-NLS-1$
+ ")|(\\.\\.\\.\\s\\d+\\smore))"); //$NON-NLS-1$
private final SortedSet<String> filteredPrefixes = new TreeSet<String>(Arrays.asList(new String[] { "java", //$NON-NLS-1$
"javax", "junit.framework", "sun.reflect" })); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
/**
* public for testing only
*/
public static class Element {
String fqn;
String methodName;
public Element(String fqn, String methodName) {
Assert.isNotNull(fqn);
this.fqn = fqn;
this.methodName = methodName;
}
public Element() {
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Element [fqn="); //$NON-NLS-1$
builder.append(fqn);
builder.append(", methodName="); //$NON-NLS-1$
builder.append(methodName);
builder.append("]"); //$NON-NLS-1$
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fqn == null) ? 0 : fqn.hashCode());
result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Element other = (Element) obj;
if (fqn == null) {
if (other.fqn != null) {
return false;
}
} else if (!fqn.equals(other.fqn)) {
return false;
}
if (methodName == null) {
if (other.methodName != null) {
return false;
}
} else if (!methodName.equals(other.methodName)) {
return false;
}
return true;
}
}
private final int maxElements = 10;
@Override
public List<Object> computeContext(IInteractionContext context, IAdaptable input, IProgressMonitor monitor) {
String text = getText(input);
if (text != null) {
return computeContext(text, monitor);
}
return Collections.emptyList();
}
private String getText(IAdaptable input) {
String text = (String) input.getAdapter(String.class);
if (text != null) {
return text;
}
return null;
}
public List<Object> computeContext(String description, IProgressMonitor monitor) {
SubMonitor progress = SubMonitor.convert(monitor);
try {
List<Element> elements = computeElements(description);
if (!elements.isEmpty()) {
progress.beginTask(
Messages.JavaStackTraceContextComputationStrategy_Finding_Java_Context_Element_Progress_Label,
elements.size());
final List<Object> javaElements = new ArrayList<Object>();
try {
for (Element element : elements) {
if (progress.isCanceled()) {
break;
}
SortedSet<String> prefix = filteredPrefixes.headSet(element.fqn);
if (prefix.isEmpty() || !element.fqn.startsWith(prefix.last())) {
try {
IType type = findTypeInWorkspace(element.fqn);
if (type != null) {
javaElements.add(type);
if (element.methodName != null) {
IMethod[] methods = type.getMethods();
for (IMethod method : methods) {
if (method.getElementName().equals(element.methodName)) {
javaElements.add(method);
}
}
}
}
} catch (CoreException e) {
JavaUiBridgePlugin.getDefault().getLog().log(e.getStatus());
}
}
progress.worked(1);
}
} finally {
progress.done();
}
return javaElements;
}
} catch (IOException e) {
// ignore
}
return Collections.emptyList();
}
/**
* Public for test purposes only
*/
public List<Element> computeElements(String description) throws IOException {
List<Element> elements = new ArrayList<Element>();
BufferedReader reader = new BufferedReader(new StringReader(description));
for (String line = reader.readLine(); line != null && elements.size() < maxElements; line = reader.readLine()) {
Matcher matcher = STACK_TRACE_PATTERN.matcher(line);
if (matcher.matches()) {
String fqn = matcher.group(6);
if (fqn != null) {
Element element = new Element(fqn, matcher.group(12));
elements.add(element);
}
}
}
return elements;
}
}